From 8f3dae5a6c39901c4a8638b69057395af6b6c081 Mon Sep 17 00:00:00 2001 From: coder-Yash886 Date: Thu, 11 Jun 2026 13:42:36 +0530 Subject: [PATCH 1/4] fix: use retrying removeDir in scanner-cache tests on Windows --- examples/readme.md | 3 +++ tests/scanner-cache.test.ts | 5 +---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/examples/readme.md b/examples/readme.md index e32f704d..c8810edc 100644 --- a/examples/readme.md +++ b/examples/readme.md @@ -29,6 +29,7 @@ Small curated projects committed to the repository. Clone the repo and scan imme | `pnpm-workspace` | pnpm (workspace) | pnpm workspace monorepo with workspace-scoped fix commands. | | `wrong-parent` | npm | 3-level transitive chain where the immediate parent's range already covers the fix — expects `npm update js-cookie`, not a parent bump. | | `no-findings` | npm | Clean project with no known vulnerabilities — demonstrates success output. | +| `dev-only-finding` | npm | Vulnerable package that only appears in devDependencies — classified as a direct finding in full scans and excluded by `--prod-only`. | | `any fixture` + `.cve-lite/baseline.json` | any | Run `cve-lite . --ratchet` on any fixture to establish a baseline. Rescan without the flag to see only new findings. `.cve-lite/` directories should NOT be committed from example fixtures. | | `mal-private-registry` | npm | `node-ipc@9.2.3` with `resolved` pointing to a private registry — demonstrates `Unverifiable (private source)` output for MAL- advisories where the artifact origin cannot be confirmed. | | `lima-site` | npm | Dev-dependency scanning in a documentation site. | @@ -167,6 +168,8 @@ node dist/index.js examples/pnpm-aliased-chain --verbose node dist/index.js examples/pnpm-workspace --verbose node dist/index.js examples/wrong-parent --verbose node dist/index.js examples/no-findings +node dist/index.js examples/dev-only-finding --verbose +node dist/index.js examples/dev-only-finding --verbose --prod-only node dist/index.js examples/lima-site --verbose # In-repo snapshot: Astro diff --git a/tests/scanner-cache.test.ts b/tests/scanner-cache.test.ts index 1017e377..3da2ba6a 100644 --- a/tests/scanner-cache.test.ts +++ b/tests/scanner-cache.test.ts @@ -5,6 +5,7 @@ import { jest } from "@jest/globals"; import type { OsvVuln, PackageRef, ParsedOptions } from "../src/types.js"; import { LocalAdvisoryDatabase } from "../src/advisory/local-db.js"; import { clearPackumentCache } from "../src/remediation/npm-registry.js"; +import { removeDir } from "./test-utils.js"; const queryBatchMock = jest.fn(); const getVulnMock = jest.fn(); @@ -25,10 +26,6 @@ function createTempCacheDir(): string { return fs.mkdtempSync(path.join(os.tmpdir(), "cve-lite-scanner-test-")); } -function removeDir(dirPath: string) { - fs.rmSync(dirPath, { recursive: true, force: true }); -} - function createOptions(cacheDir: string): ParsedOptions { return { batchSize: "100", From c19b696141a560b9513afab1dfa31d42eb38e7f3 Mon Sep 17 00:00:00 2001 From: coder-Yash886 Date: Thu, 11 Jun 2026 13:44:42 +0530 Subject: [PATCH 2/4] add dev-only-finding fixture for devDependency prod-only edge case --- examples/dev-only-finding/package-lock.json | 47 +++++++++++++++++++++ examples/dev-only-finding/package.json | 10 +++++ 2 files changed, 57 insertions(+) create mode 100644 examples/dev-only-finding/package-lock.json create mode 100644 examples/dev-only-finding/package.json diff --git a/examples/dev-only-finding/package-lock.json b/examples/dev-only-finding/package-lock.json new file mode 100644 index 00000000..908dca57 --- /dev/null +++ b/examples/dev-only-finding/package-lock.json @@ -0,0 +1,47 @@ +{ + "name": "cve-lite-example-dev-only-finding", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cve-lite-example-dev-only-finding", + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "axios": "0.21.1" + } + }, + "node_modules/axios": { + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz", + "integrity": "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.10.0" + } + }, + "node_modules/follow-redirects": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + } + } +} diff --git a/examples/dev-only-finding/package.json b/examples/dev-only-finding/package.json new file mode 100644 index 00000000..4407f1a6 --- /dev/null +++ b/examples/dev-only-finding/package.json @@ -0,0 +1,10 @@ +{ + "name": "cve-lite-example-dev-only-finding", + "version": "1.0.0", + "private": true, + "description": "Vulnerable package that only appears in devDependencies — scanner should classify it correctly and exclude it from --prod-only scans.", + "license": "MIT", + "devDependencies": { + "axios": "0.21.1" + } +} From d9d472192dfedcc0760dd38189ff357088e24f21 Mon Sep 17 00:00:00 2001 From: coder-Yash886 Date: Fri, 12 Jun 2026 13:47:05 +0530 Subject: [PATCH 3/4] Add multiple-versions-same-pkg regression fixture for dual lodash installs --- .../package-lock.json | 38 ++++++++++++++++ .../multiple-versions-same-pkg/package.json | 11 +++++ examples/readme.md | 2 + tests/fixture-scan.test.ts | 45 +++++++++++++++++-- 4 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 examples/multiple-versions-same-pkg/package-lock.json create mode 100644 examples/multiple-versions-same-pkg/package.json diff --git a/examples/multiple-versions-same-pkg/package-lock.json b/examples/multiple-versions-same-pkg/package-lock.json new file mode 100644 index 00000000..cd3a28a2 --- /dev/null +++ b/examples/multiple-versions-same-pkg/package-lock.json @@ -0,0 +1,38 @@ +{ + "name": "cve-lite-example-multiple-versions-same-pkg", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "cve-lite-example-multiple-versions-same-pkg", + "version": "1.0.0", + "license": "MIT", + "dependencies": { + "karma": "1.7.1", + "lodash": "4.17.20" + } + }, + "node_modules/karma": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/karma/-/karma-1.7.1.tgz", + "integrity": "sha512-0TylHLtdl5f1QXi1LQn9f9v9wfqQv5JEoig6/IHDmysp8Z3fSQ8WLskChqdtoMa06XWKLFq2QAzs/Qn4m1XJmw==", + "license": "MIT", + "dependencies": { + "lodash": "^3.8.0" + } + }, + "node_modules/karma/node_modules/lodash": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-3.10.1.tgz", + "integrity": "sha512-u2/S9Y9fKF3bTlG2ba7tk9poHw2fmsfa4sQi2ghvHffftq/UcD0YQ8oUoGyz9/ZMEPX7AWHf45kFA7S+8EIYow==", + "license": "MIT" + }, + "node_modules/lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcIlOINaEH7UKfZ0b8VmgphaGm+lh+4pd0s2/KCrJBD6NBkk+KNNMygi0ZWNLOd3gppdUa+dPdY0sHhUseGw==", + "license": "MIT" + } + } +} diff --git a/examples/multiple-versions-same-pkg/package.json b/examples/multiple-versions-same-pkg/package.json new file mode 100644 index 00000000..dd69a9b4 --- /dev/null +++ b/examples/multiple-versions-same-pkg/package.json @@ -0,0 +1,11 @@ +{ + "name": "cve-lite-example-multiple-versions-same-pkg", + "version": "1.0.0", + "private": true, + "description": "npm regression fixture: lodash@3.10.1 (via karma) and lodash@4.17.20 (direct) — same package at two versions, each must be a separate finding.", + "license": "MIT", + "dependencies": { + "karma": "1.7.1", + "lodash": "4.17.20" + } +} diff --git a/examples/readme.md b/examples/readme.md index c8810edc..a603ef8b 100644 --- a/examples/readme.md +++ b/examples/readme.md @@ -30,6 +30,7 @@ Small curated projects committed to the repository. Clone the repo and scan imme | `wrong-parent` | npm | 3-level transitive chain where the immediate parent's range already covers the fix — expects `npm update js-cookie`, not a parent bump. | | `no-findings` | npm | Clean project with no known vulnerabilities — demonstrates success output. | | `dev-only-finding` | npm | Vulnerable package that only appears in devDependencies — classified as a direct finding in full scans and excluded by `--prod-only`. | +| `multiple-versions-same-pkg` | npm | Same package at two installed versions (`lodash@3.10.1` via karma, `lodash@4.17.20` direct) — each version must appear as a separate finding. | | `any fixture` + `.cve-lite/baseline.json` | any | Run `cve-lite . --ratchet` on any fixture to establish a baseline. Rescan without the flag to see only new findings. `.cve-lite/` directories should NOT be committed from example fixtures. | | `mal-private-registry` | npm | `node-ipc@9.2.3` with `resolved` pointing to a private registry — demonstrates `Unverifiable (private source)` output for MAL- advisories where the artifact origin cannot be confirmed. | | `lima-site` | npm | Dev-dependency scanning in a documentation site. | @@ -170,6 +171,7 @@ node dist/index.js examples/wrong-parent --verbose node dist/index.js examples/no-findings node dist/index.js examples/dev-only-finding --verbose node dist/index.js examples/dev-only-finding --verbose --prod-only +node dist/index.js examples/multiple-versions-same-pkg --verbose node dist/index.js examples/lima-site --verbose # In-repo snapshot: Astro diff --git a/tests/fixture-scan.test.ts b/tests/fixture-scan.test.ts index e1a3d5d3..14f157ea 100644 --- a/tests/fixture-scan.test.ts +++ b/tests/fixture-scan.test.ts @@ -16,10 +16,13 @@ function itWithFixture(name: string, testName: string, testFn: () => void): void fixtureTest(testName, testFn); } -function requirePackage(scanInput: ScanInput, name: string): PackageRef { - const pkg = scanInput.packages.find(item => item.name === name); +function requirePackage(scanInput: ScanInput, name: string, version?: string): PackageRef { + const pkg = scanInput.packages.find( + item => item.name === name && (version === undefined || item.version === version), + ); if (!pkg) { - throw new Error(`Expected fixture to include ${name}`); + const versionSuffix = version ? `@${version}` : ""; + throw new Error(`Expected fixture to include ${name}${versionSuffix}`); } return pkg; } @@ -183,6 +186,42 @@ describe("fixture remediation scans", () => { ); }); + itWithFixture( + "multiple-versions-same-pkg", + "reports lodash@3 and lodash@4 as separate installed packages with correct relationships", + () => { + const scanInput = loadFixture("multiple-versions-same-pkg"); + const lodash3 = requirePackage(scanInput, "lodash", "3.10.1"); + const lodash4 = requirePackage(scanInput, "lodash", "4.17.20"); + + expect(scanInput.packages.filter(item => item.name === "lodash")).toHaveLength(2); + expect(lodash3.paths?.every(path => path.length > 2)).toBe(true); + expect(lodash4.paths?.some(path => path.length <= 2)).toBe(true); + + const directFinding = findingFor(scanInput, "lodash", { + relationship: "direct", + firstFixedVersion: "4.18.0", + validatedFirstFixedVersion: "4.18.0", + }); + directFinding.pkg = lodash4; + + const transitiveFinding = findingFor(scanInput, "lodash", { + relationship: "transitive", + firstFixedVersion: "4.18.0", + validatedFirstFixedVersion: "4.18.0", + dependencyPaths: lodash3.paths ?? [], + }); + transitiveFinding.pkg = lodash3; + + const directPlan = buildSuggestedFixCommandPlan([directFinding], scanInput); + const transitivePlan = buildSuggestedFixCommandPlan([transitiveFinding], scanInput); + + expect(directPlan?.command).toBe("npm install lodash@4.18.0"); + expect(directPlan?.targets.find(t => t.kind === "direct")?.package).toBe("lodash"); + expect(transitivePlan?.targets.find(t => t.kind === "direct")).toBeUndefined(); + }, + ); + it("mal-private-registry fixture - node-ipc resolvedUrl is from a private registry", () => { const scanInput = loadFixture("mal-private-registry"); const nodeIpc = scanInput.packages.find(p => p.name === "node-ipc"); From 5d9b4d9ed01d74830d764e68c31f7b91d4c765ba Mon Sep 17 00:00:00 2001 From: coder-Yash886 Date: Fri, 12 Jun 2026 13:55:35 +0530 Subject: [PATCH 4/4] Resolve readme conflict with upstream main --- examples/readme.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/examples/readme.md b/examples/readme.md index a603ef8b..66614396 100644 --- a/examples/readme.md +++ b/examples/readme.md @@ -33,6 +33,11 @@ Small curated projects committed to the repository. Clone the repo and scan imme | `multiple-versions-same-pkg` | npm | Same package at two installed versions (`lodash@3.10.1` via karma, `lodash@4.17.20` direct) — each version must appear as a separate finding. | | `any fixture` + `.cve-lite/baseline.json` | any | Run `cve-lite . --ratchet` on any fixture to establish a baseline. Rescan without the flag to see only new findings. `.cve-lite/` directories should NOT be committed from example fixtures. | | `mal-private-registry` | npm | `node-ipc@9.2.3` with `resolved` pointing to a private registry — demonstrates `Unverifiable (private source)` output for MAL- advisories where the artifact origin cannot be confirmed. | +| `pnpm-mal-private-registry` | pnpm v9 | `node-ipc@9.2.3` resolved from a private registry — demonstrates `Unverifiable (private source)` detection for pnpm v9 lockfiles. | +| `pnpm-legacy-mal-private-registry` | pnpm legacy (v6) | `node-ipc@9.2.3` resolved from a private registry — demonstrates `Unverifiable (private source)` detection for pnpm v6/v7/v8 lockfiles. | +| `yarn-classic-mal-private-registry` | Yarn Classic (v1) | `node-ipc@9.2.3` resolved from a private registry — demonstrates `Unverifiable (private source)` detection for Yarn Classic lockfiles. | +| `bun-mal-private-registry` | Bun | `node-ipc@9.2.3` resolved from a private registry — demonstrates `Unverifiable (private source)` detection for Bun lockfiles. | +| `git-source-mal` | npm | `node-ipc@9.2.3` resolved from a git source URL pinned to a commit SHA — demonstrates `Git source (SHA-pinned)` badge for MAL- advisories where the package originates from a git repository rather than the npm registry. | | `lima-site` | npm | Dev-dependency scanning in a documentation site. | ## In-repo snapshot: Astro