The npm package compromise that began on Tuesday, August 4, 2026 treats the AI coding toolchain as both a target and a hiding place. Wiz reports the worm reached over 400 distinct npm packages after a GitHub maintainer account was compromised. The detail that should change how your team works is smaller and stranger: it attempts to persist through Claude Code hooks and VS Code tasks.
For two years, npm hardening advice has had two chapters. Chapter one is install-time: block lifecycle scripts, pin your lockfile, run a registry proxy. Chapter two, added more recently, is import-time: code that runs the moment a module is required, where --ignore-scripts buys you nothing. This incident opens a third chapter that almost nobody has written yet — repo-open time. The malicious commit planted a .claude/settings.json session hook and a .vscode/tasks.json folder-open task in the source repository, so the trigger is a developer or a coding agent opening the checkout.
This is a defensive brief. It covers what the vendors actually observed and where their numbers disagree, the containment order that inverts the usual credential-compromise reflex, the honest read on why a signed, attested release still shipped malware, and the standing controls that would have contained it. Everything below is sourced to Wiz, Snyk, Socket and Aikido, plus a handful of checks we ran ourselves against free public APIs.
- 01Repo-open time is a real execution path now.Socket and Aikido both describe the autostart hooks running when a developer or an AI coding agent opens the cloned repository, with no npm install required. Snyk adds the necessary hedge: opening a checkout is an exposure condition, not proof code ran.
- 02Hunt the revocation monitor before you rotate anything.Socket's teardown describes a background watcher that polls the GitHub API every 60 seconds and evaluates a remote-supplied handler the moment its stolen token stops working. Both Socket and Snyk put containment before revocation for exactly that reason.
- 03The library code was never touched.Snyk and Socket independently found every file under dist/ byte-identical between the last clean release candidate and the compromised release. Only the package manifest and two added files changed — which is why a source diff of the library tells you nothing.
- 04Provenance attested a build whose source was already bad.Aikido describes valid provenance signed by GitHub Actions; Snyk describes an npm attestation under a trusted publisher; Socket confirmed a passing attestation directly. All three agree the signing pipeline behaved correctly. Provenance attests build integrity, not source integrity.
- 05The counts disagree, and no victim count exists.Wiz says over 400 distinct packages, Aikido says 444 across 1,381 versions, and Wiz's own public IOC list carries 446 names. Snyk states plainly that there is no verified public count of successful second-stage executions or exfiltrated credentials at the time of writing.
01 — The IncidentA maintainer account, a manifest, and two added files.
Wiz published its analysis on August 4, 2026 and describes the attack as beginning at 09:00 UTC that day, following what it calls the compromise of a GitHub maintainer account. Its headline scope figure is deliberately conservative: the worm has since propagated to over 400 distinct npm packages. That is a count of package names appearing in Wiz’s tracking, not a count of organisations, victims, or packages you personally depend on.
Snyk reconstructed the first hour against commit hashes. It enumerated every package npm returns for the maintainer account behind keyv and cacheable — the endpoint returned 61 package names — and found 11 malicious releases across the keyv family, the cacheable family and ecto. Snyk notes that early package lists omitting ecto are incomplete. The first of those releases, keyv@6.0.0, was published to npm at 09:35 UTC; we confirmed that timestamp independently from the npm registry’s own publish-time map, which retains the entry even after a version is removed.
The mechanics of the release itself are almost boring, and that is the point. Snyk and Socket independently found that every file under dist/ was byte-identical by SHA-256 between the last clean release candidate and the compromised release. Three things changed: the package manifest gained a preinstall script and two extra files in its published file list, and those two files were added. Snyk’s summary of the consequence is the one worth internalising — a developer does not need to import the library, start an application, or call a vulnerable API; resolving and installing the affected package is sufficient.
distinct npm package names
Wiz's own conservative wording for the worm's reach. It is a count of package names in Wiz's tracking — not victims, not organisations, and not packages in your tree. Wiz publishes no download or prevalence figure to go with it.
malicious releases of 61 packages
Snyk's denominator is one maintainer account: 11 malicious releases among the 61 package names npm returns for it. This is a different population from Wiz's 400+, which is other maintainers' packages reached by propagation. The two never add up.
verified public count
Snyk states there is no verified public count of successful second-stage executions or exfiltrated credentials at the time of writing. Aikido's roughly 1,300 GitHub repositories are drop points, and Aikido says so. Do not convert repos, installs or downloads into victims.
One absence matters for anyone triaging this against a vulnerability feed. At the time of writing there is no CVE and no GitHub Security Advisory covering the incident. We pulled every npm advisory published in GitHub’s advisory database across the week of the attack — 95 records — and none of them mention keyv, cacheable, flat-cache, file-entry-cache or the malware family, and none carry the embedded-malicious-code weakness class. That matches Snyk’s own statement that no CVE or GitHub Advisory record had been assigned during its research. The one formal record that does exist is Snyk’s own advisory, which classifies the release as embedded malicious code.
If your detection posture assumes an advisory feed will tell you, this incident is the counter-example. Four security vendors analysed it and a national cyber agency picked it up before any CVE or GitHub Security Advisory record existed.
02 — Execution PathsThe third path: repo-open time.
Wiz states it plainly: persistence is attempted via Claude Code hooks and VS Code tasks.json. Snyk describes the pair as cross-wired — the Claude configuration registers a session-start command that invokes the script under .vscode/, and the VS Code task uses a folderOpen trigger to invoke the script under .claude/. Socket independently describes the same pair. Two vendors, one mechanism.
The reason this is a genuinely new control problem, rather than a colourful detail, is the trigger. Socket describes the autostart hooks executing when a developer or an AI coding agent opens the cloned repository, with no npm install required. Aikido reports the same effect in its own words. A team can have lifecycle scripts blocked everywhere, a strict lockfile policy and a registry proxy in front of npm, and still be exposed the moment somebody clones a repository and points a coding agent at it.
Install time
The vector this incident actually used first: a preinstall script added to the published manifest. Snyk's guidance is a narrow lifecycle-script allowlist rather than blanket enablement, because some legitimate packages genuinely need install scripts.
Import time
Not the vector here — the library code was byte-identical, so nothing in the shipped module ran differently. This is the path the AsyncAPI incident exercised, and the reason install-script blocking alone is never a complete answer.
Repo-open time
The new one. A session-start hook and a folderOpen task committed into the repository itself, triggered by opening the checkout rather than by installing anything. Vendor-documented, intended capabilities — used exactly as designed, by the wrong party.
| Execution path | What triggers it | Needs an install? | What this incident used | Documented control that limits it |
|---|---|---|---|---|
| Install time | npm resolves and installs the package | Yes | A preinstall script added to the published manifest, plus two added payload files (Snyk) | Lifecycle-script policy. Snyk recommends a narrow allowlist rather than enabling scripts globally. |
| Import time | Application code requires or imports the module | No, once the package is on disk | Not this vector. Snyk and Socket both found the shipped library files byte-identical to the clean release. | Import-time review and behavioural scanning; lockfile pinning so a fresh version cannot arrive silently. |
| Repo-open time | A developer or a coding agent opens the cloned repository | No (Socket, Aikido) | A session-start hook in .claude/settings.json and a folderOpen task in .vscode/tasks.json, cross-invoking each other’s loader (Wiz, Snyk, Socket) | Claude Code’s /hooks browser, allowManagedHooksOnly and disableAllHooks; VS Code Workspace Trust. |
We have written the first two rows of that table before. The install-time row is the subject of our guide to npm 12 blocking install scripts by default, and the import-time row is the whole story of the AsyncAPI import-time npm attack, where install-script blocking bought defenders nothing. The third row is what this incident adds, and it is the row your existing hardening checklist almost certainly does not cover.
There is a second, quieter reason AI-tool config matters here. Aikido reports that the generic filesystem scanner in the payload runs roughly 200 glob patterns and explicitly targets IDE configuration files including .vscode/tasks.json and .claude/settings.json. So the same files are both the persistence mechanism and a theft target. Whatever sits in your agent configuration — endpoints, project context, tokens somebody pasted in once — is in scope.
03 — LineagePersistence relocated into the AI toolchain in under three months.
The most useful thing in Wiz’s write-up is not the package count. It is the lineage. Wiz describes the payload as a descendant of the “Mini” Shai-Hulud malware family and says it is heavily based on public repositories of that codebase published by the group Wiz labels TeamPCP. That gives us a dated before-and-after against Wiz’s own earlier research.
On May 19, 2026, Wiz published an analysis of an earlier wave in the same family. That payload also used a downloaded Bun runtime for secondary execution and also exfiltrated through attacker-created public GitHub repositories — but it persisted through a Python-based backdoor polling GitHub for signed messages. Operating-system-level persistence, in other words: the ordinary place malware hides.
By August 4, the same lineage persists through a session hook in an AI coding agent and a folder-open task in an editor. Socket makes the same observation from the technique side, describing the autostart hooks as among the techniques not documented in earlier reporting on this family. In under three months, a malware family moved its persistence out of the operating system and into developer tooling — because that is where the trusted, committable, automatically-executed configuration surface now lives. Agent config is the new autostart directory, and it is version-controlled and shared by design.
OS-level backdoor
Wiz's May 19 analysis of an earlier wave in the same family: a downloaded Bun runtime for secondary payloads, exfiltration through attacker-created public GitHub repositories, and persistence sitting at the operating-system layer.
Agent-config persistence
Same lineage, same Bun-based second stage, same public-repo exfiltration — but persistence has moved into committable developer tooling. Socket calls the autostart hooks a technique not documented in earlier reporting on this family.
One naming note, for the same reason. CSA Singapore’s advisory calls the malware “ChainDrop”. That label appears in the agency notice and in none of the four vendor write-ups, so it is not a consensus name. Cite the advisory as evidence that a national cyber agency picked this up, but note that its references point back to the vendors and its version count and remediation steps track theirs closely — it is government uptake, not a second measurement.
04 — Containment OrderHunt first, rotate second.
The industry reflex on credential compromise is to rotate immediately. Here, rotating first is the trigger. Socket describes a host-level dead-man’s switch: the stolen GitHub token and a handler command are written to a private config directory, the component persists as a macOS LaunchAgent or a Linux user-level systemd service configured to survive logout, and a watcher script polls the GitHub API every 60 seconds. On an HTTP 4xx — the moment the token is revoked or rotated — it evaluates a remote-supplied handler string, clears its own state and exits. It also self-clears after a 24-hour time-to-live.
Both Socket and Snyk therefore put containment before revocation, and Snyk notes it has analysed this same monitor pattern in a prior npm compromise. Socket’s instruction is one sentence: “Before rotating any credential, hunt for and remove the host-level dead-man’s switch. Revocation is its trigger.” The table below reconciles Socket’s remediation sequence with Snyk’s six-step version and Wiz’s higher-level recommendations into one ordered checklist, with the reason for the ordering attached to each step.
| Step | Why this order | Exact artefact or path | Source |
|---|---|---|---|
| Phase 1 — contain, before you revoke anything | |||
| 1 · Isolate the host, preserve logs | Everything after this is destructive to evidence | Shell history, process list, launch-agent and systemd unit listings | Snyk |
| 2 · Hunt the revocation monitor | It polls every 60 seconds and fires on revocation | ~/.local/bin/gh-token-monitor.sh; ~/.config/gh-token-monitor/; ~/Library/LaunchAgents/com.user.gh-token-monitor.plist; ~/.config/systemd/user/gh-token-monitor.service; /tmp/gh-token-monitor.out.log and /tmp/gh-token-monitor.err.log | Socket, corroborated by Snyk |
| 3 · Disable persistence, keep a forensic copy | Deleting without unloading leaves a live process | Unload the LaunchAgent on macOS; on Linux disable the user service and its lingering session | Socket + Snyk |
| 4 · Remove the repo-level implant | Otherwise reopening the checkout re-arms everything | .claude/settings.json, .claude/setup.mjs, .claude/math_init.js, .vscode/setup.mjs, .vscode/tasks.json | Wiz; file list confirmed on GitHub’s public API |
| Phase 2 — only now, revoke and rotate | |||
| 5 · Revoke, do not merely rotate | Socket assumes an equivalent monitor may watch the npm token too | npm and GitHub tokens, including CI-held copies | Socket |
| 6 · Rotate everything reachable, from a clean machine | Rotating from the affected host re-exposes the new secret | Cloud, secret-manager, Kubernetes and CI credentials; crypto keystores if present | Socket + Snyk |
| Phase 3 — clean up what the registry cannot | |||
| 7 · Review audit logs | Establishes whether the second stage actually ran | Account, cloud and source-control audit logs; unexpected publishes, repositories and commits | Snyk + Wiz |
| 8 · Purge proxies and caches | npm removal does not delete copies already stored internally | Private registry proxies, CI caches, developer package caches | Snyk |
| 9 · Pin away from the affected range | Caret and tilde ranges can pull a fresh release | Snyk names the latest clean stable 5.x as the fallback and warns a downgrade may require code changes; Socket suggests blocking the whole scope in a registry proxy | Snyk + Socket |
Two live-state notes on that last row. Socket’s warning was written under an August 4 condition — while the maintainer account remained compromised — and should not be restated as a current statement of that account’s status. Separately, the state on npm has moved since the vendor posts: the compromised release is gone from the registry and the latest tag has fallen back to the previous stable release, published in January 2026. Snyk’s August 4 snapshot recorded eight malicious releases still carrying the latest tag, so read that observation as dated to the day it was made.
The rotation half of this is a standing capability, not an incident task. If step 6 is a multi-day scramble for your team, the fix is upstream of any single incident — our credential rotation reference covers zero-downtime rotation and short-lived tokens, and the process wrapper around all of the above is in our incident-response playbook for agentic workflows.
05 — ProvenanceThe signing pipeline worked perfectly.
This is the part most worth getting right, because the wrong lesson is easy and expensive. The malicious release shipped with valid provenance. Aikido’s wording is that the poisoned versions were published to npm with valid provenance signed by GitHub Actions. Snyk’s is that the npm manifest identifies GitHub Actions as the trusted publisher and links to an npm attestation for the release, and that the malicious source was present in the tagged repository state, so the legitimate workflow built and attested the malicious artifact. Socket confirmed it directly, describing a passing attestation because the legitimate release workflow built already-trojanized source. Three vendors, three independent observations, one conclusion.
Use each source’s own vocabulary rather than upgrading it. “Valid provenance signed by GitHub Actions” is Aikido’s phrasing; “npm attestation” and “trusted publisher” are Snyk’s; sigstore provenance is Socket’s. Reaching for a grander supply-chain-framework acronym overstates every one of them.
Socket is careful to separate two facts that read as one, and the separation matters. The first is a capability: the payload itself contains a provenance component that builds attestation envelopes and submits transparency-log entries, so republished versions can carry freshly minted provenance rather than merely inheriting it. The second is an observation: the original malicious release carried a passing attestation because the legitimate workflow built trojanized source. Socket says explicitly that these are two distinct facts. Fusing them produces a much scarier and much less accurate story.
The same care applies to commit signatures. It is tempting to write that the malicious commits were signed, and that is wrong in a way that matters. One of the two commits is signed and verified; the other, the release commit itself, is unsigned under the maintainer account’s own identity. Snyk’s reading is the correct one.
keyv and cacheable is the first victim in this incident, and coverage that turns the individual into the story is getting the security lesson wrong as well as the ethics.There is a related trap in the release process itself. Snyk reports that the release commit also added a test which executed the loader through a direct process call, and that a later commit removed only that test — meaning running the release test suite could itself have executed the payload in CI. If your pipeline treats “tests passed” as a safety signal, that is the shape of the failure to think about.
One piece of context that must not be misread: GitHub published a post on July 28, 2026 describing supply-chain hardening for npm and GitHub Actions shipped over the preceding months. It predates this attack by a week and is preventive work, not a response to it. It is fair to note that the hardening was already in flight and did not prevent this — that is the honest read, and it points at protecting the source and release path rather than at more signing.
06 — ScaleThe counts disagree, and that is the useful finding.
Aggregator coverage of this incident has fused several different measurements into single confident-sounding figures. They are not the same measurement. Wiz’s prose says over 400 distinct npm packages; its own public indicator list carries more names than that; Aikido’s tracker gives a different pair of numbers again; and the download figures in circulation come from at least three sources using at least two different time units. Publishing the spread is more useful than picking a number, and it inoculates you against the fused versions.
| Source | Package count | Version count | Download / install figure, with unit | Independent or derivative? |
|---|---|---|---|---|
| Wiz (blog post) | “over 400 distinct npm packages” | Not stated | None. Wiz publishes no download or prevalence figure in its text. | Independent — primary research |
| Wiz’s public indicator list | 446 unique package names | 2,317 versions | Not applicable | Independent — we parsed the published file ourselves |
| Snyk | 11 malicious releases among the 61 packages returned for one maintainer account | 11 releases enumerated by timestamp | keyv 619,682,667 · flat-cache 579,751,309 · file-entry-cache 571,240,025 — all July 5 to August 3, 2026, i.e. monthly | Independent — cites the npm API and names its window |
| Socket | Not stated as a total | Not stated as a total | “tens of millions of weekly downloads”, collectively — deliberately imprecise | Independent — payload teardown |
| Aikido | 444 packages | 1,381 versions | “over 2 billion monthly installs” combined; its per-package figures differ between two places in the same post | Independent, but treat its download figures as approximate |
| CSA Singapore advisory | Lists the same 11 named packages | “over 1,300 package versions” | “a combined 2 billion monthly downloads” | Derivative — its references list Wiz, Socket and Aikido |
| SC Media | “400-plus packages”, restating Wiz | Not stated | None | Trade coverage — valuable for the attribution nuance |
Two rules make that table safe to use. First, never mix units: a monthly download count and a weekly download count are different measurements, and an install count is a third thing again. Snyk is the only source that names both a figure and its window, which is why it is the one to quote if you need a number at all. Second, Snyk attaches the caveat that must travel with any of these figures — those numbers overlap heavily because the packages depend on one another and appear in the same toolchains, they are measures of ecosystem reach rather than counts of compromised hosts, and the exposure window for each malicious version was much shorter than a month.
The reach is wide for a structural reason rather than a popularity one. Socket puts it precisely: “These are foundational packages that sit deep in dependency trees, so their reach is largely indirect. A common chain is eslint to file-entry-cache to flat-cache to keyv, which means most affected users never install any of them directly.” If you are checking whether you are affected, a direct-dependency grep will not answer the question. The lockfile will.
The shape of the indicator list is the other thing the aggregate hides. Parsing Wiz’s published file, the 446 package names are heavily concentrated in a small number of scopes — mostly a handful of organisations’ internal package families rather than 446 household-name libraries. That is the honest scale of it, and it changes who should be worried most.
Where the 446 package names sit · share of Wiz's indicator list by scope
Source: our own parse of Wiz's published indicator list · shares are of the 446 package names it contains, not of npm, not of victimsOne more denominator, because it is the single most misquoted figure in this incident. Wiz reports that the credential-theft target list was expanded by almost 70 percent. That is growth in the number of credential-theft targets relative to the earlier public codebase this payload derives from. It is not 70 percent of packages, not 70 percent of victims, and not 70 percent of installs. It is also the only percentage in Wiz’s entire post — if you see a second percentage attributed to Wiz, it did not come from Wiz.
What the expanded list contains is the reason this post exists. Wiz names AI-agent credential stores for Claude, OpenAI, Codex, Cursor and Gemini among the new additions, alongside crypto keystores, self-hosted CI and registry secrets, additional cloud CLI configurations, and system files. The AI toolchain has graduated from incidental collateral to a named collection target. We saw the earlier version of that trend in the previous attack aimed squarely at AI infrastructure, and in the way AI-hallucinated package names widen the npm attack surface from the other end.
07 — Standing ControlsThe controls that already exist.
The strongest defensive framing here is also the simplest: the vendor documents this exact threat model, and the attack is a faithful instance of it. Claude Code’s hooks reference describes SessionStart as firing when a session begins or resumes, lists .claude/settings.json as project-scoped and shareable — explicitly committable to the repository — and carries a security disclaimer that reads like a description of this incident written in advance.
Three documented controls follow from that, and they are the first things to check. The in-product /hooks browser is read-only and shows every hook event with a count of configured hooks, lets you inspect each handler, and — crucially — shows which settings file each hook came from. That is the single most useful “did something plant a hook in this repository?” check available, and it is built in. For managed fleets, allowManagedHooksOnly lets administrators block user, project and plugin hooks, and disableAllHooks exists as a setting, though it cannot disable managed hooks.
Be careful about what is and is not gated by workspace trust. The documentation states that frontmatter hooks in a project subagent run only after you accept the workspace-trust dialog for the folder the agent file came from, and notes that before a specific version those hooks could run from folders you had not trusted. That sentence is scoped to subagent frontmatter hooks. The documentation does not state whether .claude/settings.json hooks are gated by the same dialog, and it does not state that they are not. Do not extrapolate in either direction — including from this post. Run /hooks against the version you actually have installed and see for yourself. On the editor side, VS Code’s Workspace Trust is the mechanism governing the automatic-task path, which is what Snyk’s hedge points at.
Lifecycle-script allowlist
Snyk's framing is the honest one: disabling lifecycle scripts limits this execution path, but some legitimate packages require install scripts, so maintain a narrow allowlist rather than enabling scripts globally. Blanket enablement and blanket disablement both fail in practice.
Audit hooks with /hooks
A read-only browser that shows every hook event, each handler, and which settings file it came from. Make it a step in the same reflex as reading a Dockerfile before you docker build a stranger's repository. Verify on your own installed version rather than trusting any blog post.
Workspace Trust, kept on
The editor-side control for the automatic-task path. Snyk's hedge is that VS Code may ask before allowing automatic tasks depending on workspace trust and user settings — which is only true if you have not clicked through the dialog on every repository you have ever opened.
Short-lived, scoped credentials
The dead-man's switch is only interesting because a long-lived token was sitting on a developer machine. Machine identities that expire in minutes and are scoped to one repository make both the theft and the revocation-monitor trick far less valuable to an attacker.
The organisational version of this is a policy question, not a tool question: what is your team’s rule for inheriting a repository’s agent configuration? Most teams do not have one, because until now agent config was a productivity artefact rather than an execution surface. Our write-up of Claude Code team-adoption anti-patterns is the natural home for that rule, and the standing answer to stolen machine identities sits in our non-human credential access playbook. Writing that rule down, and wiring it into how a team adopts coding agents in the first place, is one of the first things we set up in our AI and digital transformation engagements.
There is an uncomfortable timing coincidence worth naming. The same week this attack proved that shareable agent configuration is an execution surface, the industry shipped an open standard for portable, shareable agent extensions. That is not an argument against the standard — portability is plainly the right direction, and a common format makes auditing easier, not harder. It is an argument that the review discipline has to arrive at the same time as the distribution mechanism, and historically it does not.
08 — VerificationHow we checked, and what we could not confirm.
Most coverage of this incident restates Wiz. We went to the free public APIs instead. Each check below is reproducible in minutes, which is the point of printing them.
The persistence commit. Using GitHub’s public REST API for the affected repository, the commit whose short hash is d8c850c7 was authored at 09:04:30 UTC on August 4, 2026 under the commit message chore: update config, and adds exactly five files: .claude/math_init.js, .claude/settings.json, .claude/setup.mjs, .vscode/setup.mjs and .vscode/tasks.json. GitHub reports it as verified with reason valid, under an automation-bot author identity, and the commit body carries a co-authorship trailer naming a coding agent. That is first-hand confirmation of Wiz’s persistence claim, Wiz’s commit-message claim and Aikido’s observation that the commits are dressed in an agent’s identity — from one API call, no vendor restatement involved.
The release commit is a different shape. The commit two minutes earlier, tagged as the version release, is attributed to the maintainer’s own identity and is reported as unsigned, while the persistence commit that followed is signed and verified. That asymmetry is why “the malicious commits were signed” is the wrong summary.
The registry state. The compromised release is no longer retrievable from npm, and the latest tag points back to the previous stable release from January 2026. The attestation URL that Snyk cites for the compromised release now returns a not-found response, which is why we have not linked it — the provenance finding rests on three vendors’ contemporaneous observation, not on a currently-live artifact.
The advisory sweep. Pulling every npm advisory published in GitHub’s advisory database across the week of the attack returned 95 records, none of which relate to this incident. That is the basis for the no-CVE, no-advisory statement above, and it is narrower and more checkable than “there is no advisory anywhere”.
What we did not resolve. The vendors’ indicator tables do not fully agree — Socket and Aikido describe the two loader files as identical with a single hash, while Wiz lists two different values in a different hash algorithm — so we have not printed per-file hashes as settled fact; use the vendor advisories directly for indicators. The sources also describe the propagation discovery step slightly differently, so we have described it in plain language rather than picking one endpoint. And Socket’s detection-speed figures, an average of five minutes and eighteen seconds across the campaign, are telemetry about Socket’s own product — a real data point about that product, not an industry benchmark.
09 — ConclusionAgent config is now production surface.
Treat agent configuration the way you already treat CI configuration.
Strip out the package names and the hash tables and one structural change remains. A malware family that persisted through an operating-system backdoor in May persisted through a coding agent’s session hook and an editor’s folder-open task in August. Nothing about that required a vulnerability. It required a configuration file that is designed to be committed, shared, and executed on open — and a team culture in which nobody reads it.
The practical consequence is a third column in every npm hardening checklist. Install-time controls remain necessary and remain insufficient. Import-time review remains necessary and remains insufficient. Repo-open time is the path that bypasses both, and the controls for it already exist and are documented — a hook browser, a managed-hooks setting, workspace trust. They are simply not in anybody’s onboarding checklist yet.
Expect this shape to repeat rather than to be a one-off. Every portable agent format, skill marketplace and shareable configuration standard shipping this year widens the same surface, and the review discipline reliably lags the distribution mechanism by a release or two. The teams that come out of this well will be the ones that decided, before the next incident, what their rule is for inheriting a stranger’s agent configuration — and that practised the containment order, so that when a token is stolen the first instinct is to hunt rather than to revoke.