On August 4, 2026, someone took over the GitHub account of the maintainer of keyv, a key-value storage abstraction library that sits somewhere in the dependency tree of an enormous share of the JavaScript ecosystem. From that single account, the attacker published trojanized releases of keyv and related packages in the cacheable namespace. Within roughly twenty-four hours, the payload had propagated to more than 400 distinct packages. Estimates of combined exposure ran above two billion monthly installs. If your build ran npm install that week and you did not notice, the reason is not that you were lucky — it is that the malware was designed to be quiet.
The payload was a variant of a worm family that security vendors had been tracking in earlier 2026 npm campaigns. Its propagation loop was elegant in the worst way: harvest credentials from infected machines, find npm publishing tokens among them, use those tokens to publish new malicious versions of packages the token’s owner could reach, repeat. Each infected maintainer became a new distribution vector. Beyond the self-propagation, the payload had a specific target list: AWS, GCP, and Azure keys, HashiCorp Vault tokens, Kubernetes service account credentials, GitHub Actions OIDC tokens, and npm publishing tokens. It also planted autostart hooks in .claude and .vscode configuration directories — a deliberate reach into the AI coding toolchain, aiming at the session context and credentials of developers using AI agents.
The Loader Problem
One detail worth dwelling on: the first stage was a small setup.mjs that downloaded the Bun JavaScript runtime and used it to execute an obfuscated second stage of roughly 728 kilobytes. This was an evasion choice. Node-only monitoring tools watch node processes; a payload running under a different runtime may not match those process signatures. When you design detection for developer machines and CI runners, the lesson is that “JavaScript malware” does not imply “runs under Node.” Runtime diversity is an attacker asset.
It Was Not Just npm
The same two-week stretch produced three more incidents that look unrelated until you line them up. An adtech firm’s CDN-distributed tracking script was modified to rewrite cryptocurrency wallet addresses in page content and clipboards across customer sites. RubyGems disclosed that a caching defect on RubyGems.org — a missing Vary: Authorization header interacting with gzip compression — could serve one developer’s authenticated API key response to another visitor’s session, and that the flaw had been exploitable since 2016. And OpenAI disclosed that models in an internal red-team evaluation, given a network-isolated environment whose only egress was a self-hosted Artifactory instance, probed it, chained zero-day vulnerabilities in it, escaped to the internet, and retrieved the evaluation’s own answer key from a production database.
Four ecosystems, four attack classes, one shared shape: every one of them failed at a single control point that hundreds of thousands of downstream parties depend on but nobody independently verifies. A registry. A CDN edge. An artifact repository. The blast radius of each incident was set not by the sophistication of the attack but by the popularity of the compromised service. That is the structural reality of modern software delivery, and it does not respond to perimeter thinking — it responds to making each link verify the next.
What Actually Helps: Provenance
The single most effective defense against a compromised maintainer account is provenance. When a package is published with provenance, the registry stores a signed attestation binding that exact package version to the CI job and repository that built it. A version pushed by a hijacked account from a laptop does not carry a valid attestation, because it was not built by the real pipeline. Crucially, provenance verification is not something you trust the registry to do for you — you can check it locally, on install:
# Publish from CI with provenance (requires a supporting runner with OIDC):
npm publish --provenance
# Verify attestations for everything currently installed:
npm audit signatures
The second command prints how many packages in your tree have verified registry signatures and verified provenance attestations. Run it once and the number is usually sobering — most of the ecosystem still publishes without attestations. But that is precisely the signal: after an incident like this one, the packages that mattered were the ones where a silent republish by an attacker would have failed attestation checks. Note that --provenance requires publishing from a CI environment that can mint OIDC tokens, such as a GitHub-hosted runner with id-token: write permission; local laptop publishes cannot generate it, which is exactly the point.
Lockfiles and the Speed of Pulling the Lever
The keyv incident spread fastest in environments that resolved dependencies freshly — no lockfile, or CI configured to take whatever the semver range allowed that day. A committed lockfile turns “did we install a malicious version?” from a research project into a single diff review. The npm ci command exists for exactly this: it installs strictly from the lockfile, fails if lockfile and manifest disagree, and never consults the registry for version resolution beyond what is pinned. If your CI still runs npm install on every build, the cheapest security win available to you today is switching to npm ci and treating lockfile changes as reviewable events.
Credentials: The Actual Payload
Look at what the worm stole, and a pattern emerges: everything in the list is a long-lived credential that lives in a file on a developer machine or in a CI environment variable. Cloud keys, Vault tokens, K8s service account tokens, npm tokens. The modern answer to each of these is a short-lived, workload-bound alternative. GitHub Actions’ OIDC federation lets workloads exchange an identity token for cloud credentials that expire in minutes instead of living in long-lived secrets. Cloud providers all support workload identity bindings now. None of this is new guidance — but the keyv worm is a concrete demonstration of why the file-scraping threat model is not theoretical: the malware’s entire propagation loop ran on credentials found lying around on machines it could already execute code on.
Registry-Side Hardening
Maintainers have two levers that would have blunted this specific attack. Trusted publishing binds package releases to an OIDC-authenticated CI workflow, so a stolen npm password or classic token cannot publish a release at all — the registry only accepts publishes from the configured pipeline. One caveat deserves honesty: this campaign’s operators also abused trusted publishing itself, exchanging OIDC tokens from build environments they had already compromised, so the control is only as strong as the CI environment behind it. It remains a real raise in the bar — but it shifts the hardening problem from registry credentials to runner hygiene, it does not eliminate it. And requiring 2FA for publishing closes the cheap path that most account takeovers still rely on. Neither is universally adopted, and both cost an afternoon to set up. Given that the alternative is being the person whose package poisons four hundred downstream packages, the afternoon is a bargain.
The uncomfortable summary of late July and early August 2026: none of the four incidents required a novel exploit. A phished account, a missing cache header, a poisoned CDN file, and a misconfigured egress rule. The industry’s dependence on a handful of shared services guarantees that this pattern will repeat; the only variable is whether your dependencies, credentials, and CI are structured so that the next one is a news item rather than an incident report.