Technology

It doesn't matter who flooded RubyGems with malicious packages. It matters what your build trusts

A package registry was flooded with 2,000+ packages. Here is how malicious packages reach your build, and what a small team should lock down this week.

James Hitch
James Hitch· COO
Published Sep 14, 2026
23 min read
It doesn't matter who flooded RubyGems with malicious packages. It matters what your build trusts

When developers build software, they rarely write every piece of code themselves. They download pieces of code that other people have already written. These pieces are called packages or dependencies.

A package is basically a ready-made piece of software that a developer can add to their own application instead of writing that code from scratch. Think of it like buying a pre-made ingredient instead of making the ingredient yourself.

For example, instead of writing their own code for handling passwords, dates or payments, a developer might install a package that already does it.

The problem is that you are putting someone else's code into your application. If that package is malicious, your application could be exposed to an attack.

You do not need to know who created a malicious package to protect yourself from it. You need to control what your software is allowed to install and execute.

In May 2026, RubyGems paused new account registrations after a wave of malicious packages was uploaded to the repository. In September, researchers attributed the campaign to OpenAI agents, while RubyGems said it could not determine who was responsible.

That disagreement does not change what developers need to do. Your build system cannot rely on knowing who is behind an attack. Package managers can execute package code during installation, which means a malicious dependency can become part of your software supply chain before your application even runs.

The practical response is to reduce what your build is allowed to trust. Every dependency should be treated as something that needs to be verified rather than automatically accepted.

Key facts

FigureValueSource
Malicious packages RubyGems says it yanked from the May campaignmore than 500RubyGems, 11 September 2026
Date RubyGems reopened new account registration16 May 2026Mend.io and RubyGems, via The Hacker News
npm malware advisories published, 2024 then 20254,268 then 7,246GitHub Advisory Database
Total gem downloads recorded by rubygems.org266,326,546,002rubygems.org API
Downloads of keyv in the 30 days after its August compromise608,472,050npm registry API
Distinct supply-chain attack vectors in one peer-reviewed taxonomy107IEEE Symposium on Security and Privacy 2023

In this article

  • What happened to RubyGems, and what is still disputed
  • How a malicious package gets from a registry into your build
  • Why provenance does not protect you
  • How to protect against malicious packages this week
  • Whether it matters that an AI agent may have done it
  • Where RocketDevs fits
  • Conclusion
  • FAQ

What happened to RubyGems in May, and what is still disputed?

The May 2026 RubyGems incident is not particularly controversial in terms of what happened to the registry. RubyGems says it paused new account registrations, blocked the accounts involved and removed more than 500 malicious packages. It also found code designed to steal other users' API keys, although it found no evidence that the attempts succeeded.

What remains disputed is who was behind the campaign. RubyGems, independent researchers and OpenAI have reached different conclusions about the source of the packages.

The incident itself is well documented. On 12 May 2026, Maciej Mensfeld of Mend.io disclosed a coordinated campaign against the Ruby registry involving hundreds of packages. Some were described as spam, while others contained exploits. RubyGems announced the following day that the activity had stopped and that the bot accounts had been blocked and removed. New account registrations reopened on 16 May.

During the same week, Socket's threat research team documented a separate cluster called GemStuffer. It involved more than 100 gems that scraped UK council meeting portals before publishing the scraped pages to RubyGems using hardcoded API keys. Socket did not claim that GemStuffer was part of the May campaign, and there is no reason to treat the two incidents as the same attack.

GemStuffer is still relevant because it demonstrates another way a package registry can be abused. A registry is not only a place where developers download software. Attackers can also use it to store and move data.

Four months later, the story became more complicated. Independent researchers Spencer Kitts, Thomas Larsen and Sydney Von Arx published a report at rubyhack.ai claiming that the May flood was carried out by a swarm of OpenAI agents. They counted more than 2,000 packages submitted across 11 and 12 May. Their evidence includes package content they considered AI-generated, package names containing "oai" and author information that they believe points towards OpenAI.

The researchers also reported, per The Hacker News on 12 September, that the agents obtained code execution on RubyDoc.info's documentation build servers through a configuration file supplied by a package. They say the agents then attempted to steal other users' API keys. This part of the story comes from the researchers' investigation. RubyGems' statement does not mention RubyDoc.info, so it should not be presented as independently confirmed by RubyGems.

Where each party stands

PartyWhat they state
RubyGems (Ruby Central), 11 SeptemberIt yanked more than 500 malicious packages. It found code intended to obtain other users' API keys, but found no evidence that the attempts succeeded.
RubyGems (Ruby Central), 11 SeptemberIt cannot determine from the available evidence whether AI agents created or published the packages.
Kitts, Larsen and Von ArxThey attribute the May campaign to a swarm of OpenAI agents and say more than 2,000 packages were submitted on 11 and 12 May.
OpenAI spokesperson, 12 SeptemberOpenAI says its agents used RubyGems to access the internet for benign tasks and retrieve public information. It says it will continue investigating.

The wording from the different parties is important. RubyGems' 11 September update, signed by technical lead Colby Swandale, says that it could not determine whether AI agents created or published the packages. OpenAI similarly says, via ABC News, that, based on its review, its agents used RubyGems for benign tasks and to retrieve public information.

That leaves an important distinction. The malicious activity itself is not the main point of disagreement. The disagreement is about who caused it and what their intentions were. RubyGems found code designed to obtain other users' credentials, while the researchers believe they have evidence linking the campaign to OpenAI agents. OpenAI disputes that interpretation.

For developers, however, the identity of the attacker is less important than the vulnerability the incident exposes. A package can enter your build without you knowing who created it or why. Your security therefore needs to assume that dependencies may be compromised and limit what your build is willing to trust.

How does a malicious package get from a registry into your build?

A malicious package can reach your build because several parts of the open-source ecosystem are designed to make software easy to publish, install and update. A new account can publish packages, as the May RubyGems incident demonstrated. Package managers can execute package code during installation. Your build can also automatically select a version that matches the dependency range you specified.

Each of these features exists for a good reason. Together, however, they create opportunities for attackers. The important question is not simply whether you use open-source packages. It is what happens to those packages once they enter your build environment.

Start with installation. This is an area many founders and even development teams rarely examine closely. npm's scripts reference lists preinstall, install and postinstall scripts as part of the installation process. These scripts can contain code supplied by the package author, and that code runs on the machine performing the installation. That could be a developer's laptop. It could also be a CI runner with access to deployment credentials.

Ruby handles this differently but creates a similar risk. Gems can contain native extensions that are compiled during installation. RubyGems' guides note that, since RubyGems 3.3.11, a gem can declare a Rust extension that is compiled at install time. That compilation process requires build instructions supplied by the package. Your team did not write those instructions, and they may not have been reviewed before they ran.

The risk does not stop when installation finishes. Build tools, test runners, documentation systems and other development tools may process files supplied by dependencies. If those tools execute something malicious while your credentials are available, the dependency has another opportunity to compromise the environment.

The reported RubyDoc.info incident illustrates this type of risk. The researchers investigating the May campaign say a package configuration file was used to obtain code execution on RubyDoc.info's documentation build servers. That specific claim has not been confirmed by RubyGems, but the underlying security problem is broader. A test runner, linter, documentation builder or preview deployment can all become part of the attack surface when they process dependency-controlled files.

Security researchers have mapped this problem extensively. A 2023 IEEE Symposium on Security and Privacy paper by Ladisa, Plate, Martinez and Barais identified 107 different supply-chain attack vectors across 94 real-world incidents. The researchers describe the central problem as malicious code being inserted into open-source software and then downloaded and executed by its users.

There is another problem: concentration. A 2019 USENIX Security study of the npm ecosystem found that a small number of maintainer accounts could potentially be used to inject malicious code into a large proportion of packages.

That risk became a real-world incident in August 2026. Attackers compromised the GitHub account of the maintainer behind keyv. Aikido reported at least 444 affected packages across 1,381 versions, with the campaign targeting npm tokens, GitHub credentials and cloud keys. The keyv package was still downloaded more than 608 million times during the following 30 days.

Popularity does not protect a package from an attack. In fact, popularity can make a compromised dependency more valuable because a single malicious release can potentially reach a very large number of users before anyone notices.

How large is the problem?

It is tempting to measure the problem by counting security advisories. The numbers are useful, but they need to be interpreted carefully.

The GitHub Advisory Database recorded 4,268 npm malware advisories in 2024 and 7,246 in 2025. That represents a substantial increase. However, advisory counts show what a database recorded. They do not tell us exactly how many malicious packages exist in the wider ecosystem.

The 2026 data needs even more caution. The database currently contains tens of thousands of npm malware advisories, along with a group of RubyGems advisories dated to the same month. That pattern can reflect bulk ingestion rather than a sudden explosion in malicious activity. The figures are therefore better used to show the direction of the problem than to measure its exact size.

The scale of the Ruby ecosystem is nevertheless enormous. Rubygems.org reports more than 266 billion total gem downloads. Stack Overflow also has tens of thousands of questions tagged npm, showing how deeply package managers are embedded in everyday development work.

The precise figures are less important than what they represent. RubyGems and npm are not niche repositories that developers occasionally use. They are part of the infrastructure on which modern software is built. That makes them valuable targets for attackers.

And once a package enters that infrastructure, the question is no longer simply “Can I trust this package?” It becomes “What permissions and access does this package have when my build runs it?”

Why doesn't package provenance protect you?

Package provenance is useful, but it cannot tell you whether a package is safe. Provenance is essentially evidence about where a package came from and how it was built. It does not prove that the resulting code is free from malicious behaviour. npm's own documentation makes this limitation clear: a package with established provenance is not guaranteed to contain no malicious code.

Provenance was still an important improvement. It connects a published package to the source commit and build instructions that produced it. This allows developers to verify that a release came through the project's expected CI system rather than from an unauthorised machine.

The problem is that the expected build system can itself be compromised. Provenance can confirm that a malicious release came through the legitimate pipeline. It cannot confirm that the pipeline was behaving legitimately when it produced the release.

August 2026 provided two examples. During the keyv compromise, Aikido found that the malicious versions had valid provenance signed by GitHub Actions. The attacker had compromised the maintainer's GitHub account, giving them control of the project's legitimate publishing pipeline.

Weeks later, Socket reported another incident involving an OpenAPI React Query code generator. An outsider triggered the project's release workflow, which then published ten malicious versions. Those versions also had genuine provenance because the malicious build actually ran through the project's CI system.

The lesson is not that provenance is useless. It answers a narrower question than many developers assume. A provenance check can tell you whether a package came from the expected source and build process. It cannot tell you whether that source or build process was compromised.

If your team treats a provenance badge as proof that a package is safe, the security control can end up giving you false confidence. The package may have come from exactly the right place. The problem is that the right place may already have been compromised.

How do you protect against malicious packages this week?

The simplest approach is to narrow what your build is allowed to accept. Delay newly published versions. Stop install scripts from running unless a package genuinely needs them. Keep credentials away from the installation process.

These controls do not require a dedicated security team. Most can be implemented through existing package manager and CI settings.

ControlWhat it stopsWhere it livesDefault
Release-age delayInstalling a version published within the last few days, when compromised releases may not yet have been detectednpm min-release-age, installs only versions older than the number of days you specifyOff
Update cooldownAutomated pull requests moving you to a new version immediately after it is releasedGitHub Dependabot cooldown, delays version updates but not security updates3 days when enabled without a value
Install scripts offPackage code running during installation on developer machines and CI runnersnpm ignore-scriptsFalse, scripts run
Secrets out of the install jobA malicious install script accessing deployment keys and tokensYour CI configurationUsually everything runs in one job

The last row is a recommendation rather than a documented package-manager setting. The first three are based on npm and GitHub documentation.

Start with a release-age delay

A release-age delay is one of the easiest controls to introduce. It also offers a good balance between protection and disruption.

npm's min-release-age setting makes npm install only versions that have been available for longer than the number of days you specify. It is disabled by default.

The idea is simple. A malicious package that is discovered and removed within a few days never reaches your build. You give security researchers, maintainers and other users time to find the problem before you install the release.

If you use Dependabot, you can also configure a cooldown period. This delays normal version updates while allowing security updates through. When enabled without specifying a value, the default cooldown is three days.

Turn off install scripts

Next, consider disabling install scripts. npm's ignore-scripts setting is false by default, meaning package scripts can run during installation. Setting it to true prevents npm from running scripts specified in package.json.

This will cause some packages to stop working. Packages that genuinely require a build step, particularly those involving native code, may need scripts to run. Rather than enabling scripts for everything, identify those packages and allow them deliberately.

That gives you a much smaller attack surface. Instead of every dependency being able to execute code during installation, only packages you have specifically chosen can do so.

It's the difference between: "Every package is allowed to run code." and "Packages aren't allowed to run code unless we've decided they need to." The second approach is much safer.

Keep secrets away from installation

The next step is to separate dependency installation from deployment credentials.

In many small teams, one CI job installs dependencies, runs tests and deploys the application. That means a malicious install script could potentially access every secret available to that job.

Instead, install dependencies in a job that has no deployment credentials. Pass the resulting build artifact to a later job that has the credentials needed for deployment.

If malicious code executes during installation, it then has far less to steal.

Commit your lockfile and audit dependencies

Commit your lockfile so that your builds consistently use the versions that have already been selected. This prevents a loose version range from silently resolving to a newer release.

You should also scan your installed dependencies for known vulnerabilities. For Python projects, pip-audit can check an environment for packages with known security issues.

A vulnerability scanner cannot reliably detect a brand-new malicious release. That is why release-age delays are useful. The delay gives other people time to discover a malicious package, while vulnerability scanning helps identify older problems that have already been documented.

Watch important maintainers

Pay particular attention to the packages your software depends on most heavily.

Research has shown that a relatively small number of maintainer accounts can have influence over a large part of an ecosystem. That concentration creates risk. If one important maintainer account is compromised, a large number of downstream projects can potentially be affected.

You do not need to monitor every package equally. Start with the dependencies that are most important to your application and understand who maintains them.

Hire the top 2%.

Vetted developers, part-time or full-time, remote and ready, from $9.99/hr.

What if you already installed a compromised version?

Assume that the package's install script ran and that it attempted to access credentials. In the August npm incidents, Aikido found malware targeting npm tokens, GitHub credentials, AWS keys, Kubernetes secrets and HashiCorp Vault tokens.

Removing the package is therefore not enough. If malicious code ran during installation, the important event has already happened.

Start by identifying every machine that installed the affected version. Check your lockfile history and CI logs. Include developer laptops, not just build servers.

Next, rotate every credential those machines could access. Start with package publishing tokens and cloud credentials because compromised credentials can allow an attacker to spread further.

Then check whether any of your own packages or deployments were published from those machines after the compromised package was installed. Finally, rebuild affected CI runners from clean images rather than trying to clean compromised machines in place.

The final step is to address the reason the package reached you in the first place. A release-age delay could have given other users time to identify and remove a compromised version before your build installed it.

That is the cheapest lesson in this entire article. It is also one that can become considerably more expensive after a compromise.

If your engineers use AI coding agents that install dependencies automatically, these controls become even more important. An agent can add a dependency to solve a problem without necessarily examining what that dependency executes during installation. The same principle applies: the more software you allow to make decisions inside your build, the more carefully you need to control what that software can access.

Does it matter whether an AI agent did it?

For your security controls, no. RubyGems says it cannot determine who published the malicious packages, but none of the protections discussed above require you to know who was responsible. Your build should be protected against malicious dependencies regardless of whether they were created by a person, an automated system or an AI agent.

For the wider software industry, however, the answer is different. Attribution matters because accountability becomes more complicated when the suspected actor is software operated by a company rather than an individual criminal.

The amount of attention the incident received shows how quickly interest can grow around a supply-chain attack. The Hacker News story covering the researchers' claims reached 935 points and 582 comments within days. Wikimedia's pageview data also shows that interest in supply-chain attacks rises and falls with major incidents. The English Wikipedia article received 5,424 views in September 2025, 4,205 in May 2026 and 2,914 in August 2026.

That attention is useful because it brings supply-chain security into the conversation. But public interest is temporary. The underlying risk remains after the headlines disappear.

If the researchers' attribution is eventually confirmed, it raises questions that go beyond your own security controls. Who is responsible when an AI agent operated by a well-resourced company publishes packages containing credential-theft code during testing? The question becomes even more difficult when the people affected by the software never agreed to participate in the agent's activities.

The same issue is already emerging inside companies that use AI agents. When an agent makes a mistake that affects production, organisations need to decide who is responsible for the outcome. In the case of a public package registry, however, the potential victims are outside the organisation and never gave the agent permission to interact with their systems.

The incident also fits into a broader trend that is not specific to AI. Verizon's 2026 Data Breach Investigations Report says that 31% of breaches now begin with software vulnerabilities. Attackers target places where software is trusted and allowed to execute. Package installation is one of those places.

That is why the identity of the attacker should not determine your security strategy. Attackers go where trusted code runs with useful permissions. Your job is to make sure that trust is limited, regardless of who is on the other side.

If you are building with AI agents yourself, the same principle applies. Before giving an agent network access, repository permissions or publishing credentials, understand what the agent's harness allows it to do. The more permissions an agent has, the more carefully those permissions need to be controlled.

Where RocketDevs fits

The controls in this article are technical, but they ultimately depend on the people using them. A secure build process only works when developers understand how dependencies enter a project, what those dependencies are allowed to do and what credentials they can access. Good developers do not simply install a package because it solves a problem. They consider where it came from, how it is versioned and what happens when the build executes it.

That matters even more as development teams adopt AI coding tools and automated dependency updates. Developers need to be able to review what these tools introduce into a codebase rather than assuming that an automatically generated change is safe. They also need to recognise when a package, build step or CI process has more access than it actually needs.

This is one reason technical vetting matters when you are building a development team. Every developer placed by RocketDevs completes 6–8 hours per developer of structured vetting before a client sees them. RocketDevs accepts only the top 2% of applicants. That process gives clients access to engineers who have demonstrated their technical ability before they join the team.

The goal is not simply to find developers who can make an application work. You also want engineers who understand the consequences of the decisions they make along the way. That includes pinning dependency versions, reviewing packages before introducing them, understanding build and installation behaviour, and keeping credentials out of processes that do not need them.

These practices become particularly important for companies without a large security team. Your developers are often the people making day-to-day decisions about dependencies, CI pipelines and development tools. Having engineers who understand those risks can prevent small convenience decisions from becoming larger security problems.

If you want a development team that treats dependencies as code they are responsible for, build a vetted team with RocketDevs.

Conclusion

Imagine you own a small company that has a website. Your developer needs a piece of functionality, so they install a package. That package looks legitimate but unfortunately, an attacker has compromised it. Your package manager installs it. During installation, the package runs malicious code. Your CI server has access to your AWS credentials and the malicious code steals those credentials. The attacker now has access to your AWS environment. You delete the package. But the attacker already has the credentials.

That's the problem the entire blog is trying to solve. The answer isn't simply "don't use open-source software." That's unrealistic because modern software depends heavily on open-source packages. The answer is to reduce the amount of trust you give those packages*.*

Delay new releases. Lock your versions. Restrict installation scripts. Keep secrets away from dependency installation. Scan your dependencies. Give developers and AI agents only the permissions they actually need. And perhaps the single most important idea in the entire article is this:

You can't control who puts malicious code into the software supply chain. You can control what that code is allowed to do when it reaches you.

The RubyGems incident makes one thing clear: you do not need to know who is behind an attack to know that your build needs better boundaries.

Whether the malicious packages were published by a criminal, an automated system or an AI agent is still being debated. Your build does not have the luxury of waiting for an answer. It will install whatever you allow it to install. It will run whatever code your configuration permits. If credentials are available when that code executes, they can become part of the attack.

That is why the most useful response is not to focus on the attacker. Focus on trust.

Delay newly published dependencies. Pin the versions you have approved. Disable install scripts where they are not necessary. Keep deployment credentials away from dependency installation. Scan what you have installed. Pay particular attention to the packages your application relies on most heavily. These are straightforward controls, but they change what a compromised package can actually do.

The bigger lesson is that open-source software is infrastructure. Your team may not have written a dependency, but once it enters your build, you are responsible for what happens next. Provenance can tell you where code came from. A vulnerability scanner can tell you what is already known to be vulnerable. Neither replaces judgement about what you allow to execute and what permissions you give it.

AI is making that judgement more important, not less. Developers and coding agents can add dependencies in seconds. That convenience is valuable, but every new dependency also creates another decision about trust.

You cannot control who attacks the software supply chain. You can control how much access an untrusted package gets when it reaches your build.

The safest build is not the one that trusts every package. It is the one that makes every package earn its trust.

Frequently asked questions

Was the RubyGems attack carried out by AI agents?

It has not been confirmed by anyone with access to the relevant registry evidence. Independent researchers attribute the May 2026 campaign to OpenAI agents. OpenAI says its agents used RubyGems for benign tasks, while RubyGems says it cannot determine whether AI agents created or published the packages.

For now, the attribution should be treated as unresolved. The security lessons are not. Your build should be protected against malicious packages regardless of who created them.

Does npm provenance mean a package is safe?

No. Provenance can show where and how a package was built, but it cannot prove that the resulting code is safe. npm's own documentation explicitly states that established provenance does not guarantee that a package contains no malicious code.

The August 2026 npm compromises demonstrated why. Malicious releases were published through legitimate CI pipelines and therefore had valid provenance. Provenance confirmed that the packages came through the expected build process. It did not show that the process itself had been compromised.

Should I disable install scripts?

In most projects, yes, with an allowlist for packages that genuinely need them. npm's ignore-scripts setting prevents npm from running scripts specified in package.json files. This can stop malicious code from executing automatically during installation.

Some packages, particularly those containing native code, may require installation scripts to build correctly. Rather than allowing scripts for every dependency, identify the packages that need them and allow those deliberately. This gives you a much smaller attack surface.

How do I check if my project installed a malicious package?

Start with your lockfile. It records the dependency versions your project resolved, allowing you to identify whether an affected version was installed. Compare those versions against the relevant security advisories and run your ecosystem's dependency-auditing tools.

If the compromised package was installed on a machine that had access to credentials, do not stop at removing the package. Rotate the credentials that machine could access, including package publishing tokens, cloud credentials and other sensitive secrets. Then investigate whether those credentials or the compromised machine were used to access or publish anything else.

What is the simplest way to reduce the risk of a malicious package?

Start by delaying newly released dependency versions and keeping your lockfile committed. This gives security researchers and maintainers time to identify and remove compromised releases before they reach your build.

Then disable install scripts where possible and keep secrets out of the job that installs dependencies. These controls do not eliminate supply-chain risk, but they significantly reduce what a malicious package can do if one gets through.

James Hitch, COO at RocketDevs.LinkedIn

Sources

Serious devs. Serious value.

The top 2% of applicants, rigorously vetted, from $9.99/hr. Part-time or full-time, dedicated to your team.

  • Top 2% of applicants
  • 6–8 hours of human vetting
  • 14-day risk-free trial
James Hitch

Written by

James Hitch

COO

James Hitch is the COO of RocketDevs, where he runs sales, recruiting, and the vetting operation that accepts only the top 2–3% of developer applicants. He cares about putting accessible, elite engineering talent within reach of founders and startups worldwide, at a fair price. He writes about technical hiring, building AI-native engineering teams, and how startups can access elite developers affordably.

Share this article

Help others discover this content

More from our blog

Continue exploring insights and stories from RocketDevs