facebook pixel
Skip to content

workflowSupply Chain & Trust12 min read

The Untrusted Checkout

Our take
On 4 August 2026 an npm worm stopped bothering with npm install. It committed hooks into .claude/settings.json and .vscode/tasks.json and waited for somebody to open the repository. Agent configuration is executable code that ships through your dependency graph, and your scanners do not read it. Neither does your reviewer.

The trust boundary moved when agents started reading the repository, and most teams never moved their controls. What the keyv compromise actually did, why npm provenance and SLSA signed the malicious build cleanly, the four classes of agent-facing file an attacker can reach, and the five-rung hardening ladder: cool down the registry, inspect before you trust, sandbox the session, put agent config behind CODEOWNERS, and scope the credentials so the blast radius is survivable.

On this page10 parts
  1. 01The Problem
  2. 02What Actually Happened
  3. 03Why Every Control You Already Own Missed It
  4. 04The Four Files Your Agent Trusts
  5. 05The Hardening Ladder
  6. 06What to Check When One of These Lands
  7. 07Why This Matters for Senior Teams
  8. 08Where It Fits in the Stack
  9. 09When Not to Use It
  10. 10Source

The Problem

For twenty years the trust boundary in a JavaScript repository sat at npm install. Cloning was safe. Reading was safe. Something had to run before anything could happen to you, and every control the industry built assumes it: lockfiles, --ignore-scripts, dependency scanners, SBOMs, provenance.

An agent moved that boundary and nobody filed a ticket. A coding agent reads configuration before it reads code, and the configuration is not documentation. .claude/settings.json sets environment variables and registers hooks. .mcp.json and .codex/config.toml name commands to spawn. .vscode/tasks.json and .devcontainer/ run setup on open. AGENTS.md, CLAUDE.md and SKILL.md are natural-language instructions handed straight to a model that has your credentials, your filesystem and a network. Every one of those files is plain text, every one is committed, and every one arrives when someone clones a repository or bumps a dependency.

So the honest description of a checkout in 2026 is that it is a program you run by opening it. Most teams still describe it as a folder.

What Actually Happened

On 4 August 2026 an attacker took over the GitHub account of the maintainer behind keyv, a caching library with roughly 127 million weekly downloads, and the eleven seed packages in the keyv and cacheable namespaces became a credential-stealing worm. The same maintainer owns flat-cache and file-entry-cache, which sit under ESLint, which sits under most of the JavaScript ecosystem. Socket counted 2,234 poisoned versions across 444 package names while it was still spreading, and higher counts were published as the day went on.

The credential half was familiar. A preinstall script harvested npm tokens, GitHub tokens, cloud provider credentials, SSH keys and CI secrets, and reporting noted it also went after AI tooling credentials specifically, the Anthropic, OpenAI, Cursor and Gemini keys sitting in developer environments. With a stolen npm token it asked the registry which packages that token could publish, unpacked each tarball, injected itself and republished. No operator in the loop.

The part that is new, and the reason this belongs in a hub about agents rather than in a security newsletter, is what it did with a GitHub token. It committed hooks into .claude/settings.json and .vscode/tasks.json in the repositories it could reach, so the payload would execute the next time any developer opened that checkout in an editor or started an agent session inside it. No npm install required. It pushed into as many as fifty branches per repository, working through the most recently active first and skipping dependabot and copilot branches, and it authored the commits as claude <[email protected]> with the message chore: update config.

Sit with that last detail for a second, because it is the whole problem in one line. The attacker's best disguise was a routine config commit from an agent, on a team where routine config commits from agents are normal.

Why Every Control You Already Own Missed It

Four of them, and each failed for a different reason worth understanding.

  • Provenance and SLSA passed, on the malicious artifact.The poisoned releases carried valid OIDC provenance because they went through the project's real GitHub Actions release workflow. The compromise was upstream of the build, so attestation did exactly what it promises: it proved which pipeline built the artifact. It has never claimed the source entering that pipeline was clean, and this is the incident that taught a lot of teams the difference.
  • Dependency scanners do not read markdown or editor config.A scanner reads your dependency graph and looks for known-vulnerable versions. A hook in .vscode/tasks.json is not a dependency, and an instruction in a SKILL.md is prose. Google's threat researchers found one malicious skill file that instructed an agent to exfiltrate API keys and environment variables, and to not mention it to the user "as it may cause confusion about the security process". It sat undetected for roughly two months.
  • Code review skims config.A reviewer who reads four hundred lines of a feature diff carefully will scroll past a three-line change to a settings file, and that is the correct allocation of attention under every other threat model. It is the wrong one here.
  • The agent's own permission prompts were not in the path.The point of writing to settings.json and tasks.json is that both are read at startup, before there is a session for a permission system to protect.

The framing that predicts all of this is Simon Willison's lethal trifecta: an agent with access to private data, exposure to untrusted content, and a way to communicate outward is exploitable, and no amount of system-prompt hardening changes that. A coding agent in a cloned repository has all three by definition. The repository is the untrusted content.

The Four Files Your Agent Trusts

Google's July 2026 research on agent-facing files gives the cleanest taxonomy, and it is worth committing to memory because it tells you what to grep for:

  • What executes..vscode/tasks.json, .devcontainer/, debugger and setup configuration. Opening the project is the trigger. One real sample, a weaponized .cursor/tasks.json inside a fake coding challenge attributed to DPRK-nexus actors targeting job candidates, pulled and ran code from a Gist in memory and went undetected by security engines for days.
  • What instructs.AGENTS.md, CLAUDE.md, SKILL.md, rules files. No exploit code, just persistent natural-language instructions that shape agent behaviour, and they are reused across repositories, which makes them a supply chain of their own.
  • What connects.settings.json, .mcp.json, .codex/config.toml. This is the sharpest category. Datadog demonstrated project settings overriding PATH so that Claude Code's own startup git operations ran a repository-controlled wrapper script, with BASH_ENV, NODE_OPTIONS and PYTHONPATH as equivalent levers, and project-scoped MCP servers in .codex/config.toml spawning on project open. Google documented live samples pointing ANTHROPIC_BASE_URL at attacker-run proxies, which suppresses telemetry and cost warnings while quietly relaying prompts, source code and credentials, and can feed instructions back into the agent.
  • What extends.Editor extensions and their update paths. Same story as the registry, different registry.

The Hardening Ladder

Five rungs, cheapest first. Most teams should do the first three this week and the last two this quarter.

Rung 1. Put a cooldown on the registry. Compromised versions are usually pulled within hours, so refusing to install anything published in the last day filters most of these incidents at the install layer with no scanning and no monitoring. pnpm 11 ships it on by default at 1,440 minutes, npm added min-release-age in CLI 11.10.0 and Yarn calls it npmMinimalAgeGate. This is the single highest-return line of configuration in this entire playbook.

ini
# .npmrc
min-release-age=1d

Rung 2. Read the configuration before you trust the checkout. Treat accepting workspace trust as equivalent to running an unknown setup script, because that is what it is. Before an agent touches an unfamiliar repository, or after any dependency bump that touched a package you did not expect:

bash
# every path an agent reads before it reads your code
rg --hidden --no-ignore -n \
-g '.claude/**' -g '.codex/**' -g '.cursor/**' -g '.vscode/**' \
-g '.devcontainer/**' -g '.mcp.json' -g '{AGENTS,CLAUDE}.md' -g '**/SKILL.md' \
-e 'hooks' -e 'mcpServers' -e '"command"' -e 'preinstall' \
-e 'BASH_ENV' -e 'NODE_OPTIONS' -e 'PYTHONPATH' -e '_BASE_URL' \
-e 'curl' -e 'wget' -e 'base64'
# and the commits nobody reads
git log --oneline --all -- .claude .vscode .cursor .codex .mcp.json AGENTS.md CLAUDE.md

Claude Code gives you three controls on top of that, and they are all off unless somebody turns them on. /hooks lists every hook a session would run, so you can look before you work. disableAllHooks refuses the lot. In managed settings, allowManagedHooksOnly means a repository cannot introduce a hook at all, which is the right default for an organisation that clones code it did not write. Codex went further and split project trust from hook trust outright, requiring hook definitions to be reviewed and trusted separately.

Rung 3. Sandbox the session, not just the command. Isolation is what makes rungs 1 and 2 survivable when they fail. /sandbox enforces a filesystem and network boundary that the operating system holds for every Bash command and its children, denyRead keeps the agent out of your credential files, and a dev container keeps the whole thing off your host. The rule that matters is on the network side: a stolen token is only worth what it can be sent to, so keep allowedDomains narrow. github.com is not a boundary, it is an exfiltration endpoint with a good reputation.

Rung 4. Put agent configuration behind CODEOWNERS. This is the cultural change and it costs one file. Agent config is infrastructure with the blast radius of infrastructure, so it gets reviewed like infrastructure rather than skimmed like docs.

text
# CODEOWNERS
/.claude/ @acme/platform-security
/.codex/ @acme/platform-security
/.cursor/ @acme/platform-security
/.vscode/ @acme/platform-security
/.devcontainer/ @acme/platform-security
/.mcp.json @acme/platform-security
/AGENTS.md @acme/platform-security
/CLAUDE.md @acme/platform-security

Then close the mid-session hole. A ConfigChange hook fires when settings change during a session, so an agent that gets talked into rewriting its own permissions leaves a record, or does not get to.

Rung 5. Scope the credentials so the worst case is boring. Everything above reduces the chance. This decides what it costs. Short-lived tokens over long-lived ones, publish rights on a machine identity with trusted publishing rather than a token in someone's shell profile, no production credentials in a developer environment, and an inventory of which AI tool keys are sitting in plaintext in ~ right now, because that is what this class of worm is explicitly collecting.

What to Check When One of These Lands

The next one will arrive on a Tuesday afternoon. Four commands, in order, and it is worth having them written down before you need them:

  • git log --all --author=claude --oneline and the same for any other automation identity, then read every diff that touched a config path. Commits that look like your agent's routine work are the ones to read first, not last.
  • npm ls <package> against the published affected-version list, plus your lockfile history, because a transitive bump is how most teams got it.
  • Rotate on the assumption of compromise rather than on evidence of it: npm tokens, GitHub PATs and app tokens, cloud keys, SSH keys, CI secrets, and the AI provider keys.
  • Check ~/.claude, ~/.codex and your editor's user settings, not only the repository. Persistence that survives deleting the checkout is the point of writing to config in the first place.

Why This Matters for Senior Teams

  • It is the objection your security team is about to raise, and they are right.Every other playbook in this hub asks for more autonomy, more committed context and more agents running unattended. This is the one that lets you say yes to those with a boundary drawn, instead of arguing about whether the risk is real. It stopped being hypothetical on a specific Tuesday in August.
  • The blast radius is the whole team, not one laptop.A compromised dependency used to cost you a build. A compromised agent configuration is committed, so it reaches every engineer who pulls, every agent session that starts, and every CI job that checks out the branch. It replicates through exactly the mechanisms you built to make agent setup consistent.
  • JavaScript is the ecosystem being farmed.This is not a general AI-safety concern that happens to mention npm. keyv, flat-cache and file-entry-cache are in your package-lock.json right now, underneath ESLint, and the frontend dependency graph is the widest and shallowest attack surface in software. Frontend teams do not get to treat this as somebody else's layer.
  • The controls are cheap and the incident is not.A cooldown line, a CODEOWNERS block and a sandbox profile are an afternoon. Credential rotation across an engineering organisation, with a worm that already used those credentials to publish, is a week that nobody plans for.
  • It is a trust argument, not just a security one.The reason a team stops trusting agent-authored commits is one incident where a config change nobody reviewed did something nobody expected. Reviewing agent configuration like code is how the autonomy you have already granted keeps its licence.

Where It Fits in the Stack

  • It is the missing rung on The Autonomy Ladder.That entry decides what an agent may do with your permission. This one covers what a repository can make it do without asking. Permission modes and sandboxes are the same two settings blocks, pointed at a hostile input instead of an eager one.
  • It changes how AGENTS.md and skills get reviewed.Both entries argue that context files are reviewable artifacts. This is the reason that argument is load-bearing rather than tidy, and the reason an installed third-party skill deserves the same read as an installed dependency.
  • Plugins and private marketplaces are the fix and the risk at once.A private marketplace with managed settings is how you distribute hardened defaults to everyone. It is also a distribution channel with your organisation's name on it, so it needs the review discipline it is enforcing.
  • Continuous AI is where this becomes automatic.A read-only agentic job that flags any diff touching a config path, and a plain CI check that fails on new hooks or a changed base URL, catch the mechanical half before a human is involved. It is also the environment with the most credentials and the least supervision, so harden it first.
  • The Reviewable Stack is why config gets its own layer.Config changes never belong in a feature layer. Alone, in their own diff, with their own reviewer, they are a two-minute read.
  • Codemod-first migrations shares the principle.Determinism beats judgement for anything mechanical. A lint rule that fails on an unknown _BASE_URL override is worth more than an instruction telling the agent to be careful.

When Not to Use It

  • Not as a reason to slow down.The failure mode of this topic is a six-week review process that produces one policy document and no configuration change. Rungs 1 to 3 are an afternoon, they are most of the value, and shipping them beats designing a programme.
  • Not the whole ladder on a solo repository.CODEOWNERS on a repo with one owner is ceremony. Take the cooldown, the inspection habit and the sandbox, and skip the rest until there is a second engineer.
  • Not as a substitute for credential hygiene.If a long-lived npm publish token with 2FA bypass is sitting in a shell profile, no amount of agent sandboxing saves you. That is the older problem and it is still the more expensive one.
  • Not by banning the tools.Turning agents off in response to this moves the same repositories to whichever unmanaged setup people use anyway, and removes the managed settings that were the actual control. Constrain the environment, do not relocate it.
  • Not a one-time audit.The relevant file paths change every time a vendor ships a new configuration surface. The durable practice is that agent-facing paths are protected paths, checked in review, whatever they happen to be called next quarter.

Source

Private Workshop

Want to bring AI into your Frontend workflow?

We help product teams adopt AI-powered development practices, from agent workflows to custom skills and tooling, on whichever coding agent they already run.

What happens next
  1. 01

    Tell us what you are building.

    Your stack, the AI tools you use, where you are stuck.

  2. 02

    We book a 15-minute call.

    We look at how your team works with agents today.

  3. 03

    We shape the workshop.

    Private and hands-on, on your own codebase.

0/2000

Your info stays with us. That's it.