Config Files That Run Code: Supply Chain Security Blindspot
Source: Hacker News
Cloning a repository and opening it in an editor can run an attacker’s code before a developer reads a single line. The trigger is not a malicious dependency or a hidden install script. It is an ordinary-looking config file already sitting in the repo, the kind an IDE, an AI coding agent, or a package manager reads and acts on automatically.
VS Code, Cursor, Claude Code, Gemini CLI, npm, Composer, and Bundler all support config files that can carry a shell command. Some run it when dependencies install or tests run. Others run it when the folder is opened or an agent session starts, in most cases after a one-time trust prompt that developers click through without reading. A config file that runs a command is an execution primitive, not metadata, and supply chain attackers have started using it as one. Almost nobody reviews these files. This post walks the config injection vectors with real source, then maps the broader class so the pattern is easy to spot in a diff.
The Miasma worm is the worked example. One commit to icflorescu/mantine-datatable, commit f72462d9, is unsigned, authored as github-actions , titled chore: update dependencies [skip ci], and adds six files. Five of them exist to launch the sixth, a single dropper at .github/setup.js. SafeDep’s Miasma source-repo analysis documents the full incident, the dropper internals, and the 121 affected repositories. This post stays narrow and looks at the config surface itself.
The dropper
The dropper is .github/setup.js, 4,348,254 bytes, one statement in a try/catch. That size is not padding. It holds the encrypted payload and stays above the roughly 384 KB limit where GitHub code search stops indexing, so the small launcher files, not the dropper, are what expose the repo to a search. Its first bytes:
// .github/setup.js @ f72462d9 (first 180 bytes of a 4.3 MB file)
try{eval(function(s,n){return s.replace(/[a-zA-Z]/g,function(c){var b=c/dev/null
grep -nE '^[[:space:]]*(system|exec|`)' Gemfile 2>/dev/null # top-level shell-out in a Gemfile
The first check finds this campaign. The second looks for the auto-run behavior itself, which is what survives the attacker renaming the dropper, and it is the version worth keeping in a pre-clone hook or CI step.
The deeper fix is treating editor and package-manager config as part of the trusted computing base. A SessionStart hook is a postinstall for the editor. A .cursor/rules file is a prompt injection that ships in the repo. Both run with the developer’s credentials, and neither shows up in a dependency scan.
If one of these repos was already opened or trusted, treat the machine as exposed rather than clean. The credentials the session could reach are the ones to rotate first: GitHub and npm tokens, and any AWS, Azure, or GCP keys loaded in the environment. Check other recently opened clones for .github/setup.js, and use SafeDep’s incident analysis for the full indicator list, since the dropper is recompiled per wave and the file hash alone will not match.
Detection here is the same problem as malicious dependency detection, applied to a wider set of files. Scanning a lockfile catches a poisoned package. It does not catch a poisoned tasks.json. SafeDep’s vet covers the package side and some adjacent auto-run surfaces, including GitHub Actions workflows and VS Code extensions, but the editor and agent config launchers are a newer surface that no dependency scan fully covers yet. Tool aside, the config files in a repo are part of its attack surface, and the SDLC has to review them as code.
Related reading. Miasma worm targets AI coding agents via GitHub repos is SafeDep’s analysis of the incident this draws from. The Miasma registry arm deobfuscates the dropper in full. A threat model for malicious pull requests covers how these commits land in the first place.