← All issues
Issue #9 · August 8, 2026 · ~5 min read

The worm that earned its green check

Welcome back to Zero to MVP Weekly. Same deal: one signal, one tool, one MVP idea — five minutes, every Saturday. No link dumps. This week the attack came with a green checkmark, and the files it targeted are the ones your coding agent reads before you do. Let's go.

1. The signal of the week

On August 4 an npm worm shipped with every trust signal intact — valid provenance, a verified badge, the maintainer's own CI — and the files it planted were aimed at your coding agent.

The Shai-Hulud worm surfaced in the keyv and cacheable namespaces: an attacker compromised the GitHub account of the maintainer and pushed malicious code into the tagged source of eleven seed packages. The maintainer's own legitimate GitHub Actions workflow then built, signed and published them — so the poisoned releases carried valid OIDC and SLSA provenance attestations, a green GitHub-verified badge, github-actions[bot] as the commit author. The trust signals weren't faked; they were earned. Provenance attests build integrity, not source integrity, and this compromise sat upstream of the build.

The mechanics were fast and thorough. keyv@6.0.0 introduced a preinstall script that pulled down a ~727 KB compiled bundle harvesting npm tokens, cloud keys, Vault secrets, Kubernetes configs and GitHub Actions runner memory — then republished itself into further packages with the stolen tokens, propagating between organizations every two to seven minutes. Vendor counts of the damage range from roughly 350 to 1,700 poisoned versions across hundreds of packages.

Here's the detail aimed at this audience. Beyond the npm lifecycle script, the worm planted execution hooks in agent and editor config: a SessionStart hook in .claude/settings.json calling .vscode/setup.mjs, and a task in .vscode/tasks.json with runOn: folderOpen calling .claude/setup.mjs — each payload hidden in the other tool's directory, in files security scanners historically never read. Opening a poisoned repo in Claude Code or VS Code was a delivery path. Both tools put that path behind an explicit workspace-trust prompt by default — and as far as anyone has reported, that prompt held.

The defenses are the other half of the story, and the timing is almost comic. Days before the worm hit, GitHub shipped a three-day Dependabot cooldown on non-security version bumps, and PyPI began rejecting new file uploads to releases older than 14 days — both designed against exactly this: fast propagation and stable-release poisoning. Two days after, on August 6, Anthropic turned on skill and plugin security scanning for Claude Enterprise plans — third-party skills and plugins are now checked automatically for malicious content on upload or edit. Notice what worked and what shipped: not smarter scanners — time and trust boundaries.

Why this leads the issue: three separate facts converged on one sentence this week. The files your agent reads are now executable, and the trust signals you were told to check don't cover them. Your dependency graph was already the attack surface — issue #008's PyPI incident made that point. Now your agent config is too: .claude/, .vscode/, hooks, skills, plugins. The agent ecosystem is replaying npm's security history at agent speed, and the fixes arriving are npm's fixes: cooldowns, upload windows, scanning at the boundary.

One caveat to keep us honest: no confirmed compromise via the .claude/.vscode hooks has been reported — workspace trust held, as designed. The hooks are an attempt, not a proven infection path. And provenance didn't fail: it verified exactly what it claims to verify, which is the build. The honest lesson is about what it never claimed to cover — not "provenance is theatre."

2. My take

Your agent config is code now. The worm treated .claude/settings.json exactly like a preinstall script — and it was right to: a hook runs with your permissions, the same as anything you install. We all learned to glance at package.json before running npm install; nobody I know gives that glance to .claude/ or .vscode/ yet.

So my move this week isn't a tool, it's a thirty-second habit: before opening a cloned repo in an agent, look inside those two folders. Hooks, tasks, anything that points at the other tool's directory — that's the tell. And turn on the Dependabot cooldown while you're there; time was the only defense that actually scaled this week. Try now, calmly. Workspace trust held this time. The habit is for the time it doesn't.

3. Tool worth trying

LFM2.5-2.6B — a real agent model in 2.5 GB of RAM.

On August 4 Liquid AI released LFM2.5-2.6B: an open-weight 2.6B-parameter model built specifically for agentic workloads — native tool calling, a 128K context window — running at 220 tokens/s in under 2.5 GB on an M5 Max CPU, around 30 tokens/s on a phone. No GPU, no cloud. It shipped on Hugging Face with day-one support for llama.cpp, MLX, vLLM, SGLang and ONNX, plus a base checkpoint if you want to fine-tune.

It's a fitting counterweight to this week's signal: the signal is about how much you're forced to trust what arrives over the network — the counterweight is how much of the agent stack you can now run without the network at all. And it continues the cost thread from #007: for the high-volume, mechanical steps of an agent pipeline — tool-calling routers, document triage, background routines — the marginal cost of a local model is zero, and the privacy question disappears with the bill.

Two limits regardless. It's 2.6B — it trails larger models on coding and anything judgment-shaped, and Liquid's own positioning is "well-defined tasks," not general agents; the benchmark tables are the vendor's. And read the license terms on the model page before you build a product on it — "open weights" and "do whatever you want" are not the same sentence.

4. MVP idea of the week

A "who can run code on my machine?" repo auditor.

  • Problem: cloning a repo now means trusting a dozen auto-execution surfaces nobody reviews: .claude/settings.json hooks, .vscode/tasks.json folder-open tasks, devcontainer postCreateCommand, git hooks, npm lifecycle scripts. The keyv worm just used two of them, cross-referenced to evade scanners. Security tooling reads your dependencies; nothing reads your workspace.
  • Solution: a CLI that scans a repo — or all of ~/code — and prints one table: every file that executes code on open, install or session start, what it runs, and a risk flag (new since last scan, obfuscated, downloads-and-executes, references another tool's config directory — the worm's tell).
  • For whom: developers who clone OSS repos into coding agents daily; team leads who want a pre-git clone gate; the security-curious solo builder.
  • Minimal v1: one script, ~8 known surfaces (Claude Code, VS Code, devcontainers, npm, git), a table out, non-zero exit on high risk. No daemon, no service.
  • Demand check: run it on the 50 most-cloned repos of the week and publish the counts — "N of 50 execute something on folder open." The replies tell you if it's a product; the number is content either way.
  • Why now: the worm made the attack concrete on August 4, and workspace-trust prompts are the only existing defense — a yes/no dialog with zero detail. Showing what would run is the unbuilt half.

5. Useful links

Before you open the next repo you clone, look inside .claude/ and .vscode/ yourself — then tell me what you found. Even "nothing" is a data point I'll take.
— Nick

P.S. Three of my own things, free and open on GitHub:

  • Texodus — a modern, fast, small and beautiful Markdown viewer and editor.
  • Prompt-Vault — coding prompts and detailed project specs for benchmarking and testing LLMs.
  • AI-Vault — my curated vault of practical AI-engineering resources: agentic coding, RAG, local models, evals.

Get the next one in your inbox.

One signal, one tool, one MVP idea — every Saturday. Free.