Securing the Agent Harness

Start with a number. An agent that opens 1,000 pull requests a week at a 1% vulnerability rate ships 10 new vulnerabilities every week — quietly, confidently, indefinitely. That’s the uncomfortable arithmetic of autonomy: a small per-action error rate, multiplied by machine-speed volume, becomes a steady stream of security holes. A human writing ten PRs a day can’t produce vulnerabilities fast enough to matter at that scale. An agent fleet can.

This reframes the whole problem. Security for AI agents isn’t a model-quality question you can solve with a better prompt. It’s a systems question, and the answer to “how do I keep this safe?” is the same answer we’ve used for every other dangerous-at-scale system: build the controls into the workflow so they can’t be skipped, and contain the blast radius for when — not if — something goes wrong. As the field’s bluntest formulation puts it: autonomy without security is an automated vulnerability.

You’re securing a harness, not a model

The single most useful mental shift in this entire topic: you cannot fully secure the model, so secure the harness instead.

The “harness” is everything around the model — the runtime scaffold that turns a text predictor into something that acts: its tools, its permissions, its credentials, its sandbox, its network access, the context you feed it, and the gates between it and the real world. The model is the part you can’t make trustworthy (more on why in a moment). The harness is the part you can engineer, and it’s where all real agent security lives.

Diagram illustrating the concept of securing the harness rather than the model, highlighting tools, sandbox, credentials, context & data, network, and human & policy gates.

Why can’t you just secure the model? Because of a result every agent builder should internalise. A 2026 joint study by OpenAI, Anthropic, and Google DeepMind — titled, ominously, “The Attacker Moves Second” — found that under adaptive attack conditions, every published prompt-injection defence was bypassed with success rates above 90%. You cannot prompt-harden your way to safety. The only robust posture is to assume the model will be compromised and build controls outside it that contain what a compromised model can do. This is the same shift the security industry made with malware a decade ago: stop trying to prevent every execution, assume breach, and focus on segmentation, least privilege, and blast-radius containment.

The blast radius principle

Here’s the principle that makes the harness concrete: an agent’s potential damage equals that of an employee holding the same credentials. If the agent can merge to main, it can ship a vulnerability. If it holds a production database token, it can drop a table. If it can make outbound network calls, it can exfiltrate data. The blast radius scales precisely with the privileges you hand it — and in 2026 that’s not hypothetical. In April 2026, a coding agent reportedly wiped a startup’s production database. The damage wasn’t exotic; it was simply an over-privileged agent doing a destructive thing with credentials it should never have held.

So the first question for any agent isn’t “is it smart enough?” — it’s “what’s the worst thing it can do with what we’ve given it?”

Prompt injection: the attack that makes it real

The mechanism that turns “the model might be wrong” into “the model is now working for an attacker” is prompt injection — OWASP’s #1 LLM vulnerability, present in an estimated 73% of production deployments. It comes in two flavours:

  • Direct injection: malicious instructions in the user’s own message.
  • Indirect injection: malicious instructions hidden in external content the agent processes — web pages, documents, code comments, dependency docstrings, issue and PR bodies, API responses. This is the dangerous one for coding agents, because a coding agent’s entire job is to ingest untrusted content: the repo, retrieved docs, web search results, package documentation. Every one of those is an attacker-controllable input channel.

In agentic systems this gets worse, not better. The OWASP Top 10 for Agentic Applications 2026 introduces ASI01: Agent Goal Hijack — a single injected instruction doesn’t just corrupt one output, it reprograms the agent’s plan, redirecting its multi-step behaviour, triggering privileged tool calls, and persisting across the workflow. A real example landed in May 2026: a CVSS-10 vulnerability in Gemini CLI where a malicious package injected prompts through code comments and docstrings, causing the agent to execute arbitrary commands it believed were legitimate — indirect injection delivered straight through the software supply chain.

The lethal trifecta

The cleanest model for when prompt injection becomes a breach is Simon Willison’s lethal trifecta. An agent is structurally exploitable when it has all three of:

  1. Access to private data (your repo, your secrets, your database).
  2. Exposure to untrusted content (anything an attacker can influence — issue bodies, web pages, dependencies).
  3. The ability to communicate externally (an outbound channel — an API call, a curl, a public PR comment).
Venn diagram illustrating the concept of 'The lethal trifecta' with three circles labelled 'Private data', 'Untrusted content', and 'External comms', highlighting 'EXFILTRATION' in the overlapping centre.

When all three overlap, a single poisoned input becomes an exfiltration pipeline: the agent reads the malicious instruction, pulls your secrets, and ships them out the door — a confused deputy moving faster than your monitoring can react. Remove any one leg and the attack collapses.

And here’s the catch that makes this urgent for developers: a CI/CD pipeline contains the entire trifecta by default. Workflow secrets sit in the runner’s environment, every public issue and PR body is attacker-controlled input, and tools like gh or curl are ready-made exfiltration channels. The Gemini CLI exploit chained exactly this — injection through a public issue, credentials lifted from .git/config on disk, then a pivot to a token with write access. If you’ve added an AI triager or reviewer to your CI, you may have shipped a trifecta without noticing.

Why bolt-on security fails

Put the pieces together and the conclusion is forced. At 10 vulnerabilities a week, a quarterly pentest is laughably out of phase — you’d accumulate ~130 vulnerabilities between assessments. When the model can be compromised >90% of the time by an adaptive attacker, an after-the-fact review of its output misses the architectural problem entirely. And prompt injection, the field’s consensus holds, needs architectural fixes, not bolt-on filters. The numbers say teams know this and aren’t acting on it: an independent 2026 assessment of 100 production agents found only 11% passed a baseline security assessment, and 57% of organisations lacked the visibility to even audit their agents for the trifecta.

Security that’s added after the agent works is security that’s always behind the agent. The only posture that keeps pace is one where the controls are part of the harness from the first commit.

Building the Secure Harness

We’ve previously reached a conclusion, a design principle: assume the model is compromised, and contain the blast radius with controls outside it. Now, we’ll turn that principle into concrete architecture. The unifying idea is defence in depth — stack independent layers so an attack has to defeat every one of them in series, not just slip past a single clever filter. No layer is sufficient alone (adaptive attacks bypass even good classifiers >85% of the time); stacking them is how production contains risk.

The organising constraint: the Rule of Two

Before the individual controls, the design rule that ties them together. Meta’s “Agents Rule of Two” (October 2025) is the most actionable security constraint in agent design: within a single session, an agent should have at most two of these three properties:

  • (A) processes untrusted input,
  • (B) has access to sensitive systems or private data,
  • (C) can change state or communicate externally.
Diagram illustrating 'The Rule of Two' concept for managing input and access levels, featuring sections A (Untrusted input), B (Sensitive access), and C (External/write), with safety combinations and a warning about the lethal trifecta.

This is the lethal trifecta restated as a build rule. Keep all three apart and the trifecta never assembles. In practice that often means splitting one risky agent into two safe ones — a read-only agent that ingests untrusted content but holds no secrets and can’t reach the network, handing structured results to a write-capable agent that never touches untrusted input. Separating read and write capability is one of the highest-leverage architectural moves you can make.

With that frame, here are the layers.

Layer 1 — Least privilege and least autonomy

The blast-radius principle from before has a direct corollary: give the agent the minimum tools, data, and credentials its task requires, and no more. Treat an agent exactly like a service account — narrowly scoped, audited, and reviewed. Start each new agent with the narrowest possible capability set and a small, well-defined task where the blast radius of failure is contained, then expand deliberately. “What’s the maximum access available” is the wrong default; “what’s the minimum this task needs” is the right one.

Layer 2 — Sandboxing and isolation

Assume the agent will, at some point, run hostile code — generated by a poisoned dependency, an injected instruction, or its own mistake. So run everything it does in an isolated, ephemeral environment with hard resource limits and the ability to roll back.

The 2026 consensus on strength: default to microVMs for untrusted code (Firecracker — the technology behind AWS Lambda, exposed by platforms like E2B — gives each sandbox its own kernel and network namespace, so a guest-kernel vulnerability can’t reach the host), and relax to gVisor or plain containers only when your threat model justifies it. Make sandboxes per-session and disposable with clean teardown so nothing persists between runs, and set hard memory, disk, and CPU limits to stop resource-exhaustion attacks. For coding agents this dovetails with the isolation you already want for parallelism: each agent gets its own throwaway workspace.

Layer 3 — Network egress control

This is the layer that directly kills the trifecta’s exfiltration leg. Agents should run on a zero-trust network: default-deny all outbound traffic, and allowlist only the specific endpoints the task requires. Add DNS restrictions to prevent command-and-control lookups, segment the agent’s network from production systems and sensitive data stores, and watch for anomalies (connections to low-reputation domains, unusually large outbound POSTs). If the agent can only reach three approved hosts, a stolen secret has nowhere to go.

Layer 4 — Scoped credentials and the broker pattern

Credentials are the crown jewels, and there are two distinct goals: keep them out of the model’s context (so the LLM provider never sees them) and out of the agent runtime entirely (so a compromised agent can’t read them). The pattern that achieves both is a credential broker: the agent never holds a real secret — it calls a tool, and a separate broker process, outside the agent’s reach, attaches the short-lived, narrowly-scoped credential and makes the actual API call.

# The agent asks the broker to act; it never sees the credential.
class CredentialBroker:
def call(self, tool: str, args: dict, session: Session) -> Result:
policy.authorize(tool, args, session) # per-tool PERMIT/DENY
cred = self.vault.mint(tool, ttl_seconds=300, # short-lived, scoped
scope=min_scope_for(tool))
try:
return execute(tool, args, cred) # broker makes the call
finally:
self.vault.revoke(cred) # gone after one use

Credentials are minted just-in-time, scoped to the single operation, and expire in minutes. Even a fully hijacked agent can’t exfiltrate a secret it never possessed — and a stolen short-lived token is worthless minutes later.

Layer 5 — The tool gateway

The agent’s reasoning must never be the thing that decides whether a dangerous action is allowed — because we’ve previously established that its reasoning can be hijacked. So put all tool access behind a single policy gate that enforces authorisation, consent, filtering, and audit centrally, so nothing bypasses policy. Per-tool PERMIT/DENY rules (the Cedar policy engine and the cedar-for-agents pattern are built for this) are evaluated against a declared rule — principal, action, resource, conditions — independent of the agent’s reasoning. Expose tools with fixed schemas so the agent can’t improvise novel calls, and wrap your most sensitive integrations as sealed tools running in a separate container. An MCP gateway is a natural home for this — it’s the chokepoint where you sanitise context and enforce allowlists between the agent and everything it can touch.

def gateway(call: ToolCall, session: Session) -> Result:
if not schema_valid(call): # fixed schema — no improvising
return deny("schema violation")
decision = cedar.evaluate(call, session) # policy, not model judgment
if decision != "PERMIT":
return deny(decision)
if call.is_high_impact: # irreversible / sensitive
require_human_approval(call) # tiered gate
audit_log.record(call, session) # decision, not just output
return broker.call(call.tool, call.args, session)

Notice the gateway is also where the later layers attach: human approval for high-impact actions, and an audit record of every decision.

Putting the harness together

Stacked, these layers form a harness where a compromise stays contained:

A flowchart illustrating a secure harness system for defence in depth, featuring components such as a microVM sandbox, human approval, a tool gateway, a credential broker, and an egress allowlist.

Trace an attack through it. A poisoned dependency injects an instruction. The hijacked agent tries to exfiltrate secrets — but it holds none, because the broker does (Layer 4). It tries to call a destructive tool — but the gateway’s policy denies it regardless of the agent’s “reasoning” (Layer 5). It tries to phone home — but egress is default-deny (Layer 3). It tries to corrupt the host — but it’s in a disposable microVM (Layer 2). Every layer the attack defeats, another stands behind it. That’s defence in depth, and it’s the difference between a bad session and a production incident.

Security in the Workflow

A secure harness is necessary but not sufficient. 10 vulnerabilities a week from a 1%-error agent fleet is a flow problem, and flow problems need controls in the flow. A harness you configure once protects the runtime; it doesn’t catch the vulnerability in PR #847 on Tuesday afternoon. For that, security has to live in the workflow itself, running on every action the agent takes, automatically. This is the “built-in, not bolted-on” thesis made operational.

Gate every agent PR like untrusted code

The agent’s output is untrusted code — we’ve already established the model can be hijacked, so its commits deserve the same suspicion as a pull request from an anonymous outside contributor:

Diagram illustrating security gates in the agent workflow, including agent PR, deterministic security gates with various checks, risk routing based on security impact, and audit logging for decisions.

The deterministic gates run on every agent PR and block on failure:

  • SAST (static application security testing) for vulnerability patterns — the direct defence against the 10-vulns-a-week problem.
  • Secret scanning so a credential never lands in a commit.
  • Dependency and CVE scanning, plus a hallucinated-package check — does every new import actually exist and is it the real package, not a slopsquatted look-alike registered to catch exactly the names models invent (the supply-chain attack).
  • IaC scanning for misconfigured infrastructure.

Because these are deterministic, they make trustworthy hard gates — no human time spent on a leaked key or a known-vulnerable dependency. This is shift-left security applied to a contributor that never sleeps.

Tier human oversight so it survives contact with volume

Human approval is the control that stops a hijacked agent’s high-impact action — but naive human review doesn’t scale to agent volume, and worse, it breeds approval fatigue: ask a human to rubber-stamp 200 trivial changes and they’ll rubber-stamp the one dangerous one too. The fix is risk-based tiering, the same Green/Yellow/Red routing, applied to security impact:

  • Low-risk (docs, tests, isolated non-sensitive code) → auto-merge with notification.
  • High-impact / irreversible (auth, payments, data migrations, production deploys, anything touching the paths from your risk map) → mandatory human approval, every time.

Tiering concentrates scarce human attention exactly where the blast radius is largest, and keeps it sharp by not wasting it everywhere else. Even a lightweight approval step — a Slack confirm, a required reviewer on protected paths — is a meaningful gate. The goal is that no irreversible action happens without a human, without drowning humans in reversible ones.

Audit decisions, not just outputs

You detect a hijacked agent not by reading its final answer but by watching its behavior. So log the agent’s decisions — every tool call, every delegation, every retrieval, every hand-off — not just its outputs. Baseline what a normal tool-call sequence looks like, and alert on deviations: a triage agent that suddenly reads .git/config and opens a network connection is mid-exploit, and only a decision-level trace shows it.

This audit trail does double duty. It’s your incident-response timeline, and it’s your compliance evidence — the record of which agent accessed which data, under which policy, authorized by which human, at what time. (Treat memory writes as security events too: a poisoned memory entry is a backdoor that reloads every session.) The 900+ agent gateways found exposed on the public internet in early 2026 — plaintext credentials, no authentication — failed precisely because there was no governance layer recording and enforcing any of this. Audit is the floor of that governance layer.

The trifecta audit: a gate before production

The lethal trifecta becomes a concrete pre-deployment gate. Before any new agent ships, audit it explicitly:

TRIFECTA AUDIT (run before every agent goes to production)
[ ] Does it access private data? (repo, secrets, DB, customer data)
[ ] Does it process untrusted content? (issues, PRs, web, dependencies)
[ ] Can it communicate externally? (network, public comments, APIs)

All three present? → DO NOT SHIP without compensating controls:
• break it with the Rule of Two (split read-only from write-capable), OR
• content inspection + strict tool scoping + mandatory human gates on egress
Assign an explicit owner to each leg. "Logging" is not "prevention."

Make this audit a required, version-controlled step — not tribal knowledge. Only 11% of production agents passed a baseline security assessment in 2026, and 57% of organizations couldn’t even answer these three questions for their agents. Being able to answer them, on every agent, before it ships, already puts you ahead of the overwhelming majority.

Govern from day one

Tie it together with a governance layer that exists from the first commit, not after the first incident. Version-control your agent security policy and risk map alongside the code; align your controls to a recognised framework — the OWASP Top 10 for Agentic Applications 2026, MITRE ATLAS for adversary techniques, and the NIST AI Risk Management Framework for lifecycle governance — so you’re not inventing controls from scratch; and gate the installation of new tools and plugins behind review (CODEOWNERS plus CI schema validation), since a new tool is a new capability and a new piece of attack surface. Governance isn’t a launch-day checklist; it’s the substrate the agent runs on.

The whole picture

Assemble the three parts and the architecture is coherent. You secure the harness, not the model, because the model can be compromised — and a coding agent’s blast radius at machine speed makes that compromise expensive. The harness is defence in depth organised by the Rule of Two — least privilege, ephemeral sandboxing, default-deny egress, a credential broker, and a policy gateway that doesn’t trust the model’s reasoning. Security lives in the workflow — deterministic gates on every PR, risk-tiered human approval, decision-level audit, and a trifecta gate before production — all sitting on a governance layer present from day one.

The thread through every layer is the same: a single compromised agent should stay a single bad session, never a production-wide incident. You get there not by trusting the agent more, but by engineering the workflow so that trust is never required — the controls run whether the agent is behaving or not. That’s what “built into the workflow, not bolted on” actually means, and at 10 vulnerabilities a week, it’s the only thing that keeps pace.

Leave a Reply