Skip to content

Instantly share code, notes, and snippets.

@jariz
Last active September 18, 2025 12:33
Show Gist options
  • Select an option

  • Save jariz/1d77ded091b4a3ff35fb485411b82cb4 to your computer and use it in GitHub Desktop.

Select an option

Save jariz/1d77ded091b4a3ff35fb485411b82cb4 to your computer and use it in GitHub Desktop.
check-compromised-lockfiles-recursive.js
#!/usr/bin/env node
/**
* Recursively check npm/pnpm lockfiles for compromised packages.
* Respects .gitignore to skip ignored files/folders.
*
* NOTE: run npm i -g yaml ignore
*
* Usage:
* node check-compromised-lockfiles-recursive.js
*/
const fs = require("fs");
const path = require("path");
const yaml = require("yaml");
const ignore = require("ignore");
// https://socket.dev/blog/ongoing-supply-chain-attack-targets-crowdstrike-npm-packages#Indicators-of-Compromise
const compromised = [
"@ahmedhfarag/[email protected]",
"@ahmedhfarag/[email protected]",
"[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@art-ws/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@crowdstrike/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@ctrl/[email protected]",
"@hestjs/[email protected]",
"@hestjs/[email protected]",
"@hestjs/[email protected]",
"@hestjs/[email protected]",
"@hestjs/[email protected]",
"@hestjs/[email protected]",
"@hestjs/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nativescript-community/[email protected]",
"@nexe/[email protected]",
"@nexe/[email protected]",
"@nexe/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@nstudio/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@operato/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@teselagen/[email protected]",
"@thangved/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@things-factory/[email protected]",
"@tnf-dev/[email protected]",
"@tnf-dev/[email protected]",
"@tnf-dev/[email protected]",
"@tnf-dev/[email protected]",
"@tnf-dev/[email protected]",
"@ui-ux-gang/[email protected]",
"@yoobic/[email protected]",
"@yoobic/[email protected]",
"@yoobic/[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"[email protected]",
"@rxap/[email protected]",
"@rxap/[email protected]",
"[email protected]",
];
// Load .gitignore rules
function loadGitIgnore(rootDir) {
const ig = ignore();
const gitignorePath = path.join(rootDir, ".gitignore");
if (fs.existsSync(gitignorePath)) {
const content = fs.readFileSync(gitignorePath, "utf8");
ig.add(content);
}
return ig;
}
// Recursively find lockfiles while respecting .gitignore
function findLockfiles(dir, ig) {
const lockfiles = [];
const entries = fs.readdirSync(dir, { withFileTypes: true });
for (const entry of entries) {
const fullPath = path.join(dir, entry.name);
const relPath = path.relative(process.cwd(), fullPath);
if (ig.ignores(relPath)) continue; // respect .gitignore
if (entry.isDirectory()) {
lockfiles.push(...findLockfiles(fullPath, ig));
} else if (["package-lock.json", "pnpm-lock.yaml"].includes(entry.name)) {
lockfiles.push(fullPath);
}
}
return lockfiles;
}
// Check npm lockfile
function checkNpmLock(lockData) {
const findings = [];
function walk(deps) {
if (!deps) return;
for (const [name, info] of Object.entries(deps)) {
const candidate = `${name}@${info.version}`;
if (compromised.includes(candidate)) {
findings.push(candidate);
}
walk(info.dependencies);
}
}
walk(lockData.dependencies);
return findings;
}
// Check pnpm lockfile
function checkPnpmLock(lockData) {
const findings = [];
if (!lockData.packages) return findings;
for (const [pkgPath, info] of Object.entries(lockData.packages)) {
const parts = pkgPath.split("/");
let name, version;
if (pkgPath.startsWith("/@")) {
name = "@" + parts[1] + "/" + parts[2];
version = parts[3];
} else {
name = parts[1];
version = parts[2];
}
const candidate = `${name}@${version}`;
if (compromised.includes(candidate)) findings.push(candidate);
}
return findings;
}
// Main
function main() {
const ig = loadGitIgnore(process.cwd());
const lockfiles = findLockfiles(process.cwd(), ig);
if (lockfiles.length === 0) {
console.log("No lockfiles found.");
return;
}
let anyFindings = false;
for (const lockfile of lockfiles) {
console.log(`\nChecking ${lockfile}...`);
const ext = path.basename(lockfile);
let data;
try {
const content = fs.readFileSync(lockfile, "utf8");
if (ext === "package-lock.json") {
data = JSON.parse(content);
} else {
data = yaml.parse(content);
}
} catch (err) {
console.error(`Failed to parse ${lockfile}:`, err);
continue;
}
const findings =
ext === "package-lock.json" ? checkNpmLock(data) : checkPnpmLock(data);
if (findings.length === 0) {
console.log("✅ No compromised packages found.");
} else {
anyFindings = true;
console.log("⚠️ Found compromised packages:");
for (const f of findings) console.log(`- ${f}`);
}
}
if (anyFindings) process.exitCode = 1;
}
main();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment