Skip to content

Instantly share code, notes, and snippets.

@hungdoansy
Last active September 9, 2025 15:14
Show Gist options
  • Select an option

  • Save hungdoansy/f5c9b886094f38a57d8384e4bf48e10a to your computer and use it in GitHub Desktop.

Select an option

Save hungdoansy/f5c9b886094f38a57d8384e4bf48e10a to your computer and use it in GitHub Desktop.
A Node.js CLI utility for scanning pnpm projects (and workspaces) against a known list of vulnerable package versions.
#!/usr/bin/env node
/*
* Scan pnpm workspace/project for known bad package versions.
*/
const fs = require("fs");
const path = require("path");
const { execFile } = require("child_process");
// 👇 paste your affected list here
const AFFECTED_PACKAGES = [
["backslash", "0.2.1"],
["chalk-template", "1.1.1"],
["supports-hyperlinks", "4.1.1"],
["has-ansi", "6.0.1"],
["simple-swizzle", "0.2.3"],
["color-string", "2.1.1"],
["error-ex", "1.3.3"],
["color-name", "2.0.1"],
["is-arrayish", "0.3.3"],
["slice-ansi", "7.1.1"],
["color-convert", "3.1.1"],
["wrap-ansi", "9.0.1"],
["ansi-regex", "6.2.1"],
["supports-color", "10.2.1"],
["strip-ansi", "7.1.1"],
["chalk", "5.6.1"],
["debug", "4.4.2"],
["ansi-styles", "6.2.2"],
]
const stripAnsi = (s) => s.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
function runPnpmWhy(name) {
return new Promise((resolve) => {
const inWorkspace = fs.existsSync(path.join(process.cwd(), "pnpm-workspace.yaml"));
const args = [];
if (inWorkspace) args.push("-r");
args.push("why", name);
execFile("pnpm", args, { encoding: "utf8", maxBuffer: 20 * 1024 * 1024 }, (err, stdout, stderr) => {
const output = stripAnsi((stdout || "") + (stderr || ""));
resolve(output);
});
});
}
async function checkPackage(name, badVersions) {
const output = await runPnpmWhy(name);
if (!output || !output.includes(name)) {
return { name, status: "not-installed" };
}
const hits = [];
for (const v of Array.isArray(badVersions) ? badVersions : [badVersions]) {
const regex = new RegExp(`\\s*${esc(name)}\\s+${esc(v)}(\\s|$)`, "g");
let match;
while ((match = regex.exec(output)) !== null) {
hits.push(v);
}
}
if (hits.length > 0) return { name, status: "vulnerable", hits };
return { name, status: "safe" };
}
async function main() {
const results = [];
for (const [name, versions] of AFFECTED_PACKAGES) {
results.push(await checkPackage(name, versions));
}
console.log("\nScan results:\n");
for (const r of results) {
if (r.status === "vulnerable") {
console.log(`❌ ${r.name}: affected version(s) found → ${r.hits.join(", ")}`);
} else if (r.status === "safe") {
console.log(`✅ ${r.name}: installed, but at safe version(s)`);
} else {
console.log(`⚪ ${r.name}: not installed`);
}
}
const vulnerable = results.filter((r) => r.status === "vulnerable");
console.log("\nSummary:");
console.log(` Vulnerable : ${vulnerable.length}`);
console.log(` Safe : ${results.filter((r) => r.status === "safe").length}`);
console.log(` Not found : ${results.filter((r) => r.status === "not-installed").length}`);
process.exit(vulnerable.length > 0 ? 1 : 0);
}
main().catch((e) => {
console.error(e);
process.exit(2);
});
#!/usr/bin/env node
/*
* Scan pnpm workspace/project for known bad package versions.
*/
const fs = require("fs");
const path = require("path");
const { execFile } = require("child_process");
// 👇 paste your affected list here
const AFFECTED_PACKAGES = [
["backslash", "0.2.1"],
["chalk-template", "1.1.1"],
["supports-hyperlinks", "4.1.1"],
["has-ansi", "6.0.1"],
["simple-swizzle", "0.2.3"],
["color-string", "2.1.1"],
["error-ex", "1.3.3"],
["color-name", "2.0.1"],
["is-arrayish", "0.3.3"],
["slice-ansi", "7.1.1"],
["color-convert", "3.1.1"],
["wrap-ansi", "9.0.1"],
["ansi-regex", "6.2.1"],
["supports-color", "10.2.1"],
["strip-ansi", "7.1.1"],
["chalk", "5.6.1"],
["debug", "4.4.2"],
["ansi-styles", "6.2.2"],
]
const stripAnsi = (s) => s.replace(/\x1B\[[0-?]*[ -/]*[@-~]/g, "");
const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
function runYarnWhy(name) {
return new Promise((resolve) => {
const inWorkspace = fs.existsSync(path.join(process.cwd(), "yarn.lock"));
const args = [];
args.push("why", name);
if (inWorkspace) args.push("-r");
execFile("yarn", args, { encoding: "utf8", maxBuffer: 20 * 1024 * 1024 }, (err, stdout, stderr) => {
const output = stripAnsi((stdout || "") + (stderr || ""));
resolve(output);
});
});
}
async function checkPackage(name, badVersions) {
const output = await runYarnWhy(name);
if (!output || !output.includes(name)) {
return { name, status: "not-installed" };
}
const hits = [];
for (const v of Array.isArray(badVersions) ? badVersions : [badVersions]) {
const regex = new RegExp(`${esc(name)}@${esc(v)}`, "g");
let match;
while ((match = regex.exec(output)) !== null) {
hits.push(v);
}
}
if (hits.length > 0) return { name, status: "vulnerable", hits };
return { name, status: "safe" };
}
async function main() {
const results = [];
for (const [name, versions] of AFFECTED_PACKAGES) {
results.push(await checkPackage(name, versions));
}
console.log("\nScan results:\n");
for (const r of results) {
if (r.status === "vulnerable") {
console.log(`❌ ${r.name}: affected version(s) found → ${r.hits.join(", ")}`);
} else if (r.status === "safe") {
console.log(`✅ ${r.name}: installed, but at safe version(s)`);
} else {
console.log(`⚪ ${r.name}: not installed`);
}
}
const vulnerable = results.filter((r) => r.status === "vulnerable");
console.log("\nSummary:");
console.log(` Vulnerable : ${vulnerable.length}`);
console.log(` Safe : ${results.filter((r) => r.status === "safe").length}`);
console.log(` Not found : ${results.filter((r) => r.status === "not-installed").length}`);
process.exit(vulnerable.length > 0 ? 1 : 0);
}
main().catch((e) => {
console.error(e);
process.exit(2);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment