SBOM Schema Validation
Audience: scanner and fixture maintainers who add, update, or debug SBOM test fixtures in Stella Ops.
This document describes how Stella Ops validates SBOM (Software Bill of Materials) fixtures against official JSON schemas, how to run that validation locally, and how to troubleshoot the common failures.
Status (reconciled against source): The standalone schema-validation CI flow described below is currently archived — the workflows live under
.gitea/workflows-archived/(schema-validation.yml,determinism-gate.yml), not under.gitea/workflows/, so they do not run on the active PR/push gate today. The validation scripts (.gitea/scripts/validate/validate-*.sh) and the JSON schemas (docs/schemas/) still exist and can be run locally. CycloneDX schema validation for downstream/consumer pipelines is exposed as the reusable workflow template.gitea/workflow-templates/sbom-canonicalization-check.yml. Treat the per-format CI jobs in this document as a reference and roadmap until the workflows are restored.
Overview
Stella Ops validates SBOM fixtures against official JSON schemas to catch schema drift before runtime. This ensures:
- CycloneDX fixtures are compliant with the official schema (the runtime validator,
StellaOps.Scanner.Validation.CycloneDxValidator, supports schema versions 1.4, 1.5, 1.6, and 1.7; the bundled schema iscyclonedx-bom-1.7.schema.json) - SPDX 3.0.1 fixtures meet specification requirements
- OpenVEX fixtures follow the 0.2.0 specification (see the OpenVEX caveat in the table below)
- Invalid fixtures are detected early in the validation pipeline
Supported Formats
The canonical schema directory is docs/schemas/ (copies of the CycloneDX and SPDX schemas are also mirrored under docs/modules/sbom-service/schemas/).
| Format | Version | Schema Location | Validator |
|---|---|---|---|
| CycloneDX | 1.7 | docs/schemas/cyclonedx-bom-1.7.schema.json | sbom-utility |
| SPDX | 3.0.1 | docs/schemas/spdx-jsonld-3.0.1.schema.json | pyspdxtools / check-jsonschema |
| OpenVEX | 0.2.0 | docs/schemas/openvex-0.2.0.schema.json (MISSING — not present in repo; the validate-vex.sh script and archived workflow reference it but the file is absent) | ajv-cli |
Note (verified against source): The archived workflows and the
validate-sbom.shscript still hard-codedocs/schemas/cyclonedx-bom-1.6.schema.jsonas the schema path, but nocyclonedx-bom-1.6.schema.jsonexists — onlycyclonedx-bom-1.7.schema.jsonships. The CycloneDX runtime validator (CycloneDxValidator.cs) does not pass an explicit--schema; it invokessbom-utility validate --input-file <file> --format jsonand relies on sbom-utility’s built-in schema selection. Thesbom-utilityversion pinned by the scripts and archived workflows is0.16.0.
CI Workflows
Archived. Both workflows below currently reside under
.gitea/workflows-archived/and are not wired into the active CI gate. Their trigger paths and schema references (below) are reproduced as-authored; note the stalecyclonedx-bom-1.6.schema.jsonpath inside them (see the Supported Formats note above).
Schema Validation Workflow
File: .gitea/workflows-archived/schema-validation.yml (archived; formerly .gitea/workflows/schema-validation.yml)
Runs on:
- Pull requests touching
src/__Tests/__Datasets/golden-corpus/**,src/Scanner/**,docs/modules/**/schemas/**, orscripts/validate-*.sh - Push to
mainbranch
Jobs:
- validate-cyclonedx - Validates all CycloneDX fixtures
- validate-spdx - Validates all SPDX 3.0.1 fixtures
- validate-vex - Validates all OpenVEX 0.2.0 fixtures
- validate-negative - Verifies invalid fixtures are correctly rejected
- summary - Aggregates results
Determinism Gate Integration
File: .gitea/workflows-archived/determinism-gate.yml (archived; formerly .gitea/workflows/determinism-gate.yml)
The determinism gate runs a schema-validation job before the determinism-gate job; the drift-check job fails the workflow if schema validation fails, blocking the determinism check.
To skip schema validation (e.g., during debugging) the workflow exposes a workflow_dispatch input:
# Via workflow_dispatch input
skip_schema_validation: true
Fixture Directories
The archived schema-validation workflow scans these directories for SBOM fixtures:
| Directory | Purpose |
|---|---|
src/__Tests/__Benchmarks/golden-corpus/ | Golden reference fixtures for reproducibility testing |
src/__Tests/fixtures/ | Test fixtures for unit and integration tests |
src/__Tests/__Datasets/seed-data/ | Initial seed data for development environments |
src/__Tests/__Benchmarks/vex-lattice/ | OpenVEX fixtures (scanned by the validate-vex job only) |
src/__Tests/fixtures/invalid/ | Excluded from positive validation - Contains intentionally invalid fixtures used by the validate-negative job for negative testing |
Note: the local
validate-sbom.sh --allhelper scans onlysrc/__Tests/__Benchmarks/golden-corpus/, andvalidate-vex.sh --allscansbench/golden-corpus,bench/vex-lattice, anddatasets/— these script defaults differ from the workflow’s fixture-directory list above.
Local Validation
Using the Validation Scripts
The scripts live under .gitea/scripts/validate/ (run them from there or by absolute path):
# Validate a single CycloneDX file
./.gitea/scripts/validate/validate-sbom.sh path/to/sbom.json
# Validate all CycloneDX fixtures in src/__Tests/__Benchmarks/golden-corpus/
# (--all takes no directory argument; the search root is fixed in the script)
./.gitea/scripts/validate/validate-sbom.sh --all
# Validate SPDX file
./.gitea/scripts/validate/validate-spdx.sh path/to/sbom.spdx.json
# Validate OpenVEX file
./.gitea/scripts/validate/validate-vex.sh path/to/vex.openvex.json
Each script auto-installs its underlying tool if missing (validate-sbom.sh → sbom-utility; validate-vex.sh → ajv-cli + ajv-formats; validate-spdx.sh → spdx-tools / check-jsonschema). Both validate-sbom.sh and validate-vex.sh accept an optional --schema <path> override.
Using sbom-utility Directly
# Install sbom-utility (version pinned by the Stella Ops scripts/workflows)
curl -sSfL "https://github.com/CycloneDX/sbom-utility/releases/download/v0.16.0/sbom-utility-v0.16.0-linux-amd64.tar.gz" | tar xz
sudo mv sbom-utility /usr/local/bin/
# Validate against the bundled CycloneDX schema
sbom-utility validate --input-file sbom.json --schema docs/schemas/cyclonedx-bom-1.7.schema.json
# Or let sbom-utility pick the schema from the document's specVersion
# (this is what the runtime CycloneDxValidator does)
sbom-utility validate --input-file sbom.json --format json
Troubleshooting
Common Validation Errors
1. Invalid specVersion
Error: enum: must be equal to one of the allowed values
Cause: The specVersion field contains an invalid or unsupported version.
Solution:
// Invalid
"specVersion": "2.0"
// Valid
"specVersion": "1.6"
2. Missing Required Fields
Error: required: must have required property 'name'
Cause: A component is missing required fields.
Solution: Ensure all components have required fields:
{
"type": "library",
"name": "example-package",
"version": "1.0.0"
}
3. Invalid Component Type
Error: enum: type must be equal to one of the allowed values
Cause: The component type is not a valid CycloneDX type.
Solution: Use valid types: application, framework, library, container, operating-system, device, firmware, file, data
4. Invalid PURL Format
Error: format: must match format "purl"
Cause: The package URL (purl) is malformed.
Solution: Use correct purl format:
// Invalid
"purl": "npm:example@1.0.0"
// Valid
"purl": "pkg:npm/example@1.0.0"
CI Failure Recovery
- Identify the failing fixture: Check CI logs for the specific file
- Inspect the fixture: open
path/to/failing-fixture.json - Run local validation:
./.gitea/scripts/validate/validate-sbom.sh path/to/failing-fixture.json - Fix the schema issues: Use the error messages to guide corrections
- Verify the fix: Re-run local validation
- Push and verify CI passes
Negative Test Failures
If negative tests fail with “UNEXPECTED PASS”:
- The invalid fixture in
src/__Tests/fixtures/invalid/somehow passed validation - Review the fixture to ensure it contains actual schema violations
- Update the fixture to include more obvious violations
- Document the expected error in
src/__Tests/fixtures/invalid/README.md
The existing negative fixtures are
cyclonedx-wrong-version.json,cyclonedx-missing-required.json, andcyclonedx-invalid-component.json; their expected errors are tabulated insrc/__Tests/fixtures/invalid/README.md.
Adding New Fixtures
Valid Fixtures
- Create fixture in appropriate directory (
src/__Tests/__Datasets/golden-corpus/,src/__Tests/fixtures/) - Ensure it contains the format marker:
- CycloneDX:
"bomFormat": "CycloneDX" - SPDX:
"spdxVersion"or"@context"with SPDX - OpenVEX:
"@context"withhttps://openvex.dev/ns(or an"@type"ofhttps://openvex...)
- CycloneDX:
- Run local validation before committing
- CI will automatically validate on PR once the schema-validation workflow is restored from
.gitea/workflows-archived/
Invalid Fixtures (Negative Testing)
- Create fixture in
src/__Tests/fixtures/invalid/ - Add
$commentfield explaining the defect - Update
src/__Tests/fixtures/invalid/README.mdwith expected error - Ensure the fixture has the correct format marker
- The
validate-negativejob verifies it fails validation
Schema Updates
When updating schema versions:
- Place the new schema in the canonical
docs/schemas/directory (keep thedocs/modules/sbom-service/schemas/mirror in sync). Also update theSCHEMA/DEFAULT_SCHEMApath references inside.gitea/scripts/validate/validate-*.shand the archived workflows — they currently still point at the obsoletecyclonedx-bom-1.6.schema.json. - Update
SBOM_UTILITY_VERSIONin the scripts/workflows if needed (currently0.16.0) - Run full validation to check for new violations
- Update documentation with the new version
- Update reproducibility.md with schema version changes (its Schema Versions table currently lists CycloneDX 1.6 and likewise needs reconciling to 1.7)
References
Related Stella Ops docs
- Reproducibility — schema-version table and determinism guarantees
- Determinism gates — the gate that schema validation feeds into
- Test suite overview — where SBOM validation sits in the wider test strategy
