From 8a413cb49f9368c1c7f1e26315785fcbd0b4b349 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 13:56:52 +0000 Subject: [PATCH 1/2] fix(ci): resolve the lockfile from npmjs and update simple-git to 4.0.2 The CI audit step failed with two critical findings in simple-git 3.36.0. Version 4.0.2 is the first fixed release, and 3.x has no fix. Version 4 has no default export, so gitService.ts now imports the named simpleGit. The lockfile had 16 resolved URLs on a third-party mirror. Point every resolved URL at registry.npmjs.org. The integrity values are unchanged. Add scripts/check-lockfile-registry.js and a CI step that fails on any other registry. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_016vMfdfTLtvm4G2UANfJw8q --- .github/workflows/ci.yml | 4 ++ package-lock.json | 82 +++++++++++++++--------------- package.json | 3 +- scripts/check-lockfile-registry.js | 25 +++++++++ src/services/gitService.ts | 2 +- 5 files changed, 73 insertions(+), 43 deletions(-) create mode 100644 scripts/check-lockfile-registry.js diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 746a2f1..87f129d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,6 +37,10 @@ jobs: node-version: 22 cache: npm + - name: Check lockfile registry + # Every resolved URL must point at registry.npmjs.org. + run: npm run check:lockfile + - name: Install dependencies run: npm ci diff --git a/package-lock.json b/package-lock.json index 200f5b0..f67c4bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.10.0", "license": "MIT", "dependencies": { - "simple-git": "^3.22.0" + "simple-git": "^4.0.2" }, "devDependencies": { "@eslint/js": "^9.x", @@ -521,7 +521,7 @@ }, "node_modules/@kwsites/file-exists": { "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", "dependencies": { "debug": "^4.1.1" @@ -529,7 +529,7 @@ }, "node_modules/@kwsites/promise-deferred": { "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" }, "node_modules/@nodelib/fs.scandir": { @@ -769,18 +769,18 @@ } }, "node_modules/@simple-git/args-pathspec": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.3.tgz", - "integrity": "sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.4.tgz", + "integrity": "sha512-EtMX6XjRWSastG2SdkmSQByPtJ9dx/NIjnHbpQPCYt62j4RmSx5rgLTGpw0YCjF5h191gZOmBfheOT23cRSFdw==", "license": "MIT" }, "node_modules/@simple-git/argv-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-1.1.1.tgz", - "integrity": "sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-2.0.1.tgz", + "integrity": "sha512-M++IaVWrN+vYalilOHfwvT2IL3wYZtVEBQpmX6DLSOJLtxbZ+SUeIDE3IiPogzhPACLajwtxfa5rp+ZX1FczzQ==", "license": "MIT", "dependencies": { - "@simple-git/args-pathspec": "^1.0.3" + "@simple-git/args-pathspec": "^1.0.4" } }, "node_modules/@sindresorhus/merge-streams": { @@ -898,7 +898,7 @@ }, "node_modules/@types/node": { "version": "20.19.26", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.26.tgz", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.26.tgz", "integrity": "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==", "dev": true, "dependencies": { @@ -921,7 +921,7 @@ }, "node_modules/@types/vscode": { "version": "1.106.1", - "resolved": "https://registry.npmmirror.com/@types/vscode/-/vscode-1.106.1.tgz", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.106.1.tgz", "integrity": "sha512-R/HV8u2h8CAddSbX8cjpdd7B8/GnE4UjgjpuGuHcbp1xV6yh4OeqU4L1pKjlwujCrSFS0MOpwJAIs/NexMB1fQ==", "dev": true }, @@ -1872,7 +1872,7 @@ }, "node_modules/debug": { "version": "4.4.3", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dependencies": { "ms": "^2.1.3" @@ -3601,7 +3601,7 @@ }, "node_modules/ms": { "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "node_modules/mute-stream": { @@ -4519,15 +4519,15 @@ } }, "node_modules/simple-git": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.36.0.tgz", - "integrity": "sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-4.0.2.tgz", + "integrity": "sha512-l0sIsv9VrPwovKELFDBOj/lhmIcbMvxBk2B+hRhsMM4/VL3gKtuZvYnBNixVo+N/0HnqQx2cBdLvxnAbygHpqA==", "license": "MIT", "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", - "@simple-git/args-pathspec": "^1.0.3", - "@simple-git/argv-parser": "^1.1.0", + "@simple-git/args-pathspec": "1.0.4", + "@simple-git/argv-parser": "2.0.1", "debug": "^4.4.0" }, "funding": { @@ -4980,7 +4980,7 @@ }, "node_modules/typescript": { "version": "5.9.3", - "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "bin": { @@ -5017,7 +5017,7 @@ }, "node_modules/undici-types": { "version": "6.21.0", - "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true }, @@ -5581,7 +5581,7 @@ }, "@kwsites/file-exists": { "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/@kwsites/file-exists/-/file-exists-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", "requires": { "debug": "^4.1.1" @@ -5589,7 +5589,7 @@ }, "@kwsites/promise-deferred": { "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", + "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==" }, "@nodelib/fs.scandir": { @@ -5769,16 +5769,16 @@ "dev": true }, "@simple-git/args-pathspec": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.3.tgz", - "integrity": "sha512-ngJMaHlsWDTfjyq9F3VIQ8b7NXbBLq5j9i5bJ6XLYtD6qlDXT7fdKY2KscWWUF8t18xx052Y/PUO1K1TRc9yKA==" + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@simple-git/args-pathspec/-/args-pathspec-1.0.4.tgz", + "integrity": "sha512-EtMX6XjRWSastG2SdkmSQByPtJ9dx/NIjnHbpQPCYt62j4RmSx5rgLTGpw0YCjF5h191gZOmBfheOT23cRSFdw==" }, "@simple-git/argv-parser": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-1.1.1.tgz", - "integrity": "sha512-Q9lBcfQ+VQCpQqGJFHe5yooOS5hGdLFFbJ5R+R5aDsnkPCahtn1hSkMcORX65J2Z5lxSkD0lQorMsncuBQxYUw==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@simple-git/argv-parser/-/argv-parser-2.0.1.tgz", + "integrity": "sha512-M++IaVWrN+vYalilOHfwvT2IL3wYZtVEBQpmX6DLSOJLtxbZ+SUeIDE3IiPogzhPACLajwtxfa5rp+ZX1FczzQ==", "requires": { - "@simple-git/args-pathspec": "^1.0.3" + "@simple-git/args-pathspec": "^1.0.4" } }, "@sindresorhus/merge-streams": { @@ -5872,7 +5872,7 @@ }, "@types/node": { "version": "20.19.26", - "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.26.tgz", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.26.tgz", "integrity": "sha512-0l6cjgF0XnihUpndDhk+nyD3exio3iKaYROSgvh/qSevPXax3L8p5DBRFjbvalnwatGgHEQn2R88y2fA3g4irg==", "dev": true, "requires": { @@ -5893,7 +5893,7 @@ }, "@types/vscode": { "version": "1.106.1", - "resolved": "https://registry.npmmirror.com/@types/vscode/-/vscode-1.106.1.tgz", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.106.1.tgz", "integrity": "sha512-R/HV8u2h8CAddSbX8cjpdd7B8/GnE4UjgjpuGuHcbp1xV6yh4OeqU4L1pKjlwujCrSFS0MOpwJAIs/NexMB1fQ==", "dev": true }, @@ -6478,7 +6478,7 @@ }, "debug": { "version": "4.4.3", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "requires": { "ms": "^2.1.3" @@ -7601,7 +7601,7 @@ }, "ms": { "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" }, "mute-stream": { @@ -8183,14 +8183,14 @@ } }, "simple-git": { - "version": "3.36.0", - "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.36.0.tgz", - "integrity": "sha512-cGQjLjK8bxJw4QuYT7gxHw3/IouVESbhahSsHrX97MzCL1gu2u7oy38W6L2ZIGECEfIBG4BabsWDPjBxJENv9Q==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-4.0.2.tgz", + "integrity": "sha512-l0sIsv9VrPwovKELFDBOj/lhmIcbMvxBk2B+hRhsMM4/VL3gKtuZvYnBNixVo+N/0HnqQx2cBdLvxnAbygHpqA==", "requires": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", - "@simple-git/args-pathspec": "^1.0.3", - "@simple-git/argv-parser": "^1.1.0", + "@simple-git/args-pathspec": "1.0.4", + "@simple-git/argv-parser": "2.0.1", "debug": "^4.4.0" } }, @@ -8506,7 +8506,7 @@ }, "typescript": { "version": "5.9.3", - "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true }, @@ -8530,7 +8530,7 @@ }, "undici-types": { "version": "6.21.0", - "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "dev": true }, diff --git a/package.json b/package.json index 92121d8..564112c 100644 --- a/package.json +++ b/package.json @@ -152,6 +152,7 @@ "watch": "tsc -watch -p ./", "lint": "eslint src", "package": "vsce package", + "check:lockfile": "node scripts/check-lockfile-registry.js", "smoke:paths": "node scripts/smoke-paths.js", "smoke:status": "node scripts/smoke-status.js", "smoke:picker": "node scripts/smoke-picker.js", @@ -174,6 +175,6 @@ "typescript": "^5.x" }, "dependencies": { - "simple-git": "^3.22.0" + "simple-git": "^4.0.2" } } diff --git a/scripts/check-lockfile-registry.js b/scripts/check-lockfile-registry.js new file mode 100644 index 0000000..c2ff53a --- /dev/null +++ b/scripts/check-lockfile-registry.js @@ -0,0 +1,25 @@ +'use strict'; +// Fails when package-lock.json resolves any package from a registry other than +// the public npm registry. Run before `npm ci` so a mirror URL never reaches CI. +const fs = require('fs'); +const path = require('path'); + +const ALLOWED_PREFIX = 'https://registry.npmjs.org/'; +const lockPath = path.join(__dirname, '..', 'package-lock.json'); +const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8')); + +const bad = []; +for (const [name, entry] of Object.entries(lock.packages ?? {})) { + const resolved = entry.resolved; + if (typeof resolved === 'string' && !resolved.startsWith(ALLOWED_PREFIX)) { + bad.push(`${name || '(root)'}: ${resolved}`); + } +} + +if (bad.length > 0) { + console.error(`package-lock.json resolves ${bad.length} package(s) outside ${ALLOWED_PREFIX}:`); + for (const line of bad) console.error(` ${line}`); + console.error(`Fix: run "npm install --package-lock-only --registry=${ALLOWED_PREFIX}" and commit the lockfile.`); + process.exit(1); +} +console.log(`check-lockfile-registry OK (${Object.keys(lock.packages ?? {}).length} entries)`); diff --git a/src/services/gitService.ts b/src/services/gitService.ts index 0a86576..13fcc35 100644 --- a/src/services/gitService.ts +++ b/src/services/gitService.ts @@ -1,4 +1,4 @@ -import simpleGit, { SimpleGit } from 'simple-git'; +import { simpleGit, SimpleGit } from 'simple-git'; import * as path from 'path'; import * as fs from 'fs'; import { STATUS_MAP, type FileStatus } from './fileStatus'; From f1a61abe6e5273f5c1827c3eaac3fe3ca02e5db4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 26 Oct 2026 14:33:40 +0000 Subject: [PATCH 2/2] fix(ci): check both lockfile layouts and add a self-test The lockfile check read only the packages map. A v1 lockfile keeps its entries in the dependencies tree, so the check found nothing. Scan the packages map and the nested dependencies tree. Fail if the lockfile has neither of them. Add a self-test with one v1 fixture and one v3 fixture, and run it before the check. Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_016vMfdfTLtvm4G2UANfJw8q --- package.json | 2 +- scripts/check-lockfile-registry.js | 69 ++++++++++++++++++++----- scripts/check-lockfile-registry.test.js | 25 +++++++++ 3 files changed, 82 insertions(+), 14 deletions(-) create mode 100644 scripts/check-lockfile-registry.test.js diff --git a/package.json b/package.json index 564112c..925c4d9 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,7 @@ "watch": "tsc -watch -p ./", "lint": "eslint src", "package": "vsce package", - "check:lockfile": "node scripts/check-lockfile-registry.js", + "check:lockfile": "node scripts/check-lockfile-registry.test.js && node scripts/check-lockfile-registry.js", "smoke:paths": "node scripts/smoke-paths.js", "smoke:status": "node scripts/smoke-status.js", "smoke:picker": "node scripts/smoke-picker.js", diff --git a/scripts/check-lockfile-registry.js b/scripts/check-lockfile-registry.js index c2ff53a..e0d1cec 100644 --- a/scripts/check-lockfile-registry.js +++ b/scripts/check-lockfile-registry.js @@ -1,25 +1,68 @@ 'use strict'; // Fails when package-lock.json resolves any package from a registry other than // the public npm registry. Run before `npm ci` so a mirror URL never reaches CI. +// Reads both lockfile layouts: `packages` (v2, v3) and the legacy +// `dependencies` tree (v1, and v2 for older npm). const fs = require('fs'); const path = require('path'); const ALLOWED_PREFIX = 'https://registry.npmjs.org/'; -const lockPath = path.join(__dirname, '..', 'package-lock.json'); -const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8')); -const bad = []; -for (const [name, entry] of Object.entries(lock.packages ?? {})) { - const resolved = entry.resolved; - if (typeof resolved === 'string' && !resolved.startsWith(ALLOWED_PREFIX)) { - bad.push(`${name || '(root)'}: ${resolved}`); +function isObject(value) { + return typeof value === 'object' && value !== null && !Array.isArray(value); +} + +function checkEntry(label, entry, offenders) { + if (isObject(entry) && typeof entry.resolved === 'string' && !entry.resolved.startsWith(ALLOWED_PREFIX)) { + offenders.push(`${label}: ${entry.resolved}`); } } -if (bad.length > 0) { - console.error(`package-lock.json resolves ${bad.length} package(s) outside ${ALLOWED_PREFIX}:`); - for (const line of bad) console.error(` ${line}`); - console.error(`Fix: run "npm install --package-lock-only --registry=${ALLOWED_PREFIX}" and commit the lockfile.`); - process.exit(1); +function walkLegacy(tree, trail, offenders) { + for (const [name, entry] of Object.entries(tree)) { + const label = `${trail}${name}`; + checkEntry(label, entry, offenders); + if (isObject(entry) && isObject(entry.dependencies)) { + walkLegacy(entry.dependencies, `${label} > `, offenders); + } + } +} + +// Returns { offenders }. Throws if the lockfile has neither layout. +function scanLockfile(lock) { + const hasPackages = isObject(lock) && isObject(lock.packages); + const hasLegacy = isObject(lock) && isObject(lock.dependencies); + if (!hasPackages && !hasLegacy) { + throw new Error('lockfile has neither a "packages" map nor a "dependencies" tree'); + } + const offenders = []; + if (hasPackages) { + for (const [name, entry] of Object.entries(lock.packages)) { + checkEntry(name || '(root)', entry, offenders); + } + } + if (hasLegacy) { + walkLegacy(lock.dependencies, '', offenders); + } + return { offenders }; +} + +module.exports = { scanLockfile, ALLOWED_PREFIX }; + +if (require.main === module) { + const lockPath = path.join(__dirname, '..', 'package-lock.json'); + let result; + try { + result = scanLockfile(JSON.parse(fs.readFileSync(lockPath, 'utf8'))); + } catch (err) { + console.error(`check-lockfile-registry: cannot read ${lockPath}: ${err.message}`); + process.exit(1); + } + if (result.offenders.length > 0) { + console.error(`package-lock.json resolves ${result.offenders.length} package(s) outside ${ALLOWED_PREFIX}:`); + for (const line of result.offenders) console.error(` ${line}`); + console.error(`Fix: run "npm install --package-lock-only --registry=${ALLOWED_PREFIX}" and commit the lockfile.`); + process.exit(1); + } + console.log('check-lockfile-registry OK'); } -console.log(`check-lockfile-registry OK (${Object.keys(lock.packages ?? {}).length} entries)`); diff --git a/scripts/check-lockfile-registry.test.js b/scripts/check-lockfile-registry.test.js new file mode 100644 index 0000000..e6d4f94 --- /dev/null +++ b/scripts/check-lockfile-registry.test.js @@ -0,0 +1,25 @@ +'use strict'; +// Self-test for check-lockfile-registry.js with one v1 and one v3 fixture. +const assert = require('assert'); +const { scanLockfile } = require('./check-lockfile-registry'); + +const npmjs = 'https://registry.npmjs.org/a/-/a-1.0.0.tgz'; +const mirror = 'https://registry.example.invalid/b/-/b-1.0.0.tgz'; + +// v1: only the legacy `dependencies` tree; the offender is nested. +const v1Clean = { lockfileVersion: 1, dependencies: { a: { resolved: npmjs, dependencies: { b: { resolved: npmjs } } } } }; +const v1Dirty = { lockfileVersion: 1, dependencies: { a: { resolved: npmjs, dependencies: { b: { resolved: mirror } } } } }; +assert.deepStrictEqual(scanLockfile(v1Clean).offenders, []); +assert.deepStrictEqual(scanLockfile(v1Dirty).offenders, [`a > b: ${mirror}`]); + +// v3: only the `packages` map. +const v3Clean = { lockfileVersion: 3, packages: { '': { name: 'x' }, 'node_modules/a': { resolved: npmjs } } }; +const v3Dirty = { lockfileVersion: 3, packages: { '': { name: 'x' }, 'node_modules/b': { resolved: mirror } } }; +assert.deepStrictEqual(scanLockfile(v3Clean).offenders, []); +assert.deepStrictEqual(scanLockfile(v3Dirty).offenders, [`node_modules/b: ${mirror}`]); + +// Neither layout: the check must fail, not pass on nothing. +assert.throws(() => scanLockfile({ lockfileVersion: 3 }), /neither/); +assert.throws(() => scanLockfile(null), /neither/); + +console.log('check-lockfile-registry self-test OK');