Skip to content

fix: parse dual-document pnpm v9 lockfiles (bootstrap + project sections) - #743

Merged
sonukapoor merged 2 commits into
OWASP:mainfrom
Ayush7614:fix/pnpm-dual-document-lockfile-669
Jun 23, 2026
Merged

fix: parse dual-document pnpm v9 lockfiles (bootstrap + project sections)#743
sonukapoor merged 2 commits into
OWASP:mainfrom
Ayush7614:fix/pnpm-dual-document-lockfile-669

Conversation

@Ayush7614

@Ayush7614 Ayush7614 commented Jun 22, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes #669 — pnpm v9 lockfiles that contain two YAML documents (----separated bootstrap + project sections) no longer crash CVE Lite with YAML.parseAllDocuments() errors.

Approach

Per @sonukapoor's review on the issue:

  • Try YAML.parse() first (single-document lockfiles unchanged)
  • On multi-document error, use YAML.parseAllDocuments() and select the project lockfile document by finding the document whose importers include project dependency sections (dependencies / devDependencies / optionalDependencies)
  • Fallback to any document with importers, then the last document

Bootstrap documents in v9 also carry an importers block (with packageManagerDependencies only), so the project-section check is more deterministic than document size scoring.

Changes

  • src/parsers/pnpm-lock.ts — shared parsePnpmLockContent() used by loadFromPnpmLock and buildPnpmWorkspaceMap
  • examples/pnpm-dual-document/ — minimal dual-document fixture (bootstrap + lodash project lockfile)
  • tests/parsers/pnpm-lock.test.ts — dual-document parsing regression test
  • tests/fixture-scan.test.ts — fixture loads project packages, excludes bootstrap pnpm
  • tests/e2e/cve-scanning.test.ts — e2e scan completes with findings on the new fixture

Test plan

  • npm run build
  • npm test -- tests/parsers/pnpm-lock.test.ts tests/fixture-scan.test.ts
  • CI (build, e2e, self-scan)

@Ayush7614
Ayush7614 requested a review from sonukapoor June 22, 2026 06:53
@Ayush7614

Copy link
Copy Markdown
Collaborator Author

Thanks @sonukapoor — opened PR implementing your suggested approach from #669.

Document selection: on multi-document pnpm-lock.yaml, use YAML.parseAllDocuments() and pick the document whose importers include project dependency sections (dependencies / devDependencies / optionalDependencies). Bootstrap documents also carry importers (with packageManagerDependencies only), so that check is more deterministic than size scoring. Fallback: any document with importers, then the last document.

Fixture + tests: added examples/pnpm-dual-document/ plus parser, fixture-scan, and e2e regression tests confirming dual-document lockfiles parse and scan without throwing.

Closes #669

@sonukapoor sonukapoor left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The try-first / fall-back pattern is the right approach - single-document lockfiles hit zero overhead, and the three-function decomposition is clean and readable. Two things to address before this merges.

peerDependencies note: hasProjectImporterSections only checks dependencies, devDependencies, and optionalDependencies. A lockfile where importers declare only peerDependencies would miss tier-1 and fall to tier-2 - still correct, just worth knowing.

Fixture note: lodash@4.17.20 being intentionally vulnerable is the right call for the e2e scan test, but a short comment in the fixture package.json or readme.md would help a future contributor who might try to "fix" it.

Comment thread src/parsers/pnpm-lock.ts Outdated
return YAML.parse(content);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
if (!message.includes("multiple documents")) {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String matching on the error message is fragile - the yaml library exposes a stable code property on YAMLParseError that is part of its public TypeScript API since v2.0. The message could be reworded in a future release (yaml v3 is already in next) and this catch would silently stop working.

The safe version:

import { YAMLParseError } from "yaml";

} catch (error) {
  if (!(error instanceof YAMLParseError) || error.code !== "MULTIPLE_DOCS") {
    throw error;
  }
}

Comment thread src/parsers/pnpm-lock.ts Outdated
const withImporters = documents.find(doc => doc.importers && typeof doc.importers === "object");
if (withImporters) return withImporters;

return documents[documents.length - 1];

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fallback has no test coverage and silently returns the last document if neither tier-1 nor tier-2 matched. In all known pnpm v9 files the project section is last, but if that ordering ever changed the scan would use the wrong document with no error or warning.

Prefer throwing explicitly so we know if this case ever fires in the wild:

throw new Error(
  "pnpm-lock.yaml contains multiple YAML documents but none could be identified as the project lockfile. Please open an issue at https://github.com/OWASP/cve-lite-cli/issues"
);

…ons)

Closes OWASP#669

Select the project lockfile YAML document by looking for importers with
project dependency sections (dependencies/devDependencies/optionalDependencies),
falling back to any document with importers, then the last document.

Adds examples/pnpm-dual-document fixture and regression tests confirming
dual-document lockfiles parse and scan without throwing.
Ayush7614 added a commit to Ayush7614/cve-lite-cli that referenced this pull request Jun 23, 2026
Use YAMLParseError MULTIPLE_DOCS instead of message matching, throw when
no project lockfile document is found, include peerDependencies in importer
detection, document intentionally vulnerable lodash fixture, and add throw test.

Co-authored-by: Cursor <cursoragent@cursor.com>
@Ayush7614
Ayush7614 force-pushed the fix/pnpm-dual-document-lockfile-669 branch from cbff426 to 2879ab0 Compare June 23, 2026 20:54
Use YAMLParseError MULTIPLE_DOCS instead of message matching, throw when
no project lockfile document is found, include peerDependencies in importer
detection, document intentionally vulnerable lodash fixture, and add throw test.
@Ayush7614
Ayush7614 force-pushed the fix/pnpm-dual-document-lockfile-669 branch from 2879ab0 to 9158022 Compare June 23, 2026 20:54
@Ayush7614

Copy link
Copy Markdown
Collaborator Author

Thanks @sonukapoor — all review items addressed in 9158022:

  1. YAMLParseError check — replaced message string matching with error instanceof YAMLParseError && error.code === "MULTIPLE_DOCS".
  2. Explicit throw — removed silent last-document fallback; now throws with an issue link when neither tier-1 nor tier-2 can identify the project lockfile (new regression test included).
  3. peerDependencies — added to hasProjectImporterSections alongside dependencies/devDependencies/optionalDependencies.
  4. Fixture note — documented that lodash@4.17.20 is intentionally vulnerable in examples/pnpm-dual-document/package.json and examples/readme.md.

Rebased onto latest main. Ready for another look.

@sonukapoor sonukapoor left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both issues addressed: is in the check, and the fixture has the intentionally-vulnerable comment in both the package.json description and the readme. CI is green. Good to go.

@sonukapoor sonukapoor left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both issues addressed: peerDependencies is in the hasProjectImporterSections check, and the fixture has the intentionally-vulnerable comment in both the package.json description and the readme. CI is green. Good to go.

@sonukapoor
sonukapoor merged commit 2528423 into OWASP:main Jun 23, 2026
6 checks passed
@sonukapoor

Copy link
Copy Markdown
Collaborator

Merged - thank you @Ayush7614!

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.

fix: parse dual-document pnpm v9 lockfiles (bootstrap + project sections)

2 participants