## 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.:
```json
{
"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
- [x] `node . run test -- test/lib/utils/sbom-cyclonedx.js
test/lib/utils/sbom-spdx.js` — passes
- [x] 100% coverage on both touched files
- [x] Snapshot diff is purely additive (no existing snapshots changed)
- [x] Schema-validation tests in both files still pass for all snapshots
- [x] 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
Certain project dependency trees may result in an SBOM with duplicate entries. This fix ensures that each unique dependency (identified by the combination of package name and version) only appears in the SBOM once. Applies to both SPDX and CycloneDX SBOM formats.
Specific to the CycloneDX format, this change also removes the cdx:npm:package:path property from the component entries in the generated SBOM. Since the same package may be present at multiple paths within the project and we're now de-duplicating those packages, it no longer makes sense to include this in the SBOM. This does not impact the SPDX format as there is no equivalent property.
Fixes: #6967