Skip to content

fix(sbom): dedupe per-node dependsOn / relationships#9311

Open
mikaelkristiansson wants to merge 1 commit intonpm:latestfrom
mikaelkristiansson:fix/sbom-dedupe-dependson
Open

fix(sbom): dedupe per-node dependsOn / relationships#9311
mikaelkristiansson wants to merge 1 commit intonpm:latestfrom
mikaelkristiansson:fix/sbom-dedupe-dependson

Conversation

@mikaelkristiansson
Copy link
Copy Markdown

Summary

Closes the per-node duplication gap left by #7992.

A node can have multiple outgoing edges resolving to the same name@version — typically when a package declares both a direct dependency and an npm alias to the same package, e.g.:

{
  "dependencies": {
    "lodash": "^4.17.21",
    "lodash-aliased": "npm:lodash@^4.17.21"
  }
}

toCyclonedxDependency and the SPDX relationship loop both map each edge through name@version ID generation without deduplicating, so the per-node dependsOn array (CycloneDX) and DEPENDENCY_OF relationships (SPDX) end up with duplicate entries.

CycloneDX 1.5 requires dependsOn items to be unique, so downstream validators (e.g. Dependency Track) reject the SBOM with:

$.dependencies[N].dependsOn: must have only unique items in the array

Changes

  • lib/utils/sbom-cyclonedx.js: wrap the dependsOn array in [...new Set(...)] after mapping edges to refs.
  • lib/utils/sbom-spdx.js: dedupe per source-node relationships by the (spdxElementId, relatedSpdxElement, relationshipType) triple.
  • Test cases added to both test/lib/utils/sbom-cyclonedx.js and test/lib/utils/sbom-spdx.js covering the duplicate-edges-to-same-target scenario, with explicit assertions plus snapshot updates.

Test plan

  • node . run test -- test/lib/utils/sbom-cyclonedx.js test/lib/utils/sbom-spdx.js — passes
  • 100% coverage on both touched files
  • Snapshot diff is purely additive (no existing snapshots changed)
  • Schema-validation tests in both files still pass for all snapshots
  • Reproduced original issue locally with the alias example, ran patched npm against it, confirmed both CycloneDX dependsOn and SPDX relationships are now deduped

Fixes #9310

A node can have multiple outgoing edges resolving to the same
`name@version` (e.g. when a package declares both a direct dependency
and an alias to the same package: `foo: ^1` plus `foo-aliased:
npm:foo@^1`). The SBOM generators map each edge to a CycloneDX ID or
SPDX relationship, which produces duplicate entries inside the
per-node `dependsOn` array (CycloneDX) and per-node `DEPENDENCY_OF`
relationships (SPDX).

CycloneDX 1.5 requires `dependsOn` items to be unique, so downstream
schema validators (e.g. Dependency Track) reject the SBOM with
"must have only unique items in the array".

PR npm#7992 fixed the analogous duplication at the top level
(`components` / `dependencies` / `packages`) but left the per-node
arrays untouched. This change closes that gap by deduplicating both:

- CycloneDX: `dependsOn` is wrapped in `[...new Set(...)]`
- SPDX: per-source-node relationships are deduped by the
  `(spdxElementId, relatedSpdxElement, relationshipType)` triple

Fixes npm#9310
@mikaelkristiansson mikaelkristiansson requested a review from a team as a code owner May 5, 2026 13:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant