Skip to content

fix(parsers): resolve pnpm workspace deps against ancestor lockfile - #340

Merged
wakemeup0 merged 1 commit into
mainfrom
fix/issue-339
Sep 22, 2026
Merged

wakemeup0 merged 1 commit into
mainfrom
fix/issue-339

Conversation

@wakemeup0

Copy link
Copy Markdown
Contributor

Summary

Closes #339

  • hooray scan project on a pnpm workspace no longer emits
    specifier-versioned phantom components (is-even@^1.0.0) that inflated
    OSV matches (reporter: 10 true findings → 83).
  • Root cause: a workspace member's package.json has no sibling
    lockfile, so parse_package_json treated its declared constraints as
    resolved components. Manifest declarations are now superseded by the
    nearest covering lockfile in any ancestor directory — the
    workspace-root pnpm-lock.yaml/package-lock.json/yarn.lock/
    bun.lock that actually resolved the member's dependencies.
  • Defense in depth: pnpm importer dependency values that are bare
    specifier strings resolve against the lockfile's recorded versions
    instead of becoming component versions; tags (latest) and partial
    ranges (1.x) resolve by name, and unresolvable specifiers produce no
    component.

Verification

  • Reproduced the reported fixture (two-document pnpm 12 lockfile, root +
    apps/web importers): before → is-even@1.0.0, is-even@^1.0.0,
    is-odd@3.0.1; after → exactly is-even@1.0.0, is-odd@3.0.1.
  • cargo test --locked --all-targets --all-features — 708 passed,
    0 failed, including new regressions:
    input::tests::workspace_manifest_deps_are_covered_by_ancestor_lockfile
    (nested manifest + root lockfile → resolved versions only) and
    parsers::pnpm::tests::pnpm_workspace_importer_specifiers_resolve_to_locked_versions
    (bare specifier/tag/range importer values in a two-document lockfile).
  • cargo fmt --check, cargo check --locked --all-targets --all-features, cargo clippy --locked --all-targets --all-features -- -D warnings — clean.
  • cargo llvm-cov --fail-under-lines 90 — 90.12% lines.
  • cargo deny check advisories bans licenses sources — all ok.

A workspace member's package.json has no sibling lockfile, so its declared
dependency constraints were emitted as specifier-versioned phantom
components (is-even@^1.0.0) that inflated OSV matches. Manifest
dependency declarations are now superseded by the nearest covering
lockfile in any ancestor directory, matching how pnpm/npm/yarn
workspaces resolve members through the workspace-root lockfile.

pnpm importer dependency values that are bare specifier strings (pnpm 12
workspace importers) likewise resolve against the lockfile's recorded
versions instead of becoming component versions; tags and partial ranges
resolve by name, and unresolvable specifiers produce no component.

Closes #339
@wakemeup0
wakemeup0 merged commit 55b25be into main Sep 22, 2026
6 checks passed
@wakemeup0
wakemeup0 deleted the fix/issue-339 branch September 22, 2026 18:05
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.

pnpm workspace importers add phantom components with the semver specifier as version

1 participant