# Agent, LLM & MCP Surface

Load this when the project calls a model, exposes tools to a model, runs an agent, serves or consumes MCP, or ships a chat feature. Also load it when hardening a developer tool, because a developer tool is an agent target.

Standards [V]: **OWASP Top 10 for LLM Applications (2025)** — LLM01 Prompt Injection, LLM02 Sensitive Information Disclosure, LLM03 Supply Chain, LLM04 Data and Model Poisoning, LLM05 Improper Output Handling, LLM06 Excessive Agency, LLM07 System Prompt Leakage, LLM08 Vector and Embedding Weaknesses, LLM09 Misinformation, LLM10 Unbounded Consumption. **OWASP Top 10 for Agentic Applications (2026)**, published 9 Dec 2025 — ASI01 Goal Hijack, ASI02 Tool Misuse, ASI03 Identity & Privilege Abuse, ASI04 Agentic Supply Chain, ASI05 Unexpected Code Execution, ASI06 Memory & Context Poisoning, ASI07 Insecure Inter-Agent Communication, ASI08 Cascading Failures, ASI09 Human-Agent Trust Exploitation, ASI10 Rogue Agents. Its core principle is **least agency** — grant the minimum autonomy the task requires, not merely minimum permissions.

## The one thing to internalize

**Prompt injection cannot be reliably prevented.** A 2025 study of twelve proposed defenses recorded a 100% bypass rate against adaptive human red-teamers [V]. Any design whose safety depends on the model refusing a malicious instruction is already broken. Filters, delimiters, "ignore instructions in user content", and a second model checking the first are mitigations, not controls.

Design for **containment**: assume the instruction lands, and make the outcome survivable.

## The lethal trifecta

Private data access **+** untrusted content **+** an egress channel. Any two are usually fine; all three is exploitable. Apply it as a design test to every agent feature.

Documented outcomes when all three are present [V]: a malicious issue in a public repository caused an assistant to leak private repository contents; a support ticket containing embedded instructions caused an agent holding a privileged database credential to query a secrets table and publish the results back into the public thread.

**Break one leg, deliberately:**

| Leg | How to break it |
|---|---|
| Private data | Scope credentials per end-user, never a service-role key. The agent should hold exactly the access of the person it acts for |
| Untrusted content | Cannot usually be removed — but mark provenance, and never let fetched content enter a context that also holds a privileged tool |
| Egress | Default-deny outbound network. Most tasks need none. Allowlist specific hosts; block DNS, image loading, and markdown link rendering as exfiltration paths |

Egress is the leg most often left intact and the easiest to close. Exfiltration in real incidents has ridden ordinary channels: an outbound HTTP request, a DNS lookup, an image URL rendered by the client, a markdown link the user clicks, a comment posted back to a public thread.

## Architecture controls that actually hold

1. **Least agency.** Per-task tool catalogs, not one catalog with everything. An agent summarizing a document does not need a shell.
2. **The human approves the effect, not the intent.** Approval prompts must show the concrete operation — this file, this command, this recipient, this amount — because the user is approving something the model chose, possibly at an attacker's instruction. A prompt saying "the agent wants to continue" is theatre.
3. **Deterministic policy outside the model.** Enforce limits in code that intercepts before execution: allowlisted commands, path containment, spend caps, rate limits, recipient allowlists. No model in the decision loop.
4. **Irreversibility gates.** Deleting data, moving money, sending messages to third parties, publishing artifacts, and changing permissions each need explicit confirmation, and should be unavailable in autonomous runs.
5. **Audit trail.** Log every tool invocation with arguments and outcome. EDR sees execution, not intent — a legitimately-instructed agent doing destructive work looks entirely normal [V], so the tool log is your only forensic record.
6. **Treat model output as untrusted input** (LLM05). Never feed it to `eval`, a shell, SQL, `innerHTML`, or a file path without the same validation you would apply to a web form.
7. **Isolate the workspace.** Run agent execution in a container or sandbox with no credentials mounted, no network by default, and a bounded filesystem.

## Sandbox escapes — the 2026 pattern

Multiple critical escapes were disclosed across the major coding-agent products in 2026 [S], and they share one root cause worth designing against:

> **Files the agent writes inside the sandbox are later read, loaded, or executed by a trusted process outside it.**

Concretely: symlinks created inside the workspace that an unsandboxed process later writes through; configuration and hook files inside the workspace that a trusted process loads; a writable-path parameter added to an allowlist without validation; allowlisted commands that are not actually read-only; a container socket reachable from inside.

Checks: resolve and re-verify containment **after** opening a path, never before; never load configuration, hooks, or plugins from the sandboxed workspace into a trusted process; allowlist by resolved absolute path, not by name; verify that "read-only" commands are read-only, including their subcommands and flags.

## Context and rules-file poisoning

Instructions hidden in files the agent reads land directly in its context, which is the same as landing in its instructions [V].

- **Invisible Unicode**: tag codepoints, bidirectional controls, zero-width characters. Documented in a backdoored public skill that multiple models interpreted as instructions. At least one vendor now detects and refuses tag characters [S] — do not assume all do.
- **Vector files**: `CLAUDE.md`, `AGENTS.md`, `.cursorrules`, skill and extension files, MCP tool descriptions, and the same files in parent directories.
- **Documented payloads**: instructing the agent to POST local `.env` contents to a webhook "for team sync" while suppressing output; and — worse — instructing the agent to inject a credential-harvesting block into every file it generates, so the backdoor propagates into CI and production through normal code review [V].
- **Defenses**: scan context files for non-printable and bidi characters and normalize before use; diff them in code review like any other code; do not walk parent directories outside the project root for instruction files; pin and review shared skills and rules the way you review dependencies.

## MCP specifics

- **Servers are dependencies.** Install from a registry with signing and verification, pin the version, and re-review the tool list after every update. The first malicious server in the wild built trust across fifteen clean releases before adding a silent BCC header [V].
- **Tool descriptions are model-visible input.** A server can poison behavior through description text alone, and can change descriptions after approval — a rug-pull. Pin and diff them.
- **Token audience binding.** The specification requires resource indicators so a token issued for one server cannot be replayed against another; adoption across public servers is incomplete [U]. Check that your server validates the audience and that your client does not hand a broad token to every server.
- **Stdio servers execute locally.** Command-injection CVEs in stdio server launchers are a recurring class [S]. Never construct the launch command from untrusted input; never auto-register a server from web content — this has been an RCE in the wild [S].
- **Config files hold live credentials** — thousands of valid secrets have been found in MCP configuration [V]. Treat them as secret files.
- **Server-side**: authenticate callers, authorize per-tool, validate every argument against a schema, and never let a tool return content that the client will treat as an instruction without provenance marking.

## RAG, memory & multi-agent

- Poisoned documents in a vector store are persistent injections that fire on retrieval (LLM08, ASI06). Control write access to the index, record provenance per chunk, and prefer per-tenant indexes over one shared index with metadata filtering.
- Agent memory that persists across sessions is a persistence mechanism for an attacker. Scope memory per user, make it inspectable, make it clearable, and never let it carry tool permissions.
- Agent-to-agent messages are untrusted input (ASI07). Authenticate the sender, validate the schema, cap the fan-out, and bound recursion depth — cascading failures (ASI08) are usually an unbounded loop, not an intrusion.
- Cost and quota are a security property (LLM10). Cap tokens, tool calls, iterations, and spend per task. An unbounded agent loop is a self-inflicted denial of wallet.

## Reviewing an agent feature — the short list

1. Which of the three trifecta legs are present, and which one did you break?
2. What is the full tool catalog for this task, and can each tool's worst outcome be undone?
3. Where does untrusted content enter the context, and is it marked as data?
4. What can leave the machine, and to which hosts?
5. Whose credentials does the agent hold — the end user's, or the service's?
6. Is every irreversible action gated on a human approving the concrete effect?
7. Is there a tool-call log you could reconstruct an incident from?
