Trust-Path Compromise in the npm Registry
A comparative technical analysis of the chalk/debug (Sept 2025) and keyv/cacheable (Aug 2026) campaigns
Abstract
Two campaigns eleven months apart bracket the evolution of npm supply-chain attack tradecraft. On 8 September 2025, a phishing-driven account takeover placed a browser-resident cryptocurrency clipper into 18 foundational utility packages carrying more than two billion weekly downloads. On 4 August 2026, a compromised maintainer identity delivered a self-propagating credential-harvesting worm (a descendant of the "Mini" Shai-Hulud family) into the keyv/cacheable caching ecosystem, from which it spread to 400 or more unrelated packages within roughly four hours.
The two incidents share an initial-access class (maintainer identity compromise) and diverge on everything that matters operationally: execution context, persistence, propagation model, and the specific control that failed. This paper reconstructs both attack chains from public vendor telemetry, corrects two premises that recur in secondary reporting, and derives seven control-plane findings with associated detection logic.
The headline finding is uncomfortable: in the 2026 campaign the release pipeline worked exactly as designed and produced a valid, verifiable provenance attestation for malware. Signature verification, attestation checking, and SLSA provenance are all necessary, and none of them was sufficient, because every one of them attests the build, not the source.
Section 10 maps both campaigns onto EU and US regulation and sets out a dual-track remediation playbook. The timing there is not incidental: the EU Cyber Resilience Act's 24-hour reporting obligation begins on 11 September 2026, thirty-eight days after the keyv compromise. An identical campaign this autumn would put every affected EU-market software vendor on a one-day clock.
1. Scope, methodology, and confidence
1.1 Evidence base
This analysis is a synthesis of public primary reporting from vendor research teams that independently obtained and analysed the artifacts: Aikido Security (first detection, 2025), Wiz Research, Socket Threat Research, Snyk Security Research, Cycode, Semgrep, Chainguard, SafeDep, and Sonatype, together with the npm/GitHub platform changelogs. Where sources disagree (and on affected-package counts they disagree substantially), the divergence is reported rather than reconciled.
Lateos did not obtain or detonate the samples. All artifact-level claims (file hashes, byte counts, decoded strings, API endpoints) are attributed to the analysing vendor and should be treated as second-hand. No claim in this paper is derived from independent Lateos reverse engineering; nothing here should be cited as original malware analysis.
1.2 Confidence language
- Confirmed: reported consistently by two or more independent analysing vendors with artifact-level evidence.
- Reported: asserted by a single credible vendor, not independently corroborated in public reporting.
- Assessed: Lateos analytical judgement, not a factual claim.
1.3 Two premises worth correcting
Two claims circulate widely in secondary coverage of the 2026 incident and are not supported by the primary reporting.
"The attacker bypassed git and pushed tarballs directly to npm." The opposite occurred, and the difference is the whole lesson. Cycode's timeline places a malicious commit on the main branch of the keyv repository at 09:02 UTC, IDE and agent persistence hooks at 09:04, removal of a self-incriminating test at 09:23, and publication at 09:35. Snyk confirms the npm manifest identifies GitHub Actions as the trusted publisher for keyv@6.0.0 and links a valid npm attestation: the legitimate release workflow built and attested source that was already trojanized. Direct-to-registry tarball publication is a real technique (the worm's own propagation stage uses it), but it is not what happened to the origin package.
"This was an OIDC compromise." OIDC trusted publishing was not broken; it was used. The initial access was a maintainer identity compromise (initial vector unconfirmed; the maintainer is an incident victim). The worm's second stage then calls npm's OIDC token-exchange endpoint to mint short-lived publish credentials for packages reachable from a stolen identity. Trusted publishing performed to specification throughout. What it removed was long-lived token theft; what it did not remove was the value of compromising the identity that authorizes the exchange.
2. The npm trust model and its four independent paths
Analysing these incidents requires separating four trust paths that are usually collapsed into "is this package safe?"
| Path | Asserts | Enforced by | Failed in |
|---|---|---|---|
| P1: Identity | This publish came from an authorized principal | npm auth, 2FA, granular tokens, OIDC | Both campaigns |
| P2: Source | The published artifact corresponds to reviewed source | Nothing in the registry | 2026 (silently) |
| P3: Build | The artifact was produced by the declared pipeline from the declared commit | Sigstore/Fulcio/Rekor, npm provenance | Neither; it held, and attested malware |
| P4: Execution | Installing does not run arbitrary code | Lifecycle-script policy, npm v12 defaults | 2026 (primary), 2025 (n/a) |
P3 is the path with the most engineering investment behind it, and it is the path that was never attacked, because it did not need to be. An attacker who owns P1 obtains a valid P3 attestation as a side effect of publishing normally. P2 (the assertion that the tagged source was reviewed by a human who was not the attacker) has no enforcement mechanism in the registry at all. This gap is structural, not a vendor defect.
3. Case A: chalk / debug / ansi-styles, 8 September 2025
3.1 Initial access
The maintainer (Josh Junon, npm handle qix) received a credential-harvesting email from support@npmjs[.]help, a lookalike domain registered three days prior on 5 September 2025, framed as a mandatory 2FA update. The phishing page captured username, password, and a live TOTP value, giving the attacker a full session. The maintainer publicly acknowledged the compromise at approximately 15:15 UTC and began removing malicious versions, losing account access partway through cleanup.
The same operator subsequently reached at least one additional maintainer, producing malicious duckdb@1.3.3, @duckdb/node-api@1.3.3, @duckdb/node-bindings@1.3.3, @duckdb/duckdb-wasm@1.29.2, and proto-tinker-wc@0.1.87 (detected 16:58 UTC); Sonatype and Wiz also list @coveops/abi@2.0.1. Reporting on the second wave notes those packages remained available for a materially longer window than the first set.
3.2 Affected set and reach
All eighteen packages are deep-tree formatting and type-checking utilities. Weekly download figures at time of compromise, per Aikido: ansi-styles 371.41M, debug 357.6M, chalk 299.99M, supports-color 287.1M, strip-ansi 261.17M, ansi-regex 243.64M, wrap-ansi 197.99M, color-convert 193.5M, color-name 191.71M, is-arrayish 73.8M, slice-ansi 59.8M, error-ex 47.17M, color-string 27.48M, simple-swizzle 26.26M, supports-hyperlinks 19.2M, has-ansi 12.1M, chalk-template 3.9M, backslash 0.26M. Aggregate exceeds 2B weekly; Qualys and others cite 2.6B including the second wave.
3.3 Payload architecture
The injected code is browser-only. It does not touch the filesystem, does not persist, and does not harvest credentials. It is a single-purpose financial interceptor operating at two layers.
Layer 1: response rewriting. The payload replaces the global fetch with a wrapper. The wrapper clones every response, passes the body through a substitution routine, and returns a reconstructed Response preserving status and headers. XMLHttpRequest.prototype.open/send are patched, with responseText and response redefined via Object.defineProperty on readyState === 4. Substitution is regex-driven across seven address families (Ethereum, Bitcoin legacy, Bitcoin SegWit, Tron, Litecoin (two patterns), Bitcoin Cash, and three Solana variants) against roughly 200 hardcoded attacker addresses.
The substitution is the most deliberate design choice. Rather than replacing a detected address with a fixed sink, the payload computes Levenshtein distance between the victim address and every attacker address in the matching family and substitutes the nearest one. The result is visually similar in prefix and suffix, defeating the truncated-address comparison (0xFc4a…6976) that most wallet UIs and users actually perform.
Layer 2: provider hooking. If window.ethereum is present, request, send, and sendAsync are wrapped via Object.defineProperty before the transaction reaches the signer. eth_sendTransaction parameters are rewritten by ABI selector:
| Selector | Method | Manipulation |
|---|---|---|
0x095ea7b3 | approve | Spender replaced with attacker address; allowance set to 0xff…ff |
0xa9059cbb | transfer | Recipient word replaced |
0x23b872dd | transferFrom | Destination word replaced |
0xd505accf | permit | Spender and value words replaced, signature components preserved |
Value-bearing transactions with no calldata have to rewritten outright. Solana solana_signTransaction and solana_signAndSendTransaction have instruction account keys, recipient, and destination overwritten. A control object is exposed at window.stealthProxyControl with isActive(), getInterceptCount(), getOriginalMethods(), and forceShield(). That object is an unambiguous, high-fidelity detection artifact.
One behavioural detail deserves note. When a wallet provider is detected, the payload suppresses the visible display-layer swaps and relies on the silent signing-path manipulation. The two layers are deliberately mutually exclusive to reduce the chance a user notices a mismatch between what the page shows and what the wallet prompts.
3.4 Outcome
Aikido's feed flagged the activity at 13:16 UTC, roughly five minutes after publication, with public disclosure inside an hour. Malicious versions were live approximately two hours. Kaspersky's GReAT assessment put realized theft in the tens of dollars; other reporting attributes the low yield partly to code defects that crashed CI pipelines and surfaced the compromise early. Vercel identified 70 teams across 76 projects with builds containing affected versions and purged build caches.
The 2025 campaign is best understood as a near-miss: an execution primitive with a two-billion-download blast radius, wasted on a low-yield monetization path by an operator who did not attempt persistence, credential theft, or propagation.
3.5 Mobile exposure, and a methodology caveat
NowSecure's preliminary dependency analysis identified approximately 190 iOS and Android applications containing at least one of the 18 affected components in their dependency chain. That figure is a component-name match, not a version match; NowSecure explicitly stated it could not confirm the compromised versions were present.
The number is nonetheless operationally significant for a reason specific to mobile. Hybrid and web-component frameworks (React Native, Cordova, NativeScript, Capacitor) bundle JavaScript into the shipped binary, so a compromised dependency is frozen into the artifact at build time. Where a web application is remediated by a rebuild and a CDN invalidation measured in minutes, a mobile application requires a rebuild, a store submission, a review cycle, and user-side update adoption. Days to weeks of residual exposure is the norm, and a meaningful long tail never updates. A follow-on wave reported by NowSecure on 16 September 2025 affected a further 187 packages, weighted toward exactly this ecosystem: @nativescript-community/* components, Angular mobile packages, React Native adjacent libraries, and Cordova plugins.
Assessed: mobile exposure is systematically under-measured because SCA tooling is oriented toward server and web build pipelines, and because store-distributed binaries are outside most organizations' asset inventory. For a browser-context payload like this one, a WebView with wallet-adjacent functionality is a valid execution target.
4. Case B: keyv / cacheable, 4 August 2026
4.1 Target selection
keyv is a key-value storage abstraction; cacheable, cacheable-request, flat-cache, and file-entry-cache are caching libraries under the same maintainer. Their defining property is depth: the canonical chain runs eslint → file-entry-cache → flat-cache → keyv, meaning virtually no affected team chose these packages. cacheable-request sits beneath got, beneath thousands of HTTP clients and SDKs; cache-manager appears under common NestJS caching configurations. Reported reach figures range from ~127M weekly downloads for keyv alone to a combined ~1.88B monthly across the core four, and 2B+ monthly installs across the full affected set.
Assessed: depth is the target-selection criterion, not popularity. A compromise at framework level is observed within minutes by thousands of watchers; a compromise four levels down in a lint-cache utility is observed when a vendor's telemetry catches it. Nobody reads the changelog for file-entry-cache.
4.2 Timeline (UTC, 4 August 2026)
| Time | Event | Source |
|---|---|---|
| ~09:00 | First malicious repository changes under compromised identity | Wiz |
| 09:02 | Malicious commit pushed directly to main of jaredwray/keyv | Cycode |
| 09:04 | IDE/agent persistence hooks committed | Cycode |
| 09:23 | Self-incriminating test file removed | Cycode |
| 09:30–09:32 | @keyv/* scoped 6.0.0 tarballs published with no preinstall hook present | Socket |
| 09:35 | keyv@6.0.0 published; first artifact carrying the hook | Socket |
| ~09:41 | Socket malware detection flags keyv@6.0.0 (~6 min) | Socket |
| 09:38 | @thiennq/docs-viewer@1.6.2; first confirmed publish outside the two families | Socket |
| ~09:39 | Force pushes to main, repeated v6.0.0 tag deletion, activity on v5 branch | Socket |
| 10:09:44–10:14:41 | cacheable family burst (9 packages) | Socket |
| 10:12–10:46 | Eight unrelated organizations begin publishing poisoned versions | SafeDep |
| 10:28 | ecto published | Cycode |
| 11:16 | 8 of 11 identified malicious releases still tagged latest | Snyk |
| 13:18 | Last known poisoned publish | SafeDep |
The interval from origin publish to cross-organization propagation is approximately 37 minutes. The interval from origin publish to first detection is approximately 6 minutes. Detection was not the binding constraint; blocking was.
4.3 Stage 0: the manifest change
Socket's diff of keyv@6.0.0 is the cleanest artifact in either campaign. Every file under dist/ is byte-identical by SHA-256 to the clean 6.0.0-rc.1 build. The entire compromise is two added files and one added script:
"files": [ "dist", "LICENSE", "setup.mjs", "Math_Symbol.js" ],
"scripts": { "preinstall": "node setup.mjs" }
The library behaves normally forever after. There is no application-layer misbehaviour to detect, at any point, ever. All malicious capability lives in the lifecycle hook, which executes before any application code loads and before most scanning runs.
4.4 Stage 1: the Bun loader (setup.mjs)
A lightly obfuscated Node script that, if bun is absent, downloads a platform-matched standalone Bun 1.3.13 runtime from the official oven-sh/bun GitHub release URL and uses it to execute the second stage:
const V = "1.3.13";
const url = "https://github.com/oven-sh/bun/releases/download/bun-v" + V + "/" + target + ".zip";
execFileSync(bunBinary, [payloadPath], { stdio: "inherit", cwd: D });
The loader fingerprints platform and architecture including musl/Alpine (via ldd --version and /etc/os-release), and extracts using system unzip, PowerShell Expand-Archive on Windows, or a hand-written pure-JS ZIP parser as fallback. It downloads over HTTPS with no checksum or signature verification and removes its bun-dl-* temporary directory afterward.
Three design consequences follow.
- Runtime substitution as evasion. Node starts the chain; Bun runs the payload. Monitoring instrumented at the Node layer (
NODE_OPTIONSshims, Node-level EDR hooks,--requireinterposition) observes a short benign script and then nothing. Wiz's identification of theBun/1.3.13user-agent as an IOC exists precisely because the malicious network activity does not present as Node. - Trusted download source. Egress allowlists that permit
github.comrelease assets (which is nearly all of them) permit this fetch. The binary is genuine Bun. - Artifact minimization. Temp-directory cleanup means post-hoc disk forensics on the loader is largely unproductive; process-lineage telemetry is the reliable signal.
4.5 Stage 2: the payload (Math_Symbol.js / math_init.js)
A ~728 KB Bun bundle (727,680 bytes; SHA-256 9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc), byte-identical across publishers despite differing filenames. Match the hash and the hook, not the filename.
Strings are protected with polymorphic basE91: a single shared numeric opcode table drives dozens of per-scope alphabets decoded lazily at runtime. Socket reports that recovery required reimplementing basE91 and brute-forcing each alphabet. That is a materially higher analysis cost than the 2025 sample's off-the-shelf JS obfuscation, and a deliberate defence against automated string-based classifiers. Internal log tags identify four modules: [collector], [dispatcher], [provenance], [publish].
Collection. Active queries to the AWS instance metadata service (169.254.169.254, including IMDSv2 token acquisition, and the ECS task endpoint 169.254.170.2); AWS credential chains and Secrets Manager across regions; GCP service-account keys and Azure client secrets as file/regex targets; Vault tokens from /home/runner/.vault-token and /run/secrets/VAULT_TOKEN; Kubernetes service-account tokens from /var/run/secrets/kubernetes.io/serviceaccount/token; npm tokens via registry whoami and token endpoints; GitHub Actions secret metadata via API, with values recovered from environment, files, and process scanning on the runner. A TruffleHog-style regex sweep hunts generic keys, bearer tokens, and PEM blocks on disk.
Wiz reports the credential-theft target set expanded by approximately 70% over the open-sourced "Mini" Shai-Hulud baseline, with new categories that define the current threat surface:
- AI agent credential stores: Claude, OpenAI, Codex, Cursor, Gemini configuration files
- Self-hosted CI/registry secrets: Jenkins
master.key, Argo CD, Harbor - Additional cloud CLIs: Alibaba Cloud, Tencent Cloud
- Cryptocurrency keystores: Foundry, Solana, Monero
- System files:
/etc/shadow
Propagation. The worm primitive, in sequence:
registry.npmjs.org/-/whoami
registry.npmjs.org/-/v1/search?text=maintainer:<identity>
registry.npmjs.org/-/npm/v1/tokens
registry.npmjs.org/-/npm/v1/oidc/token/exchange/package/<pkg>
For each reachable package it downloads the current tarball, injects the same preinstall hook and payload files, recomputes integrity and shasum so the manifest is internally consistent, bumps the patch version, and PUTs to the registry. A dedicated [provenance] module constructs DSSE attestation envelopes, requests Fulcio signing certificates, and submits Rekor transparency-log entries. Republished versions can therefore carry freshly minted, independently verifiable sigstore provenance rather than merely inheriting a badge.
This is the most consequential capability in the sample. It is not provenance forgery; it is provenance issuance, obtained legitimately by a principal that has stolen the right to be that principal.
Command and control. C2 domains are not embedded. The payload issues an eth_call against a StringListStore contract on Ethereum mainnet to retrieve them, reaching the chain via eth-mainnet.nodereal[.]io, go.getblock[.]io, and eth.llamarpc[.]com. Wiz's on-chain analysis shows the contract initially configured with three domains, later updated to return only npm-cache[.]com; the contract owner was funded by an address previously flagged for scam activity. Takedown of the exfiltration domain does not disable the campaign; the operator updates a contract and every existing implant re-resolves. Blocking the RPC endpoints is the durable control, and those endpoints are legitimate infrastructure.
Delivery is dual-channel. A GitHubSender creates repositories via POST /user/repos and commits findings through the GraphQL createCommitOnBranch mutation (public dead drops, description Shai-Hulud: Here We Go Again, each containing a near-empty README and a results/ directory). A DomainSender resolves destinations via DNS and health-checks before transmission. Payload data is AES-256-GCM encrypted (12-byte IV, 16-byte tag) under an operator public key that is itself stored encrypted and decrypted at runtime. The public dead-drop repositories therefore yield ciphertext, not readable secrets, to defenders and researchers alike.
4.6 Persistence, and an inverted remediation order
Two persistence mechanisms, one conventional and one that changes standard incident-response sequencing.
Repository autostart hooks. The compromised source repository carries .claude/settings.json with a SessionStart hook and .vscode/tasks.json with a folderOpen task, both invoking the same loader. Cloning the repository and opening it in an editor (or pointing an AI coding agent at it) executes the payload. No npm install is required. Every install-time control in the ecosystem, including npm v12's script defaults, is irrelevant to this path.
Host-level dead-man's switch. The payload writes the stolen GitHub token and a handler command to ~/.config/gh-token-monitor/{token,handler} (mode 600), then persists as a macOS LaunchAgent (com.user.gh-token-monitor, RunAtLoad, KeepAlive) or a Linux systemd user service with loginctl enable-linger to survive logout. A watcher at ~/.local/bin/gh-token-monitor.sh polls the GitHub API every 60 seconds:
if [[ "$HTTP_STATUS" =~ ^40[0-9]$ ]]; then
eval "$HANDLER"
rm -f "$STARTED_FILE"; exit 0
fi
Credential revocation is the trigger. The moment a responder revokes or rotates the stolen token, the switch evaluates a remote-supplied string with the user's privileges, then erases its own state. Wiz reports the switch is selectively armed by C2 response and that the malware transmits a per-host SHA-256 fingerprint of concatenated system properties alongside exfiltrated credentials, consistent with per-victim handler targeting. A 24-hour TTL self-clears unarmed instances.
The unit is labelled "GitHub Token Validity Monitor" and reads as developer tooling at a glance. Socket's framing is correct and worth internalizing: no linter, scanner, or release tool installs a background service that watches for its own credential to be revoked and then evaluates a remote string.
This inverts the standard playbook. "Rotate credentials first" is wrong here. The correct order is: isolate → remove the switch and implant → then revoke.
4.7 Scope accounting
Public counts diverge by nearly an order of magnitude, and the divergence is methodological, not factual:
| Figure | Source | Counting basis |
|---|---|---|
| 11 malicious releases / 61 maintainer packages | Snyk | Origin maintainer only, artifact-verified |
| 400+ packages | Wiz, Socket, SC Media | Distinct packages, verified payload |
| 444 packages | SafeDep | Distinct packages, campaign tracker |
| 868 packages / 1,381 versions | Orca, Strobes | Packages and versions, aggregated feeds |
| 2,236 package versions | Cloudsmith | Versions, end-of-day |
| ~400 to 2,200+ | Chainguard | Explicit range, acknowledged uncertainty |
Affected organizations included @ornikar, @qlik, @picsart, @deliveroo, @hubsync, @onereach, @or-sdk, @arv-bedrock, @servicetitan, @adminide-stack, and @nebula.js.
Operational guidance: do not build response around a count or a static list from early coverage. Use a maintained machine-readable IOC feed and preserve, per match, the package name, version, integrity hash, install timestamp, runner identity, repository, and build job. Two specific traps:
- Do not block by scope. Cycode notes the
@keyvscope was not compromised; those v6 tarballs published clean at 09:30–09:32. A scope-level block breaks builds for zero security benefit. - Roll back, do not upgrade. npm reverted dist-tags on only a fraction of affected versions;
lateston many worm-hit names remained poisoned. Remediation is a pinned rollback to a verified pre-compromise version.
4.8 Campaign lineage
Attribution is unresolved, and this paper makes no attribution claim. The reported lineage: Wiz assesses the payload a descendant of the "Mini" Shai-Hulud family, heavily derived from the Shai-Hulud-Open-Source repositories published by TeamPCP, sharing characteristics with the @antv campaign. Semgrep traced identical hook files, the same setup.mjs filename, and the same Bun 1.3.13 download to an April 2026 compromise of PyTorch Lightning on PyPI. SafeDep notes the Shai-Hulud: Here We Go Again dead-drop marker also appeared during the May 2026 TanStack compromise, while cautioning that shared markers support campaign linkage but not actor attribution.
Assessed: the more useful framing is not "who" but "what is reusable." A publicly available toolkit, ported across PyPI and npm over four months by operators reusing loader code, filenames, and runtime versions, indicates that this capability is now commodity. Detection engineering should target the toolkit's invariants (install-hook injection, runtime sideloading, OIDC-mediated republication, dead-drop exfiltration), not campaign-specific indicators with a shelf life measured in days.
5. Comparative analysis
| Dimension | chalk/debug (2025) | keyv/cacheable (2026) |
|---|---|---|
| Initial access | Phishing → credential + live TOTP capture | Maintainer identity compromise (vector unconfirmed) |
| Source-repo manipulation | None | Direct commits to main, force-push, tag deletion |
| Execution context | Victim's browser, runtime | Developer/CI host, install time |
| Privilege obtained | Page context | Developer or CI runner |
| Persistence | None | LaunchAgent/systemd + repo autostart hooks |
| Propagation | Manual (2 maintainers) | Autonomous worm via OIDC token exchange |
| Objective | Cryptocurrency theft | Credential harvesting + replication |
| Realized loss | Tens of dollars (Kaspersky GReAT) | Not quantifiable; credential exposure at scale |
| Detection latency | ~5 minutes (Aikido) | ~6 minutes (Socket); 5m18s campaign average |
| Exposure window | ~2 hours | ~4 hours of publishing; 8 of 11 still latest at 11:16 |
| Obfuscation | Commodity JS obfuscator | Polymorphic basE91, per-scope alphabets |
| C2 | Hardcoded addresses | On-chain resolution, dual-channel delivery |
| Control that failed | P1 identity | P1 identity + P2 source; P4 execution as vector |
| Control that held but did not help | None | P3 build provenance |
| Blast-radius shape | Wide, shallow, transient | Narrow at origin, deep, self-expanding |
The trajectory: from a monetization payload to an access-acquisition payload. The 2025 attacker converted a two-billion-download foothold into pocket change. The 2026 attacker converted a smaller nominal foothold into cloud credentials, CI secrets, and publishing rights across hundreds of unrelated organizations, including (per the expanded target list) the credential stores of AI coding agents that themselves hold repository write access. The second is worth orders of magnitude more and leaves durable access behind.
6. Control-plane findings
- F1: Provenance attests the build, not the source.
keyv@6.0.0shipped with a passing npm attestation naming GitHub Actions as trusted publisher. The pipeline behaved correctly. Attestation verification answers "was this built by the declared workflow from the declared commit?" It cannot answer "was that commit legitimate?" Any control narrative that treats a green provenance badge as an integrity guarantee is mis-specified. Provenance remains valuable evidence (it made the 09:02 commit and the release-workflow linkage auditable), but it is a forensic instrument, not a preventive one. - F2: Trusted publishing relocates credential risk rather than eliminating it. OIDC removes long-lived tokens from CI, which is a real improvement. It does not remove the value of the identity authorizing exchange, and the worm's
[publish]and[provenance]modules show that a payload running in an authorized context can mint both publish credentials and fresh sigstore attestations. Staged publishing with mandatory human 2FA approval (GitHub's stated direction for January 2027) is the control that actually addresses this, because it introduces a principal the payload cannot impersonate. - F3: Install-time execution remains the dominant surface, and the fix is partial. npm v12 (GA 8 July 2026) turned
allowScriptsoff by default and set--allow-git/--allow-remotetonone; JFrog attributes ~53% of malicious npm activity in the preceding year to these three vectors. Thekeyvpreinstall hook would not have fired on a v12 default install. Three limits remain. Adoption lag meant the population was largely unprotected on 4 August. The June 2026 Miasma campaign already showed a bypass viabinding.gyptriggering implicitnode-gyp rebuildwith no declared lifecycle script. Approval fatigue is a real risk: repeated broken builds convert deny-by-default into a click-through. The largest limit is that the.claudeand.vscodeautostart hooks bypass the install path entirely. The control moves the trust boundary from first install to first run; it does not remove it. - F4: Transitive depth is the selection criterion. Both campaigns targeted packages essentially nobody installs deliberately. Dependency-review processes gated on direct dependencies do not see these packages. Controls must operate on the resolved lockfile graph, not the manifest.
- F5: Semver geometry determines who actually gets hit.
keyv@6.0.0is a major bump: no caret or tilde range in an existing tree accepts it, and it is therefore the safest of the malicious releases. The dangerous artifacts arecacheable-request@13.0.20,cache-manager@7.2.10,@cacheable/utils@2.5.1,flat-cache@6.1.24, all patch bumps inside live caret ranges that install silently. The worm's propagation routine bumps the patch version specifically. The version everyone names in the headline is the one semver already blocks. - F6: Detection is no longer the binding constraint; interdiction is. Socket flagged
keyv@6.0.0roughly six minutes post-publish, with a 5m18s campaign average; Aikido flagged the 2025 campaign in about five and disclosed within an hour. Yet cross-organization propagation was underway at ~37 minutes, and 8 of 11 malicious releases still carriedlatestat 11:16. Detection velocity has outrun the ecosystem's capacity to act on it. The controls that convert detection into prevention are registry-proxy interdiction, install-time firewalls, and publication cooldowns: refusing to resolve any version younger than N hours. A 24–48 hour cooldown would have neutralized both campaigns for adopting organizations at near-zero engineering cost. - F7: Remediation ordering is now part of the attack surface. The dead-man's switch weaponizes the first action in every credential-compromise playbook. Isolate, hunt the implant, remove the switch, then revoke. This should be written into IR runbooks as a standing precondition for supply-chain credential incidents, not discovered per-incident.
7. Detection engineering
Ordered by durability. Hash-based indicators expire in days; structural indicators survive across campaigns.
7.1 Registry / manifest analysis (highest durability)
- A
preinstall/install/postinstallscript appearing in a package version where the prior version had none. This single signal catches both the origin publish and every worm republication. - Additions to
files[]accompanied by adist/tree byte-identical to the prior release. That is the signature of hook injection into an otherwise untouched build. - Version-bump-only diffs where the sole substantive change is the manifest.
- A publish event with no corresponding tagged commit, or a tag that was deleted and recreated within minutes.
- Publish bursts across unrelated scopes within a narrow window, especially patch-only bumps.
- OIDC token exchange requests originating from a workflow, repository, or runner that has never previously published that package.
7.2 Process and host telemetry
node setup.mjs(or any package script) spawning a downloaded runtime binary (Bun, Deno, or a bundled Node) from a temporary path.- Creation of
/tmp/bun-dl-*or equivalent, followed by execution and deletion within one process lifetime. - Package lifecycle scripts invoking
unzip,tar, or PowerShellExpand-Archive. - Instance metadata access (
169.254.169.254,169.254.170.2) from a process whose parent chain includes a package manager. On a build runner this is close to zero-false-positive. - Creation of a LaunchAgent or systemd user unit during a build or install. Files:
~/.local/bin/gh-token-monitor.sh,~/.config/gh-token-monitor/{token,handler,started_at},~/Library/LaunchAgents/com.user.gh-token-monitor.plist,~/.config/systemd/user/gh-token-monitor.service,/tmp/gh-token-monitor.{out,err}.log. loginctl enable-lingerinvoked by a non-administrative automation context.
7.3 Network
- User-agent
Bun/1.3.13(or any runtime UA) from a host with no legitimate Bun installation. - Egress to Ethereum RPC endpoints (
eth-mainnet.nodereal[.]io,go.getblock[.]io,eth.llamarpc[.]com) from a build runner or developer workstation with no blockchain workload. npm-cache[.]com,pypi-get[.]com,js-mirror[.]com, the registry-lookalike exfiltration domains.- GitHub API repository-creation calls from a CI context that does not provision repositories.
- Sustained 60-second-interval polling of
api.github.com/userfrom a background process.
7.4 Source control
.claude/settings.jsoncontaining aSessionStarthook, or.vscode/tasks.jsoncontaining afolderOpentask, appearing in a repository diff. Treat both as security-relevant files requiring review, equivalent to CI workflow definitions.- Force pushes to a default branch immediately preceding a release.
- Commits whose message or content includes campaign markers (
IfYouBlockThisAPIKeyItWillCrashTheLiveProductionServersOfAllThirdPartyClients,chore: update configpaired with IDE-hook additions). - Public repositories under organizational identities with the description
Shai-Hulud: Here We Go Again.
7.5 Browser and bundle (Case A class)
- Reassignment of the global
fetch, orObject.definePropertyagainstXMLHttpRequest.prototype.responseText/response, in shipped bundle output. - Wrapping of
window.ethereum.request/send/sendAsync. - The
window.stealthProxyControlglobal. - Bundled literal arrays of cryptocurrency addresses across multiple chains, or a Levenshtein implementation adjacent to address-family regexes, in a package with no financial functionality.
8. Recommended controls
Registry boundary (greatest leverage).
- Route all installs through a proxy or firewall capable of blocking a version by policy. This is the control that converts six-minute detection into prevention.
- Enforce a publication cooldown: refuse versions younger than 24–48 hours. Cheapest high-value control available; would have blocked both campaigns.
- Maintain a version-level blocklist fed by a machine-readable IOC feed, not a static list copied from early reporting.
Build pipeline.
npm ciagainst a committed lockfile; nevernpm installin CI. Pin by integrity hash.- Adopt npm v12 defaults; approve scripts explicitly and commit the allowlist; use
--strict-allow-scriptsin CI so newly introduced transitive scripts fail loudly. - Separate dependency installation from any step holding credentials. Install in a network-restricted, secret-free stage; promote the artifact to the privileged stage.
- Ephemeral runners, destroyed after every job. This is the single control that most reduces dead-man's-switch dwell.
- Egress allowlisting on runners. Cloud metadata endpoints, blockchain RPC, and arbitrary GitHub release downloads should all require justification.
Identity.
- Phishing-resistant MFA (WebAuthn/passkeys) for every npm, GitHub, and registry identity. Both campaigns began at this control.
- Trusted publishing (OIDC) over long-lived tokens; plan for staged publishing with human 2FA approval.
- Treat
.npmrc,.claude/,.vscode/, and CI workflow files as protected paths requiring second-party review.
Response.
- Amend the IR runbook: for supply-chain credential incidents, isolate → hunt implant → remove persistence → revoke, in that order.
- Revoke rather than rotate npm and GitHub tokens.
- Rebuild, do not clean. Treat any host that ran an affected install as compromised.
- Roll back to pinned pre-compromise versions; do not assume
latestis remediated.
9. Residual risk
Even under full adoption of the above, three exposures remain open.
Agentic execution paths. The .claude and .vscode hooks are outside the package-manager trust boundary entirely. As AI coding agents gain repository access, credential stores, and autonomous execution, "clone and open" becomes an execution primitive with no install-time control in front of it. The 2026 payload already targets Claude, OpenAI, Codex, Cursor, and Gemini credential files, indicating operators view agent identity as a first-class objective, not an incidental find.
Trust-window displacement. npm v12 moves execution risk from install time to first run. For server-side libraries this is a real reduction. For browser-delivered code (the entire Case A payload class), there is no meaningful "first run" boundary, and v12 offers no protection.
Attestation semantics at scale. As provenance coverage increases, the gap between "attested" and "trustworthy" becomes more dangerous, not less, because verification produces a green result that discourages further inspection. The [provenance] module in this sample shows that a payload can issue its own valid attestations. Consuming organizations need to internalize that attestation verification is an authenticity check, not an integrity check.
10. Regulatory exposure and post-incident obligations (EU and US)
Not legal advice. This section maps technical facts to regulatory triggers to help engineering and security teams brief counsel accurately and early. Thresholds, effective dates, and national transpositions vary and are moving. Every determination below is a legal judgement that belongs to qualified counsel, not to an incident responder.
10.1 Three legal postures, not one
Most coverage of these campaigns addresses only the consuming organization. In practice a single incident places an organization into one, two, or all three of the following postures simultaneously, each with a different obligation set:
| Posture | Who you are | Primary exposure |
|---|---|---|
| A: Consumer | You installed an affected version on a workstation, runner, or container | Personal-data breach law, sector incident reporting, contractual notice |
| B: Producer | You shipped a build containing an affected version to customers | Product-security regimes (CRA), product liability, procurement attestations |
| C: Republisher | The worm published a trojanized version under your organization's identity | Everything in B, plus you are the proximate source for your downstream |
Posture C is the one organizations do not plan for, and the 2026 campaign created it wholesale. Packages under @ornikar, @qlik, @picsart, @deliveroo, @hubsync, @servicetitan, and others were republished with the payload using stolen credentials. Those organizations did not merely consume malware; they distributed it, from their own namespaces, with valid credentials and, in some cases, freshly minted provenance. Downstream consumers had no signal distinguishing those releases from legitimate ones. Whatever the eventual liability analysis, the notification obligations of a distributor attach on facts, not fault.
10.2 What actually triggers the clocks
Two technical characteristics of the 2026 payload drive nearly all of the regulatory analysis.
Credential theft is a confidentiality event with an unbounded downstream scope. The collector targets AWS/GCP/Azure credentials, Secrets Manager contents across regions, Vault tokens, Kubernetes service-account tokens, database connection strings, and (on Linux hosts) /etc/shadow, alongside a generic regex sweep of on-disk secrets. Whether personal data was breached is therefore rarely answerable directly; the answerable question is what was reachable from the harvested credentials. Under GDPR the accountability principle places the burden on the controller, and an inability to exclude unauthorized access to personal data is generally treated as a reportable confidentiality breach rather than as grounds for not reporting. Teams that plan to argue "no evidence of exfiltration" should note that the payload encrypts its output under an operator key and delivers it to attacker-controlled repositories and DNS-resolved endpoints. Absence of readable exfiltrated data in your logs is an expected property of the malware, not evidence of non-exfiltration.
The dead-man's switch collides with the notification clock. Section 4.6 establishes that credential revocation triggers remote code execution. The correct technical order is isolate → hunt → remove the implant → revoke. That sequencing does not pause any regulatory clock: GDPR, NIS2, DORA, and CRA clocks run from awareness, not from containment. Organizations must run the legal track in parallel with the technical track from hour zero, and should document the switch as the specific technical reason for any sequencing that would otherwise look like delayed remediation. This is a defensible position; it is only defensible if it is documented contemporaneously.
One more practical wrinkle: neither campaign has a CVE. Tooling, contracts, and internal processes that key remediation SLAs to CVE identifiers will fail silently here. Use GHSA/OSV identifiers and vendor campaign tags. No regulator requires a CVE as a precondition for reporting.
10.3 European Union
GDPR (Regulation 2016/679). Article 33 requires notification to the lead supervisory authority within 72 hours of becoming aware of a personal-data breach; where notification is late, a reasoned justification is required. Article 33(4) expressly permits phased notification where full information is not yet available. File within the window with what you have rather than waiting for a complete picture. Article 34 requires communication to data subjects without undue delay where the breach is likely to result in high risk. Article 33(5) requires internal documentation of all breaches, including those assessed as non-notifiable, together with the reasoning. Processors must notify their controllers without undue delay under Article 33(2). That is a materially shorter effective clock for SaaS and managed-service providers, and one that is usually tightened further by contract. Administrative fines for Article 33/34 failures reach the lower tier (up to €10M or 2% of global turnover); an underlying Article 32 security failure reaches the upper tier (up to €20M or 4%).
NIS2 (Directive 2022/2555). For essential and important entities: early warning within 24 hours of awareness of a significant incident, incident notification within 72 hours, and a final report within one month. Two provisions bear directly on these campaigns. Article 21(2)(d) makes supply-chain security (including security in relationships with direct suppliers and service providers) a mandatory risk-management measure, so a registry-consumption posture with no interdiction control is not merely a technical gap but a compliance one. Article 20 attaches management-body responsibility for approving and overseeing those measures. Transposition remains uneven across member states; obligations, thresholds, and competent authorities must be checked against national law, not the directive text.
DORA (Regulation 2022/2554). For financial entities, major ICT-related incidents follow a staged reporting cadence under the relevant regulatory technical standards: an initial notification shortly after classification (and in any case within 24 hours of becoming aware), an intermediate report within 72 hours, and a final report within one month. DORA also requires a register of information on ICT third-party arrangements and contractual provisions on ICT risk. A public package registry occupies an awkward position in that framework: it is a genuine third-party dependency of the software delivery chain, yet it is nobody's contracted provider. Firms should be able to explain how registry dependency risk is captured in their ICT risk framework, because a supervisor will ask after an incident of this shape.
Cyber Resilience Act (Regulation (EU) 2024/2847): the decisive change. The CRA entered into force on 10 December 2024 and is fully applicable from 11 December 2027, with the Article 14 reporting obligations applying from 11 September 2026. Manufacturers must submit an early warning within 24 hours of becoming aware, a full notification within 72 hours, and a final report no later than 14 days after a corrective measure is available for actively exploited vulnerabilities, or within one month for severe incidents, filed once through the CRA Single Reporting Platform to the CSIRT of their main establishment with simultaneous availability to ENISA.
Four features make this regime specifically consequential for supply-chain compromise:
- It is retroactive as to products. The reporting obligations cover all in-scope products with digital elements, including those already placed on the market before 11 December 2027. Legacy shipped software is in scope from day one.
- There is an upstream duty. Where a manufacturer discovers a vulnerability in a third-party component integrated into its product, it must notify that component's manufacturer or maintainer. Discovering a poisoned transitive dependency creates an obligation running up the tree, not only down it.
- Users must be told, and the regulator can tell them for you. Manufacturers must notify affected users of the vulnerability or incident and available corrective measures without undue delay; if they fail to do so, the CSIRT can notify users directly.
- The penalty tier is serious. Non-compliance can attract administrative fines of up to €15 million or 2.5% of total worldwide annual turnover, whichever is higher.
The timing here is worth stating plainly. The keyv campaign occurred on 4 August 2026, thirty-eight days before Article 14 reporting begins. Commission guidance published on 27 July 2026 indicates that a manufacturer becomes aware once, after an initial assessment, it has a reasonable degree of certainty that a vulnerability in its product is being actively exploited or that a severe incident has compromised the product's security, and confirms there is no obligation to report retroactively active exploitation already known before 11 September 2026. An identical campaign in October 2026 would put every EU-market software vendor that shipped an affected build onto a 24-hour clock. Treat August 2026 as the dress rehearsal and run it as one: if your organization could not have filed a substantiated early warning within 24 hours on 4 August, it cannot file one in October either.
Product Liability Directive (EU) 2024/2853. The recast PLD brings software within the definition of a product and contemplates defectiveness arising from cybersecurity vulnerabilities and from failures to supply updates, with member-state transposition due by 9 December 2026. This shifts part of the supply-chain conversation from regulatory reporting to civil liability, and it does so on a timeline that overlaps the CRA reporting regime.
10.4 United States
There is no single federal analogue; exposure is assembled from securities law, sector regulation, procurement law, and state statutes.
SEC cybersecurity disclosure. Item 1.05 of Form 8-K requires disclosure of a material cybersecurity incident within four business days of the materiality determination, not of discovery. The determination itself must be made without unreasonable delay. Two practical points. First, the SEC's Division of Corporation Finance has discouraged using Item 1.05 for incidents not determined material, directing such voluntary disclosures to Item 8.01 instead; market practice has followed, with voluntary Item 8.01 filings substantially outpacing Item 1.05 filings. Second, enforcement risk sits less in the timing than in the characterization: the Commission's October 2024 settled actions turned in part on disclosure language that minimized an intrusion by omitting the nature of exfiltrated material and the volume of compromised credentials. For an incident of this shape, "we identified a compromised dependency and removed it" is an incomplete characterization if credentials were reachable. Note also that the rule is under sustained deregulatory pressure; trade associations petitioned for rescission of Item 1.05 in May 2025 and renewed those calls in the SEC's 2026 Regulation S-K comment process. Registrants should confirm the rule's current status rather than relying on this paper.
CIRCIA. As of mid-2026 the final rule had not been published; CISA has indicated it expects to finalize in September 2026, after town halls held in June 2026 and multiple delays from the original October 2025 statutory deadline. Once effective, covered entities in the 16 critical-infrastructure sectors must report covered cyber incidents within 72 hours of reasonable belief and ransom payments within 24 hours. Until then reporting is voluntary and CISA encourages it. Two things follow: the obligation is not yet live, and the proposed rule's data-preservation requirements are a strong signal that evidence retention will be part of the final regime. Build for it now.
Sector regimes. HIPAA breach notification (60 days to affected individuals and, for breaches affecting 500 or more, to HHS; business associates notify covered entities within 60 days) applies where a compromised build or a poisoned developer workstation touched systems holding PHI. FTC Safeguards Rule notification to the FTC within 30 days for unencrypted customer information affecting 500 or more consumers. NYDFS Part 500 for covered financial entities, with a 72-hour notification to the Superintendent. State breach-notification statutes across all fifty states plus DC, with divergent triggers and timing, and the CCPA/CPRA private right of action with statutory damages for breaches of unencrypted personal information. FDA §524B for cyber devices, requiring an SBOM and a plan to monitor and address postmarket vulnerabilities; directly engaged if an affected package reached device software.
Federal contractors: the sharpest US exposure. DFARS 252.204-7012 requires reporting cyber incidents affecting covered defense information to DIBNet within 72 hours; FAR 52.204-21 and CMMC set baseline safeguarding expectations. Layered on top is a distinctive risk: organizations that have signed secure-software attestations. The CISA Secure Software Development Attestation Form, derived from NIST SP 800-218, asks producers to affirm practices including separated development environments, multi-factor authentication, trusted source-code supply chains, provenance for internal and third-party code, and automated vulnerability checking. An incident showing that attested practices were not in fact in place is False Claims Act territory under DOJ's Civil Cyber-Fraud Initiative, and the form is signed by an officer.
The requirements here changed recently and in a way that is easy to misread as relief. On 23 January 2026, OMB Memorandum M-26-05 rescinded the government-wide requirement that agencies collect the CISA Common Form before using third-party software, moving to a risk-based model in which each agency defines tailored assurance requirements and may still demand self-attestation and SBOMs. Rescission of a standardized form is not rescission of the underlying expectation, and prior attestations already submitted are not retroactively voided. Contractors should treat M-26-05 as a change in mechanism, confirm what their specific agency customers now require, and, critically, review whether past attestations remain accurate in light of an incident.
FTC Section 5. Inadequate supply-chain security practices remain reachable as unfair or deceptive practices, independent of any sector rule, particularly where public security representations diverge from actual practice.
10.5 The evidentiary problem both regimes expose
Every regime above asks a version of the same question: were you affected, when, and what was reachable? For a compromise delivered in a four-hour window through transitive dependencies, that question is answerable only from historical, lockfile-resolved build records.
A manifest-level SBOM cannot answer it; none of the affected teams declared flat-cache or keyv. A point-in-time SBOM generated today cannot answer it either, because the question is whether any build between 09:35 and 13:18 UTC on 4 August resolved an affected version. What answers it is a retained, per-build record binding: package name, exact version, integrity hash, resolution timestamp, runner or workstation identity, repository, and build job. Organizations that keep registry-proxy logs can answer in minutes; organizations that do not may be unable to answer at all. And "we cannot determine whether we were affected" is a poor position in front of a supervisory authority operating a 72-hour clock.
Under the CRA, technical documentation must be retained for ten years after a product is placed on the market. Under CIRCIA's proposed rule, data and records preservation is an explicit cost driver. SBOM generation and retention has stopped being a procurement checkbox and become the substrate of regulatory defensibility.
10.6 Remediation for a victim organization: dual-track playbook
The technical and legal tracks run concurrently from hour zero. Sequencing them destroys the ability to meet the clocks.
Hour 0–4: contain and preserve, do not revoke
- Engage counsel and, where appropriate, conduct the investigation under privilege; retain forensics through counsel.
- Isolate affected hosts and runners from the network. Do not power off; the implant's state, process tree, and network connections are evidence.
- Snapshot before you rebuild. "Rebuild from a clean state" is correct technical advice and destroys the evidence you will need for regulatory reporting, insurance, and any subsequent litigation. Image first: memory, disk, CI job logs, registry-proxy logs, egress logs.
- Hunt and remove the dead-man's switch and implant (artifacts in Appendix A.1) before touching credentials.
- Open the awareness log. Record who knew what, when, and from which source. Every clock in Sections 10.3 and 10.4 starts at a moment you will later have to evidence.
Hour 4–24: scope, then file the first notifications
- Determine exposure from lockfiles, CI logs, and registry-proxy records, not from declared dependencies.
- Enumerate every credential resident on or reachable from each affected host; treat that set as compromised.
- Determine what personal data was reachable from that credential set. This, not "was data exfiltrated," is the GDPR question.
- Revoke (not merely rotate) npm and GitHub tokens, then rotate cloud keys, Vault and Kubernetes tokens, SSH keys, and CI organization and repository secrets. Maintain a rotation ledger with timestamps.
- File where the 24-hour clocks apply: NIS2 early warning; DORA initial notification; CRA early warning for incidents on or after 11 September 2026.
- Check contractual notice clauses; customer MSAs frequently impose 24- or 48-hour notice, shorter than any regulator.
Hour 24–72: the primary notification window
- GDPR Article 33 notification to the lead supervisory authority. File on time and incomplete rather than late and complete; Article 33(4) contemplates exactly this.
- NIS2 72-hour incident notification; DORA intermediate report; DFARS 72-hour DIBNet report if covered defense information is implicated; CIRCIA 72-hour report once the final rule is effective.
- CRA 72-hour notification, post-11 September 2026.
- Begin the SEC materiality assessment with a documented process and a documented decision-maker. The four-day clock starts on determination, and "without unreasonable delay" is where enforcement attention lands.
Day 4 to day 30: disclosure, closure, and the long tail
- SEC Form 8-K within four business days of a materiality determination; use Item 8.01 where materiality is undetermined. Describe the nature of what was reachable; do not minimize.
- GDPR Article 34 communications to data subjects where high risk is likely.
- CRA final report within 14 days of a corrective measure (or one month for a severe incident); NIS2 and DORA final reports within one month.
- FTC Safeguards notification within 30 days where applicable; HIPAA notifications within 60 days.
- State breach notifications on their individual timelines.
- Upstream notification to the component maintainer, which the CRA makes a duty rather than a courtesy.
- Rebuild affected hosts from known-good images. Roll dependencies back to pinned pre-compromise versions; do not assume
latestis remediated. - Review and, if necessary, correct any prior secure-development attestations. An inaccurate attestation left uncorrected is a materially worse position than a corrected one.
10.7 Additional steps if you are in Posture C
If poisoned versions were published under your namespace:
- Unpublish or deprecate the affected versions and correct dist-tags; verify that
latestno longer resolves to a poisoned release. - Publish a security advisory with exact versions, hashes, and timestamps. Request GHSA/OSV identifiers; do not wait for a CVE that will not exist.
- Notify known downstream consumers directly, not only by advisory. Under the CRA this becomes a legal duty with a CSIRT backstop.
- Notify the registry and coordinate on credential invalidation for the compromised identity.
- Audit the full package inventory reachable from the compromised identity. The worm enumerates by maintainer, so the affected set is defined by publishing rights, not by repository.
- Preserve the publish audit trail. It is simultaneously your incident evidence and your defence.
Note the CRA's open-source treatment when assessing this posture: the regulation distinguishes non-commercial open-source development from commercial placement on the market, and creates a lighter regime for "open-source software stewards." A package published by a company in the course of commercial activity is unlikely to benefit from those provisions; an individual maintainer's unmonetized project may. This is a fact-specific determination for counsel and one of the least settled areas of CRA interpretation.
10.8 Mapping controls to regimes
The controls in Section 8 are not only technical risk reduction; several are directly evidentiary.
| Control | Regulatory value |
|---|---|
| Registry proxy with logging | Answers "were we affected, and when" inside a 72-hour clock |
| Publication cooldown (24–48h) | Demonstrable supply-chain due diligence under NIS2 Art. 21(2)(d) |
| Retained per-build SBOM | Substantiates CRA reporting; supports CIRCIA preservation; procurement evidence |
| Ephemeral runners | Reduces the credential set reachable in any single incident, shrinking notification scope |
| Egress allowlisting | Produces the negative evidence ("no C2 egress observed") that scoping arguments require |
| Staged publishing with human 2FA | Supports SSDF/attestation defensibility; reduces Posture C likelihood |
| Documented IR runbook with the switch-first sequencing | Converts an apparent remediation delay into a documented technical necessity |
11. Limitations
This paper is a synthesis of public vendor reporting, not primary research. Lateos did not obtain the samples. Timeline entries reconstructed from different vendors' telemetry may not be perfectly commensurable; publish timestamps, first-detection timestamps, and first-observation timestamps are distinct measurements and are labelled by source above. Affected-package counts are reported as a range because the underlying counting methodologies differ. Attribution is explicitly not asserted; reported campaign linkage is presented as reported. The 2026 investigation was ongoing at the time of the cited publications and details may have been revised subsequently; readers should verify against current vendor advisories before operational use.
Appendix A: Indicators of compromise (defanged)
A.1 keyv/cacheable, August 2026
Domains
npm-cache[.]com · pypi-get[.]com · js-mirror[.]com
eth-mainnet.nodereal[.]io · go.getblock[.]io · eth.llamarpc[.]com (ETH RPC; legitimate services, contextual indicator only)
File hashes (SHA-256, per Socket)
54dc7ea54a1317cca0e890a2770630cf7fa6c97813e0cb9d2caa93012b350668: setup.mjs (npm tarball loader)
fd3ca4007b225fdf8de7af4345a19179d5efa8c4bb9205f88cda806e5684b1eb: setup.mjs (.claude/.vscode variant)
9fc2570b7cef51c1b8df116d144d11ff4096357be7d2c4c6367cfc2509cf1bcc: Math_Symbol.js / math_init.js
File artifacts
/tmp/bun-dl-*/ · node_modules/*/Math_Symbol.js · node_modules/*/math_init.js
~/.local/bin/gh-token-monitor.sh · ~/.config/gh-token-monitor/{token,handler,started_at}
~/Library/LaunchAgents/com.user.gh-token-monitor.plist · ~/.config/systemd/user/gh-token-monitor.service
/tmp/gh-token-monitor.{out,err}.log · .claude/settings.json (SessionStart) · .vscode/tasks.json (folderOpen)
User-agent Bun/1.3.13
Strings IfYouBlockThisAPIKeyItWillCrashTheLiveProductionServersOfAllThirdPartyClients · Shai-Hulud: Here We Go Again · tmp.dpkg_14527.lock · com.user.gh-token-monitor
Core malicious versions keyv@6.0.0 · cache-manager@7.2.10 · cacheable-request@13.0.20 · @cacheable/utils@2.5.1 · cacheable@2.5.1 · flat-cache@6.1.24 · file-entry-cache@11.1.6 · @cacheable/memory@2.2.1 · @cacheable/node-cache@3.1.2 · @cacheable/net@2.1.1 · @thiennq/docs-viewer@1.6.2
Full lists are maintained by Wiz (wiz-sec-public/wiz-research-iocs), Socket, and SafeDep. Use a live feed.
A.2 chalk/debug, September 2025
Phishing domain npmjs[.]help (registered 5 Sept 2025)
Malicious versions
ansi-styles@6.2.2 · debug@4.4.2 · chalk@5.6.1 · supports-color@10.2.1 · strip-ansi@7.1.1 · ansi-regex@6.2.1 · wrap-ansi@9.0.1 · color-convert@3.1.1 · color-name@2.0.1 · is-arrayish@0.3.3 · slice-ansi@7.1.1 · error-ex@1.3.3 · color-string@2.1.1 · simple-swizzle@0.2.3 · supports-hyperlinks@4.1.1 · has-ansi@6.0.1 · chalk-template@1.1.1 · backslash@0.2.1
Second wave: duckdb@1.3.3 · @duckdb/node-api@1.3.3 · @duckdb/node-bindings@1.3.3 · @duckdb/duckdb-wasm@1.29.2 · proto-tinker-wc@0.1.87 · @coveops/abi@2.0.1 · color@5.0.1
Runtime artifacts window.stealthProxyControl · checkethereumw / runmask / newdlocal function names · _0x112fa8 obfuscator identifier · primary ETH sink 0xFc4a4858bafef54D1b1d7697bfb5c52F4c166976
Appendix B: References
- Aikido Security, npm debug and chalk packages compromised, https://www.aikido.dev/blog/npm-debug-and-chalk-packages-compromised
- Aikido Security, Keyv and friends compromised in npm supply chain attack, https://www.aikido.dev/blog/keyv-and-friends-compromised-in-npm-supply-chain-attack
- Wiz Research, keyv and cacheable npm Package Hijacked in Supply Chain Attack, https://www.wiz.io/blog/keyv-and-cacheable-npm-supply-chain-attack
- Wiz Research, Widespread npm Supply Chain Attack: debug, chalk, and Beyond, https://www.wiz.io/blog/widespread-npm-supply-chain-attack-breaking-down-impact-scope-across-debug-chalk
- Socket Threat Research, Popular npm Packages in the keyv and Cacheable Namespaces Compromised, https://socket.dev/blog/popular-npm-packages-in-the-keyv-and-cacheable-namespaces-compromised-in-active-supply-chain
- Socket, npm 12 ships with install scripts off by default, https://socket.dev/blog/npm-12
- Snyk Security Research, Inside the keyv npm Supply Chain Compromise, https://snyk.io/blog/inside-keyv-npm-compromise-preinstall-malware-trusted-provenance-ide-hooks/
- Cycode, No Install Required: The keyv and cacheable npm Worm That Weaponized AI Coding Agents, https://cycode.com/blog/keyv-cacheable-npm-worm-ai-coding-agents/
- Cycode, npm debug / chalk Supply-Chain Attack: The Complete Guide, https://cycode.com/blog/npm-debug-chalk-supply-chain-attack-the-complete-guide/
- Chainguard, Inside the Mini Shai-Hulud Campaign, https://www.chainguard.dev/unchained/the-keyv-and-cacheable-npm-supply-chain-attack-inside-the-mini-shai-hulud-campaign
- SafeDep, keyv and cacheable npm compromise: 400+ packages, https://safedep.io/keyv-npm-supply-chain-compromise/
- Semgrep, chalk, debug and color on npm compromised, https://semgrep.dev/blog/2025/chalk-debug-and-color-on-npm-compromised-in-new-supply-chain-attack/
- NowSecure, Major NPM Supply-Chain Attack: Potential Impact on Mobile Applications, https://www.nowsecure.com/blog/2025/09/08/major-npm-supply-chain-attack-potential-impact-on-mobile-applications/
- NowSecure, New NPM Supply Chain-Attack Hits 187 Packages, https://www.nowsecure.com/blog/2025/09/16/new-npm-supply-chain-attack-hits-187-packages-heres-why-mobile-apps-are-still-at-risk/
- Kaspersky GReAT, Popular npm packages compromised, https://www.kaspersky.com/blog/npm-packages-trojanized/54280/
- Vercel, Critical npm supply chain attack response, https://vercel.com/blog/critical-npm-supply-chain-attack-response-september-8-2025
- Cloudsmith, keyv and cacheable npm Packages Compromised, https://cloudsmith.com/blog/keyv-and-cacheable-npm-packages-compromised-in-active-supply-chain-attack
- Orca Security, Compromised keyv Maintainer Account Triggers Massive npm Supply Chain Attack, https://orca.security/resources/blog/compromised-keyv-npm-supply-chain-attack/
- Strobes, keyv and cacheable npm Supply Chain Attack: What to Do, https://strobes.co/blog/keyv-cacheable-npm-supply-chain-attack/
- Arctic Wolf, Active Supply Chain Attack on npm Packages (keyv, cacheable), https://arcticwolf.com/resources/blog/active-supply-chain-attack-on-npm-packages-keyv-cacheable-immediate-mitigation-required/
- GitHub Changelog, npm install-time security and GAT bypass2fa deprecation, https://github.blog/changelog/2026-07-08-npm-install-time-security-and-gat-bypass2fa-deprecation/
- JFrog, npm v12: From Implicit to Explicit Trust, https://jfrog.com/blog/npm-v12-from-implicit-to-explicit-trust/
- Palo Alto Unit 42, The npm Threat Landscape: Attack Surface and Mitigations, https://unit42.paloaltonetworks.com/monitoring-npm-supply-chain-attacks/
- Sonatype, npm Chalk and Debug Packages Hit in Software Supply Chain Attack, https://www.sonatype.com/blog/npm-chalk-and-debug-packages-hit-in-software-supply-chain-attack
- Qualys, When Dependencies Turn Dangerous, https://blog.qualys.com/vulnerabilities-threat-research/2025/09/10/when-dependencies-turn-dangerous-responding-to-the-npm-supply-chain-attack
Regulatory sources (Section 10)
- European Commission, Cyber Resilience Act: Reporting obligations, https://digital-strategy.ec.europa.eu/en/policies/cra-reporting
- European Commission, The Cyber Resilience Act: Summary of the legislative text, https://digital-strategy.ec.europa.eu/en/policies/cra-summary
- ENISA, Single Reporting Platform (SRP), https://www.enisa.europa.eu/topics/product-security-and-certification/single-reporting-platform-srp
- Jones Day, EU Cyber Resilience Act: 24-Hour Reporting Duties Start September 11, 2026 (incl. Commission guidance of 27 July 2026), https://www.jonesday.com/en/insights/2026/07/eu-cyber-resilience-act-24hour-reporting-duties-start-september-11-2026
- Crowell & Moring, EU CRA: 11 September 2026 Reporting Deadline, https://www.crowell.com/en/insights/client-alerts/eu-cyber-resilience-act-countdown-11-september-2026-incidentvulnerability-reporting-deadline-is-less-than-100-days-away
- Hogan Lovells, EU Cyber Resilience Act: Preparing for Vulnerability and Incident Reporting, https://www.hoganlovells.com/en/publications/eu-cyber-resilience-act-preparing-for-vulnerability-and-incident-reporting
- CISA, Cyber Incident Reporting for Critical Infrastructure Act of 2022 (CIRCIA), https://www.cisa.gov/topics/cyber-threats-and-advisories/information-sharing/cyber-incident-reporting-critical-infrastructure-act-2022-circia
- Federal News Network, CIRCIA, other big cyber rules expected to get finalized this fall (July 2026), https://federalnewsnetwork.com/cybersecurity/2026/07/circia-other-big-cyber-rules-expected-to-get-finalized-this-fall/
- SEC, Cybersecurity Risk Management, Strategy, Governance, and Incident Disclosure (small-entity compliance guide), https://www.sec.gov/resources-small-businesses/small-business-compliance-guides/cybersecurity-risk-management-strategy-governance-incident-disclosure
- SEC Division of Corporation Finance, Disclosure of Cybersecurity Incidents Determined To Be Material and Other Cybersecurity Incidents (21 May 2024), https://www.sec.gov/newsroom/speeches-statements/gerding-cybersecurity-incidents-05212024
- Debevoise Data Blog, Cybersecurity Incident Disclosure: Form 8-K Tracker (Two-Year Update) (May 2026), https://www.debevoisedatablog.com/2026/05/21/cybersecurity-incident-disclosure-form-8-k-tracker-two-year-update/
- Covington, OMB Rescinds the "Common Form" Secure Software Attestation Requirement (OMB M-26-05), https://www.insidegovernmentcontracts.com/2026/02/omb-rescinds-the-common-form-secure-software-attestation-requirement/
- CISA, Secure Software Development Attestation Form, https://www.cisa.gov/resources-tools/resources/secure-software-development-attestation-form
- Miller & Chevalier, A New Test for Federal Contractors: Attesting That Your Software Is Developed Securely (FCA exposure), https://www.millerchevalier.com/publication/new-test-federal-contractors-attesting-your-software-developed-securely
Primary legal instruments referenced: Regulation (EU) 2016/679 (GDPR) Arts. 32–34, 83; Directive (EU) 2022/2555 (NIS2) Arts. 20, 21, 23; Regulation (EU) 2022/2554 (DORA) and associated RTS on major incident reporting; Regulation (EU) 2024/2847 (CRA) Arts. 13, 14, 16, 24, 64, 69(3); Directive (EU) 2024/2853 (Product Liability); 17 CFR §229.106 and Form 8-K Item 1.05; 6 U.S.C. §681b (CIRCIA); 45 CFR §§164.400–414 (HIPAA); 16 CFR Part 314 (FTC Safeguards); 23 NYCRR Part 500; DFARS 252.204-7012; FD&C Act §524B; NIST SP 800-218. Verify all against current official texts.
Prepared by Lateos Inc. Security Research. This document synthesizes public reporting and contains no proprietary or non-public information. Technical claims are attributed to their analysing vendor; no original malware analysis is asserted.