runward

RW™ · V0.37.1

Docs · Decisions · ADR-0062

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-04 is published by the ajv-validator organisation — the same one that publishes ajv and ajv-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.json with its source URL, vendoring date, $id and sha256 recorded in test/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 missing runs, and a level outside 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.md beside 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.

← All ADRs