Skip to content

Fix project-scoped affects and duplicate bom-refs in VEX/VDR export#6555

Open
contain-security wants to merge 1 commit into
DependencyTrack:mainfrom
contain-security:fix/cyclonedx-vex-affects-bomref
Open

Fix project-scoped affects and duplicate bom-refs in VEX/VDR export#6555
contain-security wants to merge 1 commit into
DependencyTrack:mainfrom
contain-security:fix/cyclonedx-vex-affects-bomref

Conversation

@contain-security

Copy link
Copy Markdown

Description

CycloneDX VEX and VDR exports were producing documents that violated the
CycloneDX bom-ref uniqueness rule and could not be reliably round-tripped
back into Dependency-Track:

  • Every affects[].ref pointed at the project UUID instead of the affected
    component(s). On re-import, the importer applied one component's analysis
    to every component sharing the vulnerability.
  • One vulnerabilities[] entry was emitted per finding, and all entries for the
    same vulnerability reused the bare vulnerability UUID as their bom-ref. A
    project with several components affected by the same CVE therefore emitted
    duplicate bom-ref values, which the CycloneDX spec forbids ("Every bom-ref
    MUST be unique within the BOM").

This PR groups findings by (vulnerability, analysis fingerprint) and emits one
entry per group, with affects[] carrying the sorted union of the affected
component bom-refs — the pattern documented as CycloneDX VEX Use-Case 13. When
a single vulnerability legitimately splits across distinct analyses, each entry's
bom-ref gets a deterministic numeric suffix (<uuid>/0, <uuid>/1, …) so
uniqueness holds; the common single-group case keeps the bare vulnerability UUID,
matching the existing reference exports.

Addressed Issue

Fixes #6016
Fixes #6017

Additional Details

Why group by analysis fingerprint. VDR/VEX emit per-component analysis. Two
components affected by the same vulnerability may carry different analyses
(e.g. one RESOLVED, one untriaged), so they cannot collapse into a single entry
without losing analysis fidelity — hence grouping by
(vulnerability, analysisFingerprint). Variants that do not emit analysis
(INVENTORY_WITH_VULNERABILITIES) collapse all findings for a vulnerability into
one group.

Fingerprint contract. analysisFingerprint is a positional,
Enum.name()-based serialisation of (state, justification, response, details)
joined by U+001F, so absent fields cannot collide with present ones. It is pinned
by ModelConverterTest.

bom-ref suffixing. The <uuid>/N suffix is applied only when one
vulnerability produces more than one group; singletons keep the bare UUID for
backward-compatible output. The / separator isolates the suffix from the UUID
alphabet so a singleton UUID can never prefix-collide with a multi-group entry.

Variant refactor. The Variant enum was changed from scattered
switch/== checks to explicit capability flags (includesAnalysis(),
emitsFindings(), filtersToVulnerableComponents(), …). This is incidental to
the fix; happy to split it into a separate commit/PR if you'd prefer to keep the
fix minimal.

Performance. Per-(component, vulnerability) analysis lookups are memoised
across the group-key and group-conversion passes, so this does not add queries
relative to the previous per-finding path — it reduces them.

Tests.

  • CycloneDXVexImporterTest#shouldApplyExportedVexAnalysisOnlyToAnalyzedComponent
    — round-trip regression: exports a VEX and re-imports it, asserting the analysis
    lands only on the analysed component.
  • CycloneDxBomAssertions.assertBomRefsUnique — reusable cross-document
    bom-ref uniqueness assertion, applied to the VEX/VDR/BOM resource tests
    (JSON Schema cannot express cross-property uniqueness).
  • ModelConverterTest — pins the fingerprint serialisation contract.

ADR. I don't believe this meets the ADR bar — it's a spec-conformance bug
fix, not a new API version, auth model, schema change, or cross-cutting
convention. It does change the shape of an externally-consumed export and its
re-import semantics, so flagging it explicitly for your judgement.

Docs. The previous output was spec-violating, so I don't think the docs
describe the old affects/bom-ref behaviour specifically. Happy to open a
companion PR against DependencyTrack/docs if the new behaviour warrants a note.

Checklist

  • I have read and understand the contributing guidelines
  • This PR fixes a defect, and I have provided tests to verify that the fix is effective
  • This PR implements an enhancement, and I have provided tests to verify that it works as intended
  • This PR introduces changes to the database model, and I have updated the migration changelog accordingly
  • This PR introduces new or alters existing behavior, and I have updated the documentation accordingly
  • This PR is a substantial change (per the ADR criteria), and I have added an ADR under docs/adr/

CycloneDX VEX/VDR exports set every affects[].ref to the project UUID
and emitted one vulnerability entry per finding, all sharing the bare
vulnerability UUID as their bom-ref. That violated the CycloneDX rule
that every bom-ref is unique within a BOM, and on re-import the
project-level affects broadcast one component's analysis to every
component sharing the vulnerability.

Findings are now grouped by (vulnerability, analysis fingerprint) per
CycloneDX VEX Use-Case 13: one entry per group, affects[] carrying
the sorted union of affected component bom-refs. When a vulnerability
splits across analyses, each entry's bom-ref gets a deterministic
suffix (<uuid>/0, /1) so uniqueness holds. The Variant enum now
carries capability flags in place of scattered switch/== checks.

ModelConverter.analysisFingerprint is pinned to its positional,
enum-name contract, with a round-trip regression proving an exported
VEX re-imports its analysis onto the analysed component only.

Fixes DependencyTrack#6016
Fixes DependencyTrack#6017

Signed-off-by: Contain.Security <contain.security@pacificdigitalconsulting.com>
@owasp-dt-bot

Copy link
Copy Markdown

Snyk checks have passed. No issues have been found so far.

Status Scan Engine Critical High Medium Low Total (0)
Open Source Security 0 0 0 0 0 issues

💻 Catch issues earlier using the plugins for VS Code, JetBrains IDEs, Visual Studio, and Eclipse.

@codacy-production

Copy link
Copy Markdown

Up to standards ✅

🟢 Issues 0 issues

Results:
0 new issues

View in Codacy

🟢 Metrics 23 complexity · -50 duplication

Metric Results
Complexity 23
Duplication -50

View in Codacy

NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants