Abstract

A clean static-analysis report is only as meaningful as the analyzer's recall for the risk in question, and recall is a property of a tool on a distribution. This piece argues that the distribution that matters for AI agents — untrusted content flowing through a model into a tool call — is absent from the public benchmarks the field relies on, so their scores cannot be transferred to agent code. The contribution is a measurement design: how to construct a controlled, non-weaponized corpus of paired vulnerable and safe agent cases labeled to a common weakness vocabulary, which metrics to compute and why precision and recall must be reported separately, and an ablation protocol that isolates the effect of the analyzer's suite and its framework models. No results are reported; the artifact is the methodology and an honest reporting schema, because a defensible number for agent software does not yet exist and must be earned rather than assumed.

Ask a security team how good their scanner is and you will usually get a benchmark score: high recall on a public suite, a reassuring number on a slide. Ask the sharper question — how good is it on the agent you are actually shipping — and the number evaporates, because it was measured on code that looks nothing like an agent. The gap is not laziness; it is that the public instruments the field trusts were built for a world of conventional source-to-sink bugs, and an AI agent moves its most dangerous data through a model and a runtime tool call that those instruments never modeled. This piece does not report how well analyzers do on agent vulnerabilities. It argues that no one honestly can yet, and it lays out the measurement you would have to run to find out — the corpus, the metrics, and the controls — so that a future number means something.

Why a measured number is the only honest answer

Static analysis is the examination of a program without executing it, to infer properties of every run; recall is the fraction of real weaknesses of a given class that the analyzer actually reports. The link between them is unforgiving: a clean report from a low-recall tool is nearly information-free, because a tool that rarely catches a class will happily stay silent on code riddled with it. So any claim that a scanner 'covers' agent risk is really a claim about its recall on agent-specific weakness classes, and that is an empirical quantity, not an assumption.

The temptation is to reuse a recall figure the tool already advertises. That figure was earned on a public benchmark, and it is genuine — for the benchmark's code. The error is treating it as transferable. Recall does not travel across distributions: a query tuned to find an injection in a conventional web handler can miss the same logical flaw when the tainted value arrives from a model's output through a framework the analyzer never modeled. The number you can defend is the one measured on a corpus that resembles your target, and for agents that corpus does not exist off the shelf.

This reframes the task. To say anything trustworthy about a clean scan of an agent, you must first build the measuring instrument: a controlled body of agent code with known, labeled weaknesses, run the analyzer against it, and score what it found against what was planted. The rest of this piece is the design of that instrument and the discipline required to keep its numbers honest — including the discipline of reporting none until they are actually collected.

Recall for agent code is produced by a pipeline: build a labeled corpus, run the analyzer, score findings against ground truth. The measurement pipeline Labeled corpus seeded + paired cases Run analyzer suite + models fixed Match toground truth found vs planted Per-class metrics recall, precision
Recall for agent code is produced by a pipeline: build a labeled corpus, run the analyzer, score findings against ground truth.

What the public benchmarks measure, and the gap they leave

The field already has serious measuring instruments, and any agent benchmark should build on them rather than reinvent their rigor. The NIST Software Assurance Reference Dataset and its Juliet Test Suite provide many thousands of small programs with deliberately planted weaknesses, each labeled to a class in the MITRE Common Weakness Enumeration, the product-neutral catalogue that lets different tools be compared on a shared vocabulary. The OWASP Benchmark Project goes a step further, shipping a single runnable application salted with both true weaknesses and deliberate look-alikes that should not fire, so precision and recall can be scored on identical ground.

These instruments share an implicit subject: conventional software, where the dangerous path runs through source code the analyzer can read — a request parameter reaching a query, a filename reaching the file system. That is exactly the shape they were built to represent, and they represent it well. It is also exactly the shape an AI agent breaks. The decisive step in an agentic vulnerability is untrusted content becoming an instruction the model acts on, and the tool call that follows is selected at runtime from the model's output; neither edge exists as traceable source text, so neither is present in a corpus of conventional test cases.

The gap is therefore not a missing test case or two; it is a missing distribution. A benchmark can score an analyzer's ability to trace a filename into a path-traversal sink and say nothing about its ability to trace a retrieved document into a tool invocation, because the latter flow is not in it. Measuring static analysis on agent vulnerabilities means constructing that absent distribution deliberately, which is the subject of the next section.

Public benchmarks densely cover conventional source-to-sink flows and omit the model-mediated flows that define agent risk. Covered versus uncovered Benchmarks cover The agent gap Conventional flows request to query, file, command Well covered Juliet, OWASP Benchmark Model-mediated flows content to model to tool Not represented the agent gap
Public benchmarks densely cover conventional source-to-sink flows and omit the model-mediated flows that define agent risk.

Constructing a controlled agent corpus

A measurement corpus is a set of programs whose weaknesses are known in advance, so that what an analyzer reports can be judged against ground truth. For agents, each case is a small, self-contained program that exercises one agentic flow — a piece of untrusted content reaching a consequential tool through a model step — realized with sanitized, mock tools so nothing in the corpus attacks a real system. The unit of the design is the matched pair: two near-identical programs that differ only in whether a specific control is present, one genuinely vulnerable and one safe. The pair is what lets a single case measure both recall (does the tool catch the vulnerable variant?) and precision (does it wrongly flag the safe one?).

Three properties keep such a corpus honest. First, every case carries a label: the weakness class it instantiates, mapped to a Common Weakness Enumeration identifier where one fits, and the exact source and sink so a reported finding can be matched precisely rather than by proximity. Second, cases must avoid leakage — the vulnerable and safe variants of a pair must be structurally alike enough that a tool cannot separate them by a superficial cue rather than by tracing the actual flow, or the measured precision is an artefact. Third, the corpus must span the agent-specific classes deliberately: content-to-tool injection, retrieval-to-sink flows, memory that is written in one turn and trusted in another, and delegated calls that cross a trust boundary.

Provenance is part of the artifact, not an afterthought. Because these cases will be read as evidence, the corpus should record who authored each case, which control distinguishes the pair, and why the flow is or is not exploitable, so a later reviewer can audit the ground truth itself. A benchmark whose labels cannot be independently checked cannot support a defensible recall number, however large it is.

Each case seeds one known flow from untrusted content through a mock model step to a mock tool, with the trust boundary marked. A single sanitized case agent trust boundary Seeded content labeled untrusted Paired safe variant control present Mock model step no real model Mock tool sink no real effect flows reaches sink blocked
Each case seeds one known flow from untrusted content through a mock model step to a mock tool, with the trust boundary marked.

Metrics, defined before they are used

Two counts drive everything. A true positive is a planted weakness the analyzer reported at the right location; a false negative is a planted weakness it missed; a false positive is a report on a safe variant where no weakness exists. From these, precision is the fraction of reports that are real, and recall is the fraction of real weaknesses reported. They answer different questions — precision is how much of the alarm to believe, recall is how much of the danger was seen — and a clean report speaks only to recall, which is why recall is the headline number for assurance and precision is the number that governs whether the tool is usable day to day.

Because the two trade off, a single summary can be reported with an explicit preference between them. The F-beta score is the standard weighted harmonic mean of precision and recall, where the weight beta chooses how much recall is favoured over precision; for a security-assurance study, beta above one is appropriate because a missed vulnerability usually costs more than a false alarm. Reporting the chosen beta is mandatory, because an unstated weighting hides exactly the value judgement the reader needs to see.

Two coverage metrics complete the picture and are specific to why agents are hard. Extraction rate is the fraction of the corpus the analyzer successfully ingested into its model at all — code it could not parse or build never had a chance to yield a finding, and counting it as a clean pass would flatter the tool. Modeling coverage is the fraction of seeded flows whose framework and call mechanisms the analyzer actually models; a flow through an unmodeled tool wrapper is invisible regardless of the query, and separating this from genuine misses is essential to diagnosing why recall is what it is.

\[\mathrm{Precision} = \frac{TP}{TP + FP}, \qquad \mathrm{Recall} = \frac{TP}{TP + FN}\]
\[F_{\beta} = (1 + \beta^{2}) \cdot \frac{\mathrm{Precision} \cdot \mathrm{Recall}}{\beta^{2} \cdot \mathrm{Precision} + \mathrm{Recall}}\]
\[\mathrm{Coverage}_{\text{model}} = \frac{\#\{\text{seeded flows whose framework is modeled}\}}{\#\{\text{seeded flows}\}}\]

An ablation protocol that isolates cause

A single recall number for a whole tool is nearly useless for diagnosis, because it blends together three different failures: the flow was not extracted, the framework was not modeled, or the query was absent or imprecise. An ablation is a controlled comparison that changes exactly one factor at a time so its effect can be attributed. The protocol runs the same corpus under a fixed sequence of configurations and reports per-class recall for each, so the reader can see not just how much is caught but where the catching breaks down.

Four configurations suffice to separate the causes. The baseline is the analyzer's default rule set with only its built-in framework models. The second adds the broader, higher-recall rule set most tools offer, isolating the effect of query breadth on both recall and false positives. The third adds custom models of the agent frameworks in the corpus while holding the queries fixed, isolating the effect of closing the modeling gap. The fourth combines the broad rule set with the custom models, showing the ceiling the tool can reach on this corpus when both levers are pulled. Reporting the four side by side turns a mystery into a diagnosis: a jump from the third configuration to nowhere at the first says the losses were modeling, not queries.

Every configuration must fix and record the tool version, the corpus version, and the random or heuristic settings that affect determinism, because analyzers change materially between releases and an unpinned comparison measures drift rather than design. The output of the protocol is not one score but a small matrix of per-class recall across configurations, and that matrix, not a headline number, is the honest result.

Each stage can drop a seeded flow; the ablation attributes the loss to extraction, modeling, or query rather than blaming the tool as a whole. Where recall is lost Seeded flows all planted cases Extracted parsed into the model Modeled framework understood Queried and caught reported findings Each stage narrows the set; the ablation attributes the loss rather than blaming the tool.
Each stage can drop a seeded flow; the ablation attributes the loss to extraction, modeling, or query rather than blaming the tool as a whole.

Threats to validity

The most serious threat is construct validity: do the seeded cases actually represent the agent vulnerabilities that occur in the wild, or a researcher's guess at them? A corpus assembled from imagined flows can produce a precise, reproducible number that measures the wrong thing. The mitigation is to ground the taxonomy of seeded classes in an external, authoritative catalogue of agentic risks rather than intuition, and to state plainly that the corpus measures those classes and no others.

External validity is the transfer problem in a new guise. Even a well-grounded corpus is a sample of idioms and frameworks, and a recall number measured on it bounds what a clean report proves for code that resembles it, not for arbitrary agent code. Two further threats attack the ground truth itself: label noise, where a case is mislabeled or its safe variant is secretly exploitable, which corrupts both precision and recall; and leakage, where paired variants differ by a superficial token that lets a tool score well without tracing the flow, inflating precision. Both are controlled by independent review of the corpus and by adversarial checking of the pairs.

Finally, benchmark gaming is a standing hazard: once a corpus is public, analyzers can be tuned to its specific cases, so a high score may reflect fitting the test rather than finding the class. The defenses are a held-out portion of the corpus that is never published, periodic rotation of cases, and — most importantly — reporting the measurement design so that a suspiciously high number can be interrogated rather than trusted. None of these threats is a reason not to measure; they are the reasons to measure carefully and to caveat the result exactly.

⚠️
A number is not neutral. A recall figure inherits every choice in the corpus that produced it, so publishing the design is what lets a reader tell measurement from marketing.

What an honest result looks like — a reporting schema

Because no results are presented here, it is worth being precise about the shape a defensible result would take, so that the absence is not filled in later with something weaker. The reportable object is per-class, not global: for each seeded weakness class, the study reports the number of cases, the recall and precision under each of the four configurations, and the extraction and modeling coverage that explain them. A single blended accuracy figure across classes is precisely the summary this whole series argues against, because it lets a high score on easy conventional classes mask a floor-level score on the agentic ones that matter.

The schema below is a template, not data — every cell is a placeholder to be filled by an actual run, and presenting it filled with invented numbers would be the exact dishonesty the measurement is meant to prevent. Its value is in fixing what must be collected: the class, its size, and the configuration-by-configuration recall that turns a claim of coverage into an auditable measurement. A study that reports this table, with its methodology and its held-out caveat, has earned a number; one that reports a single figure has not.

The connection back to agents is the whole point of measuring at all. The classes that will show the widest gap between conventional and agentic recall are the content-to-tool and cross-turn memory flows, because those are the ones whose decisive edge lives in the model and at runtime rather than in the source. A measurement that reports strong overall recall while those specific rows sit near zero is not a success; it is a precise map of where static analysis stops and other assurance must begin.

Reporting schema (template only — no measured values). Each row is a weakness class; each configuration column is filled by an actual ablation run.
Weakness classCasesDefault recallExtended recall+ Models recallModel coverage
Content-to-tool injectionn
Retrieval-to-sink flown
Cross-turn memory trustn
Delegated cross-boundary calln
Conventional control (e.g. injection)n

From a corpus to a standing instrument

A one-off measurement ages the moment an analyzer ships a new release, so the design should be built to be re-run. Pinning the tool and corpus versions, keeping the scoring harness deterministic, and holding out an unpublished slice turn a single study into a standing instrument that can track whether a tool's agentic recall improves over time or merely its conventional score. That longitudinal view is what a security organization actually needs, because the question is not how a tool did once but whether trusting its clean reports is getting safer or more dangerous as both the tool and the agent frameworks evolve.

The instrument also has a governance role. Assurance frameworks decline to rest on any single verification activity precisely because each has a bounded reach, and a per-class recall matrix is what makes that reach concrete: it tells a program owner which weakness classes their scanner genuinely covers on agent code and which they must assign to review, testing, or threat modeling instead. Without the measurement, that allocation is guesswork dressed as a control.

The honest close is that this piece deliberately ends without a number. The contribution is the instrument and the discipline to read it — paired cases, per-class metrics, an ablation that attributes cause, and threats named rather than hidden. A field that adopts this design can, for the first time, say something defensible about what a clean scan proves for an agent; a field that keeps borrowing conventional benchmark scores will keep mistaking a number earned elsewhere for evidence about the system in front of it.

Key takeaways

  • Recall governs what a clean report proves, and recall is a property of a tool on a distribution — so a conventional benchmark's score cannot be transferred to agent code.
  • Public benchmarks (Juliet, SARD, OWASP Benchmark) densely cover conventional source-to-sink flows but omit the model-mediated flows that define agentic risk.
  • The measurement artifact is a controlled corpus of paired vulnerable and safe agent cases, sanitized with mock tools and labeled to a common weakness vocabulary.
  • Precision and recall must be reported separately, with an explicit F-beta weighting, alongside extraction rate and modeling coverage that explain the losses.
  • An ablation over default suite, extended suite, and custom models attributes missed detections to extraction, modeling, or query rather than blaming the tool as a whole.
  • No defensible agentic recall number exists off the shelf; the honest output is a per-class recall matrix with its design and caveats, not a single blended accuracy figure.

Practitioner Toolkit

Copy-paste, strictly defensive artifacts you can use today. Nothing here attacks a real system.

Agent measurement-corpus constructionchecklist

Run while building or reviewing a corpus so its numbers stay defensible.

  • Every case is a matched pair: vulnerable and safe variants differing by exactly one control.
  • Each case is labeled with its weakness class, a CWE id where one fits, and the exact source and sink.
  • Tools are mock/no-op and content is sanitized — nothing in the corpus attacks a real system.
  • Pairs are structurally alike enough that no superficial token separates them (no leakage).
  • Seeded classes span content-to-tool, retrieval-to-sink, cross-turn memory, and delegated cross-boundary flows.
  • A held-out slice is never published, to detect and resist benchmark gaming.
  • Provenance is recorded per case: author, the distinguishing control, and why it is or is not exploitable.
🧪Deterministic scoring harness (sanitized skeleton)harness

Pseudocode to score analyzer output against labels; computes per-class recall and precision.

for config in [default, extended, plus_models, extended_plus_models]:
    findings = run_analyzer(corpus, config)          # fixed, pinned version
    for cls in weakness_classes:
        planted = labels.where(class == cls)
        tp = count(planted matched by (source, sink) in findings)
        fn = count(planted) - tp
        fp = count(findings on safe-variant of class cls)
        recall[cls][config]    = tp / (tp + fn)
        precision[cls][config] = tp / (tp + fp) if (tp+fp) else undefined
    record(tool_version, corpus_version, config)     # reproducibility
# report the per-class matrix, never a single blended figure
No real tools invoked; matches reported findings to labels by source and sink location.
🚀Minimum viable measurement — do these firstquickstart

The shortest path from 'we trust the scanner' to a defensible statement.

  • Pick 3 agentic weakness classes that matter for your system and write 5 matched pairs each.
  • Pin the analyzer version and run the default and extended suites over the pairs.
  • Report per-class recall and precision separately — never average them into one number.
  • State the corpus size and the transfer caveat next to every figure you publish.

Glossary

Recall
The fraction of real weaknesses of a class that the analyzer reports, TP / (TP + FN); the number that governs what a clean report proves.
Precision
The fraction of reported findings that are real, TP / (TP + FP); the number that governs whether a tool is usable in practice.
Matched pair
Two near-identical cases differing only by one control — one vulnerable, one safe — used to measure recall and precision together without leakage.
Ablation
A controlled comparison that changes exactly one factor at a time so its effect on the outcome can be attributed to that factor.
Extraction rate
The fraction of the corpus the analyzer successfully ingested into its internal model; unextracted code cannot produce a finding.
Modeling coverage
The fraction of seeded flows whose frameworks and call mechanisms the analyzer models, separating invisible flows from genuine misses.
Common Weakness Enumeration
A product-neutral catalogue maintained by MITRE that names weakness classes so different tools can be compared on a shared vocabulary.
Construct validity
Whether a study measures the thing it intends to — here, whether seeded cases represent real agent vulnerabilities rather than a guess at them.

References

  1. NIST Software Assurance Reference Dataset (SARD) and Juliet Test Suite
  2. OWASP Benchmark Project
  3. MITRE Common Weakness Enumeration (CWE)
  4. Livshits & Lam, Finding Security Vulnerabilities in Java Applications with Static Analysis (USENIX Security 2005)
  5. NIST SP 800-218, Secure Software Development Framework (SSDF) v1.1
  6. OWASP Agentic AI — Threats and Mitigations
  7. Rice, Classes of Recursively Enumerable Sets and Their Decision Problems (Trans. AMS, 1953)