Writing · July 2026

Your AI Tools Are the Attack Surface Now

AI coding tools didn't create supply-chain risk. They moved the blast radius into developer and build environments.

The old supply-chain question was: can this dependency reach production? The new one is: can this tool reach the machine that can reach everything else?

The axios compromise matters here because it landed in developer and build environments that now run agents, not just editors and scripts.

A poisoned package does not need to reach production first if it can reach the workstation with source, shell access, package tokens, cloud CLIs, MCP config, browser sessions, and agent permissions.

In the Axios case, an npm maintainer account was compromised. Malicious axios versions were published. A hidden dependency, plain-crypto-js, carried a cross-platform remote access trojan. The attack used a postinstall step, which is exactly the kind of thing developers have trained themselves to ignore because most installs are boring.

A short exposure window is still enough time when half of organizations adopt a new third-party library version within a day of release.

That is the new attack surface. Not AI in the abstract. The local environment where AI tools act.

The Target Moved

Classic supply-chain defense asks whether malicious code can ship to production. That is still the right question. It is no longer the only question.

A compromised dependency used to get a few common chances: run during install, read environment variables, inspect the current project, maybe phone home. Those were serious already. AI coding tools raise the value of the same foothold because the environment now has tools, credentials, and permission paths attached.

The package does not need to own production if it can own the workstation that can later deploy, push, fetch secrets, trigger CI, or hand an agent trusted context. It can look for credentials. It can tamper with generated code. It can poison context that an assistant will later treat as project truth. It can create instructions that look like documentation. It can wait for the human to ask the agent to do the next privileged thing.

Some payloads are not binaries. In agent systems, text can become operational.

A malicious MCP tool description, README, issue body, dependency comment, or fixture can become an instruction path if the agent is allowed to treat untrusted text as trusted work context.

Three Failure Modes Are Converging

The first failure mode is old-fashioned package compromise. Axios is the clean example: compromised publishing path, malicious dependency, install-time execution, credential exposure risk, and remote access.

The second failure mode is malicious developer content. Jamf has documented attacks that abuse Visual Studio Code workspace trust and task execution. The repo looks like work. The editor asks whether you trust it. The task runs. That was already a bad pattern before agents started reading repos for intent.

The third failure mode is agent-context compromise. Research on AI-assisted development tools and MCP clients has shown that prompt injection can move through tool descriptions and other context channels. Separate MCP safety work demonstrates MCP-enabled attacks involving malicious code execution, remote access, and credential theft. A remote-MCP measurement study found 7,973 live servers, with 40.55% exposing tools without authentication; every one of 119 testable OAuth-enabled servers had at least one flaw.

Three paths into the developer environment
Install path
A dependency runs code during install and looks for credentials.
Workspace path
A repo or editor task asks the local environment to execute something.
Agent path
Untrusted text becomes operational context and tries to steer tool calls.

The common target is the developer environment.

CVE Scanners Are Necessary And Late

CVE scanners are useful. They are also designed around a slower kind of truth: a vulnerable component is known, named, assigned, indexed, and then detected.

That does not cover the first hour of a malicious package. It does not cover a maintainer account that just shipped a bad version. It does not cover a prompt injection hidden in a tool description. It does not cover a repo that weaponizes a trusted editor workflow. It does not cover an MCP server that is not "vulnerable" in the traditional sense, but is simply too powerful for the context where it runs.

This is why behavioral controls matter. The question is not only "is this dependency known-bad?" It is also "what did it try to do after it arrived?"

Did install scripts run?

Did a package read .ssh, cloud config, token stores, or .env files?

Did an agent call an MCP tool that had never been used in this project before?

Did a supposedly local task try to write outside the workspace or reach a new outbound host?

If your defense cannot answer those questions, it is not a gate. It is a report.

What A Real Receipt Blocks

A receipt is not a pretty log. It is the record that lets you prove what happened and where the run stopped.

For this class of attack, the receipt should make the boring controls visible: package scripts blocked by default, credential paths denied, tool permission separated from untrusted text, outbound hosts surfaced, and new packages, scripts, editor tasks, MCP servers, or network destinations shown as a diff.

It can be small:

run: dependency-update
scope: workspace
attempted:
  - npm install [email protected]
denied:
  - lifecycle script: postinstall
  - read: ~/.ssh/*
  - read: ~/.config/cloud/*
  - outbound: unapproved-host.example
allowed:
  - registry fetch: registry.npmjs.org
  - mcp tool: filesystem.read ./src
exception:
  - none
verdict: blocked before agent context
One kill chain, with stops
Install
A postinstall step tries to run. The package manager blocks scripts by default unless the project has a named exception.
Harvest
Code tries to read credentials. Deny rules block obvious paths first: SSH keys, cloud credentials, keychains, .env files, package tokens.
Steer
The agent encounters malicious instructions in repo text or tool metadata. The runner treats that text as input, not authority.
Exfiltrate
Something tries to phone home. Network egress is allowlisted for the job or logged as a new destination.
Persist
The next receipt shows any new package, task, tool, script, or outbound host.

None of that is exotic. Most of it is unglamorous configuration and a little discipline. That is the uncomfortable part: the gap between "defenses exist" and "defenses are deployed" is usually not research. It is an afternoon nobody has budgeted.

The Practical Minimum

Start with the controls that block the common paths.

Two minutes. Turn on ignore-scripts=true for npm by default where your projects can tolerate it. Remove MCP servers you do not actively use. Stop opening unknown repos in trusted editor mode. Stop running agents in all-access mode against unfamiliar code.

For npm, the blunt version for a dev account is:

npm config set ignore-scripts true

For a single repo, put ignore-scripts=true in that project's .npmrc.

Ten minutes. Put credential paths on a deny list. Make new outbound hosts visible during agent runs. Require a package-age or review delay for routine dependency updates. Keep a per-project exception file for install scripts instead of letting every package decide.

One afternoon. Run agents in a workspace-scoped sandbox by default. Treat every MCP server like a production integration to your machine. Record package changes, tool calls, outbound connections, denied reads, and human approvals in one receipt.

The more useful the tool becomes, the less acceptable ambient authority gets.

The Floor

The future version is stronger: sandboxed MCP runtimes, safer package distribution, agent tools designed to fail closed, and receipts that travel with the work.

The floor is simple: no silent scripts, no ambient credential reads, no unreviewed tool authority, and no success claim without a receipt.