cdx-validate

August 1, 2026 · View on GitHub

cdx-validate is a zero-install CLI and library that validates CycloneDX BOMs against structural, deep, and compliance checks.

It combines three layers:

  1. Schema validation – the existing CycloneDX JSON schema check shipped with cdxgen (lib/validator/bomValidator.js).
  2. Deep validation – metadata, purl, bom-ref and property consistency checks, also from lib/validator/bomValidator.js.
  3. Compliance rule packs – internal JavaScript rule catalog covering OWASP SCVS (all 87 controls across L1/L2/L3) and EU Cyber Resilience Act (CRA) SBOM expectations (8 controls). Rules that cannot be decided from a BOM alone (for example, "SBOMs are required for new procurements") are surfaced as manual-review items so coverage can still be tracked.

It is distributed as:

  • an npm entry point (cdx-validate — installed alongside cdxgen)
  • a single-executable-application (SEA) binary produced by the binary-builds GitHub workflow, alongside cdxgen, cdx-verify and cdx-sign.

Quick start

# Validate a BOM, print a table and a scorecard
cdx-validate -i bom.json

# Validate a protobuf BOM exported by cdxgen
cdx-validate -i bom.cdx

# Run only the SCVS Level 2 benchmark, emit SARIF for GitHub code scanning
cdx-validate -i bom.json --benchmark scvs-l2 -r sarif -o results.sarif

# Verify the signature and the structure in one go (JSON input only)
cdx-validate -i bom.json --public-key builder_public.pem --require-signature

# Machine-readable output for a CI system
cdx-validate -i bom.json -r json --fail-severity medium

CLI reference

FlagDefaultDescription
-i, --inputbom.jsonLocal SBOM JSON/protobuf path or an OCI reference (resolved natively without oras).
--platformPlatform to pass when the input is an OCI ref.
-r, --reportconsoleconsole, json, sarif, annotations.
-o, --report-filestdoutWrite the report to a file.
--schema / --no-schemaonToggle JSON-schema validation.
--deep / --no-deeponToggle metadata / purl / ref / property deep checks.
-b, --benchmarkallComma list of scvs, scvs-l1, scvs-l2, scvs-l3, cra.
--categoriesallComma list of rule categories (compliance-scvs, compliance-cra).
--min-severityinfoDrop findings below this severity from the report.
--fail-severityhighExit code 3 if any failing finding is ≥ this severity.
--include-manual / --no-include-manualonShow non-automatable manual-review findings.
--include-passoffInclude passing findings (useful for audits).
--public-keyPEM file. When set, verify the BOM signature for JSON or OCI BOM input. Local protobuf input is not signature-verifiable yet.
--require-signatureoffExit 4 if --public-key is supplied but verification fails.
--strictoffExit 2 when schema / deep validation fails.

Exit codes

CodeMeaning
0All checks passed, or no findings at/above --fail-severity.
1Configuration error (bad input, missing file, unknown reporter).
2Schema / deep validation failed (only with --strict).
3One or more failing findings at/above --fail-severity.
4Signature verification was required and failed.

Note: Local protobuf BOM input (.cdx, .cdx.bin, .proto) is supported for structure, deep, and compliance validation. JSF signature verification still requires the source JSON BOM because cdx-proto does not currently preserve signature blocks in protobuf form.


Output formats

console (default)

A three-part human-readable report:

  1. one-line summary (pass=… fail=… manual=… errors=…)
  2. per-benchmark scorecard table
  3. detail tables for Failing and Manual-review findings.

json

A stable, documented JSON schema:

{
  "schemaValid": true,
  "deepValid": true,
  "signatureVerified": null,
  "summary": { "total": 95, "pass": 28, "fail": 2, "manual": 65, "errors": 1 },
  "benchmarks": [ { "id": "scvs-l2", "name": "OWASP SCVS Level 2", "pass": 15, "fail": 1, "manual": 46, "scorePct": 94, "controls": [  ] } ],
  "findings": [
    { "ruleId": "SCVS-2.4", "status": "fail", "severity": "high", "standard": "SCVS", "standardRefs": ["SCVS-2.4"], "message": "BOM is not signed.", "mitigation": "Use cdx-sign.", "locations": [], "evidence": null }
  ]
}

sarif

A valid SARIF 2.1.0 log suitable for github/codeql-action/upload-sarif:

- uses: github/codeql-action/upload-sarif@v3
  with:
    sarif_file: cdx-validate.sarif
    category: cdx-validate

Each finding becomes a SARIF result with ruleId, level (error/warning/ note mapped from severity) and properties.standardRefs. Rules are deduplicated into the tool.driver.rules array.

annotations

Returns the original BOM with CycloneDX annotations[] appended, one per finding. Properties follow the cdx:validate:* namespace (e.g. cdx:validate:ruleId, cdx:validate:standardRefs, cdx:validate:mitigation). This is the same shape the bom-audit engine emits, making the two streams directly mergeable.


Compliance rule catalogs

OWASP SCVS

All 87 controls from OWASP SCVS (V1–V6) are present. For each control the catalog records:

  • the canonical id (SCVS-1.1, SCVS-6.7, …),
  • the levels at which it is required (L1 / L2 / L3),
  • whether it is automatable from a BOM alone,
  • a severity for failing automatable controls.

Automatable highlights (non-exhaustive): SCVS-1.1 (components + versions), SCVS-1.7 (machine-readable identifiers), SCVS-2.1 (CycloneDX format), SCVS-2.3 (unique identifier), SCVS-2.4 (signed), SCVS-2.7 (timestamp), SCVS-2.9 (dependency graph), SCVS-2.11 (asset metadata), SCVS-2.12 (valid purls), SCVS-2.13 (purl preferred), SCVS-2.14/SCVS-5.12 (license data), SCVS-2.15 (SPDX expressions), SCVS-2.16 (copyright), SCVS-2.18/SCVS-3.18 (hashes), SCVS-3.20 (no orphans), SCVS-5.11 (inventory count), SCVS-6.3 (pedigree on modified components).

Non-automatable controls (for example, "package manager enforces MFA") are reported as status: manual, severity: info, with the upstream description included so reviewers can assess them manually.

Some manual SCVS controls now include an explicit cdx-audit evidence hint. These are the controls where predictive audit can materially help a reviewer inspect repository workflows, provenance, publishing, or source-correlation signals even though the control still cannot be proven from the BOM alone.

Current mappings include:

  • SCVS-2.8 — SBOM is analyzed for risk
  • SCVS-3.3 — Application uses CI build pipeline
  • SCVS-3.6 — No arbitrary code execution
  • SCVS-4.10 — Version-to-source correlation
  • SCVS-4.11 — Package repository auditability
  • SCVS-6.1 — Point of origin verifiable
  • SCVS-6.2 — Chain of custody auditable

For these controls, use the same SBOM as input to predictive audit:

cdx-audit --bom bom.json --scope required

Then attach the resulting workflow, provenance, publishing, and repository findings as supporting evidence for the manual review.

EU Cyber Resilience Act (CRA)

Eight controls extracted from CRA Annex I and the ENISA SBOM guidance:

IdCheck
CRA-MIN-001metadata.supplier.name (or manufacturer.name) is declared.
CRA-MIN-002Manufacturer has a contact (email / phone / URL).
CRA-MIN-003BOM has a serialNumber of shape urn:uuid:<uuid>.
CRA-MIN-004BOM has a non-empty dependencies[] covering ≥ 75 % of components.
CRA-MIN-005metadata.timestamp is present and ISO-8601 parseable.
CRA-MIN-006Every component has a purl, cpe, or swid.tagId.
CRA-MIN-007Every component declares license information.
CRA-MIN-008metadata.tools records the generating tool(s).

The current CRA catalog intentionally focuses on minimum SBOM content and manufacturer-contact expectations. The new predictive-audit capabilities are therefore not currently mapped to an additional CRA-MIN-* rule. You can still use cdx-audit as supplementary evidence during a broader CRA review, but cdxgen does not currently claim a direct CRA control mapping for those workflow/provenance checks.


Benchmark scoring

For every benchmark, cdx-validate reports:

  • total controls
  • passing controls
  • failing controls
  • manual-review controls
  • automatable score = pass / (pass + fail) expressed as a percentage

Manual controls are excluded from the percentage (they are informational only) but are counted separately so you can track how much of the catalog still needs manual attention.


Library API

import {
  validateBomAdvanced,
  shouldFail,
} from "@cdxgen/cdxgen/lib/validator/index.js";
import { render } from "@cdxgen/cdxgen/lib/validator/reporters/index.js";

const bom = JSON.parse(await readFile("bom.json", "utf8"));
const report = validateBomAdvanced(bom, {
  benchmarks: ["scvs-l2", "cra"],
  minSeverity: "low",
});

console.log(
  render("sarif", report, { toolName: "cdx-validate", toolVersion: "VERSION" }),
);

const { shouldFail: fail, reason } = shouldFail(report, {
  failSeverity: "high",
});
if (fail) throw new Error(reason);

Reporters are intentionally implemented as independent modules under lib/validator/reporters/*. They accept the same report object, so bom-audit and any future validator can reuse them without duplication.


See also

  • BOM_AUDIT.md — the YAML/JSONata audit engine shipped with cdxgen. Its output shape is deliberately compatible with cdx-validate's SARIF and annotations reporters.
  • LESSON7.md — end-to-end tutorial using cdx-validate.
  • LESSON3.md and LESSON6.md — signing workflows that pair naturally with --public-key / --require-signature.