Summary
When scanning a monorepo root, PD002 reports dependencies that are declared directly in a workspace member's package.json as transitive-only phantoms. Declaration appears to be resolved against the root manifest only, while imports are collected from the whole tree — so every workspace-member dependency that is imported in that member's source is flagged.
Version: 1.28.0 · Package manager: pnpm (workspace via pnpm-workspace.yaml)
Minimal reproduction
repro/
├── package.json { "name": "pd002-repro", "private": true, "version": "1.0.0" }
├── pnpm-workspace.yaml packages: ["apps/*"]
└── apps/web/
├── package.json { "name": "@repro/web", "dependencies": { "js-yaml": "^4.1.0" } }
└── src/index.ts import yaml from 'js-yaml';
pnpm install
cve-lite . overrides --json
Actual
PD002 medium js-yaml
js-yaml is imported in source but only present as a transitive dependency - declare it explicitly
details: Imported in: apps/web/src/index.ts. If the parent package drops or changes this
dependency, your code will break without warning. Run: pnpm add js-yaml
Expected
No finding. js-yaml is a declared direct dependency of @repro/web, which is the package that imports it.
The suggested remediation (pnpm add js-yaml) would add a redundant second declaration at the root.
The detail line points at the fix
The finding already reports Imported in: apps/web/src/index.ts. So the import's owning workspace is known at the point the finding is produced — it just isn't used to pick which manifest to resolve the declaration against. Resolving against the nearest enclosing workspace member's package.json (falling back to the root) should close this.
Corroborating: scanning the member directly is clean.
cve-lite ./apps/web overrides --json # 0 findings
cve-lite . overrides --json # 1 PD002 finding
Real-world impact
On a real 2-app pnpm monorepo (root manifest declares one devDependency; apps/web declares the rest), a root scan produces 11 PD002 findings, all false — next, react, vitest, tailwindcss, @tanstack/react-query, lucide-react, clsx, tailwind-merge, @testing-library/react, @testing-library/jest-dom, @vitejs/plugin-react.
For a dashboard aggregating findings across many repos, that one project dominates the view with noise, which is what led us to filter PD001/PD002 out of our integration entirely rather than lose the signal from the OA rules.
Notes
Summary
When scanning a monorepo root, PD002 reports dependencies that are declared directly in a workspace member's
package.jsonas transitive-only phantoms. Declaration appears to be resolved against the root manifest only, while imports are collected from the whole tree — so every workspace-member dependency that is imported in that member's source is flagged.Version: 1.28.0 · Package manager: pnpm (workspace via
pnpm-workspace.yaml)Minimal reproduction
pnpm install cve-lite . overrides --jsonActual
Expected
No finding.
js-yamlis a declared direct dependency of@repro/web, which is the package that imports it.The suggested remediation (
pnpm add js-yaml) would add a redundant second declaration at the root.The detail line points at the fix
The finding already reports
Imported in: apps/web/src/index.ts. So the import's owning workspace is known at the point the finding is produced — it just isn't used to pick which manifest to resolve the declaration against. Resolving against the nearest enclosing workspace member'spackage.json(falling back to the root) should close this.Corroborating: scanning the member directly is clean.
Real-world impact
On a real 2-app pnpm monorepo (root manifest declares one devDependency;
apps/webdeclares the rest), a root scan produces 11 PD002 findings, all false —next,react,vitest,tailwindcss,@tanstack/react-query,lucide-react,clsx,tailwind-merge,@testing-library/react,@testing-library/jest-dom,@vitejs/plugin-react.For a dashboard aggregating findings across many repos, that one project dominates the view with noise, which is what led us to filter PD001/PD002 out of our integration entirely rather than lose the signal from the OA rules.
Notes