Note: this is an in-house item already being handled by the maintainer - not open for contribution. Filed for tracking only.
Problem
upsertPackage in src/parsers/utils.ts caps stored dependency paths at five per package:
existing.paths = uniquePathArrays([...(existing.paths ?? []), ...(candidate.paths ?? [])]).slice(0, 5);
That bound is fine for remediation output, where a representative path is enough to explain why a package is present. It becomes visible once those paths are used to build a dependency graph, because any package reachable by more than five routes loses edges.
Concrete case
Scanning this repo with --sbom spdx --prod-only produces 51 DEPENDENCY_OF edges. Snyk's SPDX for the same repo produces 52. The single missing edge is:
semver@7.7.4 -> node-abi@3.89.0
semver@7.7.4 is genuinely reachable through node-abi, but its five recorded paths all run through jest:
["project","jest","@jest/core","@jest/reporters","istanbul-lib-instrument","semver"]
["project","jest","@jest/core","@jest/transform","babel-plugin-istanbul","istanbul-lib-instrument","semver"]
["project","jest","jest-cli","@jest/core","@jest/reporters","istanbul-lib-instrument","semver"]
["project","jest","@jest/core","@jest/reporters","@jest/transform","babel-plugin-istanbul","istanbul-lib-instrument","semver"]
["project","jest","@jest/core","jest-config","babel-jest","babel-plugin-istanbul","istanbul-lib-instrument","semver"]
There is a second effect worth noting. Paths come from loadNpmLockGraph, which is not prodOnly-aware, so under --prod-only a package can retain only paths that traverse packages excluded from the output. The parent lookup then finds nothing and the package ends up with no parent edge at all, which is what happens to semver above.
Why it matters
Dependency relationships are one of the seven NTIA minimum elements that Executive Order 14028 points to. Packages are always complete in our SBOM; only the edges between them can be partial. That is currently documented as a known limitation in website/docs/spdx.md, but a partial graph is a weaker artifact for the compliance audience SPDX serves.
Possible directions
Not yet decided, and each needs measuring before committing:
- Raise or remove the cap. Simplest, but the bound exists to keep large trees manageable and the memory cost on a big monorepo needs measuring first.
- Make path retention
prodOnly-aware so a prod-only scan keeps prod-reachable paths rather than the first five found.
- Keep the cap for remediation output but compute graph edges from the full lock graph separately, so SBOM completeness does not depend on a display-oriented bound.
Option 3 looks most promising because it decouples the two use cases, but it needs a look at what loadNpmLockGraph can expose without a second traversal.
Notes
Problem
upsertPackageinsrc/parsers/utils.tscaps stored dependency paths at five per package:That bound is fine for remediation output, where a representative path is enough to explain why a package is present. It becomes visible once those paths are used to build a dependency graph, because any package reachable by more than five routes loses edges.
Concrete case
Scanning this repo with
--sbom spdx --prod-onlyproduces 51DEPENDENCY_OFedges. Snyk's SPDX for the same repo produces 52. The single missing edge is:semver@7.7.4is genuinely reachable throughnode-abi, but its five recorded paths all run through jest:There is a second effect worth noting. Paths come from
loadNpmLockGraph, which is notprodOnly-aware, so under--prod-onlya package can retain only paths that traverse packages excluded from the output. The parent lookup then finds nothing and the package ends up with no parent edge at all, which is what happens tosemverabove.Why it matters
Dependency relationships are one of the seven NTIA minimum elements that Executive Order 14028 points to. Packages are always complete in our SBOM; only the edges between them can be partial. That is currently documented as a known limitation in
website/docs/spdx.md, but a partial graph is a weaker artifact for the compliance audience SPDX serves.Possible directions
Not yet decided, and each needs measuring before committing:
prodOnly-aware so a prod-only scan keeps prod-reachable paths rather than the first five found.Option 3 looks most promising because it decouples the two use cases, but it needs a look at what
loadNpmLockGraphcan expose without a second traversal.Notes