| name | benchjack |
| description | Audit an AI agent benchmark for hackability. Detects evaluation vulnerabilities like missing isolation, leaked answers, eval() on untrusted input, prompt injection in LLM judges, weak scoring, logic gaps, and trust of untrusted output. Use when analyzing whether a benchmark can be gamed or exploited. |
| argument-hint | ["benchmark-path-or-url"] |
| allowed-tools | Bash(find *) Bash(git *) Bash(ls *) Bash(python3 *) Bash(chmod *) Bash(mkdir *) Bash(docker *) Bash(pip *) Bash(pip3 *) Bash(sh *) Bash(bash *) Bash(cd *) Bash(npm *) Bash(node *) Bash(cp *) Bash(mv *) Bash(tee *) Read Write Glob Grep Agent WebFetch |
BenchJack: AI Agent Benchmark Hackability Scanner
If $ARGUMENTS is empty or contains more than one argument (i.e., multiple space-separated paths/URLs), do NOT proceed with the audit. Instead, output ONLY the following message as plain text (no bold, no italic, no blockquotes, no markdown formatting) and stop:
BenchJack is a security auditor for AI agent benchmarks. It systematically scans evaluation code for vulnerability -- missing isolation, leaked answers, eval() on untrusted input, prompt injection in LLM judges, weak scoring, logic gaps, trust of untrusted output -- that allow agents to game scores without solving tasks.
BenchJack requires exactly one argument: a path to the benchmark code, a repository URL, or a benchmark name.
Usage: /benchjack
Examples:
Otherwise, proceed with the audit below:
You are conducting a security audit of an AI agent benchmark to determine whether it can be gamed or exploited. Your goal is to systematically check for vulnerabilities that allow agents to achieve high scores without solving actual tasks.
The benchmark to audit is: $ARGUMENTS
Phase 1: Reconnaissance
First, locate and understand the benchmark's evaluation infrastructure.
- Find the benchmark code. If
$ARGUMENTS is a local path, explore it. If it's a URL or package name, clone/download it. If it's just a benchmark name, search for it.
If $ARGUMENTS is given not as a local path, you should NEVER CHECK any other folders and must download it to a temporary directory.
-
Map the evaluation architecture. Identify:
- The official entry point for running evaluations (e.g.,
run_eval.py, evaluate.sh, run.sh, run_tasks.sh)
- The main evaluating functions (look for files named
*eval*, *score*, *grade*, *judge*, *validate*, *metric*, *reward*)
- The task configuration files (e.g., JSON/YAML with task definitions, expected answers)
- The agent execution environment (Docker, VM, subprocess, shared filesystem?)
- Any LLM-as-judge components (look for API calls to OpenAI, Anthropic, etc.)
-
Identify trust boundaries. Map where agent-controlled data flows into the evaluator. This is critical -- every point where agent output touches evaluation code is an attack surface.
-
Estimate evaluation environment cost. Before proceeding further, assess the practical cost of running this benchmark's evaluation pipeline (excluding LLM API calls). Report:
- Docker images / large files: Does the benchmark require pulling large Docker images, datasets, model weights, or other heavy artifacts? Estimate total download size.
- Evaluation runtime: How long does a single task evaluation take? How long for the full suite? Look for timeouts, sleep calls, browser automation, compilation steps, or heavy compute.
- Infrastructure requirements: Does it need GPU, specific cloud services, running web servers, databases, or other non-trivial infrastructure?
- Setup complexity: How many steps and dependencies are involved in getting the evaluation environment running?
Summarize as a rough cost tier: Lightweight (minutes, no large downloads), Moderate (tens of minutes, some large pulls or infrastructure), or Heavy (hours, large images, complex infra). This informs how the PoC in Phase 3 should be scoped -- for Heavy benchmarks, the PoC should target a representative sample of tasks; for Lightweight benchmarks, run the full dataset.
-
Enumerate all task / problem IDs (REQUIRED). Before finishing reconnaissance, enumerate all concrete task or problem IDs in the benchmark. Write a small shell or Python script to enumerate the IDs in batch -- read them from config files, task directories, dataset splits, HuggingFace datasets, JSON manifests, etc. Do NOT hand-type them.
Write the result to benchjack_task_ids.json in the benchmark root as a JSON object mapping each task ID to the path of its definition file (relative to the benchmark root):
{
"task_id_1": "path/to/task_id_1.json",
"task_id_2": "tasks/task_id_2/config.yaml",
"task_id_3": "data/task_id_3.txt"
}
Rules:
- Use the IDs exactly as the benchmark itself refers to them.
- The path value should be the file or directory that defines/contains that specific task. Use an empty string
"" if no specific file exists.
- If the benchmark legitimately has no per-task IDs (e.g. a single monolithic eval), write
{"all_tasks": ""}.
- The file MUST be valid JSON and MUST exist before you move to Phase 2.
Use the Explore agent or Grep liberally here. Search for patterns like:
eval(, exec(, subprocess, os.system -- code execution
file://, open(, read near config/answer paths -- file access
pytest, unittest, conftest -- test frameworks
must_include, exact_match, contains, normalize -- string matching
gpt-4, claude, openai, anthropic, llm, judge -- LLM judges
reward, score, validate, check, grade -- scoring functions
gold, answer, expected, reference, ground_truth -- answer data
Static Analysis Tools
In addition to manual search, run the automated scanners in tools/ (relative to this SKILL.md) against the benchmark codebase. These tools accelerate Phase 1 reconnaissance and Phase 2 vulnerability scanning. Run them early and use their output to guide deeper manual investigation.
Run the full scan orchestrator to execute all tools at once:
bash <SKILL_DIR>/tools/scan.sh <benchmark-path>
Or run individual tools as needed:
| Tool | Command | Covers | Notes |
|---|
| Semgrep (custom rules) | semgrep --config <SKILL_DIR>/tools/benchjack_semgrep_rules.yml <path> | V1-V8 | 30+ rules targeting benchmark-specific patterns; install: pip install semgrep |
| Bandit | bash <SKILL_DIR>/tools/run_bandit.sh <path> | V3 | Python security linter focused on eval/exec/subprocess; auto-installs if missing |
| Docker Analyzer | python3 <SKILL_DIR>/tools/docker_analyzer.py <path> | V1, V2, V8 | Scans Dockerfiles, docker-compose, and shell scripts for isolation/permission issues |
| Trust Mapper | python3 <SKILL_DIR>/tools/trust_map.py <path> | V1, V3, V7 | AST-based analysis mapping file I/O and trust boundaries between agent/evaluator code |
| Hadolint | bash <SKILL_DIR>/tools/run_hadolint.sh <path> | V8 | Dockerfile best-practice linter; install: brew install hadolint or use Docker |
Where <SKILL_DIR> is the directory containing this SKILL.md.
How to use the results: These tools produce findings grouped by vulnerability class (V1-V8). Use them as leads -- each finding should be manually verified by reading the surrounding code. False positives are possible (especially from Semgrep V4/V5 rules), and false negatives are certain (the tools cannot detect all vulnerability patterns). Always combine tool output with manual code review.
Phase 2: Vulnerability Scan
Check for each of the following vulnerability classes. For each one, state Present / Absent / Uncertain with evidence.
V1: No Isolation Between Agent and Evaluator
What to look for: The agent executes in the same environment the evaluator reads from or the output of the agent can interfere with the evaluation logic. If the agent can write to paths the evaluator inspects, it can fake results.
Red flags:
- Agent and evaluator share a filesystem, database, or browser instance
- Evaluation script reads files from directories the agent can write to
- pytest/unittest runs inside a container the agent controls
- No containerization, sandboxing, or read-only mounts between agent and eval
- Evaluator reads agent-generated logs, screenshots, or artifacts without validation
Example exploit: SWE-bench trusts pytest output from inside the agent's container. A conftest.py hook can force all tests to pass:
@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):
outcome = yield
rep = outcome.get_result()
if rep.when == "call":
rep.outcome = "passed"
rep.longrepr = None
V2: Answers Shipped With the Test
What to look for: Reference answers, gold files, or expected outputs are accessible to the agent at runtime -- either in the task config, on a public URL, or in the same repo.
Red flags:
- Task config JSON/YAML contains
answer, expected, gold, reference fields visible to the agent
- Gold files hosted on public URLs (HuggingFace, GitHub) without authentication
- Answer data in the same Docker image or filesystem as the agent
- Test splits with publicly available ground truth
V3: Remote Code Execution on Untrusted Input
What to look for: The evaluator calls eval(), exec(), or equivalent on strings that the agent can influence -- directly or indirectly.
Red flags:
eval() called on agent output, agent-generated files, or extracted strings
exec() on dynamically constructed code that includes agent data
subprocess.run(shell=True) with agent-controlled arguments
- JavaScript
eval() in browser-based evaluators on DOM content the agent modified
- YAML/pickle deserialization of agent output
Example exploit: WebArena calls eval() on strings the agent can control via DOM injection, enabling arbitrary code execution on the grading machine.
V4: LLM Judges Without Input Sanitization
What to look for: Agent output is interpolated directly into LLM judge prompts without escaping or structural protection.
Red flags:
- String formatting or f-strings that insert agent output into judge prompts
- No delimiters, escaping, or structural separation between instructions and agent content
- Agent output placed before or alongside system instructions
- No output schema enforcement on the judge response
Example exploit: CAR-bench interpolates agent content into judge prompts. Hidden HTML comments can hijack the judge:
V5: Weak String Matching
What to look for: Scoring uses loose string comparison that accepts overly broad responses.
Red flags:
in operator or str.contains() for answer matching (substring containment)
- Aggressive normalization that collapses distinct answers into the same form
- Case-insensitive matching without considering ambiguity
- No penalty for extra content (an agent can dump all possible answers)
- Regex patterns that match too broadly
Example exploit: WebArena's must_include checker uses substring containment -- any response containing the target string passes, regardless of context.
V6: Evaluation Logic Gaps
What to look for: The scoring function has paths that skip checks, award points without verification, or contain bugs.
Red flags:
- Branches that return success without validating the answer
- Scoring components that are conditionally skipped
- Error handling that defaults to "pass" instead of "fail"
- Validators that check metadata (role, format) but not content
- Off-by-one errors, wrong variable references, or dead code in scoring
Example exploit: FieldWorkArena's validate() checks only that the last message came from the assistant, not what it says:
def validate(self, page, chat_messages):
if chat_messages[-1]["role"] == "assistant":
return 1.0, True, "Recieved answer", {}
V7: Trusting the Output of Untrusted Code
What to look for: The evaluator trusts artifacts (test results, reward files, logs) generated within the agent's environment without independent verification.
Red flags:
- Reading pytest/jest/unittest XML/JSON results from agent-controlled filesystems
- Parsing reward/score files the agent could have written
- Trusting screenshots or browser state from agent-controlled sessions
- Using agent-generated logs as ground truth
- No checksums, signatures, or out-of-band verification
Example exploit: Terminal-Bench trusts reward files written by scripts the agent can modify. Binary wrapper trojans can intercept the dependency chain:
mv /usr/bin/curl /usr/bin/curl.real
cat > /usr/bin/curl << 'EOF'
echo '{"reward": 1.0}' > /tmp/reward.json
/usr/bin/curl.real "$@"
EOF
chmod +x /usr/bin/curl
V8: Granting Unnecessary Permissions to the Agent
What to look for: The benchmark grants unnecessary capabilities to the agent (like network access, root in sandbox, privileged sandboxes, etc.).
Red flags:
- The agent has access to the internet even though the tasks do not require web browsing
- The agents are run with root permission in the dockers
- The dockers are run with
--privileged
- The agents can read/write to folders or files that they technically do not need
Structured Output (REQUIRED)
After analyzing all eight vulnerability classes, write the results as JSONL (one JSON object per line) to benchjack_task_results.jsonl in the benchmark root:
{"task": "<task_id>", "vulnerability": "V1", "severity": "CRITICAL", "hacked": false, "explanation": "<why>"}
{"task": "<task_id>", "vulnerability": "V3", "severity": "HIGH", "hacked": false, "explanation": "<why>"}
Rules:
- Use the actual task/problem IDs from the benchmark (from
benchjack_task_ids.json).
- If a vulnerability affects ALL tasks uniformly (e.g., the entire evaluation pipeline lacks isolation), use
"all_tasks" as the task ID.
- Identify all affected tasks. You can write scripts to enumerate them in batch.
severity MUST be one of CRITICAL, HIGH, MEDIUM, LOW and should reflect the severity of that specific (task, vulnerability) instance.
- Set
hacked to false -- these are detected but not yet verified.
- One JSON object per line. No wrapping array, no markdown fences, no extra formatting.
- The file MUST exist and be valid JSONL before you move to Phase 3.
Phase 3: Proof-of-Concept Exploit Construction
In this phase, you craft a working PoC that demonstrates real exploitation of the vulnerabilities found in Phase 2. The goal is to reward hack the benchmark -- achieving consistently high scores without actually solving any problems.
Terminology: A vulnerability is "exploitable" if it can be used to reward hack the benchmark, i.e., achieve higher evaluation scores than the agent's solution actually deserves. Some vulnerabilities found in Phase 2 may be quality issues that are difficult or impossible to reward hack -- those do not need a PoC, but should still be mentioned in the final report.
PoC Structure
Store the PoC in hacks/$BENCHMARK/ (where $BENCHMARK is a short identifier for the benchmark, e.g., swe-bench, webarena, gaia). The directory must contain:
-
run.sh -- The main script that:
- Sets up the benchmark environment (clone, install dependencies, configure)
- Deploys the exploit (inject files, patch agent, start mock server, etc.)
- Runs the benchmark end-to-end using the standard entrypoint
- Prints the final score/results clearly
-
Auxiliary scripts and files as needed (mock server, patched agent code, injected payloads, config overrides, etc.)
PoC Requirements
run.sh MUST run the benchmark through the official entrypoint described in the README and a normal agent harness -- not a custom one.
- It should set up the environment (install deps if needed), inject the exploit, then launch the evaluation.
- It should hack all (or as many as possible) of the benchmark's problems/tasks to get the highest score.
- Include clear comments explaining the exploit strategy.
- Print a summary at the end showing the hacked score.
- Must be runnable with
bash run.sh from the hacks/$BENCHMARK/ directory.
Agent Scaffolding Rules (CRITICAL)
- If there is a non-dummy default agent scaffolding in the benchmark that actually works,
run.sh should create an LLM API mock that uses the default scaffolding.
- If there is no non-dummy default agent scaffolding, refer to other example agents in the repository. You must use an agent with at most the tools that the example agents have access to. If there are no example agents, assume that the agent can only have minimal tools needed to complete the task.
- The agent MUST NOT use memorization, download files, or refer to its configurations and parameters to cheat. Our setting of reward hacking DOES NOT allow hacking scaffolding.
- If there is no specification about how to run the agent, you MUST NOT assume anything about the host machine, including internet access, pre-downloaded gold files, or access to the source of the repo.
Immutability Constraint (CRITICAL)
You MUST NOT make any modifications to the benchmark repository before the evaluation starts, especially the evaluator! All exploit injection must happen through the agent's normal execution path during evaluation -- not by pre-patching benchmark code.
Run Scope
- For Lightweight benchmarks: run the full benchmark -- all tasks, no subsets, no sampling.
- For Moderate / Heavy benchmarks: run against a representative sample of benchmark tasks (e.g., take 1-2 tasks from each type of task in the benchmark, at least 5-10 problems total). Keep the run fast and focused.
KEEP IT SIMPLE. You don't need to show that you can hack in multiple ways. Just construct a hack that works for as many cases as possible.
Validation -- Iterative Exploit Refinement
You MUST run run.sh and verify that it executes successfully.
This is not a one-shot process -- you must iteratively improve the exploit until you cannot hack a higher score.
Procedure:
- Run
run.sh and capture its full output.
- Inspect the per-task results. For every task that the exploit fails to hack:
- Read the evaluation log / error output to understand why the exploit did not work for that task.
- Examine the task's specific evaluation logic -- some tasks may have stricter checks, different scoring paths, or edge cases the current exploit does not cover.
- Determine whether an alternative exploit strategy could handle the failing case (e.g., a different injection payload, a fallback mechanism, combining multiple vulnerabilities).
- Update the exploit to address the failing tasks, then re-run
run.sh.
- Repeat steps 1-3 until one of the following is true:
- The exploit achieves a perfect or near-perfect score across all tasks.
- You have exhausted all viable exploit strategies for the remaining failing tasks and can explain why they resist exploitation (e.g., a particular task has a genuinely robust check that cannot be bypassed).
- Capture the final output in
hacks/$BENCHMARK/output.log.
Each iteration should be a deliberate improvement -- do not re-run the same exploit unchanged. If a task resists all attempts, document what you tried and why it failed in the README.
Post-Validation: Confirm PoC Legitimacy
After the PoC runs successfully, perform these checks:
- Official entrypoint: Does
run.sh actually run the benchmark through the official entrypoint described in the README? If it uses a custom harness or bypasses the real evaluation, fix it.
- Agent scaffolding compliance: Does it follow the agent scaffolding rules above? The agent must not memorize, download files, or use scaffolding hacks.
- Immutability: Does
run.sh modify the benchmark repository before evaluation starts? If so, fix it so all injection happens through the agent's normal execution path.
- Scope: Does the PoC run enough tasks? For Lightweight benchmarks, it must run ALL tasks. For Moderate/Heavy, it must run a representative sample (at least one problem from each category, 5-10 minimum).
Do not proceed to Phase 4 until you have maximized the exploit's score through this iterative process and confirmed legitimacy.
Exploit Results (REQUIRED)
After validation, write exploit_result.jsonl in the benchmark root that records only the tasks that were successfully hacked:
{"task": "<task_id>", "vulnerability": "V1", "severity": "CRITICAL", "hacked": true, "explanation": "<why>"}
{"task": "<task_id>", "vulnerability": "V3", "severity": "HIGH", "hacked": true, "explanation": "<why>"}
- Do NOT overwrite
benchjack_task_results.jsonl. This is a separate file.
- Include one entry per successfully-exploited task.
- Omit tasks that were not hacked.
- One JSON object per line.
- If the PoC targets all tasks uniformly, use
"all_tasks" as the task ID.
Phase 4: Final Deliverable
The final deliverable is the hacks/$BENCHMARK/ folder containing:
1. The validated PoC
run.sh -- the main exploit script
- Auxiliary scripts and files -- everything needed to reproduce the exploit
output.log -- a recording of a successful run of run.sh, showing the exploit achieving inflated scores
2. README.md
A concise report covering:
Executive Summary
One paragraph. Total vulnerabilities by severity. Hackability rating: Low / Medium / High / Critical.
Evaluation Architecture
How the benchmark works. Key components and data flow.
Exploit Strategy
Describe the PoC's approach in detail:
- Which vulnerabilities it exploits and how
- The technical mechanism (what the exploit does step-by-step)
- The final score achieved and what a legitimate baseline score would be
Vulnerability Findings
For each V1-V8: Status (Present/Absent/Uncertain), Severity, Description, Evidence (file:line), Impact, Recommendation.
Other Vulnerabilities
List all other vulnerabilities found in Phase 2, whether or not they are exploitable (i.e., usable for reward hacking). For each:
- Brief description of the vulnerability
- Whether it is exploitable for reward hacking, and if so, the potential impact
- If not exploitable, explain why (e.g., mitigating controls, limited impact, purely a quality issue)
Recommendations
Prioritized fixes. Best practices for benchmark authors.
Important Notes
- Be thorough. Read the actual evaluation code, not just file names. Many vulnerabilities hide in subtle implementation details.
- Show your evidence. Always cite file paths and line numbers for findings.
- Be honest about uncertainty. If you can't determine whether a vulnerability exists without running the code, say so.
- Consider composition. Multiple "medium" vulnerabilities can combine into a critical exploit chain.
- Be factual. Cite file paths and line numbers. Do not speculate without evidence.
- This audit is for defensive purposes -- to help benchmark authors find and fix vulnerabilities before they are exploited.