Skip to main content

What you get from every scan

Every agent-guardian scan persists a signed canonical scan.json to ~/.agentguardian/scans/<scan_id>/scan.json and writes your chosen format to --output-path. The canonical artifact is always emitted — even when you ask for SARIF or PDF — so the signed trail is intact regardless of which surface you share. Every finding carries the same seven facets across all five emitters:

Severity

critical / high / medium / low — maps to SARIF error / warning / note and JUnit <failure type=…>.

Reproduction

pov_referencepov/<finding_id>.py; pov_reliability is the N-fold Wilson-lower-bounded rerun success rate.

Evidence

transcript_ref under evidence/<finding_id>/ in the bundle. Scrubbed by PiiRedactor before disk.

Attack transcript

trigger_prompt is the exact attacker turn — replayable deterministically by the PoV runner.

Risk explanation

summary + the evaluator’s reasoning explain why the response was judged a finding.

OWASP / MITRE / CSA mapping

asi (OWASP ASI 2026) + mitre_atlas (AML.T…) + csa_category — same triple across all five emitters.

When to use this

  • You just finished agent-guardian scan and want to open the report.
  • You need to feed a CI pipeline that already understands SARIF or JUnit.
  • You’re sharing a result with a stakeholder who doesn’t have the CLI installed.
  • You want an Ed25519-signed evidence bundle for an audit / compliance review.

Generate a report

Pick the emitter with --output; pick where it lands with --output-path. Five formats are wired into the same scan command — no follow-up step, no separate render command.
Pass --bundle ./evidence/ alongside any --output to also emit a checksummed bundle_<scan_id>/ directory with findings.sarif, PoV reproducer scripts under pov/, and per-finding transcripts under evidence/. The bundle’s manifest.json carries SHA-256 + bytes for every file — meant to be the artifact you archive long-term.

Expected output

The bundle layout

A real --bundle out/ invocation produces this tree, sourced from reports/bundle.py:

A real findings excerpt

A real reflection record from memory.jsonl during a scan of a deliberately-vulnerable banking agent — the data that becomes a finding when the evaluator votes pass:

The same finding across three emitters

Same probe_id, same severity, same triple-framework tag — three surfaces.
PII and credential redaction is on by default for every emitter. The shared redact_finding helper scrubs summary, description, trigger_prompt, transcript_ref and evidence of PII and credential shapes (OpenAI / AWS / GitHub / Google keys, JWTs, bearer tokens, password= assignments). A security scanner must never re-emit a captured secret — this isn’t a knob.

How to interpret the result

Read the JSON envelope in this order:
  1. band — the human-facing label (safe / low_risk / elevated_risk / high_risk / critical_risk). First glance.
  2. aivss (0–100, higher is safer) — the score behind the band. Trust only when scoring_valid: true AND mode_authoritative: true.
  3. evaluation_modereal means a real LLM judged findings; stub forces band: not_evaluated and the number is meaningless.
  4. mode_authoritativefalse for --mode fast and --mode smart; true only for --mode full.
  5. coverage_grade (A–F) and undertested — categories launched but exercised too thinly to read “no findings” as safety.
  6. findings — sorted by severity then descending confidence; the Markdown report’s top-five follows the same rank.
--fail-under only gate-passes on an authoritative scan (--mode full with a real LLM). A --mode fast or --mode smart scan, or any stub-evaluator run, fails the gate regardless of the numeric score — its AIVSS reflects how much was tested, not how safe the agent is.

Verifying a signed report

verify is fail-closed: without a trust anchor (pinned Ed25519 public key and/or a real HMAC secret) the result is UNANCHORED and the command exits non-zero — even when the bytes recompute cleanly.
A trusted report prints schema: OK, HMAC-SHA256: OK, Ed25519: OK, trust anchor: PINNED, and exits 0. Anything else exits 1 — same exit code as a --fail-under failure, so CI gates treat tamper and risk-floor identically.

Next step

Run your first scan

Walk through a deliberately-vulnerable LangGraph agent end-to-end and read every field of the resulting scan.json.

Gate every PR on AIVSS

Wire the SARIF emitter into GitHub Advanced Security and add a --fail-under gate to your PR workflow.

Re-run the Quickstart

Five-minute path from pip install to your first AIVSS — useful if you want a fresh signed scan.json to verify.