Skip to main content

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 → the scan.tool / scan.tool.version envelope facts.
  • runs[].tool.driver.rules[].properties.security-severity → a rule-level score, used as a fallback for results that reference that rule by ruleId.
  • 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…ClassFact space
no kindvulnerability findingvuln.*
an explicit kindgovernance verdictpolicy.*

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:

  1. kind present (a governance verdict):
    • kind: "pass"policy.passed.
    • any other kind ("fail", …) → policy.<severity> — severity from the result's security-severity, else its rule's, else its level.
  2. kind absent (a vulnerability finding):
    • vuln.<severity> — severity from security-severity, else its rule's, else its level.

Severity from a CVSS security-severity score:

scorebucket
≥ 9.0critical
≥ 7.0high
≥ 4.0medium
< 4.0low

Severity fallback from level when no security-severity is present:

levelbucket
errorhigh
warningmedium
note / nonelow
(other / absent)unknown
note

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.unknown
  • scan.policy.critical, scan.policy.high, scan.policy.medium, scan.policy.low, scan.policy.unknown
  • scan.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.kind on every verdict — "fail" for a breach, "pass" for a satisfied check (with level: "none", per SARIF 2.1.0);
  • carry the verdict severity as a CVSS-scaled properties.security-severity so houba buckets it (policy.criticalpolicy.low);
  • use only fail / pass — houba treats every non-pass kind as a failed verdict.

A vulnerability scanner needs no changes: omit kind, carry security-severity, and houba buckets findings into vuln.*.

See also