The single reference for *-security-audit.json reports. All three audit
skills — secure-code-audit (profile code), secure-rpm-audit (profile
rpm), and secure-container-audit (profile container) — emit this one
structure, validated by the one schemas/v1/report.schema.json +
python3 -m traust.cli reporting validate. The schema is the authoritative
source for types and constraints; this page is the human-readable companion.
Profile-specific deltas are at the bottom — everything above them is identical
across profiles, on purpose: the finding/severity/ID vocabulary must stay
unified so triage, the disposition ledger, verify-remediation, and every
dashboard work on every report kind unmodified.
Any report in this structure (including the disposition-aware
*-findings-current.json) projects to SARIF 2.1.0 via
python3 -m traust.cli reporting sarif for any SARIF consumer; see
sarif.md for the mapping and artifacts.md for the
projections.
Maintenance rule: this file is the ONLY place the structure tables live. The SKILL.mds reference it and state only their deltas — duplicated tables drift, and the alignment gate rejects a skill that restates them.
| Key | Description |
|---|---|
title |
Report title. Must contain a security-related keyword: security, audit, assessment, review, finding, or analysis. |
metadata |
Object with required date (YYYY-MM-DD, the audit execution date — never a commit or CVE date) and scope (≥10 chars). Optional: repository, commit, ref + ref_kind (branch | tag | default | stream — explicit ref provenance; stamp both together on code/rpm profiles, omit on container and for detached-SHA inputs; stream is the rpm profile's dist-git release stream, a mainline deliverable that is never a branch re-audit), framework, auditor, methodology, tools (array), loc_reviewed (integer preferred), loc_breakdown, audit_profile (code | rpm | container — set it; see deltas), additional (object). Must include additional.harness_version — the semver from the harness VERSION file plus the short git SHA (e.g. 0.51.0-370e5a2); its presence marks the report as modern-producer output, which requires stamped finding identity. additional.contracts_version records the contracts package version the report was validated against. The schema does not allow harness_version as a direct metadata key; it lives under metadata.additional, which is where the validator reads it for version-gated checks. Routed model decisions are stamped as metadata.additional.model_routing ({role, model, registry_sha, floor}, emitted via python3 -m traust.cli registry models stamp — alignment rule A12; see model-routing.md). |
executive_summary |
Object with prose (≥50 chars) and severity_counts (required critical, high, medium, low, informational integer counts). Optional: key_risks, positive_observations. |
severity_criteria |
Array of ≥4 entries, each with level (critical/high/medium/low/informational) and definition (≥20 chars). Must include at least critical, high, medium, low. |
findings |
Array of finding objects (below). |
findings_summary |
Array of ≥4 severity count entries (severity, count, finding_ids) — counts and IDs must match the findings. |
remediation_roadmap |
Array of ≥1 items (priority, action ≥10 chars, addresses finding-ID array). |
| Field | Required | Description |
|---|---|---|
id |
Yes | Globally-unique scan-scoped ID {REPO_SLUG}-{SHORTSHA}-{NNN}. Provenance to an exact commit; unique across the corpus. Never reuse across scans. |
fingerprint |
No (script-set) | Cross-scan identity: sha256(canonical repo URL | sorted lineless location paths | primary CWE). Computed by python3 -m traust.cli corpus finding-identity (fingerprint/backfill) — never authored by the model, and the only implementation: consumers read this stamp and fail closed when it is absent. Correlates the same vulnerability across re-scans; the rebaseline command maps a superseded report's IDs to their successors via layer metadata.finding_aliases. A change to the recipe is a change to ALGO_VERSION paired with a re-stamp migration, never a silent regeneration. |
title |
Yes | Short description (≥5 chars). |
severity |
Yes | critical | high | medium | low | informational. |
cwes |
Yes | ≥1 CWE-NNN identifiers; the first is the primary (feeds the fingerprint). |
locations |
Yes | ≥1 objects with required path, optional lines, description. |
description |
Yes | Detailed vulnerability description (≥50 chars). |
remediation |
Yes | Remediation guidance (≥10 chars). |
cvss |
No | score (0.0–10.0) + vector. |
capec |
No | CAPEC-NNN array. |
evidence |
No | Code blocks: required code, optional language, caption. |
attack_pattern |
No | Attack-scenario description. |
category |
No | Exactly one kebab-case token from the shared vocabulary: injection, authentication, authorization, secrets-management, supply-chain, insecure-workload-config, network-exposure, cryptography, input-validation, path-traversal, cross-site-scripting, ssrf, resource-management, logging-monitoring, data-exposure, tenant-isolation. Strict-mode validation warns on any other value. Framework mappings (OWASP Kubernetes K-IDs, CIS sections, STIG IDs, ASVS chapters) belong in description, asvs_references, and the CWE list — never in category. The rpm profile uses its own packaging taxonomy (see deltas). |
asvs_references |
No | ASVS requirement IDs. |
peach_references |
No | PEACH-P/E/A/C/H IDs (code profile, multi-tenant only). |
isolation_boundary |
No | Identifier of the tenant-facing interface this finding sits on — an interface name from peach_isolation_review.interfaces[] or an IF-n id from a service isolation review. |
isolation_dimensions |
No | Isolation-hardening dimension(s) the finding stresses; vocabulary shared with isolation-review.schema.json. Only set when the finding stresses a tenant boundary. |
passes |
No | Audit passes that independently surfaced this finding under dual-pass union-merge: [1,2] both passes, [2] second pass only. Single-pass reports omit it. |
dependency |
No | Supply-chain provenance for a dependency finding (ecosystem, package, version, advisory), so the finding is self-describing and does not depend on a sibling impact-analysis artifact. |
pqc_classification |
No | PQC relevance, assigned by table lookup against the PQC reference tables — never free-authored: shor-key-establishment | shor-signature | clock-2030-parameter | classically-broken | hndl-exposure | pqc-blocker-config | pqc-adoption. |
remediation_effort |
No | trivial | moderate | significant | blocked-external | xs | s | m | l | xl — PQC findings use the first four, derived deterministically per the PQC decision tree and emitted alongside pqc_classification; the size labels are the shared effort scale. |
validation_status |
No | confirmed | corrected | false_positive | not_verified | hardening — ledger-set, never at audit time. |
disposition |
No | Present only on cumulative reports (*-findings-current.json): both disposition axes, conflict and awaiting-signoff flags, and event history, derived from the ledger. Never authored in a baseline. |
effective_severity |
No | Present only on cumulative reports: equals a human severity override from the ledger when one exists, else the original severity. The original severity is never rewritten. |
source_findings |
No | Source scanner finding IDs / provenance refs (for routed findings: the producer's own id — e.g. a REG-* regression id — plus the producing report's path). |
origin |
No | Producer of a finding that entered the ledger as an event rather than from the baseline audit: verify-remediation | vuln-scan | create-fuzzing | validate-findings | validation-discovery | impact-analysis. Absent on findings authored by the baseline audit itself. |
| Key | Description |
|---|---|
dependency_audit |
prose + entries[] (package, version, status, notes). |
negative_results |
Areas verified clean: area, result, optional files. |
asvs_coverage |
ASVS chapter coverage entries. |
scanner_correlation |
Scanner tool results, including the judge decisions the rule-mining lane consumes. |
peach_isolation_review |
Always emitted on code and container profiles — see deltas. |
disposition_summary |
Cumulative reports only: counts per validity and resolution state, derived from the ledger. |
footer |
Free-text footer. |
metadata.audit_profile: "code".metadata.additional.deterministic_steps— an object recording the fate of every deterministic pre-scan so evidence quality is comparable across a batch. Keys:k8s-hardening,opengrep,gitleaks,osv-scanner,sbom-grype,priv-profile,fork-advisory-lag,route-guards,config-matrix; value"ran"or"skipped: <reason>"(e.g."skipped: opengrep not on PATH","skipped: no local checkout"). A skipped step means that section of the audit ran on manual review alone — downstream consumers (/mine-ledgerprecision,/verify-remediation) must not read absence of scanner facts as "scanner found nothing". The other profiles record their own step keys under the same object (see below).metadata.additional.precision_gates— the Precision Gate pass record, on all four scanning skills:crit_high_evaluatedcounts every critical/high candidate (filed or gated) andfired[]lists each gate that changed a candidate's disposition ({candidate, gate, action: downgraded|negative_results|dependency_audit}). An emptyfiredlist is legitimate; a report that files crit/high findings with no block is an incomplete run — the same contract asdeterministic_steps./vuln-scanstamps it on its supplement scans.metadata.additional.repo_status— repo liveness from the census (active | archived | moved | missing | unknown: <reason>, withstatus_since). Record-only: status never changes findings or severity; it exists so ledgers, dashboards, and sweeps can segment archived-but-shipping components instead of misreading them as unremediated backlog. The container profile records the source repo's status assource_repo_status.peach_isolation_reviewis always emitted:{"applicable": false, "rationale": …}for single-tenant components; fullinterfaces[]when multi-tenant.peach_referenceson findings only when applicable.categoryuses the shared kebab-case vocabulary above.metadata.loc_breakdownexpected on all new reports.
metadata.audit_profile: "rpm".metadata.additional.deterministic_stepskeys:sbom-grype,yara,crypto-audit.metadata.repositoryis the dist-git URL; record upstreamSource0, specVersion/Release, and applied patches undermetadata.additional.metadata.ref= the dist-git stream audited (e.g.c10s) withmetadata.ref_kind: "stream"— never"branch", which would misclassify the report as a branch re-audit and drop it from the census HEAD cuts. Reports land underanalysis-results/findings/<stream>/<component>/, the stream playing the product role.categoryuses the packaging taxonomyRPM01–RPM10(e.g.RPM02: Privileged File Modes & Capabilities).- Slug quirk: component names starting with a digit take an
RPM_prefix (389-ds-base→RPM_389_DS_BASE) because the ID regex anchors on[A-Z]. locations[].pathis the dist-git file (spec, patch, tekton) or a path in the prepared source tree.- No
peach_isolation_review(packaging repos are not tenant boundaries).
metadata.audit_profile: "container".metadata.additional.deterministic_stepskeys:k8s-hardening,priv-profile,yara.metadata.repositoryis the full image reference pinned by digest (<registry>/<namespace>/<image>@sha256:…); the tag audited, source-repo labels (vcs-url,vcs-ref), and base image go undermetadata.additional.metadata.commitcarries the manifest digest hex (the 64 hex chars aftersha256:) — the image analog of a git SHA. Finding-IDSHORTSHA= its first 7 chars, which satisfies the existing ID regex and the validator's SHORTSHA↔commit cross-check unchanged.metadata.toolsmust recordskopeo/syft/grypeversions and the grype vulnerability-DB build date — CVE results are only reproducible against a stated DB state.locations[].pathis a package URL (pkg:golang/…,pkg:rpm/…) for dependency findings, or an image pseudo-path (oci-config:User,oci-config:Env,layer:<sha256:…>) for image-configuration findings.categoryuses the shared kebab-case vocabulary (typicallysupply-chain,insecure-workload-config,secrets-management,data-exposure).dependency_auditis required in practice: every grype match lands there (package,version,status,noteswith fixed-in version); only reachable/shipping vulnerabilities are promoted to findings.- No
metadata.loc_breakdown(no source is measured); record SBOM package counts undermetadata.additional.sbom_packagesinstead. peach_isolation_reviewis still always emitted (the validator requires it from harness 0.15.0): normally{"applicable": false, "rationale": "image-level audit; tenant-isolation review lives in the source repo's code-profile report"}with a pointer to that report.
- Validate: python3 -m traust.cli reporting validate <report.json> — 0 errors required.
- Fingerprint: python3 -m traust.cli corpus finding-identity fingerprint <report.json> --write (deterministic; safe — fingerprints are outside the claim-hash fields).
- On a full re-audit of an already-audited repo:
python3 -m traust.cli corpus finding-identity rebaseline <old.json> <new.json> <layer.json>
so the disposition ledger's history transfers (fingerprint matches auto-map;
looser matches queue for human confirmation; unmatched old findings queue as
needs_review — never silently dropped). Rebaseline also migrates the layer's
claim-hash pins to the new baseline (superseded old ids with an alias or a
queued decision are unpinned; run
harnessing/4-triage/track-findings/scripts/baseline_claims.py recordafterwards to pin the new report's claims) and repointsmetadata.audit_commit.