Skip to content

Instantly share code, notes, and snippets.

@drifterz13
Created November 27, 2025 10:50
Show Gist options
  • Select an option

  • Save drifterz13/23a9d775ea699d7217231ea503e3e3d9 to your computer and use it in GitHub Desktop.

Select an option

Save drifterz13/23a9d775ea699d7217231ea503e3e3d9 to your computer and use it in GitHub Desktop.
Script to check npm package that was published during Shai-Hulud 2.0 attack timeframe (November 21-24, 2025).
import { parseArgs } from "util";
import fs from "fs";
const { values } = parseArgs({
args: Bun.argv,
options: {
path: {
type: "string",
},
},
strict: true,
allowPositionals: true,
});
const packageJson = JSON.parse(fs.readFileSync(values.path, "utf-8"));
const dependencies = packageJson.dependencies || {};
const devDependencies = packageJson.devDependencies || {};
const packages = [
...Object.keys(dependencies),
...Object.keys(devDependencies),
];
const suspectStartDate = new Date("2025-11-21T00:00:00.000Z");
const suspectEndDate = new Date("2025-11-27T23:59:59.999Z");
async function checkPackage(packageName: string): Promise<{
package: string;
suspectVersions: Array<{
version: string;
timestamp: string;
date: Date;
}>;
}> {
const resp = await fetch(`https://registry.npmjs.org/${packageName}`);
if (!resp.ok) {
throw new Error(`Failed to fetch package data for ${packageName}`);
}
const pkg = await resp.json();
const times = pkg.time || {};
const suspectVersions = [] as any[];
Object.entries(times).forEach(([version, timestamp]) => {
if (version === "created" || version === "modified") return;
const publishDate = new Date(timestamp as string);
if (publishDate >= suspectStartDate && publishDate <= suspectEndDate) {
suspectVersions.push({ version, timestamp, date: publishDate });
}
});
return {
package: packageName,
suspectVersions,
};
}
async function checkAll() {
console.log(
"Checking packages for suspicious publications between Nov 21-26, 2025...\n",
);
console.log("=".repeat(80));
for (const pkg of packages) {
try {
const result = await checkPackage(pkg);
if (result.suspectVersions.length > 0) {
console.log(`\n⚠️ ${result.package}`);
console.log(` Versions published during attack window:`);
result.suspectVersions.forEach((v) => {
console.log(` - ${v.version} published on ${v.timestamp}`);
});
} else {
console.log(`\n✓ ${result.package}`);
console.log(` No publications during Nov 21-26, 2025`);
}
// Rate limiting
await new Promise((resolve) => setTimeout(resolve, 100));
} catch (err) {
console.log(`\n✗ ${err.package}`);
console.log(` Error: ${err.error}`);
}
}
console.log("\n" + "=".repeat(80));
console.log(
"\nNote: Publications during this window are not necessarily malicious.",
);
console.log("Cross-reference with known compromised package lists.");
}
checkAll();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment