ADR-0062.
the SARIF emission is validated against the official schema, and that is worth one dev dependency
Status: accepted 2026-08-27 Date: 2026-08-27 Closes: the open decision recorded in RWD-2026-0072.
Context
The 2026-08-26 audit found that nothing checked the SARIF runward emits. test/fixtures/ vendors
a NIST OSCAL schema and an in-toto schema, and each emission is validated against its own; the SARIF
had no net at all. The auditor ran the missing one by hand — 22 mission states against the official
OASIS schema, all valid — so the emitter is well formed today, and nothing would have said if it
stopped being.
A structural net shipped the same day (test/sarif-shape.js) and was recorded as not being schema
validation, because it is not: it checks the invariants a consumer relies on, not conformance.
Claiming otherwise would have been the exact overstatement the audit spent itself correcting.
The obstacle was mechanical. The OASIS SARIF 2.1.0 schema is draft-04; ajv 8 dropped draft-04
(id rather than $id, boolean exclusiveMinimum) and refuses to compile it. Full validation needs
ajv-draft-04. On a project that ships an attested CycloneDX SBOM on every release, adding a
dependency is not a detail, so it was left as an operator decision rather than taken silently.
Decision
Add ajv-draft-04 as a dev dependency and validate the emitted SARIF against the vendored official
OASIS schema, on four mission states.
What makes the trade a small one, checked rather than assumed:
- Same maintainer, already trusted here.
ajv-draft-04is published by theajv-validatororganisation — the same one that publishesajvandajv-formats, both already dev dependencies of this project. The marginal trust surface is one package from a maintainer whose code already runs in this test suite. - Dev-only, never shipped. It appears in
devDependencies; the published package's runtime dependencies remain@inquirer/prompts,chalk,commander. - Offline. The schema is vendored at
test/fixtures/sarif_schema.v2.1.0.jsonwith its source URL, vendoring date,$idand sha256 recorded intest/fixtures/README.md, exactly as the OSCAL and in-toto schemas are. The network-isolated CI leg is unaffected. - It detects. Verified before adoption, not after: the real emission validates; a wrong
version, a missingruns, and aleveloutside the SARIF enumeration are each refused.
The structural checks stay, and are not redundant. A schema cannot say that an
artifactLocation.uri RESOLVES in the checkout — RWD-2026-0041 shipped uris that no checkout held,
and every one of them was schema-valid. Nor that two runs on an unchanged tree are byte-identical.
Conformance and truthfulness are different questions; this file now asks both.
Consequences
- Positive. The three emissions with a published schema are now each held to it. The gap the audit named is closed as a gap rather than narrated as a limitation.
- Negative. One more dev dependency, and one more vendored third-party artifact to refresh when
OASIS revises the schema. The refresh procedure is in
test/fixtures/README.mdbeside the others. - Accepted. A schema is a floor, not a proof: a document can be perfectly conformant and still say something false about the tree. That is why the structural half exists.
Reevaluation trigger
Trigger set on: 2027-02-27.
The decision is wrong and must be revisited if: ajv-draft-04 stops being maintained by the
ajv-validator organisation (the whole basis of the trade); or OASIS publishes a SARIF revision in a
draft ajv supports natively, which would make the dependency removable; or the vendored schema starts
refusing an emission that consumers accept, which would mean the schema, not the emitter, is the
thing out of date.
Watched via: test/sarif-shape.js, .github/dependabot.yml, and the SBOM drift job.