Static-Analysis Confidence Gap · 10 of 10L3data science
An Honest Assurance Report for Agent Software
Replace the pass/fail verdict with a coverage manifest — a structured statement of exactly what was modeled, what was excluded, and what residual risk still needs a runtime test.
Abstract
The usual output of a security assessment — a pass/fail verdict, a green checkmark — is a claim without a boundary, and for AI-agent software it is actively misleading, because the parts a static assessment cannot reach are precisely where an agent is most likely to be broken. This piece proposes replacing the verdict with an Agent Security Coverage Manifest: a structured, honest report modeled on a scientific methods-and-limitations section and on assurance-case reasoning. The manifest states the system model, what was extracted and modeled, which analyses ran, what was excluded and assumed, the findings and their dispositions, the residual risk broken out by detectability class, and the runtime tests still required to cover what static analysis could not decide. The core claim: assurance is a vector, not a scalar, and an honest report publishes the vector — turning 'trust me, it is secure' into 'here is exactly what we checked, what we did not, and what still needs verifying.'
At the end of a security review someone wants a single answer: is it safe to ship? The pressure to collapse a complex assessment into a green checkmark is enormous, and for most software the collapse is tolerable, because the assessment covered most of the real risk. For an AI agent it is not tolerable, because the assessment's reach and the agent's risk are badly misaligned — the tool covers the code-detectable slice while the agent is most exposed in the parts decided by the model, the deployment, and the runtime. A green checkmark on such a system is not a lie about what was found; it is a lie about what was looked for. The honest alternative is not a better verdict. It is to stop issuing verdicts and start issuing a coverage manifest: a document that says, in structured detail, exactly what was modeled, what was assumed, what was excluded, and what still has to be tested somewhere the scanner could never see.
Why a verdict is the wrong deliverable
A verdict answers a question the assessment cannot honestly answer. 'Is this agent secure?' is a claim about the whole system under all conditions; a static assessment is a claim about a specific set of properties over a specific model of the code. Collapsing the second into the first discards exactly the information a decision-maker needs: the boundary of the claim. A green checkmark tells you a tool was satisfied. It does not tell you what the tool examined, what it assumed about the deployment, what it could not decide, or what class of risk it never modeled. Those omissions are not footnotes — for an agent they are the majority of the risk surface.
The failure is one of scope disclosure, and it has a precise shape. An assessment that soundly clears the code-detectable defects has made a real, valuable claim about one class of risk. Presented as 'secure', that same result silently asserts coverage of the model's behaviour, the deployment configuration, and the runtime state — none of which a static tool examined. The reader cannot distinguish 'we checked this and it is clean' from 'we did not check this', because the verdict flattens both into the same green. That flattening is where agents with poisoned-memory channels and over-broad tool permissions ship with a clean bill of health.
So the deliverable has to change shape, not just improve in accuracy. What a decision-maker actually needs is the boundary drawn explicitly: here is what we modeled and checked, here is what we assumed, here is what we could not decide and handed to runtime, and here is the residual risk that remains. That artifact already has mature analogues in other disciplines, and it is worth borrowing their rigor rather than inventing a weaker version.
What an honest report is modeled on
Two established forms already solve this problem, and the coverage manifest is their fusion. The first is the scientific paper's discipline of methods plus threats-to-validity: a credible result states exactly how it was obtained and, just as prominently, the conditions under which it may not hold. A paper that reported only its conclusion and hid its methods and limitations would be rejected; a security report that reports only its verdict is doing the same thing and should be held to the same standard. The limitations section is not an admission of weakness — it is what makes the claim usable.
The second is the assurance case, formalized in standards such as ISO/IEC/IEEE 15026: a structured argument in which a top-level claim is decomposed into sub-claims, each supported by explicit evidence, with the assumptions and the gaps made visible. An assurance case does not assert 'the system is safe'; it asserts 'this claim holds, given this argument, supported by this evidence, under these assumptions', and it exposes exactly where the argument is thin. NIST's AI Risk Management Framework pushes the same posture for AI systems — govern, map, measure, manage, with the boundaries of each activity stated rather than implied.
The coverage manifest takes the methods-and-limitations honesty of a paper and the claim-argument-evidence structure of an assurance case and applies them to an agent security assessment. It replaces 'secure' with a decomposed, evidenced, bounded set of claims, each tagged with what supports it and what it does not cover. That is the whole move: a report you could not skim into a false sense of safety, because its structure forces the boundary into view.
The Agent Security Coverage Manifest
Concretely, the manifest is a structured document with a fixed set of sections, each of which forces a disclosure the verdict would have hidden. The system model states what the agent is, its capabilities, its trust boundaries, and the threat scope of the assessment. Extraction and modeling records what was actually modeled — which sources, sinks, and trust boundaries the analysis represented — and, critically, the coverage: what fraction of the agent's real interfaces this represents and what was left out. Analyses and queries lists the specific checks run, so a reader knows which properties were even asked about. Tools, models, and versions pins the instruments used, because an assessment is only reproducible if its apparatus is named.
The next sections carry the honesty. Exclusions and assumptions states, as first-class content, what was assumed about the deployment and what was not examined — the configurations taken on faith, the model behaviour not evaluated, the runtime not observed. Findings and dispositions lists what was found and how each was resolved, with a recorded reason for every closed item rather than a bare count. Residual risk by class breaks the remaining, unverified risk out along the axis that determines detectability — code, model, deployment, runtime — so the reader sees which classes were covered and which were merely assumed. Required runtime tests states what the static assessment could not decide and therefore must be verified elsewhere, turning the manifest's gaps into an actionable handoff rather than a silence.
The discipline of the structure is that no section can be empty by omission. An assessment that examined only the code cannot leave 'residual risk by class' blank; it must state that the model, deployment, and runtime classes were not assessed. The manifest makes 'we didn't look' a visible entry rather than an invisible gap, and that single property is most of its value.
Extraction coverage: the number behind the findings
The most important number in the manifest is not the count of findings; it is extraction coverage — the fraction of the agent's real security-relevant interfaces that the analysis actually modeled. A scanner can only search the sources, sinks, and boundaries it was given a model for, so a clean report over a partial model is clean about a partial system. Reporting the finding count without the coverage is like reporting a survey's results without its response rate: the number is meaningless until you know what share of the population it represents. For an agent, the population is its sources (retrieval, tool results, memory, peer messages), its sinks (tool execution, credential access, memory writes), and its interior trust boundaries.
Making coverage explicit changes the conversation from 'how many findings' to 'how much of the system did we even look at'. On a support assistant, the manifest would state plainly: modeled the ticket-read source and the send-email sink; did not model the memory-write-then-read channel; treated the retrieval corpus as first-party by assumption. A reader now knows the clean result covers two interfaces and explicitly excludes a third — a far more useful statement than a green light. Coverage is the denominator that gives the findings meaning.
This also disciplines the assessment itself. A team that has to publish its coverage number is motivated to raise it — to model more of the real interfaces — rather than to tune the query for a cleaner-looking result over the same partial model. The number that gets published is the number that gets improved, which is why the manifest puts coverage, not findings, in the position of prominence.
Exclusions and assumptions as first-class content
The section most reports omit entirely is the one that carries the most safety: what was excluded and what was assumed. Every static assessment rests on assumptions about things it did not examine — that the deployment grants least privilege, that the retrieval corpus is first-party, that a policy engine sits on a path, that the model resists a class of injection. Each of those assumptions is a load-bearing precondition for the result, and each is invisible unless the manifest names it. An assumption that stays implicit is a vulnerability waiting for the day the assumption stops holding — the day the corpus starts accepting third-party content, or the permission is widened.
Stating exclusions explicitly also correctly locates responsibility. When the manifest says 'assumed the send-email tool is scoped to internal recipients; this was not verified', it hands a specific, checkable obligation to whoever owns the deployment. The assessment has not silently absorbed a risk it could not evaluate; it has surfaced the risk and assigned it. This is the composition principle that runs through all serious assurance — NIST's Secure Software Development Framework is explicit that assurance is a composition of practices, and a composition only holds if each part states what it relies on the others to provide.
The uncomfortable but honest consequence is that a rigorous manifest will have a substantial exclusions section, and that is a feature. A short exclusions section on an agent assessment is not evidence of thoroughness; it is evidence that the assessment did not understand its own boundary. The goal is not to minimize the exclusions but to make them true, explicit, and owned.
Residual risk by detectability class
The manifest's residual-risk section must be broken out along the axis that actually determines whether a risk was observable: what decides its outcome. Code-detectable risks are fixed by the source and are the class a static analysis can soundly own. Model-dependent risks turn on what the model chooses given its input and are not decided by any path in the code. Deployment-dependent risks are settled by configuration the analyzer never read. Runtime-only risks emerge from state and interaction that do not exist at analysis time. A single 'residual risk: low' collapses four very different situations; a per-class breakout keeps them distinct.
This is why assurance is a vector, not a scalar. The honest summary of an agent assessment is not a number but a tuple — coverage and residual for the code class, the model class, the deployment class, and the runtime class — and any attempt to average them into one figure destroys the information that matters. A system can be fully covered on the code class and entirely unassessed on the runtime class, and 'secure' erases that difference while the vector preserves it. The manifest reports the vector because the vector is the truth and the scalar is a convenient fiction.
For the support assistant the residual-risk section reads honestly and usefully: code class covered and clean; model class not assessed, residual risk unknown, injection resistance never measured; deployment class assumed least-privilege, not verified; runtime class not assessed, memory-poisoning channel not covered. No reader of that section could mistake the assessment for a certification of the whole agent — which is exactly the point.
Required runtime tests: turning gaps into a handoff
The manifest's final section converts what static analysis could not decide into an explicit, actionable list of tests that must run somewhere else. This is what distinguishes an honest report from a defeatist one: the classes a scanner cannot reach are not written off as unknowable, they are handed to the instruments that can reach them. Model-dependent risk becomes a required adversarial evaluation — an injection-resistance measurement run as a gate. Deployment-dependent risk becomes a required posture check of tool permissions, egress allow-lists, and identity scopes. Runtime-only risk becomes required provenance labelling, memory-read re-checks, and cumulative-authority monitoring in production.
Framed this way, the static assessment and the manifest are not the end of assurance but its first, bounded contribution, with the remaining obligations named precisely enough to be scheduled and owned. The report does not say 'we could not evaluate the model, good luck'; it says 'the model class requires an injection-resistance eval at threshold X before this claim is complete', which a team can act on. The gaps become work items, and the manifest becomes a plan for full-lifecycle assurance rather than a single-activity verdict.
This is the same division of labour that any honest agent security posture rests on: each instrument reads a different input and covers a different class, none substitutes for another, and the report's job is to state which instrument covered which class and which are still outstanding. A manifest whose required-runtime-tests section is populated is telling the truth about a partial assessment; a verdict that omits it is telling a comfortable falsehood about a complete one.
| Class | Static verdict | Required runtime test |
|---|---|---|
| Code-detectable | covered + clean | regression tests on guarded flows |
| Model-dependent | not decidable | adversarial injection-resistance eval (gate) |
| Deployment-dependent | assumed | posture scan of permissions, egress, identity |
| Runtime-only | structurally blind | provenance, memory re-check, authority monitor |
Why this matters for AI agents
For AI agents the manifest is not a documentation nicety; it is the difference between a report that correlates with security and one that does not. Because an agent's dominant risk sits outside the code-detectable class, a verdict derived from a code scan is weakly correlated with the agent actually being safe, and presenting it as a verdict actively misleads. A manifest restores the correlation by refusing to overclaim: it correlates its stated coverage with reality precisely because it states its coverage. The honesty is not ethical decoration; it is what makes the report predictive.
The engineering practice that follows is to make the manifest the required deliverable of any agent security assessment, and to treat a bare verdict as a defect in the assessment process itself. A team that ships because 'the report is green' has accepted a claim without a boundary; a team that ships because 'the manifest shows code and deployment classes covered, model class evaluated at threshold X, runtime controls in place, residual risk owned' has made an accountable decision on real information. The manifest does not make the agent more secure by itself — it makes the security decision an honest one, which is the precondition for the agent becoming secure over time.
Keep the frame that unifies the whole discipline: you hold the intent, the tools are instruments, and no single instrument sees the whole risk. The manifest is where that reality is written down — a report that names each instrument, the class it covered, and the classes still waiting for one. It is the artifact that lets a human own the security of an agent honestly, rather than delegating that judgement to a green checkmark that was never entitled to make it.
The honest posture
The one-line takeaway is that assurance is a vector, not a scalar, and an honest agent security report publishes the vector. Replace the pass/fail verdict with a coverage manifest: the system model, what was extracted and its coverage, the analyses run, the exclusions and assumptions, the findings and dispositions, the residual risk by detectability class, and the required runtime tests. Each section forces into view a disclosure the verdict would have hidden, and the result is a report that cannot be skimmed into a false sense of safety.
The manifest is not a lower standard than a verdict; it is a far higher one, because it holds the assessment to the discipline of a scientific methods-and-limitations section and an assurance case rather than the comfort of a checkmark. It says, without flinching, 'here is exactly what we checked, here is what we assumed, here is what we could not decide, and here is what still needs testing.' A team that adopts it stops asking a scanner to certify what no scanner can, and starts making agent-security decisions on the only basis that is ever honest: a clear-eyed account of coverage and its limits.
Key takeaways
- A pass/fail verdict is a claim without a boundary; for an agent it silently asserts coverage of the model, deployment, and runtime that a static assessment never examined.
- Replace the verdict with an Agent Security Coverage Manifest modeled on a scientific methods-and-limitations section and an assurance case (claim-argument-evidence with assumptions and gaps made visible).
- The manifest's fixed sections force disclosure: system model, extraction coverage, analyses and versions, exclusions and assumptions, findings and dispositions, residual risk by class, and required runtime tests.
- Extraction coverage — the fraction of real interfaces modeled — is the denominator that gives findings meaning; a clean report over low coverage is clean about little.
- Assurance is a vector, not a scalar: report coverage and residual per detectability class (code, model, deployment, runtime); averaging them into 'secure' destroys the information that matters.
- Turn every gap into a handoff: each class the scanner cannot reach becomes a required runtime test (injection-resistance eval, posture scan, provenance and authority monitoring), owned and scheduled.
Practitioner Toolkit
Copy-paste, strictly defensive artifacts you can use today. Nothing here attacks a real system.
A manifest is incomplete until every section below is populated — 'not assessed' is a valid, required entry.
- System model + threat scope stated.
- Extraction coverage reported as a fraction of real interfaces, with what was left out.
- Analyses, queries, tools, models, and versions pinned.
- Exclusions and assumptions listed as first-class content, each owned.
- Findings with a recorded disposition/reason for every closed item.
- Residual risk broken out per class: code, model, deployment, runtime.
- Required runtime tests named for every class the assessment could not decide.
- No section left blank by omission — 'not assessed' is written explicitly.
A paste-ready structure to fill in per assessment; keeps the boundary of the claim explicit.
system_model: { agent, capabilities, trust_boundaries, threat_scope }
extraction:
interfaces_modeled: [ ... ]
coverage: modeled / present # the key number
left_out: [ ... ]
analyses: [ queries + versions ]
tools_models_versions: [ ... ]
exclusions_assumptions:
- assumption: "..." owner: "..." verified: false
findings: [ { id, disposition, reason } ]
residual_risk_by_class:
code: { coverage, residual }
model: { coverage, residual }
deployment: { coverage, residual }
runtime: { coverage, residual }
required_runtime_tests: [ per unreached class ]The smallest change that turns a verdict into an honest report.
- Stop shipping a pass/fail verdict; ship the per-class coverage vector.
- Publish extraction coverage next to every finding count.
- Write the exclusions and assumptions section, with an owner per line.
- List a required runtime test for every class the scan could not decide.
Glossary
- Coverage manifest
- A structured security report stating what was modeled, assumed, excluded, found, and still requires runtime testing, in place of a pass/fail verdict.
- Extraction coverage
- The fraction of an agent's real security-relevant interfaces (sources, sinks, boundaries) that the analysis actually modeled.
- Assurance case
- A structured argument decomposing a top-level claim into evidenced sub-claims with assumptions and gaps made explicit (e.g. ISO/IEC/IEEE 15026).
- Detectability class
- A category of risk defined by what determines its outcome — code, model, deployment, or runtime — and therefore which instrument can observe it.
- Residual risk
- The risk that remains unverified after an assessment, reported per detectability class rather than as a single figure.
- Required runtime test
- A verification the static assessment could not decide, handed to the instrument that can (adversarial eval, posture scan, runtime monitoring).
References
- NIST SP 800-218, Secure Software Development Framework
- NIST AI 100-1, Artificial Intelligence Risk Management Framework
- NIST SP 800-207, Zero Trust Architecture
- ISO/IEC/IEEE 15026-2:2022, Systems and software assurance — Assurance case
- OWASP Agentic AI Threats and Mitigations
- MITRE Common Weakness Enumeration (CWE)