Skip to content

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:

  1. the manifest digest;
  2. that the members match the manifest;
  3. each member's digest;
  4. that the organization matches --organization;
  5. that the embedded key equals the key you supplied;
  6. that the algorithm matches your key's type;
  7. 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, and facts such as the operating system, kernel, CPU and devices;
  • optionally scan, challenge and the hardware evidence.

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.