When Config Became Executable: The Twenty-Year Pattern Behind Supply Chain Attacks
By Dmitriy
A developer clones a repository and opens the folder in their editor. Nothing else. No npm install, no build, no script anyone chose to run. The machine begins sweeping the disk for private keys.
That was one of the execution paths in the compromise of the keyv npm packages, and the part worth pausing on is where the code lived. Not in a package. The worm wrote a SessionStart entry into .claude/settings.json and a folderOpen task into .vscode/tasks.json, files that execute when the cloned folder is simply opened. Neither file is a package. Neither is scanned. Both run.
It reads like a new category of attack. It isn’t. Roughly every four years since the mid-2000s, some file that nobody classifies as code turns out to execute anyway — and the tooling built to inspect the previous generation of artifacts takes years to notice.
The artifact that isn’t in the repository
Three of the most-studied supply chain incidents share a structural signature so precise it should have become a checklist item years ago: the malicious thing was absent from the source repository and present only in what people actually ran.

In 2018, the maintainer of event-stream handed publishing rights to a volunteer who asked for them politely. The malicious dependency that followed, flatmap-stream, carried an extra file that was not in the GitHub repository, AES256-encrypted using the parent package’s own npm_package_description field as the decryption key. It did nothing on a normal install. It fired inside one company’s release build script, targeting a Bitcoin wallet, and shipped in Copay 5.0.2 through 5.1.0. Anyone reading the repo saw clean code, because the repo was clean.
In 2020, attackers put an implant called SUNSPOT inside SolarWinds’ build server. It watched for MsBuild.exe, hijacked the build when it recognized Orion, and injected the SUNBURST backdoor into the output. Up to 18,000 customers installed the result. The update was correctly signed. The signature was honest — it certified that SolarWinds built the artifact, which SolarWinds had.
In 2024, the backdoor in xz utils depended on a build-to-host.m4 macro that existed only in the release tarball and not in the Git distribution. The second-stage payload sat in the repository disguised as test data, completely inert, waiting for a macro that only appeared downstream.
Different decades, different ecosystems, one mechanism. Review looks at the source. Execution happens somewhere else.
Why the scanner never had a chance
The uncomfortable finding isn’t that scanners missed these. It’s that scanners were structurally incapable of catching them.
Phoenix Security’s malware package corpus tracks the acceleration: 6 campaigns and 30 malicious packages in 2024, 14 campaigns and 111 packages in 2025, then 37 campaigns and 497 packages in the first half of 2026 alone. One month, May 2026, produced more indexed packages than the preceding four combined. npm accounts for 79.3% of the indicators; PyPI 9%; VS Code and OpenVSX extensions another 6.2%.
Buried in that dataset is the number that explains the whole problem. Across 59 documented campaigns, zero CVEs were assigned during active exploitation. Not few. Zero. CVE-feed scanners had no detection surface for 100% of them.
That is not a failure of diligence. A CVE describes a defect in code — a buffer that overflows, a parser that trusts input it shouldn’t. None of these attacks involved a defect. They involved a stolen maintainer account, a self-propagating worm, or a configuration file doing exactly what configuration files are designed to do. There is nothing to assign a number to, and no version range to flag as vulnerable, because the vulnerable version and the safe version are the same code published by different hands. Sonatype counted 454,600 new malicious packages across 2025, with quarterly growth of 188% year over year — a volume that no CVE process was ever meant to absorb.
We’ve written before about the gap between a patch shipping and agencies applying it. That gap at least assumes a patch exists. This is the other category — the one where there is no advisory to be late on.
The newest file that runs
Which brings us back to the file that starts executing when you open a folder.

The pattern’s current instance began earlier in 2026 with a campaign researchers named TrapDoor: 34 packages across 384-plus versions, planted simultaneously on npm, PyPI and Crates.io, each with an execution path matched to its runtime — postinstall hooks, import-time fetch, a malicious build.rs. The novel component was the payload’s hiding place. The packages carried .cursorrules and CLAUDE.md files containing instructions written in zero-width Unicode characters: invisible to a human reading the file in an editor, fully legible to the AI coding assistant that reads the same file as project guidance. The hidden text instructed the assistant to run a "security scan" that exfiltrated SSH keys, AWS credentials, GitHub tokens and browser login databases. Some of these files arrived as pull requests with titles like docs: add .cursorrules with dev standards and build verification.
Then came the keyv compromise, which started, as these usually do, with a compromised GitHub maintainer account rather than any technical flaw. A malicious release reached more than 400 npm packages in roughly thirty minutes; one analysis put it above 440 packages and two thousand versions within hours. The payload pulled down a standalone Bun runtime and a 728 KB obfuscated second stage, harvested cloud metadata, Vault tokens, Kubernetes service-account tokens and CI secrets, then republished itself using the npm token it had just stolen. Its credential target list had grown about 70% over the previous variant, and the additions are telling: Claude, OpenAI, Cursor and Gemini API keys. Command-and-control domains were fetched from an Ethereum smart contract, so infrastructure could rotate without touching the malware.
One detail deserves to survive this particular incident. The worm installed a host-level watcher — a LaunchAgent on macOS, a systemd user service on Linux — that polled GitHub every 60 seconds with the stolen token. When the token stopped working, which is precisely what revoking it produces, the watcher evaluated an attacker-supplied handler string fetched from the network. The standard first move in incident response was itself the trigger. Isolating the host from the network had to come before rotating anything.
An arXiv study of 1.43 million agent skills recently described the ecosystem’s metadata layer as "activation-ready but governance-poor," with recursive reuse creating what the authors call a hidden package inventory — dependencies that exist in effect but appear in no manifest. That’s a familiar sentence with the nouns swapped. It could have been written about npm in 2015.
What actually holds
The defenses that work are the ones that stopped asking whether an artifact is malicious and started asking where it came from.
npm’s trusted publishing, wired to GitHub Actions or GitLab CI, generates SLSA Build Level 2 provenance signed through Sigstore with no extra configuration, letting a consumer verify that a package was built by the workflow in the repository it claims to come from. That’s real progress, and it’s worth stating its limit plainly: provenance proves origin, not intent. It would not have stopped event-stream, where the malicious release came from the legitimate publisher through the legitimate pipeline. Attestation raises the cost of impersonation. It does nothing about a maintainer who has been replaced.
The Codecov breach makes the second point. Attackers modified the Bash Uploader script to exfiltrate CI environment variables, and it ran undetected from January 31 to April 1 of 2021 across more than 23,000 customers — found not by a scanner but by a customer who noticed a checksum mismatch between the script on GitHub and the one they’d downloaded. Verifying the hash of anything your pipeline fetches at runtime is unglamorous and remains one of the highest-yield controls available.
The third is new, and cheap, and almost nobody does it yet: treat agent instruction files as reviewable code. .claude/, .cursorrules, .vscode/tasks.json, and whatever the next editor introduces belong in code review with the same scrutiny as a shell script, because functionally that is what they are. Screen them for invisible Unicode. Treat a docs:-prefixed commit that adds one as a change to the build. The convention that these files are documentation is a convention, and the last two years have established that attackers do not share it.
None of this is exotic. It’s the same set of controls that has always applied when developer machines and build pipelines hold the credentials — which is why credential exposure through developer and account compromise keeps showing up as the actual root cause, and why pipeline and test automation hygiene is a security control and not just an engineering-quality one.
The question worth carrying forward
The useful lesson from twenty years of this is not "watch out for agent config files." That’s the specific answer to the last attack, and specific answers to last attacks are how the industry got here.
The lesson is the shape. Every generation of tooling inspects the artifact that the previous generation was exploited through, and attackers move one layer down to whatever the tooling has classified as inert. Antivirus watched binaries, so the payload moved into packages. Dependency scanning watched packages, so it moved into install hooks and build scripts that run before anything is declared. Now scanners read lockfiles and postinstall scripts, and it has moved into a plain-text file that no scanner classifies as code at all.
The question to ask about your own repository is not whether it contains a known-bad file. It’s which file in it currently executes without anyone calling it code.
- On August 10, 2026
- 0 Comment
