10 min read

Your AI Coding Assistant's Config Folder Is a Persistence Surface

Supply-chain malware started writing into the config files your coding agent reads on every run. Nothing in the usual toolchain is watching that surface.
Your AI Coding Assistant's Config Folder Is a Persistence Surface
Photo by James Harrison / Unsplash

July 2026


In June 2026 an npm worm did something that sat a few layers below the credential-theft headline. It planted persistence in the config directories that AI coding assistants read on every run.

The June 2026 Miasma worm wrote self-executing hooks into AI coding assistant config directories including .claude/, .cursor/rules/, and .vscode/, files that no dependency scanner audits and no endpoint tool flags (StepSecurity, 2026). A supply-chain compromise now persists in a place nothing in the standard toolchain inspects. The write-ups led with the credential theft and the install trick, and both were worth reporting. The config files the malware left behind got far less attention, and they are the reason removing the package does not clean the project, the backdoor re-runs the moment it is opened.

Nothing about this attacker's logic is new, only the surface is. The developer ecosystem has been the target for years, the maintainers, the registries, the trust that installs code without a second look, and this worm moved that same pattern one node closer, into the agent's own config.

The campaign, which StepSecurity named Phantom Gyp, compromised 57 packages across more than 286 malicious versions in under two hours on 3 June 2026 (StepSecurity, 2026). The speed is not the part that should worry you. The persistence is, because it outlives the one thing a developer would normally do to clean up. That is what turns this from a fast-moving worm into something a serious team has to keep watching after the campaign is over.

Why uninstalling does not fix it: the hooks are not in node_modules. They are committed straight into the repository, so they travel with the project, survive a clean reinstall, and run again the next time anyone opens it in their agent.

How the June 2026 npm worm reached your coding agent

This campaign was a variant of the Miasma worm, a self-spreading supply-chain malware family. Its largest single victim was @vapi-ai/server-sdk, the official Vapi.ai voice AI server SDK, which draws more than 408,000 monthly downloads (StepSecurity, 2026). An hour after that first hit, the attacker published malicious versions of more than 50 packages belonging to a single maintainer, including ai-sdk-ollama at more than 120,000 monthly downloads (StepSecurity, 2026). The logic is the one attackers keep returning to. Go where the install base already is, and one compromise reaches thousands of machines.

The install vector has been analysed thoroughly elsewhere, so a paragraph here is enough before moving to the part that has had less attention. Most npm hardening guidance tells developers to watch the preinstall and postinstall lifecycle scripts. This attack declared none. It shipped a 157-byte binding.gyp file instead (Snyk, 2026), and because npm automatically runs node-gyp against any package carrying one, the file's command-substitution syntax executed the payload during the build. No lifecycle script, no entry in package.json, nothing for a script-field scanner to flag.

That is how it got in. What it did next is the story.

What a compromised .claude or cursor rules file can do

Once the payload ran, it stole credentials, which is what the headlines covered. It also did something else. Using stolen GitHub tokens, it committed backdoor configuration files directly into the victim's repositories through the GitHub GraphQL API (StepSecurity, 2026). The named file set is specific:

  • .claude/setup.mjs and .claude/settings.json (Claude Code, run on session start)
  • .cursor/rules/setup.mdc (Cursor, loaded on project open)
  • .gemini/settings.json (Gemini)
  • .vscode/tasks.json and .vscode/setup.mjs (VS Code, runOn: folderOpen)
  • .github/setup.js

That list looks like several backdoors. It is one, planted behind several doors. Each tool offers its own way to run code automatically, a Claude Code session-start hook, a Cursor rules file loaded on open, a VS Code task set to runOn: folderOpen, and the attacker uses whichever fits. The filenames differ, but every one of them does the same thing when it fires: bun run against the same stealer payload the original install delivered (StepSecurity, 2026). Whichever editor the developer opens the project in, the full chain runs again, credential theft and worm propagation included.

None of this requires the developer to make a mistake. There is no risky click, no ignored warning, no careless step. The package installs normally, the payload runs at build time, and the config files are committed with a stolen token before anyone opens the editor.

Running that payload through a downloaded Bun runtime rather than Node is deliberate. Bun is a fast alternative to Node.js, the standard runtime that executes JavaScript outside the browser, and because most monitoring watches the Node process tree, running the payload under Bun sidesteps it (StepSecurity, 2026; Snyk, 2026). This is an evasion the worm inherits from the Shai-Hulud lineage rather than one it invented.

A normal config file holds settings the tool reads. This one holds code the tool runs.

It hides in plain sight in your git history, committed under what looks like a routine setup change, with a message engineered to look legitimate: that the file is required for IDE integration and dependency setup (StepSecurity, 2026).

A backdoor that re-runs on project open is bad enough. A backdoor sitting in the config an AI assistant consults before it writes code is a different category of problem. StepSecurity's analysis puts it plainly: the technique poisons the tools that generate code, not just the code itself, and every subsequent AI-assisted generation in that project could be shaped by the attacker's instructions, potentially seeding subtle vulnerabilities into work that looks developer-written (StepSecurity, 2026).

That last part is a documented risk in the report, not an observed outcome in the wild, and it is worth stating at exactly that strength. The capability is confirmed. Exploitation in the wild has not yet been seen.

Flow diagram of the June 2026 Miasma npm worm. Phase one, the machine is compromised during npm install via a hidden binding.gyp file that steals credentials. Phase two, the payload uses stolen tokens to commit backdoor config files into AI coding assistant directories. Phase three, opening the project in an agent re-runs the payload via bun run, and the worm spreads to other packages.
Two moments, not one. The project is compromised at install, then re-compromised every time it is opened, which is why removing the package does not end it.

Why your dependency scanner and your endpoint tool both miss this

We have documented this supply-chain pattern across the npm ecosystem for the better part of a year at CyberDesserts, from individual maintainer compromises to successive generations of the self-replicating worm. This is the first turn I have seen where the persistence lands in a spot the two obvious controls were never built to cover.

Software composition analysis scans your declared dependencies and your manifests. It reads package.json, the lockfile, the dependency tree. It does not audit a .mdc file an agent wrote into a dotfolder, because that file is not a dependency, it is project configuration. Endpoint tooling watches process and binary behaviour, and it does not know that a single line in .cursor/rules/ is load-bearing instruction for a code-generating agent.

Neither tool is failing. They were built before an agent's config file could rewrite what the agent does, and neither was ever scoped to watch it. That is the point. Coding agents opened a new attack surface simply by reading and acting on their own config, and the security stack has not caught up to a file that can quietly change what the agent builds.

Attackers have already used stolen credentials to write persistence into CI and workflow files. This worm takes the same move one step further, into the config files an AI agent reads on startup. That target barely existed two years ago, because the agents that read those files barely existed either. It is the one place a developer opens every day and no scanner thinks to check.

The industry is filing this under novel AI threat, and that framing is where teams go wrong. The defence does not need a new category of AI-security product. It needs discipline you already have, verify what executes, trust nothing in the repo by default, applied to a directory you were not yet treating that way.

How to detect and harden AI coding assistant config directories

The first move is not a command. It is a decision: treat the agent config directory as a surface someone owns, the way you already treat CI configuration. A change to .github/workflows/ gets reviewed. A change to .cursor/rules/ usually does not, even though both can execute code in your environment. Claude Code hooks, to take one example, run arbitrary shell with your full user permissions, automatically, with no sandbox and no per-run prompt (Anthropic, 2026). A file an attacker committed into that directory carries the same privilege as one you wrote yourself. Close that review gap and most of the detection follows.

What you are watching for is behaviour, not this campaign's signatures. The specific filenames and byte counts will change with the next worm; the behaviours that make a config file dangerous will not. Four are worth watching in any repo:

  • A config file that executes something on open or on session start, rather than just holding settings. A hook, a setup script, a task set to run on folder open.
  • A config change nobody on the team authored. A commit to .cursor/rules/ or .claude/ from an automated token, or one that no reviewer signed off, is a finding on its own.
  • A config file that reaches out to the network. Nothing in a rules or settings file has a legitimate reason to fetch a binary or call out to a URL.
  • A config file whose contents you cannot fully account for. If you cannot explain why every line is there, treat it as suspect until you can.

Standard npm supply-chain hygiene still applies underneath all of this, disabling install scripts, pinning dependencies with integrity hashes, holding freshly published versions before they reach a build, and scoping tokens tightly. That is a workflow in its own right, and I have set it out separately as an explicit-trust workflow for npm. The point of this piece is the layer above it, the config surface those controls were never scoped to watch.

One caveat cuts across all of it. Do not treat a package looking legitimately signed as evidence it is safe. A worm that has compromised the publishing pipeline can forge valid signing and provenance, so a reinfected package can pass a signature check clean (StepSecurity, 2026; Snyk, 2026). The signature tells you the pipeline signed it, not that the code is trustworthy.

Prevention lowers the odds of a poisoned package reaching you. It says nothing about whether your config is clean right now, and that is a separate job, the one this incident makes urgent, because the config surface is exactly what nothing in the standard toolchain inspects.

The reflex is to point an AI at it. Resist that as the primary check. Asking an AI agent to read a possibly-poisoned config and declare it safe is circular: the reviewer is an instruction-follower reading attacker-controlled text, and a config crafted to survive review can carry instructions aimed at the reviewer itself. The verification that certifies has to be deterministic, computing facts an agent cannot argue with.

A baseline is the simplest deterministic check. Record the config surface when the repo is trusted, commit that record, and compare against it later. A new file, a changed file, or a change nobody authored is a fact that does not depend on any model's judgement. It will not tell you whether a change is malicious, only that it happened, but a change to .cursor/rules/ that no one made is exactly the signal worth catching early.

A baseline only works if you run it. A native control runs itself, which makes it the better defence where the tool offers one. One caveat first, because it matters: this worm did not write those files through the agent. It used stolen tokens to commit them straight into the repo over git, which is outside anything Claude Code can intercept. So the native controls are not preventing the injection, they are catching the file when the agent next reads it. Claude Code can fire a hook when a config file changes or when a rules file is loaded into context, and refuse to act on it, which is the exact surface this attack poisons (Anthropic, 2026).

The catch is that a hook in a project or user settings.json sits in a file an attacker can also write to, so it can be disabled by whoever gets there first. Managed settings solve that: an administrator deploys them to a system path that needs elevated privileges to write, and they override every project and user setting (Anthropic, 2026). A deny rule set there, blocking the agent from reading credential files or making network fetches, holds even on a machine whose local config has been tampered with.

What these have in common is the whole point: none of them asks the compromised surface to vouch for itself. A baseline, a native hook, and a human reviewing an unexplained change are all checks the attacker in the config cannot reach into.


The direction of travel is not new either. Zero trust reached the build pipeline years ago: verify every artifact, trust no runner, expire every credential, assume the registry can be poisoned (CISA and NSA CI/CD guidance). What this incident exposes is an element that principle has not caught up to. The config your coding agent reads on every run is now part of the pipeline in every practical sense, because it can execute code, fetch resources, and shape what gets built. It has not been treated as part of the pipeline, so nothing verifies it.

This is not a concern reserved for high-profile or mission-critical teams. Anyone who lets an agent read and write code has taken on this surface, whether they have thought about it or not. Code integrity, the plain ability to trust that what the agent built is what you asked for and nothing else, now depends on watching a directory that no standard security tool covers.

Every element the pipeline used to assume was safe now has to earn that trust. The agent's config directory is the newest name on that list, and it will not be the last. Close the gap before someone else finds it open.


References

  • StepSecurity, "Miasma npm Supply Chain Attack: Self-Spreading Worm via Phantom Gyp", 3 June 2026. https://www.stepsecurity.io/blog/binding-gyp-npm-supply-chain-attack-spreads-like-worm
  • Snyk, "node-gyp supply chain compromise: self-propagating npm worm via binding.gyp", 4 June 2026. https://snyk.io/blog/node-gyp-supply-chain-compromise-self-propagating-npm-worm-binding-gyp/
  • Anthropic, Claude Code hooks documentation (hook execution model, ConfigChange, InstructionsLoaded, exit-code-2 blocking, managed policy settings). https://code.claude.com/docs/en/hooks-guide
  • Anthropic, Claude Code settings documentation (settings scopes, managed settings precedence). https://code.claude.com/docs/en/settings
  • CISA and NSA, "Defending Continuous Integration/Continuous Delivery (CI/CD) Environments" (joint Cybersecurity Information Sheet), 28 June 2023. https://www.cisa.gov/news-events/alerts/2023/06/28/cisa-and-nsa-release-joint-guidance-defending-continuous-integrationcontinuous-delivery-cicd