Stop your AI agent from being weaponized by a malicious package — in three steps
On August 26, 2025, a compromised version of Nx shipped via npm. The malicious postinstall script didn’t try to do the recon itself — it called the local claude, gemini, and q CLIs already installed on developer machines and asked them to recon the filesystem for SSH keys, .env files, GitHub tokens, and crypto wallets. The agents complied. Snyk later counted ~2,349 leaked secrets, 1,000+ GitHub tokens, and ~20,000 files exfiltrated to public repos under victims’ names.
This is a new class of attack: malicious packages don’t write the malware themselves — they prompt your AI agent to write the exfiltration code for them. The agent has filesystem access, credentials in its environment, and an instruction to “find anything sensitive and send it to this URL.” From the OS’s perspective, this looks indistinguishable from a legitimate user-driven session.
It will happen again. The supply-chain attack surface for AI-agent-having developers is now strictly larger than for everyone else.
Why the agent is the perfect malware vector
The Nx attack worked because of three properties that your AI-agent setup probably has:
- Local AI CLIs are everywhere. If you run Cursor, Claude Code, Codex CLI, or any agent SDK, your machine has a binary that can read files, make HTTP requests, and execute arbitrary code on your behalf.
- Those CLIs are typically authenticated. A logged-in
claudeorghCLI carries the credentials needed to read your repos, post to gists, and call your cloud APIs. - Postinstall scripts run in your shell. When you
npm installa compromised package, itspostinstallruns as you, with your full environment loaded — every API key in.env, every cached credential in~/.awsor~/.config.
A malicious package combines those three: it shells out to your local AI CLI, uses your credentials, exfiltrates whatever the agent can find. The agent does what it always does — read files, make HTTP calls — except the instructions came from the package, not from you.
What ACP can — and can’t — prevent
Honest scope:
- ACP cannot prevent the malicious package from being installed. That’s a supply-chain problem. Use Socket, Snyk, GitHub Dependabot, npm provenance, lockfile audits.
- ACP cannot prevent your AI CLI from being invoked by a postinstall script. That’s an OS / shell problem.
- ACP can prevent the AI CLI from reading sensitive paths or calling unauthorized URLs once invoked. That’s the layer that turns “the agent exfiltrated everything” into “the agent attempted to read
~/.ssh/id_rsaand was denied — log row written, alert fired.”
The Nx attack succeeded because no policy stood between “the malicious package told the agent to read ~/.ssh” and “the agent read ~/.ssh.”
Three steps that turn your agent into a hardened target
Step 1 — Install the hook
For Cursor, Claude Code, or Codex CLI:
curl -sf https://agenticcontrolplane.com/install.sh | bash
The hook intercepts every tool call before it executes. It runs regardless of who prompted the agent — you, a postinstall script, a Jira ticket the agent ingested, a README file with hidden instructions. The hook doesn’t trust the source of the prompt.
Step 2 — Deny reads on sensitive paths and unknown egress hosts
In your dashboard (cloud.agenticcontrolplane.com) → Policies:
{
"mode": "enforce",
"tools": {
"Read.credentials": { "background": { "permission": "deny" }, "interactive": { "permission": "deny" } },
"Read.env": { "background": { "permission": "deny" }, "interactive": { "permission": "ask" } },
"Bash.cat": {
"patterns": [
{ "match": "cat .*\\.ssh.*", "permission": "deny" },
{ "match": "cat .*\\.aws.*", "permission": "deny" },
{ "match": "cat .*\\.config.*", "permission": "deny" },
{ "match": "cat .*\\.env.*", "permission": "ask" }
]
},
"Bash.curl": {
"patterns": [
{ "match": "curl.*", "permission": "deny", "unless": "host IN egressAllowList" }
]
},
"WebFetch": {
"if": "url.host NOT IN egressAllowList",
"permission": "deny"
}
},
"egressAllowList": [
"api.anthropic.com",
"api.openai.com",
"api.github.com",
"registry.npmjs.org"
]
}
The semantic: agents (regardless of who prompted them) cannot read SSH keys, AWS credentials, or arbitrary config without explicit permission. Outbound HTTP can only reach hosts on the allow list — calls to attacker-controlled exfil endpoints are denied at the hook layer, before the request leaves the machine.
The Nx attackers hard-coded their exfil URL into the prompt. With this policy, the agent attempts the call, the hook resolves the host against the allow list, the call is denied, the secret stays on the machine, and the audit log captures the attempt.
Step 3 — Bind end-user identity, per request
For server-side agents (CI runners, scheduled jobs, multi-tenant services), bind the actual end user’s identity. This matters during a supply-chain attack because the policy lookup is keyed by user — even if the malicious code somehow ends up running, the policy that applies is the user’s policy, not a service account’s.
from acp_governance import set_context
set_context(
user_token=request.headers["Authorization"],
agent_name="ci-build",
agent_tier="background",
)
A compromised postinstall running in CI under a service account can still read whatever the service account’s policy allows. With end-user identity propagation, the policy is your scoped IdP role — much smaller blast radius.
(Free fourth step) — Audit + anomaly alerts
Every denied read of a sensitive path or denied egress call writes a structured row. The pattern of a supply-chain attack is visible: a sudden burst of Read.credentials denials, all from the same agent, all within minutes of an npm install event. Set an alert for “more than N denied sensitive reads in any 5-minute window” and the next Nx-class attack wakes you up at minute six instead of week six.
The total time investment
- One
curlcommand (Step 1): ~30 seconds - Sensitive-path + egress-allow-list config (Step 2): ~5 minutes
- End-user identity binding (Step 3, server-side only): ~1 minute
Five to six minutes from blank slate to “a malicious package that calls my local AI CLI cannot read SSH keys, AWS credentials, or arbitrary config files, and cannot exfiltrate to anywhere not on a known allow list.”
The Nx attack happened in August 2025. The next AI-CLI-weaponized supply-chain attack is not a hypothetical — it’s an inevitability of how the ecosystem now works. The fix lives one curl command and one policy entry away.