SARIF ingestion profile
The contract any analyzer writes against so houba ingests its report correctly. houba accepts
SARIF 2.1.0 as the sole scan-report format (houba attach … --report <file>) and summarizes each
result into a tool-agnostic fact space attached to the image digest. houba keys on standard SARIF
fields only — never on the producing tool — so any analyzer that follows this profile is
supported without a houba change.
What houba reads
From the report:
runs[].tool.driver.name/.version→ thescan.tool/scan.tool.versionenvelope facts.runs[].tool.driver.rules[].properties.security-severity→ a rule-level score, used as a fallback for results that reference that rule byruleId.runs[].results[]→ each result is classified into exactly one fact key (below).
The raw report travels verbatim as the OCI referrer blob; houba never rewrites it.
Finding classes — kind is the discriminator
houba splits results into two classes by the standard SARIF result.kind:
| Result has… | Class | Fact space |
|---|---|---|
no kind | vulnerability finding | vuln.* |
an explicit kind | governance verdict | policy.* |
An explicit kind wins over any CVSS security-severity the result carries: a result that
declares a kind is a verdict, even when it is also scored. This is how a policy / posture analyzer
(license, EOL, best-practice, compliance) keeps its verdicts out of the vulnerability counts.
Classification rules
For each result:
kindpresent (a governance verdict):kind: "pass"→policy.passed.- any other
kind("fail", …) →policy.<severity>— severity from the result'ssecurity-severity, else its rule's, else itslevel.
kindabsent (a vulnerability finding):- →
vuln.<severity>— severity fromsecurity-severity, else its rule's, else itslevel.
- →
Severity from a CVSS security-severity score:
| score | bucket |
|---|---|
| ≥ 9.0 | critical |
| ≥ 7.0 | high |
| ≥ 4.0 | medium |
| < 4.0 | low |
Severity fallback from level when no security-severity is present:
level | bucket |
|---|---|
error | high |
warning | medium |
note / none | low |
| (other / absent) | unknown |
security-severity is a string-encoded numeric CVSS score carried in properties (the GitHub
convention). Emit it on every scored result — houba's buckets are CVSS bands, finer than SARIF's
coarse level.
Published facts
Every attached scan referrer carries these annotation keys, each prefixed with the configured label
prefix (default io.houba) as {prefix}.scan.<key>. An empty prefix emits no summary annotations.
Envelope:
scan.tool,scan.format,scan.timestamp,scan.subject(always present)scan.tool.version(when the report declares one)
SARIF facts (counts, string-encoded):
scan.vuln.critical,scan.vuln.high,scan.vuln.medium,scan.vuln.low,scan.vuln.unknownscan.policy.critical,scan.policy.high,scan.policy.medium,scan.policy.low,scan.policy.unknownscan.policy.passed
The same facts populate the signed https://houba.dev/predicate/scan/v1 attestation summary (see the
scan-predicate schema).
The --fail-on gate
houba attach --fail-on <severity> gates only on vuln.* counts (severity order
critical > high > medium > low > unknown). Governance verdicts (policy.*) are recorded in the
stamp but never affect the exit code.
Minimal example
{
"version": "2.1.0",
"$schema": "https://json.schemastore.org/sarif-2.1.0.json",
"runs": [
{
"tool": { "driver": { "name": "example-analyzer", "version": "1.2.0" } },
"results": [
{ "ruleId": "CVE-2024-0001", "properties": { "security-severity": "9.8" } },
{
"ruleId": "license/gpl-in-proprietary",
"kind": "fail",
"level": "error",
"properties": { "security-severity": "9.0" }
},
{ "ruleId": "eol/base-image", "kind": "pass", "level": "none" }
]
}
]
}
Resulting facts: vuln.critical=1 (the kind-less CVE), policy.critical=1 (the scored verdict),
policy.passed=1 (the satisfied check); every other count is 0.
Producer guidance
To emit governance verdicts (not vulnerabilities):
- set
result.kindon every verdict —"fail"for a breach,"pass"for a satisfied check (withlevel: "none", per SARIF 2.1.0); - carry the verdict severity as a CVSS-scaled
properties.security-severityso houba buckets it (policy.critical…policy.low); - use only
fail/pass— houba treats every non-passkind as a failed verdict.
A vulnerability scanner needs no changes: omit kind, carry security-severity, and houba
buckets findings into vuln.*.
See also
- Attach a scan result — the task this profile feeds.
- scan-predicate schema — the signed
scan/v1attestation. - ADR 0039 — SARIF
kinddiscriminates a policy verdict from a vulnerability finding, which supersedes ADR 0027.