A standardized test corpus for evaluating AI agent egress security tools, covering secret exfiltration, prompt injection, SSRF, hostname exfiltration, MCP tool poisoning, chain detection, MCP drift, A2A protocol scanning, WebSocket DLP, encoding evasion, shell obfuscation, and cryptocurrency/financial data protection. Current loader-backed counts are in cases/STATS.md.

This tests the security tool, not the agent. Most benchmarks in this space (AgentDojo, InjecAgent, CyberSecEval, AgentHarm) test whether the LLM behaves correctly. This one tests whether the firewall, proxy, or scanner sitting between the agent and the network catches the attack.

┌─────────────────────┐     ┌──────────────────────┐     ┌──────────┐
│  AI Agent           │     │  Security Tool        │     │          │
│  (has secrets,      │────▶│  (proxy / firewall /  │────▶│ Internet │
│   runs tools)       │     │   MCP wrapper)        │     │          │
└─────────────────────┘     └──────────────────────┘     └──────────┘
                                     ▲
                            agent-egress-bench
                            tests THIS layer

Why this exists

AI agents that can browse the web, call APIs, and use MCP tools need network-layer security. An agent with access to secrets and an internet connection is an exfiltration risk, whether through prompt injection, tool poisoning, or simple misalignment.

Tools exist to sit between agents and the network (proxies, firewalls, MCP wrappers). But there was no standard way to test them. This corpus fills that gap: a shared set of attack cases that any security tool can run against.

What's in the corpus

Category Directory What it tests
URL DLP cases/url/ Secrets leaked via query strings, encoded paths, high-entropy subdomains, SSRF, domain blocklist
Request body DLP cases/request-body/ Secrets in POST bodies (JSON, YAML, CSV, multipart, base64, hex, env dumps)
Header DLP cases/headers/ API keys and tokens in HTTP headers (Bearer, JWT, AWS, multi-header)
Hostname exfiltration cases/hostname-exfiltration/ Encoded secrets in DNS hostname labels before resolution
Response injection (fetch) cases/response-fetch/ Prompt injection in fetched web content
Response injection (MITM) cases/response-mitm/ Injection via tampered TLS-intercepted responses
MCP input scanning cases/mcp-input/ DLP and injection in MCP tool arguments (base64, hex, scattered, SSH keys)
MCP tool poisoning cases/mcp-tool/ Poisoned tool descriptions, schema injection, rug-pull changes
MCP chain detection cases/mcp-chain/ Multi-step exfiltration sequences (read-then-send, env-to-network)
MCP drift cases/mcp-drift/ Multi-file before/after tool snapshots for rug-pull and benign drift detection
A2A message scanning cases/a2a-message/ Secrets and injection in A2A message parts
A2A Agent Card poisoning cases/a2a-agent-card/ Injection in Agent Card skill descriptions, card drift
WebSocket DLP cases/websocket-dlp/ Secrets in WebSocket frames, fragment reassembly evasion
SSRF bypass cases/ssrf-bypass/ Private IP detection, cloud metadata, encoded IPs
Encoding evasion cases/encoding-evasion/ Multi-layer encoding chains, Unicode tricks, zero-width insertion
Shell obfuscation cases/shell-obfuscation/ Backtick substitution, brace expansion, IFS manipulation
Crypto/financial DLP cases/crypto-financial/ Wallet addresses, seed phrases, credit cards, IBANs
False positive suite cases/false-positive/ Benign traffic that must not be blocked

Counts are logical cases, not fixture files. Most cases are single JSON files; MCP drift cases are multi-file before/after snapshots, and each drift directory counts as one case.

The loader-backed statistics include the block, allow, and warn-class breakdown used to assess containment and false-positive behavior.

Most cases are self-contained JSON files with the attack payload, expected verdict (block or allow), severity, capability tags, and a machine-readable reason for the expected outcome. MCP drift cases under cases/mcp-drift/ are multi-file before/after fixtures with case.yaml metadata.

Quick start

Prerequisites: Go 1.25+ for the validator and portable runner. The runner uses its own Go module dependencies for fixtures and multi-file case parsing.

Build the validator:

cd validate && go build -o aeb-validate .

Validate the corpus:

./aeb-validate ../cases

Validate a runner's results or tool profile:

./aeb-validate results path/to/results.jsonl ../cases
./aeb-validate profile path/to/tool-profile.json

Run against a tool. Each tool ships its own runner. The Go program in runner/ is the reference implementation; it brings up HTTP, TLS, WebSocket, DNS, and MCP HTTP fixtures, executes declared transports through the selected adapter, and emits the Gauntlet summary and an optional receipt-scoring profile.

For the pinned Pipelock release, use the portable entry point from a clean Linux clone on origin/main or a tag:

./scripts/run-pipelock-gauntlet.sh --doctor
./scripts/run-pipelock-gauntlet.sh

Run --doctor before the benchmark. It checks the platform, the required commands, an MCP stdio bridge, the working directory, and the reviewed release pin, without starting a run or writing an evidence directory. --doctor-json returns the same checks as JSON. Both commands exit nonzero when a check fails. A ready result is not a prediction that the run will succeed: it does not check the installed Go version, the origin remote, whether the checkout is clean, or network reachability, and the run itself still fails on each of those.

The command downloads the reviewed Pipelock release, verifies its pinned asset digest, published checksum, and reported version, then confines target writes with Landlock and denies Unix-domain sockets with seccomp. It starts the required local fixtures and managed Pipelock processes, runs the single-file and multi-file cases, and leaves one timestamped directory under continuous-gauntlet-runs/. That directory contains the exact internal command, stdout results, stderr, summary, case index, corpus stats, release identity, file digests, and a machine-readable execution decision.

It requires Linux, Go 1.25 or newer, Python 3, Git, curl, jq, tar, GNU timeout, SHA-256 utilities, and one of socat, ncat, or nc. Use --output-dir to place the self-contained run directory somewhere else. The Pipelock reference-runner guide documents the evidence files, explicit development mode, the underlying long-form command, and a neutral scheduling example.

The raw directory intentionally has no made-up public URL. GitHub Actions or another retaining platform adds its real artifact ID and HTTPS location later, without modifying the evidence bytes. Creating a schedule and publishing a result are separate operator decisions.

The repository's scheduled Pipelock lane is read-only and produces review candidates, not automatic public claims. Approved candidates are retained as digest-addressed, hash-linked evidence directories, and a reviewed pull request advances the latest-verified pointer. The included reference renderer verifies and displays score, scope, N/A reasons, false-positive rate, and the canonical run URL together. It renders this repository's first-party Pipelock history and ranks nothing. See Continuous Gauntlet Results for the repository review and publication contract.

For other tools, the runner writes per-case JSONL results to stdout (one object per case, see docs/RUNNER.md) and a Gauntlet summary JSON to the path passed via --output (containment, false-positive rate, non-scoring output-field diagnostics, and per-category results, see docs/gauntlet.md). --emit-receipt-profile additionally writes a byte-reproducible receipt-scoring profile (see docs/RECEIPT-SCORING.md).

A minimal legacy shell example for fetch-only cases lives at examples/pipelock/harness.sh. It covers a single transport (/fetch?url=... GET) and is kept for illustration only — it is not the Gauntlet and will misreport every body, header, WebSocket, MCP, and response-content case. Use the Go runner for any real benchmark.

Gauntlet scoring

The Gauntlet reports containment and false-positive rate separately, plus non-scoring field-presence diagnostics. docs/gauntlet.md owns metric definitions, result states, denominators, and measurement status. Results Use and Attribution owns the claims and disclosures that travel with a public result.

The maintainer's disclosed first-party Pipelock history is published at pipelab.org/gauntlet. Every other publisher owns its results.

What this does NOT test

This corpus has a specific scope. It does not cover:

  • Model alignment. Whether the LLM refuses harmful instructions. Use AgentDojo, AgentHarm, or ASB for that.
  • Application-layer guardrails. Whether a guardrail API flags a prompt as malicious. Use AgentShield-benchmark for that.
  • Code generation safety. Whether the model writes insecure code. Use CyberSecEval for that.
  • Authentication or authorization. Whether the agent has valid credentials for the APIs it calls.
  • Inbound traffic. What enters the agent's environment. This corpus focuses on outbound (egress) traffic.

If you need to test the model, use a model benchmark. If you need to test the network security layer, use this.

How it works

Cases encode wire inputs and expected verdicts. A runner sends each input through the tool and records the observed result. docs/SPEC.md owns case shape, docs/RUNNER.md owns runner I/O, and docs/gauntlet.md owns result states and scoring.

Writing a runner for your tool

Start from the runner template or the complete Pipelock reference runner. RUNNER.md defines the contract, and ADOPTION.md covers the contribution workflow.

OWASP Agentic Top 10 mapping

Every case category maps to the OWASP Top 10 for Agentic Applications (2026). make check-readme-categories fails when this table and the corpus disagree, so the mapping stays complete as categories are added:

Case category OWASP item What the cases cover
url ASI02 Tool Misuse Secret exfiltration via URL query strings and paths
request_body ASI02 Tool Misuse Secret exfiltration via POST bodies
headers ASI02 Tool Misuse Secret exfiltration via HTTP headers
hostname_exfiltration ASI02 Tool Misuse Encoded data in DNS hostname labels
response_fetch ASI01 Goal Hijack + ASI06 Memory Poisoning Prompt injection in fetched content
response_mitm ASI01 Goal Hijack + ASI04 Supply Chain Injection via tampered responses
mcp_input ASI02 Tool Misuse DLP and injection in tool arguments
mcp_tool ASI04 Supply Chain Poisoned tool descriptions, rug-pull changes
mcp_chain ASI02 Tool Misuse + ASI08 Cascading Failures Multi-step exfiltration sequences
mcp_drift ASI04 Supply Chain Tool inventory changes after approval
a2a_message ASI07 Inter-Agent Communication Secrets and injection in A2A messages
a2a_agent_card ASI04 Supply Chain + ASI07 Inter-Agent Poisoned Agent Card skill descriptions
websocket_dlp ASI02 Tool Misuse Secrets in WebSocket frames, fragment evasion
ssrf_bypass ASI02 Tool Misuse SSRF via IP encoding, cloud metadata
encoding_evasion ASI02 Tool Misuse Multi-layer encoding to bypass scanning
shell_obfuscation ASI02 Tool Misuse + ASI05 Code Execution Obfuscated shell commands in tool args
crypto_financial ASI02 Tool Misuse Wallet addresses, seed phrases, credit cards
false_positive N/A Benign traffic that must not be blocked

Full mapping with MITRE ATT&CK techniques: docs/OWASP-MAPPING.md

Scope

This corpus evaluates the security tool that sits between an AI agent and the network (a proxy, firewall, or MCP wrapper): given an attack, did the tool catch it. It does not evaluate the agent or model's own behavior. Cases test observable outcomes at the wire level, such as whether an exfiltrated secret in a query string was blocked or whether prompt injection in a tool response was detected.

Each publisher publishes and owns its own results. This repository publishes no ranking, leaderboard, or cross-tool comparison table.

Docs

Core contracts:

  • SPEC.md: case schema, field definitions, enums, payload formats
  • RUNNER.md: runner input, output, adapter, and verdict-mapping contract
  • gauntlet.md: result states, scoring, scope, and publication methodology
  • GOVERNANCE.md: neutrality, case immutability, versioning, and compatibility
  • RELEASES.md: pinned runner, corpus, and schema release verification
  • OCI-RUNNER.md: pinned runner image, reusable Action, devcontainer, and offline operation
  • contracts/artifacts.json: machine-readable artifact compatibility inventory

Evidence and publication:

Integration and reference:

  • GATEWAY-ADAPTER.md: the narrow generic MCP gateway plugin contract and its current limits
  • ADOPTION.md: guide for vendors adopting the benchmark
  • GLOSSARY.md: definitions of key terms (agent firewall, egress security, etc.)
  • OWASP-MAPPING.md: case categories mapped to OWASP Agentic Top 10
  • schemas/: JSON Schema files for cases, tool profiles, and results

Contributing

See CONTRIBUTING.md. Cases, runners, and documentation improvements are all welcome.

Case IDs are immutable. Once merged, a case ID never changes. Semantic changes to existing cases require a new case with a new ID.

Governance

This corpus was created by the Pipelock author. Contributions from any vendor or individual are welcome. This repository publishes no ranking, leaderboard, or cross-tool comparison table, and the maintainer awards no verification mark to anyone else's result. Each publisher publishes and owns its own results, under the labels defined in docs/RESULTS-USE.md. Publishing a result that reflects badly on Pipelock needs no notice or approval.

Conflict of interest disclosure: The author builds an agent egress security tool. This corpus was designed to be tool-neutral: cases test observable behavior (did the request get blocked?), not implementation details. The Pipelock runner is a reference implementation, not a privileged position.

Full governance policy: docs/GOVERNANCE.md.

Learn more

License

Apache 2.0. See LICENSE.


If this corpus is useful to you, give it a star. It helps others find it.