Skip to main content

tripwire

A red-teaming harness for tool-using AI agents. Point it at an agent — yours, or one of the built-in reference implementations — hit it with prompt injection attacks, and get a deterministic, no-human-judgment answer: did it leak a planted secret, or take a forbidden action? Designed to run in CI, against your own agent, not just a fixed benchmark.

The headline skill here is evaluation rigor, not security research for its own sake: a pure-Python scoring path (no LLM judge, no ambiguity), fixed seeds for reproducibility, and Wilson confidence intervals instead of bare percentages. Prompt injection is the domain because it's one of the few places in LLM evaluation where "did it leak or not" is a clean, unambiguous string match — everything downstream of that (attack suites, defenses, adaptive attackers, multi-agent pipelines) is built on top of that one deterministic check.

Install

pip install tripwire-eval          # or: uv pip install tripwire-eval

The install name is tripwire-eval; the import package and the CLI command are just tripwire (like pip install scikit-learnimport sklearn).

Core install is small (an OpenAI-compatible client, LangGraph adapters). Opt into the heavier pieces:

pip install "tripwire-eval[agentdojo]"   # the real AgentDojo benchmark bridge
pip install "tripwire-eval[viz]"         # Pareto / analysis plots
pip install "tripwire-eval[all]"         # everything

Or run it without installing, via uv:

uvx --from tripwire-eval tripwire --config path/to/threat_model.yaml --smoke

60-second quickstart

cp .env.example .env   # set OPENAI_API_KEY (or GROQ_API_KEY -- free tier: console.groq.com)

tripwire --config tripwire/config/threat_model.example.yaml --smoke

(From a source checkout: pip install -e ".[dev]" then python -m tripwire …; run the tests with pytest.)

That runs a small sweep — the built-in raw_loop and langgraph reference adapters against a real model, under one attack — and prints an ASR (attack success rate) table. Here's a real run from this session, one seed per row:

adapter          attack                   defense           n leak    ASR  95% CI          utility
--------------------------------------------------------------------------------------------------
langgraph        direct                   no_defense        1    1  100%  [21%, 100%]          0%
raw_loop         direct                   no_defense        1    0    0%  [0%, 79%]        100%

Same model, same attack, same seed — one adapter got fully compromised (100% leaked, and failed its actual job too), the other held and did its job correctly. That gap, reproducible and measurable instead of anecdotal, is the whole point of the project. (Single-seed runs are for smoke-testing the pipeline, not for drawing conclusions — the Wilson CI column exists precisely to keep you honest about that; run more seeds before trusting a number.)

Point it at your own agent instead of the built-in ones:

python -m tripwire --config tripwire/config/threat_model.example.yaml \
    --agent examples/byo_agent_example.py --smoke

--agent loads any Python file exposing run(spec) -> NormalizedTrace (or an adapter = ... object) and runs the identical attack suite, environment, judge, and reporting against it. Two runnable references ship in examples/: examples/my_agent.py (a LangGraph agent, the adapter = ... shape) and examples/byo_agent_example.py (a minimal raw tool loop, the bare-run shape). The full interface — what the spec hands you, what the trace must record, and the two optional hooks that let defenses gate your agent — is in docs/instrument-your-agent.md (a <15-minute wire-in).

Gate it in CI

The pitch made enforceable: a change that reopens a leak fails the build. .github/workflows/asr-gate.yml runs the offline test suite on every push/PR, then (given a GROQ_API_KEY secret) runs a smoke attack sweep against a defended config and fails if attack success rate exceeds a threshold:

python -m tripwire --config tripwire/config/ci.example.yaml --smoke --output results/results.json
python scripts/ci/check_asr_threshold.py --results results/results.json --threshold 0.5

Point --config at your own deployed agent + defense, and narrow the gate to just that configuration with --defense/--adapter so baseline rows in the same sweep don't trip it. An empty result set fails rather than passes — a silent green when the sweep never ran would defeat the purpose.

What's actually in here

  • Three reference adapters, all interchangeable via one line in a config's adapters: list: raw_loop (minimal ReAct loop, no framework), langgraph (two-node LangGraph graph), and multi_agent — a two-agent LangGraph relay pipeline (summarizer → actor) that demonstrates second-order injection: the acting agent can leak a secret it was never told existed, because the injection rode in on the summarizing agent's own output.
  • A graded attack suite: five templated jailbreaks ported from AgentDojo's baseline attacks, plus a PAIR-style adaptive attacker that reads back a 0/1/2 "getting warmer" signal per attempt and refines its payload across a budgeted campaign instead of firing once and quitting.
  • A defense ladder, cheap-weak → expensive-strong, so the security/utility frontier has shape instead of a single point: prompt_hardening (L1, a distrust-tool-content system-prompt instruction), spotlighting (L2, fences untrusted tool output in delimiters — Microsoft, Hines et al. 2024), tool_filter (removes the leak-capable tool from the menu), and outbound_guard (L3, a second model inspects each send_email before it fires and blocks a detected leak — the dual-LLM pattern). They hang off three enforcement points: filter_tool_calls decides what's offered up front, check_tool_call is a mid-loop backstop immediately before a tool executes (catching a model that free-forms a call outside its menu), and wrap_tool_result transforms a tool's output before the model sees it.
  • A utility signal alongside ASR: every episode also carries a real, deterministic benign task (forward an invoice total to the right address). A defense that shows 0% ASR by nuking every tool call is a very different, much worse result than 0% ASR with 100% utility — the reporter shows both, so that distinction is never hidden.
  • --agent: bring your own agent, get the same eval for free.

How it works

agent + task  ──>  adapter  ──>  normalized trace  ──>  judge  ──>  pass / fail
                (per framework)   (framework-agnostic)  (pure Python,
                                                          no LLM)

Every orchestrator plugs in behind one contract (tripwire/adapters/base.py): it takes an EpisodeSpec and returns a NormalizedTrace. The judge, the attacks, and the defenses all read only that trace — they never know which framework actually ran, which is what makes the adapter-vs-adapter comparison fair instead of hand-wavy.

Layout

Path What
tripwire/harness/ runner (episode + campaign orchestration), canary injection, deterministic judge, reporter, Wilson CI
tripwire/adapters/ the Adapter contract + raw_loop, langgraph, multi_agent, and the --agent/registry loader
tripwire/attacks/ templated + adaptive injection strategies
tripwire/defenses/ the Defense contract + the ladder: prompt_hardening, spotlighting, tool_filter, outbound_guard
tripwire/config/ YAML config loader + example threat model
examples/ runnable BYO-agent references: my_agent.py (LangGraph) and byo_agent_example.py (raw loop)
spike/ week-0 experiments that validated the approach before the full build
docs/ provider ToS notes

Status

Past the skeleton stage: every module above is implemented and tested (160+ tests, offline and fast — no network calls in the test suite itself). Not yet built: a CrewAI adapter (the third leg of the original orchestrator-comparison bet) and a real AgentDojo suite integration (the current benign task is a small, deterministic scripted scenario by design — see the code comments in tripwire/harness/runner.py for why that tradeoff was made over wiring in AgentDojo's own environment model).

Credits

Builds on AgentDojo (attack templates, benchmark methodology) and known attack methods (e.g. PAIR). tripwire's contribution is the harness, the cross-framework adapter contract, the measurement methodology, and the defense/utility/multi-agent extensions on top.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

tripwire_eval-0.1.0.tar.gz (61.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

tripwire_eval-0.1.0-py3-none-any.whl (73.6 kB view details)

Uploaded Python 3

File details

Details for the file tripwire_eval-0.1.0.tar.gz.

File metadata

  • Download URL: tripwire_eval-0.1.0.tar.gz
  • Upload date:
  • Size: 61.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tripwire_eval-0.1.0.tar.gz
Algorithm Hash digest
SHA256 2efd095a4d94fcfe8326b707b525dc15c1a83b5607712cb10d9305bde7ee04bb
MD5 ddfc0a318758603b12f67eb4227260b3
BLAKE2b-256 eae14d4ebe721cdc0c00ac622de200b54873a9f696348508aa091eca94233a24

See more details on using hashes here.

File details

Details for the file tripwire_eval-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: tripwire_eval-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 73.6 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.11.2 {"installer":{"name":"uv","version":"0.11.2","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for tripwire_eval-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 847b3be81627d7b322820f65050de6fd2f356701cf3f4c5c49e8bcc809970b89
MD5 82748f1e6c6919aff1b27114bfea450f
BLAKE2b-256 9b535db48295609b298ab78d992aa2d873b41ba5d7ffb67b913ea01bfc82fcb6

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page