Magic Tools
Developer ToolsBy CooconAugust 5, 2026151 views5 min read

That npm install You Ran Yesterday May Have Stolen Every Key You Own

On August 4, 2026, someone pushed two malicious files to the keyv GitHub repo and cut a release tag. npm published it automatically. GitHub Actions signed the provenance. Everything looked legitimate.

One detail didn't fit: the push didn't come from the maintainer. That account had been taken over.

Anyone who ran npm install that afternoon—possibly you—quietly executed a 728KB obfuscated script. Its job was simple: collect every credential it could find, encrypt the haul, upload it to a public GitHub repository.

Description on that repo, in full: "Shai-Hulud: Here We Go Again."

Not one package. An entire chain.

keyv was just the doorway. The same maintainer also owned flat-cache (580M monthly downloads), file-entry-cache (571M/month), cacheable-request (137M/month), and eight other caching utilities. One stolen account, eleven poisoned packages.

Then came the part that makes this genuinely nasty: the worm. Math_Symbol.js doesn't stop at stealing credentials. It searches your disk for source code belonging to other npm packages, and if it decides you're a maintainer, it takes your npm token and ships an infected release under your name.

Current count: 434 packages, 1,381 versions, more than 2 billion monthly installs. @deliveroo/reevent, @picsart/ai-sdk, @qlik/embed-runtime, picasso.js—packages from serious engineering organizations, now distributing the same malware on the attacker's behalf.

What it actually steals

Read through the extractor list in Math_Symbol.js and your stomach drops:

  • npm tokens: Reads ~/.npmrc, then sweeps the whole filesystem for stray .npmrc files, pulls out every _authToken, and validates each one live against registry.npmjs.org/-/whoami
  • GitHub tokens: Classic PATs, OAuth tokens, GitHub App server-to-server tokens, JWT OIDC tokens—harvested from ~/.config/gh/hosts.yml, environment variables, and CI runner process memory
  • AWS credentials: Every named profile in ~/.aws/credentials, environment variables, EC2 Instance Metadata Service (IMDSv2, falling back to v1), ECS container metadata, plus a direct secretsmanager:ListSecrets call across regions
  • Kubernetes secrets: Grabs the service account token, asks the K8s API for every secret in the namespace, pockets kubeconfig on the way out
  • HashiCorp Vault tokens: Six sources tried in order—environment variable, ~/.vault-token, CI runner paths, K8s JWT auth, AWS IAM auth—then it walks every KV store within reach
  • Stripe & Slack tokens: Filesystem scan for anything starting with sk_ or xox
  • Generic filesystem scan: Roughly 200 glob patterns covering .env files, private keys (.pem, .key, .p12, .pfx), SSH keys, Terraform state, browser cookies, password databases. Call it "everything you'd rather nobody saw"

One line of "preinstall": "node setup.mjs" in package.json sets all of it off. Your node_modules folder isn't finished populating and your secrets are already gone.

Why signatures and 2FA didn't help

Two questions come up immediately: doesn't npm have provenance signatures? Isn't 2FA mandatory now?

Here's the uncomfortable part. Once an attacker controls the maintainer's GitHub account, the packages they publish are indistinguishable from the real thing.

GitHub Actions OIDC tokens belong to the runner, not to a person. Push malicious code to main, tag a release, and the pipeline builds, signs, and publishes on its own. Every step passes inspection, because as far as GitHub can tell, the "maintainer" is doing it.

2FA fares no better. A stolen session cookie or OAuth token gets an attacker through the door without ever triggering a second factor—no password, no code, just a cookie that hasn't expired yet. That's session hijacking.

Dependency risk isn't only about malware

August 4 produced two dependency stories, not one. Bending Spoons also announced it was acquiring Airtable for $1.3 billion.

Different mechanism, same lesson: the things you depend on are not under your control.

Keyv is "someone deliberately broke it." Airtable is "the company changed hands and pricing is about to move." Either way, a developer building on that foundation ends up in the same place—your stack is suddenly someone else's decision.

Bending Spoons runs a consistent playbook: acquire, cut headcount, raise prices, shrink the free tier. Expect reduced free API quotas, repackaged plans, and a few features quietly retired. Teams running automation on Airtable—plenty use it as a no-code backend—should start sketching a migration path now. Not because Airtable disappears tomorrow, but because the free ride most likely ends when the deal closes at the end of 2026.

Call that risk management, not FUD.

Five things you can do today

Don't panic, but don't close the tab either. In priority order:

1. Check your lockfile (5 minutes)

grep -E "keyv|flat-cache|file-entry-cache|cacheable|cache-manager" package-lock.json yarn.lock 2>/dev/null

Got a hit? Check the version. Known-affected releases include keyv 6.0.0, flat-cache 6.1.24, and file-entry-cache 11.1.6. Upgrade to a clean version, then rotate every token that machine has ever touched.

2. Turn off install scripts (10 minutes)

npm config set ignore-scripts true

No more automatic preinstall and postinstall hooks. You'll pay for it with the occasional manual npm rebuild for native modules, which I'd argue is a bargain. Want something softer? Run one full install with --ignore-scripts and inspect the tree before you commit to anything.

3. Add a dependency review step to CI (30 minutes)

For GitHub Actions:

- name: Audit dependencies
  run: |
    npm audit --audit-level=high
    npx socket scan

Not ready for new tooling? Then confirm two things: your lockfile is committed, and CI runs npm ci rather than npm install.

4. Rotate credentials—right now, if you installed yesterday

Ran npm install on August 4 with an affected package in your lockfile? Start rotating:

  • npm tokens: regenerate at npmjs.com, revoke every old one
  • GitHub PATs: Settings → Developer settings → Personal access tokens
  • AWS access keys: IAM → Users → Security credentials
  • Every API key sitting in ~/.env on that machine

The script went looking for exactly these. Rotating them isn't paranoia.

5. Map your single points of failure (this week)

Open a blank note and answer three questions:

  • Which third-party services would take my product down if they vanished? (Skip database, CDN, and auth—you already worry about those. Hunt for the ones you never think about.)
  • Which npm packages would take me longest to replace if they were compromised or pulled?
  • Which SaaS products would break my cost model if they doubled their price?

Write the answers down. No action plan required yet. The gap between "I know" and "I never thought about it" is the gap between a bad afternoon and a bad quarter.


Supply chain attacks aren't going away. AI writes more of our code every month, dependency trees keep getting deeper, and the attack surface grows right along with them.

So don't just nod and move on. Open a terminal and run that grep from step one—two minutes, start to finish. If something comes back, you'll be glad you didn't wait until tomorrow.

✨ Draft generated by DeepSeek, reviewed and polished by Claude.

Sources:

Related Articles

Blocking rm -rf in Claude Code: A Deny Rule Stopped 14 Deletes in Two Weeks, 7 Other Spellings Still Get Through, and a Hook That Closes the Gap

To keep Claude Code from deleting things by accident, I added a deny rule, Bash(rm -rf *), to my global settings. Across my sessions from October 7 to 21 it blocked 14 deletes. After each denial the model picked a new directory name 8 times and dropped the delete 6 times. It never tried another spelling. Then I replayed 22 delete commands verbatim against Claude Code 2.1.285 through a stub. rm -fr, rm -r -f, rm -Rf and rm -r deleted the directory in manual, auto and bypass mode alike. /bin/rm, find -delete and bash -c deleted it in manual and bypass mode. In auto mode, rm -fr inside the working directory skips the classifier because it 'would be allowed in acceptEdits mode'. A second, built-in dangerous-rm check fires on targets like $D/*, even in bypass mode, but loses track of a variable assignment when a heredoc comes earlier in the same command. The post ends with a PreToolUse hook that blocks every recursive spelling in all three modes and tells the model what to do instead.

claude-codehooks+5
pitfallsOct 21, 20268 min
418

The Self-Reinforcing Flywheel Is Harder Than It Looks: 40 Days of an LLM Auto-Tuning Loop

We ran an 'LLM proposes, code decides' auto-tuning loop on a daily WeChat newsletter for 40 days: 18 versions, 8 rollbacks. Three of those rollbacks overturned decisions a human had made, and one version whose knobs were identical to its parent still got rolled back. Using our real numbers, telling apart a 20% difference needs about 25,000 reads per version. At 60 reads a day, that is more than 400 days. This postmortem covers five reasons the flywheel never turned, and what actually moved the numbers.

wechatpostmortem+4
pitfallsOct 21, 20268 min
277

Dev Breakfast · 2026-10-21

Today's headline: A single "bonsoir, Elliot" makes the model leak its .env: a backdoor for under $50. Plus 4 more: DeepSeek open-sources its inference engine: V4.1 Flash only, and KV cache still missing; TorchTPU goes open source: PyTorch models can run directly on TPU; and more.

daily-intelOct 21, 20268 min
626

Dev Breakfast · 2026-10-20

Today's headline: Should You Merge AI Code You Don't Understand: Settle the Abstraction Layer Yourself First. Plus 4 more: tmux as a Remote Control: Multi-Session Claude Code and send-keys; headroom Goes Open Source: 95% Token Savings on JSON, Only 20% for Coding Agents; and more.

daily-intelOct 20, 20265 min
560

Published by Magic Tools