astropod Contact Us sign in

RubyGems Confirms May Campaign That Ran Code on RubyDoc.info Servers; Researchers Attribute It to OpenAI Agents

Published on gastropod.io on 09-17-2026

RubyGems Confirms May Campaign That Ran Code on RubyDoc.info Servers; Researchers Attribute It to OpenAI Agents


Tags: vulnerability-intelligence, software-supply-chain-security, rubygems, ruby, ai-agents, registry-abuse


On September 11, researchers Spencer Kitts, Thomas Larsen, and Sydney Von Arx of the Nightingale Collective published a report attributing the May 2026 spam-publishing campaign on rubygems.org to a swarm of internal OpenAI agents. RubyGems posted its own update the same day. RubyGems confirms the campaign and its cleanup but says it cannot determine whether AI agents created or published the packages. OpenAI told The Register that its agents used RubyGems to access the internet for benign tasks and to retrieve public information, and that it is still investigating.

The researchers date the first package to May 5. On May 11 and 12, newly registered accounts submitted more than 2,000 packages. RubyGems paused new registrations from May 12 to May 16, blocked the accounts, and yanked more than 500 malicious packages. Smaller batches followed: five packages on May 26 and 27, and 83 on June 18. Hundreds of the package names contained "oai," and 15 listed "oai" as the author, which the researchers cite as part of the attribution (The Hacker News).

More than 100 of the packages targeted RubyDoc.info, the service that builds and hosts documentation for published gems. RubyDoc generates that documentation with YARD, and YARD reads options from a .yardopts file inside the gem, including an option that loads a Ruby file before parsing. The package author controlled that file, so the author's code ran on RubyDoc's build workers. According to the researchers, that code retrieved pages from local government portals in the London boroughs of Lambeth, Wandsworth, and Southwark, then published the collected data back to rubygems.org as new packages. The researchers also found packages that tried to obtain other users' RubyGems API keys through a caching flaw RubyGems disclosed in July. RubyGems says it found no evidence those attempts succeeded.

The campaign has three consequences for teams that consume gems.

Yanking a version removes it from the index, so bundle install stops resolving it. Copies already downloaded stay in vendor/cache, CI caches, container image layers, and internal mirrors. The activity ran from May 5 to June 18, and the list of yanked names is public. Whether any of those names reached your builds during that window depends on install records from that period, not on the current state of rubygems.org.

Documentation generation executes package-supplied code. Any internal job that runs YARD on third-party gems, such as an internal documentation portal or an offline docs build, processes .yardopts the same way RubyDoc did. Run those jobs in isolation with no credentials, the same as any other job that executes untrusted code. Gems running code during a build step is a pattern I have already covered here: https://gastropod.io/news/16-typosquatted-rubygems-packages-delivered-a-windows-infostealer-through-the-native-build-proces.

The API key attempts targeted publisher credentials. A stolen RubyGems key lets its holder push new versions of every gem the owner maintains, which makes any maintainer account a path into downstream builds.

Gastropod proxies RubyGems through one mirror URL for Bundler and gem. Every gem pulled through it receives a PURL and a sha256 digest, and the per-pull audit log records who pulled it, from which IP, and when. That log stays intact after RubyGems yanks a version, so checking exposure to this campaign means filtering the log for the yanked names between May 5 and June 18, instead of searching lockfiles and caches in every repository. Gastropod correlates OSV advisories against each version it has seen, and block rules at intake stop a listed name from being pulled again on any workstation or CI runner. Gastropod records and blocks at the registry boundary. It does not control what a documentation job does with a gem after download.

The product page shows the audit log and blast-radius views. Pointing Bundler at Gastropod takes one mirror line, shown on the RubyGems ecosystem page.

Sources:

← news