PkgRadar

Flagged before the advisory

The cacheable / keyv compromise: caught on the diff, an hour before the advisory.

On 2026-08-04, attackers who had compromised the npm/GitHub maintainer account behind the keyv and cacheable package families cut trojanized releases of four packages with millions of weekly downloads between them. PkgRadar flagged all four as high-risk within 19–56 seconds of publication and 57–83 minutes before the corresponding public OSV advisories — on the behavior change alone. Here is what the scanner saw, and why reputation and provenance did not stop this attack but a behavioral diff did.

First malicious release seen
2026-08-04 10:10:44 UTC
Packages
cacheable, cache-manager, @cacheable/utils, @cacheable/memory
Campaign
Shai-Hulud “Here We Go Again” (Aug 2026 npm worm)
Static-evidence verdict
High on all four trojanized releases

Detection timeline

These four are the cacheable-family packages we flagged; keyv-namespace packages under the same maintainer account were hit in the same compromise (per the advisories). For each: when the trojanized version hit npm, when PkgRadar flagged it, and when the public OSV malware advisory appeared. Our flag timestamps and the OSV publish times are both matters of record; the lead column is the gap between them.

Package@versionnpm published (UTC)PkgRadar flaggedLatencyOSV advisoryLead vs OSV
[email protected]10:10:4410:11:1127sMAL-2026-11963 (11:28:00)77 min
@cacheable/[email protected]10:11:2910:12:1243sMAL-2026-11558 (11:35:29)83 min
[email protected]10:14:4110:15:0019sMAL-2026-11523 (11:12:20)57 min
@cacheable/[email protected]10:14:2110:15:1756sMAL-2026-11561 (11:36:30)81 min

At the moment PkgRadar flagged these releases, no public advisory existed and our own records carried no known-malicious cross-reference for them — the verdict came from static evidence, not from mirroring someone else’s finding.

What the payload does

We attribute this section to the public OSV advisories and Amazon Inspector’s analysis: after takedown, npm removed the malicious tarballs, so the authoritative account of the payload’s runtime behavior is the public record, not our own re-execution. What PkgRadar independently detected at scan time is in the next section.

Per the advisories, the trojanized package.json adds a preinstall hook (node setup.mjs) that runs on a bare npm install, before any project code, while leaving the normal compiled output intact so the package still works. setup.mjs downloads a standalone Bun runtime and uses it to run a heavily-obfuscated second stage that ships inside the package itself (Math_Symbol.js, a ~728 KB Bun bundle) — that packed, in-tarball bundle is what PkgRadar’s static scan saw. The stealer harvests cloud instance-metadata credentials (AWS IMDS at 169.254.169.254, ECS, GCP, Azure), AWS/GCP/Azure keys, HashiCorp Vault tokens, Kubernetes service-account tokens, GitHub Actions OIDC and org/repo secrets, and npm tokens, then runs a TruffleHog-style regex sweep for more. Using the stolen npm identity it republishes further packages the token can reach — a worm — and plants autostart hooks in .claude/ and .vscode/so the loader runs again when a developer or AI coding agent opens the repository. Findings are exfiltrated to attacker-created GitHub repositories whose descriptions read “Shai-Hulud: Here We Go Again.”

What PkgRadar saw at scan time

PkgRadar never runs package code. It flagged these releases on static evidence from the tarball and a diff against the previous version — the following findings, which are our own and were recorded at the flag times in the timeline above:

Finding kindSeverityWhat it saw
new_lifecycle_script_vs_previousHighA preinstall hook — "node setup.mjs" — that did not exist in the immediately prior release (e.g. 7.2.10 vs 7.2.9, 2.5.1 vs 2.5.0). A brand-new install-time hook on a mature package is the single strongest maintainer-compromise signal PkgRadar tracks.
js_obfuscator_packerHighA javascript-obfuscator string-array packer: 30+ hex-indexed decoder calls plus the tool's self-defending rotator — the obfuscated form npm dropper/exfil families use to hide C2 and child-process exec from static readers. A caching library has no honest reason to ship packed source.
credential_pathsLowA reference to aws_access_key in the added code — corroborating, not decisive on its own.

The decisive pair is new_lifecycle_script_vs_previous + js_obfuscator_packer: a mature, trusted package suddenly grew an install-time hook and shipped packed code in the same release. That is the shape of a maintainer-compromise, and it does not require decoding the payload to call it high-risk.

Why provenance didn’t help

Per the advisories, these releases carried a valid, signed npm provenance attestation: the attackers pushed their payload to the source repository and cut releases through GitHub Actions, so the poisoned versions reached npm with clean-looking build provenance. That is working as designed: provenance attests build integrity, not source integrity.A green provenance check tells you the artifact was built from some commit by some workflow — not that the commit was written by the person you trust. A consumer keying purely on “has provenance” would have installed all four.

Why reputation didn’t fool the scanner

These are not obscure packages. They are core caching infrastructure with millions of weekly downloads, from a maintainer with years of releases. Every reputation heuristic — download volume, tenure, publisher history — says trust this. PkgRadar uses exactly those signals to discount noise, and we’ve written about why a scanner shouldn’t block the AWS SDK over dual-use behavior. But reputation only softens the boring signals. It is deliberately blind to the dangerous ones: a new install hook and an obfuscator packer are behavioral guns that stay High no matter how popular the package is. Reputation earns a package the benefit of the doubt on ambiguity — never on a fresh dropper.

If you installed one of these

  • The affected versions are [email protected], [email protected], @cacheable/[email protected], and @cacheable/[email protected] (npm has since removed them and restored the clean prior releases). Check every package-lock.json in your fleet for these exact versions.
  • If any host installed one with install scripts enabled, treat it as compromised and rotate every reachable credential — cloud keys, Vault tokens, npm and GitHub tokens, Kubernetes service accounts. Per the advisories, cloud instance-metadata credentials were a primary target.
  • Check .claude/ and .vscode/ in affected repositories for planted autostart hooks before opening a clone in an editor or AI agent.
  • This was one node of the August 2026 npm worm that the advisories describe as poisoning 400+ packages across many organizations. See our Shai-Hulud writeup for the broader campaign.

References