Skip to content

fix: include transitive peer dependencies in npm lock v2/v3 graph [OSM-3794]#316

Open
gergo-papp wants to merge 1 commit into
mainfrom
fix/osm-3794-peer-deps-transitive
Open

fix: include transitive peer dependencies in npm lock v2/v3 graph [OSM-3794]#316
gergo-papp wants to merge 1 commit into
mainfrom
fix/osm-3794-peer-deps-transitive

Conversation

@gergo-papp

@gergo-papp gergo-papp commented Jul 15, 2026

Copy link
Copy Markdown
Contributor
  • Tests written and linted
  • Documentation written / README.md updated
  • Follows CONTRIBUTING agreement
  • Commit history is tidy
  • Reviewed by Snyk team

What this does

Fixes OSM-3794: Snyk misses vulnerabilities in the peer dependencies of a (transitive) dependency for npm v7+ projects.

From npm v7 onwards, peerDependencies are installed by default and recorded in the lockfile (flagged "peer": true). The npm lock v2/v3 dep-graph builder honoured this only for the root package (getTopLevelDeps(..., includePeerDeps: true)) — getChildNode collected dependencies / devDependencies / optionalDependencies but never peerDependencies. As a result, a peer dependency of any non-root package (e.g. a direct dependency's peer, or a peer-of-a-peer) was silently dropped from the dep graph and its vulnerabilities went unreported.

This also covers the duplicate reports OSM-3645, OSM-2116 and OSM-1226.

How it's fixed

  • getChildNode now merges a resolved package's peerDependencies into its subtree, via a new getPeerDependencies helper in util.ts.
  • Optional peers (peerDependenciesMeta[name].optional === true) are excluded — npm v7+ does not auto-install those (see OSM-1079).
  • Peer entries are flagged isPeer, so an unmet/conflicting peer with no lockfile entry is skipped rather than raising OutOfSyncError. Previous attempts that threw here (PRs fix: [OSM-1079] support the parsing of transitive peer deps - if they are installed #233 / Fix/ignore optional deps if not installed #239, epic OSM-2056) were reverted for exactly this reason.
  • Peers are spread first when building a node's dependency map, so a real dependencies/optional/dev entry for the same name takes precedence over the peer range.

Notes for the reviewer

  • Scoped to the v2/v3 builder, which by definition parses npm-v7+ lockfiles — so "peers are installed" is guaranteed by the lockfile itself, and installed peers are physically present as "peer": true entries. The legacy v1 (buildDepTree) path is untouched.
  • The 5 updated expected.json fixtures were regenerated and verified purely additive (node/edge set comparison: 0 nodes removed, 0 edges removed in every fixture — only new, genuine peer edges such as react-dom → react, @babel/* → @babel/core, ajv-errors → ajv, cosmiconfig-typescript-loader → typescript). The large deeply-scoped diff is only DFS traversal-order reshuffling of the same nodes.
  • New transitive-peer-deps fixture + peerDependencies test block assert the peer subtree is included and that an optional, uninstalled peer is skipped without throwing under strictOutOfSync: true.
  • Full suite: 394 passing. Reproduced against the original customer projects (consumer-app-using-remote-lib-2): now resolves minimatch → brace-expansion → balanced-match.

More information

🤖 Generated with Claude Code

…M-3794]

npm v7+ installs peerDependencies by default and records them in the
lockfile (flagged "peer": true). The V2/V3 dep-graph builder only walked
peerDependencies for the root package — getChildNode collected
dependencies/devDependencies/optionalDependencies but never peers — so a
peer dependency of a (transitive) dependency was dropped from the graph
and its vulnerabilities went unreported.

getChildNode now merges a resolved package's peerDependencies into its
subtree via a new getPeerDependencies helper, which:
- excludes peers marked optional in peerDependenciesMeta (npm v7+ does
  not auto-install those), and
- flags entries isPeer so an unmet/conflicting peer with no lockfile
  entry is skipped rather than raising OutOfSyncError. Earlier attempts
  that threw here (PRs #233/#239, epic OSM-2056) were reverted for this.

Verified purely additive across existing fixtures (no nodes/edges
removed). Adds a transitive-peer-deps fixture and peerDependencies tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@gergo-papp
gergo-papp requested a review from a team as a code owner July 15, 2026 12:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant