Skip to main content
RJL.pub logo

RJL.pub - AI-Native Development Journey

Chapter 20: Agent Security & Safety

A New Threat Surface

An autonomous agent reads untrusted content, holds credentials, and calls tools that change the world. That combination is genuinely dangerous in a way that a chatbot was not. Securing agents is not an afterthought you bolt on before launch - it is a design constraint, and by 2026 it is the failure mode most likely to turn a productivity win into an incident.

The Lethal Trifecta

The sharpest way to reason about agent risk is the lethal trifecta: an agent becomes dangerous when it simultaneously has (1) access to private data, (2) exposure to untrusted content, and (3) a way to communicate externally. Any two are usually fine. All three at once means a malicious instruction hidden in the untrusted content can read your private data and exfiltrate it. The first line of defense is architectural: design workflows so an agent never holds all three at the same time.

Venn diagram of private data, untrusted content, and external communication overlapping at danger
Any two are usually fine. All three at once is the lethal trifecta.

Prompt Injection

Prompt injection is the core attack. Untrusted content - a web page, a GitHub issue, an email, a tool's output, a dependency's README - carries instructions, and the agent follows them as if they came from you. Indirect injection is the dangerous variant: the payload arrives through a tool the agent called, not from the user. Defenses:

* Treat all tool output and fetched content as untrusted data, never as instructions.
* Constrain what the agent can do, so a successful injection has a small blast radius.
* Require human approval for any sensitive or irreversible action, regardless of how confident the agent is.
* Do not give an agent that reads untrusted content both broad data access and an exfiltration channel.

Least-Privilege Tool Permissions

Scope every tool to the minimum it needs. This is where the intent spec's permission block does real security work:

tool_permissions:
  - mcp:filesystem (read+write, path-scoped to /src/migrated)
  - mcp:playwright (read-only screenshots)
  - mcp:git (commit only - no push)
  # No database, no network egress, no deploy

Read-only where possible. Path-scoped, not repo-wide. Commit but not push. An agent that cannot push, deploy, or reach the database cannot cause those classes of damage even if it is fully compromised.

Secrets and Data Handling

Never place secrets in the context window. Inject them through environment variables or a secrets manager at the tool boundary, redact them from logs and traces, and scope every credential to a single purpose with a short lifetime. Assume anything in the context could end up in a trace, a cache, or an exfiltration attempt - and keep secrets out of it.

Sandboxing and Isolation

Run agents in isolated, ephemeral environments - a container, a fresh worktree, a disposable cloud sandbox - with controlled network egress and credentials that expire. This is also why async cloud agents (Chapter 15) are often safer than a local agent with full machine access: the blast radius is a throwaway environment, not your laptop and its logged-in sessions.

Supply Chain: Vet Your MCP Servers

An MCP server or IDE extension is code you are granting tool access to your environment. Treat it like a dependency: review what it can do, pin versions, prefer official or audited servers, and watch for a server that requests far more access than its job requires. A malicious or compromised server is a supply-chain attack with an agent's permissions behind it.

Defense in Depth

No single control is enough. Layer them: least-privilege tools, untrusted-content discipline, approval gates for irreversible actions, full audit logs, anomaly alerting, and the stop rules from the intent spec that abort on runaway behavior. Security is a governance problem as much as a code problem - which is exactly why it sits next to the governance chapter that follows.