STP
SBOM Observer/

Validate Quality

Assessing SBOM completeness, accuracy, and fitness for operational use

Receiving SBOM from supplier is insufficient—you need trustworthy SBOM. Low-quality SBOMs create false confidence worse than having no SBOM. SBOM claiming product contains only 15 components when it actually contains 150 creates dangerous blind spot. SBOM listing wrong component versions leads to incorrect vulnerability assessments. SBOM missing license information undermines compliance efforts. Garbage in, garbage out applies to software transparency as much as any other data practice.

Quality validation transforms SBOM from interesting document to operational data source. Before incorporating supplier SBOM into vulnerability management, incident response, or compliance reporting, verify it meets minimum quality standards. Identify gaps requiring supplier remediation. Document quality concerns affecting how SBOM can be used. Build quality metrics tracking supplier maturity over time.

Quality Dimensions

SBOM quality isn't single metric—it's multi-dimensional assessment covering completeness, accuracy, format compliance, and operational fitness.

Completeness

Does SBOM enumerate all components present in software?

Component enumeration completeness: Product with 200 dependencies shouldn't have SBOM listing 20. Gross incompleteness suggests automated generation failed, transitive dependencies were omitted, or supplier provided placeholder rather than real SBOM.

Heuristic checks:

  • Modern web application (React/Angular/Vue): Expect 100+ npm dependencies
  • Java enterprise application: Expect 50+ Maven dependencies
  • Python data science application: Expect 30+ pip packages
  • Microservice with minimal dependencies: Might legitimately have 10-20 components

Red flags:

  • Component count under 10 for complex application (almost certainly incomplete)
  • Round numbers suggesting manual counting rather than automated generation (exactly 50 components is suspicious)
  • Missing common dependencies for technology stack (Node.js app with no Express/Fastify/Koa suggests gaps)

Transitive dependency depth: Complete SBOMs include full transitive dependency trees, not just direct dependencies. Check maximum dependency depth: shallow trees (1-2 levels) suggest incomplete enumeration. Modern applications typically have 3-5+ levels of transitive dependencies.

Metadata completeness: Beyond component names and versions, complete SBOMs include:

  • Package URLs (PURLs) for unambiguous identification
  • License information
  • Supplier/author data
  • Component relationships (dependency tree structure)
  • Hashes for integrity verification

Completeness scoring:

def score_sbom_completeness(sbom):
    """Calculate completeness score 0-100"""
    score = 0
    components = sbom.get('components', [])

    # Component count sanity check (30 points)
    if len(components) >= 10:
        score += 30
    elif len(components) >= 5:
        score += 15

    # PURL presence (20 points)
    components_with_purl = sum(1 for c in components if c.get('purl'))
    purl_percentage = (components_with_purl / len(components)) * 100 if components else 0
    score += (purl_percentage / 100) * 20

    # License information (20 points)
    components_with_license = sum(1 for c in components if c.get('licenses'))
    license_percentage = (components_with_license / len(components)) * 100 if components else 0
    score += (license_percentage / 100) * 20

    # Version information (15 points)
    components_with_version = sum(1 for c in components if c.get('version'))
    version_percentage = (components_with_version / len(components)) * 100 if components else 0
    score += (version_percentage / 100) * 15

    # Relationship/dependency information (15 points)
    has_dependencies = 'dependencies' in sbom or any(c.get('dependencies') for c in components)
    score += 15 if has_dependencies else 0

    return round(score, 1)

Scores above 80 indicate good completeness. Scores 60-80 suggest usable but imperfect. Scores below 60 indicate significant gaps requiring supplier remediation.

Accuracy

Does SBOM correctly describe software composition?

Version accuracy: Most critical accuracy dimension. Wrong component versions lead to incorrect vulnerability assessments. SBOM claims OpenSSL 3.0.8 (patched) but deployed software actually contains 3.0.7 (vulnerable) creates dangerous false negative.

Verification approaches:

Deployment inspection: For software you can inspect (deployed in your environment, available as container image, accessible binaries), compare SBOM claims against actual artifacts.

# Compare npm package.json against SBOM
jq -r '.components[] | select(.purl | startswith("pkg:npm/")) | "\(.name)@\(.version)"' sbom.json > sbom-packages.txt
jq -r '.dependencies,.devDependencies | to_entries[] | "\(.key)@\(.value)"' package-lock.json > actual-packages.txt
diff sbom-packages.txt actual-packages.txt

Discrepancies indicate accuracy problems requiring investigation.

Build artifact analysis: For container images, use tools like Syft or Trivy to generate SBOM from actual image, compare against vendor-provided SBOM.

# Generate SBOM from container image
syft packages docker:vendor/product:v1.2.3 -o cyclonedx-json > actual-sbom.json

# Compare vendor SBOM against actual
compare-sboms vendor-sbom.json actual-sbom.json --report-differences

Perfect match is rare (different tools enumerate slightly differently), but major discrepancies (different component versions, missing entire dependencies) indicate accuracy concerns.

Sampling approach: For large SBOMs, sample random components and verify versions against package repositories, source control, or documentation. 10-20 component sample can indicate whether SBOM is generally accurate or systematically wrong.

Format Compliance

Does SBOM conform to specification?

Schema validation: Most basic check. SBOM must be valid CycloneDX or SPDX according to published schemas.

# CycloneDX validation
cyclonedx-cli validate --input-file sbom.json --schema-version 1.6

# SPDX validation
java -jar spdx-tools.jar Verify sbom.spdx

Schema violations indicate generation problems or file corruption. Reject or quarantine schema-invalid SBOMs until supplier provides corrected version.

Specification compliance: Beyond schema validation, check for specification best practices:

  • Required fields populated (not empty strings or "N/A")
  • SPDX license identifiers used (not free-text license names)
  • PURLs follow PURL specification syntax
  • Timestamps in ISO 8601 format with timezone
  • UUIDs valid where UUIDs required

Version compatibility: Ensure SBOM format version is one your tools support. CycloneDX 1.6 SBOM won't parse correctly with tool expecting 1.4. Check format version in SBOM metadata matches your ingestion tool capabilities.

Operational Fitness

Is SBOM suitable for your intended uses?

Use case mapping:

Vulnerability management: Requires PURLs or CPEs for matching against vulnerability databases. Requires accurate versions. Requires component relationships for understanding transitive vulnerability exposure.

License compliance: Requires license information for every component. SBOM with 80% components showing "NOASSERTION" for license is insufficient for compliance work.

Supplier risk assessment: Requires supplier/author information. Component age context helpful. Evidence of component health (maintenance activity, update frequency) valuable.

Incident response: Requires rapid queryability. File formats must be machine-readable (JSON preferred over XML). Component enumeration must be comprehensive to avoid missing vulnerable components.

Fitness assessment:

def assess_operational_fitness(sbom, intended_use):
    """Assess whether SBOM supports intended operational use"""
    fitness = {
        'vulnerability_management': False,
        'license_compliance': False,
        'supplier_risk': False,
        'incident_response': False,
        'issues': []
    }

    components = sbom.get('components', [])
    if not components:
        fitness['issues'].append('No components listed')
        return fitness

    # Vulnerability management fitness
    purl_coverage = sum(1 for c in components if c.get('purl')) / len(components)
    if purl_coverage > 0.9:
        fitness['vulnerability_management'] = True
    else:
        fitness['issues'].append(f'Only {purl_coverage:.0%} components have PURLs')

    # License compliance fitness
    license_coverage = sum(1 for c in components if c.get('licenses')) / len(components)
    if license_coverage > 0.9:
        fitness['license_compliance'] = True
    else:
        fitness['issues'].append(f'Only {license_coverage:.0%} components have license info')

    # Supplier risk fitness
    supplier_coverage = sum(1 for c in components if c.get('supplier') or c.get('author')) / len(components)
    if supplier_coverage > 0.7:
        fitness['supplier_risk'] = True
    else:
        fitness['issues'].append(f'Only {supplier_coverage:.0%} components have supplier/author')

    # Incident response fitness
    if sbom.get('format') in ['json', 'JSON']:
        fitness['incident_response'] = True
    else:
        fitness['issues'].append('Non-JSON format complicates automated processing')

    return fitness

SBOM unsuitable for intended use has limited operational value regardless of completeness score.

Validation Workflow

Automated Validation Pipeline

Stage 1: Format validation

def validate_format(sbom_file):
    """Validate SBOM format and schema compliance"""
    try:
        sbom = json.load(open(sbom_file))
    except json.JSONDecodeError as e:
        return {'valid': False, 'error': f'Invalid JSON: {e}'}

    # Detect format
    if 'bomFormat' in sbom and sbom['bomFormat'] == 'CycloneDX':
        return validate_cyclonedx_schema(sbom)
    elif 'spdxVersion' in sbom:
        return validate_spdx_schema(sbom)
    else:
        return {'valid': False, 'error': 'Unknown SBOM format'}

Stage 2: Completeness assessment

def assess_completeness(sbom):
    """Calculate completeness score and identify gaps"""
    score = score_sbom_completeness(sbom)
    gaps = []

    if score < 80:
        components = sbom.get('components', [])

        # Identify specific gaps
        missing_purl = sum(1 for c in components if not c.get('purl'))
        if missing_purl > 0:
            gaps.append(f'{missing_purl} components missing PURL')

        missing_license = sum(1 for c in components if not c.get('licenses'))
        if missing_license > 0:
            gaps.append(f'{missing_license} components missing license information')

    return {'score': score, 'gaps': gaps}

Stage 3: Accuracy spot checks

def spot_check_accuracy(sbom, sample_size=10):
    """Sample components and verify versions against authoritative sources"""
    components = sbom.get('components', [])
    sample = random.sample(components, min(sample_size, len(components)))

    discrepancies = []
    for component in sample:
        expected_version = lookup_latest_version(component)
        sbom_version = component.get('version')

        if expected_version and sbom_version:
            if compare_versions(sbom_version, expected_version) == 'significantly_older':
                discrepancies.append({
                    'component': component['name'],
                    'sbom_version': sbom_version,
                    'expected_version': expected_version
                })

    return {'discrepancies': discrepancies}

Stage 4: Fitness assessment

def validate_fitness(sbom, required_capabilities):
    """Verify SBOM supports required operational capabilities"""
    fitness = assess_operational_fitness(sbom, required_capabilities)

    blockers = []
    if 'vulnerability_management' in required_capabilities and not fitness['vulnerability_management']:
        blockers.append('Insufficient PURL coverage for vulnerability management')

    if 'license_compliance' in required_capabilities and not fitness['license_compliance']:
        blockers.append('Insufficient license data for compliance')

    return {'fitness': fitness, 'blockers': blockers}

Quality Gate Decisions

Based on validation results, make ingestion decision:

Accept:

  • Schema valid
  • Completeness score above 80
  • No critical accuracy discrepancies
  • Fits operational requirements
  • Action: Ingest into repository, mark as "validated quality"

Accept with caveats:

  • Schema valid
  • Completeness score 60-80
  • Minor accuracy concerns
  • Partially fits operational requirements
  • Action: Ingest but document limitations, flag for supplier feedback

Reject:

  • Schema invalid
  • Completeness score below 60
  • Major accuracy problems
  • Unsuitable for any operational use
  • Action: Quarantine, request corrected SBOM from supplier

Request clarification:

  • Ambiguous findings requiring supplier explanation
  • Unusual patterns that might be legitimate or might be errors
  • Missing context needed for full assessment
  • Action: Hold pending supplier response to specific questions

Supplier Quality Feedback

Don't silently accept low-quality SBOMs. Provide constructive feedback helping suppliers improve.

Feedback template:

Subject: SBOM Quality Feedback - Product X v1.2.3

Thank you for providing SBOM for Product X v1.2.3. Our validation identified
areas for improvement:

Completeness Issues:
- SBOM lists 23 components. For application of this complexity, we expect
  100+ including transitive dependencies. Current SBOM appears to include
  only direct dependencies.
- 18 of 23 components (78%) missing PURL identifiers, preventing automated
  vulnerability matching
- 15 of 23 components (65%) missing license information

Recommendations:
1. Configure SBOM generation tool to enumerate full transitive dependency tree
2. Enable PURL generation in tool configuration
3. Include license data from package metadata

We've accepted current SBOM with documented limitations. Updated SBOM
incorporating above improvements would significantly increase operational value.

Resources:
- PURL specification: https://github.com/package-url/purl-spec
- CycloneDX tool guide: [link]

Questions? Contact sbom-quality@example.com

Constructive feedback positions you as partner helping supplier mature rather than critic rejecting their efforts.

Tracking Quality Over Time

Monitor supplier SBOM quality trends demonstrating improvement or decline.

Quality scorecard per supplier:

SupplierProductVersionCompletenessAccuracyFormatFitnessOverall
Acme CorpWidgetv1.065%UnknownValid50%Poor
Acme CorpWidgetv1.182%VerifiedValid90%Good
Beta SystemsAnalyticsv2.045%UnknownValid30%Poor

Track quarter-over-quarter changes. Acme Corp improving (65% to 82% completeness) demonstrates responsiveness to feedback. Beta Systems persistently low quality may trigger supplier performance discussions.

Common Validation Pitfalls

Pitfall: Accepting SBOMs without validation Blindly trusting supplier SBOMs. Discovering quality problems only when operational use fails (vulnerability scanning produces nonsensical results, compliance reports have gaps).

Prevention: Implement automated validation pipeline. Every SBOM passes through quality gates before operational use.

Pitfall: Binary accept/reject decisions Treating validation as pass/fail. Rejecting imperfect-but-usable SBOMs or accepting critically-flawed SBOMs without documentation.

Prevention: Multi-level quality classification (excellent, good, acceptable with limitations, unacceptable). Use appropriate quality level for operational context.

Pitfall: No supplier feedback loop Identifying quality problems internally but never communicating to suppliers. Suppliers continue providing low-quality SBOMs because they don't know improvement is needed.

Prevention: Systematic supplier feedback on quality findings. Treat as partnership opportunity.

Pitfall: Inconsistent validation standards Different validators apply different standards. SBOM accepted by one team, rejected by another. Creates supplier confusion and internal friction.

Prevention: Document validation criteria. Automated validation ensures consistency across organization.

Measuring Validation Effectiveness

Validation coverage: Percentage of received SBOMs that undergo quality validation. Target: 100%. Gap indicates process bypassing.

Quality acceptance rate: Percentage of SBOMs passing quality gates on first submission. Trend should increase as suppliers improve. Low rate (under 50%) suggests unrealistic standards or immature supplier ecosystem.

False negative rate: Operational failures due to SBOM quality problems that validation didn't catch. "Vulnerability scanner failed because SBOM lacked PURLs, but validation passed SBOM." Indicates validation gaps requiring improvement.

Supplier quality improvement: Average completeness score change over time. Should trend upward as suppliers respond to feedback and mature practices.

Next Steps

On this page