All posts
Unproven Execution

PromptMink: AI-Authored npm Commit Plants Backdoor

A Claude Opus co-authored commit added a Layer-1 bait npm dependency that pulled a Famous Chollima credential-stealing payload

Securityv0 Intelligence Team OWASP: ASI05 sv0 finding: unproven_execution
npm supply-chain ai-coding-agent dprk famous-chollima transitive-dependency

The Incident

On February 28, 2026, a commit co-authored by Anthropic’s Claude Opus was merged into the npm package openpaw-graveyard, an autonomous crypto trading agent. The commit added @solana-launchpad/sdk as a dependency. That package is a Layer-1 “bait” — it ships no malicious code itself, but it transitively pulls @validate-sdk/v2, which is the actual payload. ReversingLabs disclosed the broader campaign as PromptMink and attributed it to Famous Chollima, a DPRK threat cluster also linked to the graphalgo developer-lure campaign. Across roughly seven months, the campaign published more than sixty unique malicious packages spanning over three hundred versions.

The payload is heavyweight. @validate-sdk/v2 exfiltrates .env and .json files, crypto wallet credentials, and host system metadata, then plants attacker-controlled SSH keys on Linux and Windows for persistent remote access. A later Rust-based variant escalated the data-theft scope to zip and exfiltrate entire project source trees. Famous Chollima built the trap with what ReversingLabs and the broader security press call “LLM Optimization abuse” — long, well-formatted READMEs and convincing documentation engineered to make the bait packages read as ideal matches for whatever task an LLM coding agent has been handed.

MITRE ATT&CK coverage: T1195.002 (Compromise Software Supply Chain), T1059.007 (Command and Scripting Interpreter: JavaScript), T1098.004 (Account Manipulation: SSH Authorized Keys), T1552.001 (Unsecured Credentials: Credentials in Files).

The Authority Path That Failed

The identity that carried execution authority at the moment of failure was Claude Opus, acting as a coding assistant on behalf of the developer who would eventually merge the PR. Its held scope was substantial: write access to package.json, the ability to introduce new dependencies, and — by virtue of npm install semantics — the ability to expand the project’s runtime code-execution surface to anything those new dependencies depend on. Its exercised scope went far beyond what the human reviewer thought they were authorizing. A single line in package.json adding @solana-launchpad/sdk was the visible change, but the transitive dependency chain pulled @validate-sdk/v2; once the package was installed and loaded, the payload could steal credentials and implant SSH keys from the host environment.

The trust anchor that failed first was the AI assistant’s package-selection heuristic. Famous Chollima engineered for that heuristic by writing the bait package’s docs to look like a perfect contextual match for the task. The gap between “developer authorized one dependency” and “agent attached an unbounded transitive code-execution chain” is exactly what an SV0 inventory would surface pre-incident: per-package authorization for any new dependency an AI agent introduces, plus provenance, age, and reputation gates on the package itself, not just the diff that adds it.

SecurityV0 Perspective

This is unproven_execution. SV0 normally writes about an agent inside the victim’s environment over-reaching its scope; here the agent — Claude Opus, acting as a coding assistant — was the unwitting attacker, and the deploying operator is the human who clicked merge. The pattern is the Langflow CSV Agent / Python REPL case lifted from the agent runtime to the agent-assisted commit: a framework attached a code-execution surface (the transitive npm dependency chain) that the operator never explicitly authorized at the package level.

The evidence pack SV0 would produce here is concrete: per AI co-authored commit, the diff of dependencies introduced (direct and transitive), the registry provenance, age, and weekly-download counts of every newly-added package, and the install-script and post-install footprint each new dependency executes. Pre-exfiltration, that pack answers: did we let an AI agent attach a code-execution surface our operator never reviewed at the package level? Post-exfiltration, it answers: which AI-authored commit, on which machine, was the dependency-graph entry point that planted the SSH key?

What To Do

  • Gate AI-authored dependency changes behind explicit per-package approval. A human-merged PR that adds package.json entries authored by an LLM is not authorization to install transitive code. Require maintainers to acknowledge each new direct dependency an AI agent introduces by name, not the diff that contains it.
  • Inventory transitive execution paths for every new dependency. Run npm-audit, Socket CLI, or osv-scanner against the diff produced by AI commits before the lockfile lands on main. Block merges that add packages with lifecycle scripts, native binaries, loader wrappers, or unusual transitive dependencies unless the maintainer has explicitly justified them.
  • Treat package age, weekly downloads, and publisher provenance as merge-blocking signals. Bait packages like @solana-launchpad/sdk are typically days old, have low download counts, and ship from publishers with no prior history. Your CI must enforce minimum age and download thresholds for new dependencies introduced by AI agents.
  • Audit ~/.ssh/authorized_keys writes and .env reads on developer workstations. The PromptMink payload plants SSH keys for persistence and steals wallet and cloud credentials from hosts where the dependency executes. Endpoint detection that alerts on package-manager or Node processes writing to ~/.ssh or reading dotfiles is a high-signal, low-noise tripwire.
  • Filter LLM co-authored commits into a higher review threshold. Co-Authored-By: Claude (and equivalent trailers from Cursor, Copilot, Cline, Gemini CLI) are now an attribution surface in your audit trail. Route those commits to reviewers who understand which dependencies the agent proposed and whether the transitive graph was inspected.

Sources