Skip to content

fix: pnpm v9 parser misclassifies dev/prod for packages shared across workspaces #949

Description

@sonukapoor

Note: this is an in-house item already being handled by the maintainer - not open for contribution. Filed for tracking only.

The pnpm v9 lockfile loader classifies packages as dev vs prod with a name-based helper: it aggregates every devDependency name across all importers/workspaces, then marks a package dev if every recorded path's first segment name is in that set.

In a monorepo where the same package is a devDependency in one workspace but a production dependency in another, this wrongly marks the production package (and its production transitives) as dev. Modern pnpm v9 snapshots no longer carry a per-package dev: field, so this name-based helper is the sole classifier and the misclassification is not corrected anywhere.

Impact: under a production-scoped scan (dev findings filtered, or --fail-on scoped to production), a genuinely production dependency that happens to be a devDependency elsewhere in the monorepo is treated as dev and its vulnerabilities are silently suppressed - a false negative.

Reproduction

pnpm v9 lockfile with two importers - packages/web declares lodash as a devDependency, packages/api declares it as a production dependency. lodash is reported as dev even though api depends on it in production. The same happens to production transitives of such a package.

Fix

Classify by graph reachability from production roots, matching pnpm's own semantics: build the set of packages reachable from any importer's dependencies/optionalDependencies by walking the snapshot graph, and mark a reached package dev only when it is not in that set. Unresolved fallback production deps (declared but absent from snapshots) are added to the set directly. Only the v9 loader is affected; the legacy (v5/v6) loader already classifies from the authoritative per-package dev: field. Displayed dependency paths are unchanged (classification-only). The now-unused name-based helper is removed.

Scope: in-house (scanner/parser internals).

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions