# When an AI agent writes your code, a poisoned instruction is remote code execution (Part 3 of 5)

If an AI agent turns a task file into code and runs it, then a poisoned task file is remote code execution by proxy. Here is how to secure the build plane, including the agent-writes-tasks worm path.

Author: J.A. Watte
Published: July 17, 2026
Source: https://jwatte.com/blog/securing-ai-coding-agents/

---

I have shipped enough AI systems now to have a rule I did not have two years ago: I treat every file an agent reads as code. Not "sort of like code" or "code-adjacent." Code. Because when a coding agent reads a task file, turns it into a patch, and runs the patch, the distance between "some text in a Markdown file" and "a process executing on my machine with my credentials" is one agent loop. That is the whole build plane in one sentence.

This is Part 3 of a five-part series on securing AI systems that actually do things. Part 1 drew the three trust boundaries. Part 2 covered the data plane. This part is about the boundary most people building with coding agents have not drawn yet: the build plane.

## A file an agent reads is code by proxy

Think about what an agentic coding tool actually does. It reads instructions from somewhere: a task file, a GitHub issue, a design doc, a pull request description, a comment thread, sometimes a web page it browses for "context." Then it writes code. Then, in a lot of setups, it runs that code. Tests, migrations, install steps, a quick script to "verify the fix."

So the input is natural language and the output is execution. That is the exact shape of a remote code execution vector. The only difference from a classic RCE is that the payload is polite and reads like a Jira ticket.

OWASP's Top 10 for LLM Applications (2025) names the pieces that show up here: LLM03 Supply Chain, LLM05 Improper Output Handling, LLM06 Excessive Agency, and LLM07 System Prompt Leakage. MITRE ATLAS catalogues the adversary techniques, and the OWASP Agentic AI Threats and Mitigations guide is the one to read if your agent chains multiple steps. I will not recite them. I would rather tell you what changed in how I work once I took them seriously.

The mental model is short: an agent-facing input is a proposal, not an authority. A human gates the moment it becomes an action.

## The worm path: agents that write tasks other agents run

Here is the failure mode I worry about most, because it is the one that scales.

Say you have an agent that can create new task files, open issues, or write follow-up tickets. And say another agent (or the same one, later) reads those tasks and executes them. Now walk the chain. A piece of untrusted content gets ingested. It carries a hidden instruction. The first agent, doing its job, writes that instruction into a new task. The second agent reads the task and runs it. The instruction tells it to write another task. You have built a self-propagating loop. That is not hypothetical. It is just what happens when agent output becomes agent input with no human in between.

This is the LLM06 Excessive Agency problem wearing its worst outfit. The fix is not clever. It is structural:

- **Agent-authored work is a proposal.** A task, issue, or PR an agent writes does not get executed by another agent until a human has read it. The human is the air gap that breaks propagation.
- **Never let agent output flow directly into agent input.** If agent A writes something agent B consumes, put a review gate on the edge, not a hope.
- **Treat ingested content the agent reads as hostile.** A web page, a doc, a review, a support ticket, a resume, a calendar invite: all of it can carry an injected instruction (that is Part 2's territory, and it feeds straight into this one).

If you remember one thing from this post: the loop where an agent generates work another agent runs, unattended, is the single most dangerous pattern in agentic development. Break the loop with a person.

## Least privilege, because a hijacked agent inherits your access

A coding agent that gets hijacked does whatever it can reach. So the question is not "will my agent misbehave," it is "what can my agent touch when it does." Scope that down hard.

- **No production secrets.** A build agent does not need prod database credentials, prod API keys, or your cloud admin token to write a function. If it has them, a poisoned task can exfiltrate them or use them. Keep prod credentials out of the agent's environment entirely.
- **No push rights, no deploy rights.** The agent proposes a diff. It does not push to main and it does not deploy. A human merges. Running is not the same as committed, and committed is not the same as shipped.
- **Scoped tools only.** Give the agent exactly the tools the task needs and nothing else. No arbitrary shell if it only needs to edit files. No arbitrary network egress: if it does not need to reach the internet, it does not get to. Unbounded tool access is unbounded blast radius.
- **A separate principal.** The agent runs as its own identity, not as you. Different account, different token, its own scoped permissions. When something goes wrong you want to see "the agent did this," not "someone using your name did this."

The principle is old and correct: least privilege. Agents just make it urgent, because they act at machine speed while you are getting coffee.

## Slopsquatting: the dependency the agent hallucinated

This one is specific to LLM-generated code and it is nasty. Agents confidently suggest package names. Sometimes those packages do not exist. Attackers have figured this out, so they pre-register the plausible-sounding names an agent is likely to hallucinate, and now the "helpful" install command pulls a malicious package. The community named it slopsquatting, a cousin of typosquatting where the model, not a typo, invents the bad name.

This maps to LLM03 Supply Chain, and the defence is boring and non-negotiable:

- **Never auto-install a dependency an agent suggested.** Not in the agent's sandbox, not in CI, not "just to test it."
- **Pin and hash.** Lockfiles with integrity hashes. If the resolved artifact does not match the hash, the install fails. A hallucinated or swapped package cannot slip through a hash check.
- **Verify the package exists and is the real one** before it enters your lockfile. Real maintainer, real download history, real repo. A package that appeared last week to match an agent's guess is a red flag, not a convenience.

```
# what I actually want from a build agent
- proposes: "add dependency X for parsing"
# what I do before it goes anywhere:
- confirm X exists and is the genuine project
- pin exact version + integrity hash in the lockfile
- never let the agent run the install unattended
```

## Insecure output handling: review the diff, verify before "done"

LLM05 is Improper Output Handling, and with a coding agent it has a very concrete meaning: do not trust the diff because the agent says it is finished. Models declare victory constantly. "Done, all tests pass." Maybe. Maybe it edited the test to pass. Maybe it deleted the assertion. Maybe it wrote a real fix and also, three files over, changed something you did not ask about.

So the human diff review is not a formality, it is the control. Read the whole diff, not the summary of the diff. Pay attention to files the task did not mention. Run the verification step yourself, or run it in an environment the agent could not have tampered with, before anything merges.

I learned a milder version of this the hard way, outside of agents entirely. A pipeline I run once published a near-empty dataset over a good one, because a half-finished run reported success and nothing checked the row count before publishing. Same lesson: "the process said it worked" is not evidence that it worked. With an agent writing code the stakes are higher, because the thing declaring success also wrote the code that decides what success means.

## Attribution and scanning: make a bad change traceable

Two more habits, cheap to adopt, expensive to skip.

**Attribute every agent commit to a distinct identity.** The agent commits as itself, with its own author identity, never masquerading as a human. When you are staring at a regression at 11pm, "this line came from the agent, this line came from a person" is the difference between a five-minute fix and a two-hour archaeology dig. It also means that if agent-authored code ever does something malicious, one query finds every commit it touched.

**Scan for secrets on every diff, human or agent.** Agents paste keys into code with cheerful confidence. Secret scanning in the commit path catches the obvious leaks before they reach a repo, and it is one of the few controls that costs nothing to run and saves you a credential rotation drill you really do not want.

## A build-plane checklist

Here is what I actually run down before I let an agent near a repo that matters:

- Agent has **no production secrets** in its environment.
- Agent has **no push and no deploy rights**. It proposes; a human merges.
- Agent's tools are **scoped**: no arbitrary shell, no arbitrary network egress it does not need.
- Agent runs as a **separate principal** with its own identity and permissions.
- **No auto-install** of agent-suggested dependencies. Pin and hash everything.
- Every agent-authored task, issue, or PR is a **proposal a human reads** before any agent runs it.
- **No edge** where agent output becomes another agent's input unattended.
- **Human diff review** of the full change, and an **independent verification** step, before merge.
- **Secret scanning** on every commit.
- **Distinct commit identity** for the agent, for traceability.

None of this slows down the good case much. What it does is make sure the bad case, the poisoned task, the hallucinated package, the runaway loop, hits a wall you built on purpose instead of a gap you left by accident.

The build plane is where AI stops being a text generator and starts being a thing with your access and your credentials. Draw the boundary. Put a human on it.

Previous: [Prompt injection when the data is the attack](/blog/ai-indirect-prompt-injection/)

Next: [Never let a model pull the trigger: the action boundary for autonomous AI](/blog/ai-action-boundary/)

## Related reading

- Part 1: [The three trust boundaries every AI system that acts has to draw](/blog/ai-security-three-trust-boundaries/)
- Part 2: [Prompt injection when the data is the attack](/blog/ai-indirect-prompt-injection/)
- Part 4: [Never let a model pull the trigger: the action boundary for autonomous AI](/blog/ai-action-boundary/)
- Part 5: [Eight data-flow security habits from running real pipelines](/blog/ai-data-flow-security-habits/)
- Related career context: [Becoming an AI/ML platform engineer](/blog/becoming-ai-ml-platform-engineer/)

*This post is informational, not security-consulting advice. Framework references are nominative fair use; no affiliation is implied.*


---

Canonical HTML: https://jwatte.com/blog/securing-ai-coding-agents/
RSS: https://jwatte.com/feed.xml
JSON Feed: https://jwatte.com/feed.json
Hero image: https://jwatte.com/images/blog-securing-ai-coding-agents.webp
