Report and evidence formats¶
The JSON files the CLI writes and the files it verifies, field by field. There's no separate JSON Schema: these shapes are defined by the code that writes and checks them. Read every format defensively. A field marked optional may be absent, and a newer version may add keys, so ignore keys you don't recognise.
All hashes are SHA-256. Where a file is hashed or signed, it's first written as canonical
JSON: keys sorted, separators , and : with no spaces.
Scan report¶
Written by untrust scan --output report.json (or --json to standard output). This is
the file you upload to the console. Output and exit codes shows the
text form.
| Key | Type | Contents |
|---|---|---|
schema_version |
integer | 1 |
version |
string | Scanner version. |
timestamp |
string | When the scan ran, ISO 8601 UTC. |
simulated |
boolean | true for --demo. |
scan_mode |
string | active, passive (--read-only), demo, or collector for untrust collector scan. |
target |
object | The identifiers you passed: bucket, KMS key, instance, region, account, project, VM, key vault and so on. Only the ones you passed are filled in. |
summary |
object | total, pass, fail, skip, error. |
findings |
array | One object per check, below. |
coverage |
object | What was and wasn't assessed, below. |
inventory |
object | Optional. The read-only inventory, below. Absent with --no-inventory. |
findings[]¶
| Field | Values |
|---|---|
check_id |
For example KMS-01. Each has a page under Checks. |
title |
The check's title. |
status |
PASS, FAIL, SKIP or ERROR. See Verdicts. |
severity |
critical, high, medium, low or info. |
assurance |
probed, report-derived, operator-declared, or null. |
assurance_note |
Optional. Set when a report-derived pass depends on a signature that this check couldn't verify. |
summary |
One line saying what was found. |
remediation |
How to fix a failure. |
evidence |
What the check read, such as policy statements or rule summaries. Its shape depends on the check. |
coverage¶
| Field | Contents |
|---|---|
catalog_check_ids |
Every check the platform has. |
reported_check_ids |
The checks this report has a result for. |
absent_check_ids |
Catalog checks with no result. |
mode_excluded_check_ids |
Checks --read-only skipped. Their findings are SKIP with evidence.reason scan_mode. |
scope_complete |
Whether every catalog check reported. |
meaning |
A fixed sentence explaining that the coverage block describes the scan's scope. |
inventory¶
Collected for nitro, sev-snp and azure-cvm when you write JSON. It never changes a
finding.
| Field | Contents |
|---|---|
schema |
1 |
provider |
aws, gcp or azure. |
collected_at |
When it was collected. |
instance |
The instance or VM. |
access, access_scope |
Who can reach the bucket, key or instance, and how far the search went. |
events, events_window_days |
Recent audit events, over 7 days. |
discovered |
Related resources it found. |
errors |
Calls that failed. The CLI prints Inventory collected with <n> gap(s); see inventory.errors. |
capped, complete |
Whether any list was cut short, and whether the inventory is complete. |
| AWS only | security_groups, roles, users, discovered_regions |
| GCP only | firewalls, service_accounts |
| Azure only | network_rules, identities, credentials |
Review before sharing
The report and its inventory contain resource identifiers, account IDs, principals, source IP addresses and audit events. They never contain your cloud credentials.
Drift report¶
Written by untrust diff OLD NEW --json.
| Key | Contents |
|---|---|
same_target |
Whether both reports scanned the same target. The account ID and attestation token path are ignored in the comparison. |
new |
Checks that were PASS and are now FAIL. |
fixed |
Checks that were FAIL and are now PASS. |
severity_changed |
Failing checks whose severity changed. Each has before. |
still_failing |
Checks that fail in both. |
not_comparable |
Check IDs that are SKIP, ERROR or missing on one side while FAIL on the other. |
Items in new, fixed, severity_changed and still_failing are
{check_id, title, severity}, sorted by severity, then ID.
Build receipt¶
Written by untrust buildscan --output. --sarif writes the same findings as SARIF 2.1.0,
each with an untrustFingerprint/v1 fingerprint.
| Key | Contents |
|---|---|
schema_version, kind |
1, untrust-build |
scanner_version, target, timestamp |
Scanner version, --target, and when it ran. |
commit, run_id |
The labels you passed: a full commit SHA and a run ID. |
artifact |
name, sha256 of the whole input, bytes, format (eif, rootfs-tar or vm-bundle), and optionally image with ref and digest from --image. |
provenance |
Always Commit and run identity supplied by caller; not cryptographically verified. |
findings[] |
rule, category, attack_class, cwe, severity, confidence (confirmed or possible), location, detail, snippet, source_url, remediation, fingerprint, evidence, assurance, boundary. |
coverage |
first_party_files, rulepack_sha256, rules_executed, external_analyzers, hardware_validation, optional incomplete, and custom_rules when you passed them. VM bundles add the boot files, firmware and computed launch_measurement. |
errors |
Why the analysis was incomplete, if it was. |
started, finished |
Epoch seconds. |
trigger, ref, base_ref, run_url, ref_type, actor |
Optional run metadata from GitHub Actions or GitLab CI/CD. |
gate |
status (PASS, FAIL or ERROR), blocking_findings, custom_blocking, fail_on, policy, meaning. |
The receipt isn't signed. When it's uploaded from CI, the service ties it to the pipeline's identity and recomputes the gate.
IaC receipt¶
Written by untrust iacscan --output.
| Key | Contents |
|---|---|
schema_version, kind |
1, untrust-iac |
analyzer |
terraform-plan-1, cloudformation-1, arm-template-1, bicep-1, gcp-deployment-manager-1 or kubernetes-manifest-1. |
ruleset_version |
Version of the IaC rule set. |
source_name, source_sha256 |
The input file and its hash. |
assurance |
States that the commit and run labels are supplied by the caller. |
ci |
commit, run_id, and optional run metadata (started, finished, trigger, ref, base_ref, run_url, ref_type, actor). |
plan_incomplete |
Whether the plan had values it couldn't resolve. |
summary |
PASS, FAIL, SKIP, resources, entries, excluded. |
findings[] |
rule_id, title, address, pointer, status, observed, expected, severity, reason, remediation, optional source (file, line, best_effort: true from --source-dir) and detail. |
excluded[] |
Resources left out, with address, type and reason. |
analysis_error |
Optional. Why the analysis stopped. |
gate |
status, fail_on, blocking_findings, custom_blocking, reasons, scope. |
Registry report¶
Written by untrust registry scan --output.
| Key | Contents |
|---|---|
schema |
untrust.registry/v1 |
generator |
untrust <version> |
observed, valid_until |
When it was observed, and until when it counts as fresh (the policy's freshness_hours). |
policy |
version, sha256, freshness_hours, trust_roots of the trust policy used. |
collection_complete |
Whether every artifact was reached. |
summary |
artifacts, verified, failed, unassessed. |
artifacts[] |
ref, source (ecr, artifact-registry, ghcr, acr, oci, s3-eif), registry, repository, tag, digest, media_type, observed, trust_roots, access, facts, signature, measurement (PCR0, 1, 2 and 8 for enclave images), platforms, provenance, checks, verdict. |
Each item in checks is {check_id, title, severity, status, summary, remediation,
evidence}, for the registry checks.
Detection events¶
Written by untrust detect --output and untrust registry detect --output as
{"events": [...]}. Every event has:
| Field | Contents |
|---|---|
rule_id, title, severity |
The detection rule. |
resource |
What the event touched. |
principal, action |
Who did what. |
observed |
Epoch seconds. |
evidence |
The matched log record's details. On AWS: event_id, event_source, event_name, source_ip, region, user_agent, the related posture_check, the request, and for some rules an evaluated list of {field, expected, observed, verdict}. |
source_id |
ID of the source log record. |
On --platform nitro, --coverage-output writes
{"window": <until>, "rules": [{rule_id, evaluated, matched, ok}]} to show which rules ran.
Evidence pack¶
One JSON file with a manifest, the members it lists, and optionally a signature.
Verify evidence offline shows how to request and check
one.
| Key | Contents |
|---|---|
manifest |
Below. |
members |
Map of member name to content. |
signature, signature_algorithm, public_key |
Present when signed. Packs the console signs use ecdsa-p256-sha256; pack verify takes the algorithm from the type of the key you supply, never from the pack. |
The manifest holds:
| Field | Contents |
|---|---|
organization |
The organization the pack belongs to. |
policy_id, policies or framework |
Exactly one: the policy, policies or compliance framework (id, version) the pack covers. |
window |
start and end. |
generated |
When it was built. |
members[] |
name, kind (policies, decisions, findings, exceptions, compliance, evidence), digest, observed_only, stale, optional policy. |
unavailable |
Member kinds that couldn't be included, with the reason. |
caveats |
observed_only and stale member lists. |
manifest_digest |
Hash of the manifest without this field. |
A member's digest is the hash of {"kind": ..., "content": ...}. The signature is over
the manifest_digest string. untrust pack verify checks, in order:
- the manifest digest;
- that the members match the manifest;
- each member's digest;
- that the organization matches
--organization; - that the embedded key equals the key you supplied;
- that the algorithm matches your key's type;
- the signature.
Key-release ledger entries¶
A decided entry is a decision untrust made through untrust release verify. An
observed entry is a release found in a cloud log by untrust ledger collect.
| Field | Decided | Observed |
|---|---|---|
id, source, time |
Yes (source is decided) |
Yes (source is observed) |
key, requester, workload |
Yes | Yes |
verdict |
released, denied or released-against-policy |
released or denied |
flags, policies_checked, evidence_ref, note |
Yes | Yes. evidence_ref.kind is cloudtrail, gcp-audit or azure-skr. |
receipt |
{status: "unsigned", reason} or {status: "signed", algorithm, public_key, signature} |
null |
org_id, entry_version |
Yes | |
chain_prev, chain_hash |
Yes |
chain_hash is the hash of the entry without chain_hash and the receipt's signature.
chain_prev is the previous entry's chain_hash, and the first entry chains to 64 zeros.
A signed receipt signs the chain_hash.
untrust ledger verify checks an exported decided entry's hash, its organization binding
and, if signed, its signature against the key you supply. For an observed entry, it
rebuilds the entry from the raw log record and compares them. A single exported entry
can't prove its link to the previous one; the console checks the whole chain.
untrust ledger collect writes {"records": [...]}: the raw log records, which the
service turns into observed entries.
Collector reports¶
untrust collector run sends a signed host report:
organization,fleet_id,collector_id;observed,version;host, andfactssuch as the operating system, kernel, CPU and devices;- optionally
scan,challengeand the hardwareevidence.
The collector signs the canonical JSON with the Ed25519 key it created at enrollment. Hardware evidence is bound to the service's nonce and the collector's public key. The private key never leaves the host.
untrust collector scan writes a scan report with scan_mode
collector. That report isn't signed.