supply-scan 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +148 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +1017 -0
- package/dist/index.js.map +1 -0
- package/package.json +64 -0
- package/rules/axios-2026.json +31 -0
- package/rules/chalk-debug-2025.json +40 -0
- package/rules/coa-rc-2021.json +25 -0
- package/rules/colors-faker-2022.json +20 -0
- package/rules/eslint-scope-2018.json +21 -0
- package/rules/event-stream-2018.json +22 -0
- package/rules/glassworm-2026.json +25 -0
- package/rules/lottie-player-2024.json +19 -0
- package/rules/node-ipc-2022.json +28 -0
- package/rules/shai-hulud-2025.json +25 -0
- package/rules/solana-web3-2024.json +20 -0
- package/rules/ua-parser-js-2021.json +24 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/utils.ts","../src/ui.ts","../src/checks/packages.ts","../src/checks/files.ts","../src/checks/network.ts","../src/checks/processes.ts","../src/checks/cache.ts","../src/scanner.ts"],"sourcesContent":["import { fileURLToPath } from 'node:url';\nimport { dirname, join } from 'node:path';\nimport type { Rule } from './types.js';\nimport {\n parseArgs,\n loadRules,\n findProjects,\n getCommonProjectDirs,\n prompt,\n} from './utils.js';\nimport * as ui from './ui.js';\nimport { scan } from './scanner.js';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst RULES_DIR = join(__dirname, '..', 'rules');\n\nconst VERSION = '1.0.0';\n\nconst HELP_TEXT = `\n${ui.c.white}supply-scan${ui.c.reset} — Universal npm supply chain attack scanner\n\n${ui.c.yellow}USAGE${ui.c.reset}\n npx supply-scan Interactive mode (default)\n npx supply-scan --all Scan all attacks, skip prompts\n npx supply-scan --rule axios-2026 Scan specific attack(s)\n npx supply-scan --list List all available rules\n npx supply-scan --ci CI mode (non-interactive)\n\n${ui.c.yellow}OPTIONS${ui.c.reset}\n -a, --all Scan all attacks (skip rule selection)\n -r, --rule <id> Scan specific rule (repeatable)\n -p, --path <dir> Scan specific directory\n -l, --list List all available rules\n --ci CI mode (non-interactive, exit codes only)\n -h, --help Show this help\n -v, --version Show version\n\n${ui.c.yellow}EXIT CODES${ui.c.reset}\n 0 All clear\n 1 Compromise detected\n 2 Warnings found\n\n${ui.c.yellow}EXAMPLES${ui.c.reset}\n npx supply-scan --path ~/projects/my-app\n npx supply-scan --rule axios-2026 --rule node-ipc-2022\n npx supply-scan --ci --all\n`;\n\nexport async function run(argv: string[]): Promise<void> {\n const opts = parseArgs(argv);\n\n // Handle --version\n if (opts.version) {\n console.log(VERSION);\n process.exit(0);\n }\n\n // Handle --help\n if (opts.help) {\n console.log(HELP_TEXT);\n process.exit(0);\n }\n\n // Load rules\n const allRules = loadRules(RULES_DIR);\n\n if (allRules.length === 0) {\n console.error(`${ui.c.red}No rules found in ${RULES_DIR}${ui.c.reset}`);\n process.exit(1);\n }\n\n // Handle --list\n if (opts.list) {\n ui.banner(allRules.length);\n ui.printRuleList(allRules);\n process.exit(0);\n }\n\n // Filter rules if --rule specified\n let selectedRules: Rule[];\n if (opts.rules.length > 0) {\n selectedRules = allRules.filter((r) => opts.rules.includes(r.id));\n if (selectedRules.length === 0) {\n console.error(`${ui.c.red}No matching rules found. Use --list to see available rules.${ui.c.reset}`);\n process.exit(1);\n }\n } else if (opts.all || opts.ci) {\n selectedRules = allRules;\n } else {\n // Interactive: show banner and rule selection\n ui.banner(allRules.length);\n selectedRules = await interactiveRuleSelection(allRules);\n }\n\n if (!opts.ci && (opts.all || opts.rules.length > 0)) {\n ui.banner(selectedRules.length);\n }\n\n // Determine scan directories\n let projectDirs: string[];\n\n if (opts.path) {\n if (!opts.ci) ui.spinnerStart('Searching for npm projects...');\n projectDirs = findProjects([opts.path]);\n if (!opts.ci) ui.spinnerStop();\n } else if (opts.ci || opts.all) {\n // Default to current directory in non-interactive modes\n if (!opts.ci) ui.spinnerStart('Searching for npm projects...');\n projectDirs = findProjects([process.cwd()]);\n if (!opts.ci) ui.spinnerStop();\n } else {\n projectDirs = await interactivePathSelection();\n }\n\n if (!opts.ci) {\n console.log(`\\n ${ui.c.dim}Scanning ${projectDirs.length} projects with ${selectedRules.length} rules...${ui.c.reset}`);\n }\n\n // Run scan\n const summary = await scan({\n rules: selectedRules,\n projectDirs,\n ci: opts.ci,\n });\n\n // Print summary\n if (!opts.ci) {\n ui.printSummary(summary);\n }\n\n // Exit code\n if (summary.failed > 0) {\n process.exit(1);\n } else if (summary.warnings > 0) {\n process.exit(2);\n } else {\n if (opts.ci) {\n console.log('OK');\n }\n process.exit(0);\n }\n}\n\n// ─── Interactive Prompts ────────────────────────────────────────────\n\nasync function interactiveRuleSelection(rules: Rule[]): Promise<Rule[]> {\n console.log('');\n console.log(` ${ui.c.white}Select attacks to scan:${ui.c.reset}`);\n console.log('');\n console.log(` ${ui.c.cyan}0${ui.c.reset}) ${ui.c.green}All attacks (${rules.length} rules)${ui.c.reset}`);\n\n for (let i = 0; i < rules.length; i++) {\n const r = rules[i];\n const sc = ui.severityColors[r.severity] || ui.c.dim;\n console.log(` ${ui.c.cyan}${i + 1}${ui.c.reset}) ${r.name} ${sc}(${r.severity})${ui.c.reset}`);\n }\n\n console.log('');\n const answer = await prompt(` ${ui.c.yellow}Enter numbers separated by commas (default=0): ${ui.c.reset}`);\n\n if (!answer || answer === '0') {\n return rules;\n }\n\n const indices = answer.split(',').map((s) => parseInt(s.trim(), 10) - 1);\n const selected = indices\n .filter((i) => i >= 0 && i < rules.length)\n .map((i) => rules[i]);\n\n return selected.length > 0 ? selected : rules;\n}\n\nasync function interactivePathSelection(): Promise<string[]> {\n console.log('');\n console.log(` ${ui.c.white}Where should I scan?${ui.c.reset}`);\n console.log('');\n console.log(` ${ui.c.cyan}1${ui.c.reset}) Current directory`);\n console.log(` ${ui.c.cyan}2${ui.c.reset}) Common project directories`);\n console.log(` ${ui.c.cyan}3${ui.c.reset}) Enter a custom path`);\n console.log('');\n\n const choice = await prompt(` ${ui.c.yellow}Choose (1/2/3, default=1): ${ui.c.reset}`);\n\n let baseDirs: string[];\n switch (choice) {\n case '2':\n baseDirs = getCommonProjectDirs();\n if (baseDirs.length === 0) {\n console.log(` ${ui.c.dim}No common directories found, scanning current directory...${ui.c.reset}`);\n baseDirs = [process.cwd()];\n }\n break;\n case '3': {\n const customPath = await prompt(` ${ui.c.cyan}Enter path: ${ui.c.reset}`);\n baseDirs = [customPath.replace(/^~/, process.env.HOME || '')];\n break;\n }\n default:\n baseDirs = [process.cwd()];\n }\n\n console.log('');\n ui.spinnerStart('Searching for npm projects...');\n const projects = findProjects(baseDirs);\n ui.spinnerStop();\n\n console.log(` ${ui.c.dim}Found ${projects.length} npm projects${ui.c.reset}`);\n\n return projects;\n}\n\n// ─── Auto-run when executed as CLI ──────────────────────────────────\n\nconst isCLI =\n process.argv[1]?.includes('supply-scan') ||\n process.argv[1]?.includes('dist/index');\n\nif (isCLI) {\n run(process.argv.slice(2));\n}\n","import { readFileSync, readdirSync, existsSync, statSync } from 'node:fs';\nimport { join, resolve } from 'node:path';\nimport { platform, homedir } from 'node:os';\nimport { execSync } from 'node:child_process';\nimport { createInterface } from 'node:readline';\nimport type { Rule, CLIOptions } from './types.js';\n\n// ─── OS Detection ───────────────────────────────────────────────────\n\nexport function getOS(): string {\n return platform();\n}\n\nexport function getHomeDir(): string {\n return homedir();\n}\n\n// ─── JSON Reader ────────────────────────────────────────────────────\n\nexport function readJSON<T>(path: string): T | null {\n try {\n const content = readFileSync(path, 'utf-8');\n return JSON.parse(content) as T;\n } catch {\n return null;\n }\n}\n\n// ─── Version Matching ───────────────────────────────────────────────\n\nexport function getPkgVersion(pkgJsonPath: string): string | null {\n const pkg = readJSON<{ version?: string }>(pkgJsonPath);\n return pkg?.version ?? null;\n}\n\nexport function isVersionCompromised(\n version: string | null,\n badVersions: string[]\n): boolean {\n if (!version) return false;\n return badVersions.includes(version);\n}\n\n// ─── Path Expansion ─────────────────────────────────────────────────\n\nexport function expandPath(p: string): string {\n let result = p;\n if (result.startsWith('~')) {\n result = join(homedir(), result.slice(1));\n }\n if (result.includes('%PROGRAMDATA%')) {\n result = result.replace('%PROGRAMDATA%', process.env.PROGRAMDATA || 'C:\\\\ProgramData');\n }\n if (result.includes('%TEMP%')) {\n result = result.replace('%TEMP%', process.env.TEMP || '/tmp');\n }\n if (result.includes('%USERPROFILE%')) {\n result = result.replace('%USERPROFILE%', homedir());\n }\n return result;\n}\n\n// ─── Project Discovery ──────────────────────────────────────────────\n\nexport function findProjects(baseDirs: string[], maxDepth = 8): string[] {\n const projects: Set<string> = new Set();\n\n function walk(dir: string, depth: number): void {\n if (depth > maxDepth) return;\n\n try {\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) {\n if (entry.name === 'package.json' && depth > 0) {\n projects.add(dir);\n }\n continue;\n }\n\n // Skip irrelevant directories\n const skip = [\n 'node_modules', '.git', 'bower_components',\n '.cache', '.Trash', 'Library', '.npm',\n ];\n if (skip.includes(entry.name)) continue;\n\n walk(join(dir, entry.name), depth + 1);\n }\n } catch {\n // Permission denied or broken symlink\n }\n }\n\n // Also check base dir itself for package.json\n for (const base of baseDirs) {\n if (!existsSync(base)) continue;\n if (existsSync(join(base, 'package.json'))) {\n projects.add(base);\n }\n walk(base, 0);\n }\n\n return Array.from(projects);\n}\n\n// ─── Common Project Directories ─────────────────────────────────────\n\nexport function getCommonProjectDirs(): string[] {\n const home = homedir();\n const candidates = [\n 'Desktop', 'Documents', 'Projects', 'projects',\n 'Developer', 'dev', 'code', 'Code',\n 'Sites', 'sites', 'www', 'Work', 'work',\n 'workspace', 'Workspace', 'repos', 'Repos',\n 'src', 'github', 'GitHub',\n ];\n return candidates\n .map((d) => join(home, d))\n .filter((d) => existsSync(d));\n}\n\n// ─── Rule Loader ────────────────────────────────────────────────────\n\nexport function loadRules(rulesDir: string): Rule[] {\n const rules: Rule[] = [];\n\n try {\n const files = readdirSync(rulesDir).filter((f) => f.endsWith('.json'));\n for (const file of files) {\n const rule = readJSON<Rule>(join(rulesDir, file));\n if (rule && rule.id && rule.packages) {\n rules.push(rule);\n }\n }\n } catch {\n // Rules directory not found\n }\n\n // Sort by date descending (newest first)\n rules.sort((a, b) => b.date.localeCompare(a.date));\n return rules;\n}\n\n// ─── CLI Arg Parser ─────────────────────────────────────────────────\n\nexport function parseArgs(argv: string[]): CLIOptions {\n const opts: CLIOptions = {\n rules: [],\n all: false,\n list: false,\n path: null,\n ci: false,\n help: false,\n version: false,\n };\n\n for (let i = 0; i < argv.length; i++) {\n const arg = argv[i];\n switch (arg) {\n case '--all':\n case '-a':\n opts.all = true;\n break;\n case '--list':\n case '-l':\n opts.list = true;\n break;\n case '--ci':\n opts.ci = true;\n opts.all = true;\n break;\n case '--help':\n case '-h':\n opts.help = true;\n break;\n case '--version':\n case '-v':\n opts.version = true;\n break;\n case '--rule':\n case '-r':\n if (i + 1 < argv.length) {\n opts.rules.push(argv[++i]);\n }\n break;\n case '--path':\n case '-p':\n if (i + 1 < argv.length) {\n opts.path = resolve(argv[++i]);\n }\n break;\n }\n }\n\n return opts;\n}\n\n// ─── Shell Command Runner ───────────────────────────────────────────\n\nexport function runCommand(cmd: string): string {\n try {\n return execSync(cmd, { encoding: 'utf-8', timeout: 10000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();\n } catch {\n return '';\n }\n}\n\n// ─── Interactive Prompt ─────────────────────────────────────────────\n\nexport function prompt(question: string): Promise<string> {\n const rl = createInterface({\n input: process.stdin,\n output: process.stdout,\n });\n return new Promise((resolve) => {\n rl.question(question, (answer) => {\n rl.close();\n resolve(answer.trim());\n });\n });\n}\n\n// ─── File Exists Check ──────────────────────────────────────────────\n\nexport function fileExists(path: string): boolean {\n try {\n return statSync(path).isFile();\n } catch {\n return false;\n }\n}\n\nexport function dirExists(path: string): boolean {\n try {\n return statSync(path).isDirectory();\n } catch {\n return false;\n }\n}\n\n","import { type ResultType, type CheckResult, type ScanSummary, type Rule } from './types.js';\n\n// ─── ANSI Color Codes (zero deps) ──────────────────────────────────\n\nconst ESC = '\\x1b';\n\nexport const c = {\n red: `${ESC}[1;31m`,\n green: `${ESC}[1;32m`,\n yellow: `${ESC}[1;33m`,\n cyan: `${ESC}[1;36m`,\n white: `${ESC}[1;37m`,\n dim: `${ESC}[2m`,\n bold: `${ESC}[1m`,\n reset: `${ESC}[0m`,\n bgRed: `${ESC}[41m`,\n bgGreen: `${ESC}[42m`,\n bgYellow: `${ESC}[43m`,\n bgBlue: `${ESC}[44m`,\n};\n\n// ─── Symbols ────────────────────────────────────────────────────────\n\nconst sym = {\n pass: '\\u2705',\n fail: '\\uD83D\\uDEA8',\n warn: '\\u26A0\\uFE0F ',\n info: '\\uD83D\\uDCA1',\n scan: '\\uD83D\\uDD0D',\n shield: '\\uD83D\\uDEE1\\uFE0F ',\n skull: '\\uD83D\\uDC80',\n clean: '\\u2728',\n gear: '\\u2699\\uFE0F ',\n pkg: '\\uD83D\\uDCE6',\n lock: '\\uD83D\\uDD12',\n globe: '\\uD83C\\uDF10',\n folder: '\\uD83D\\uDCC1',\n};\n\n// ─── Banner ─────────────────────────────────────────────────────────\n\nexport function banner(ruleCount: number): void {\n const w = process.stdout.write.bind(process.stdout);\n w('\\n');\n w(`${c.cyan} ___ _ _ ___ ___ _ _ _ ___ ___ _ _ _ ${c.reset}\\n`);\n w(`${c.cyan} / __| | | | _ \\\\ _ \\\\ | | | | | / __|/ __| /_\\\\ | \\\\| |${c.reset}\\n`);\n w(`${c.cyan} \\\\__ \\\\ |_| | _/ _/ |_| |_| |_| (__| (__ / _ \\\\| .\\` |${c.reset}\\n`);\n w(`${c.cyan} |___/\\\\___/|_| |_| \\\\__, |_____|\\\\___|\\\\___|_/ \\\\_\\\\_|\\\\_|${c.reset}\\n`);\n w(`${c.cyan} |___/ ${c.dim}v1.0.0${c.reset}\\n`);\n w('\\n');\n w(` ${c.white}Universal npm Supply Chain Attack Scanner${c.reset}\\n`);\n w(` ${c.dim}Detects ${ruleCount} known attacks | Zero dependencies${c.reset}\\n`);\n w('\\n');\n divider();\n}\n\n// ─── Divider ────────────────────────────────────────────────────────\n\nexport function divider(): void {\n console.log(`${c.dim}${'─'.repeat(60)}${c.reset}`);\n}\n\n// ─── Section Header ─────────────────────────────────────────────────\n\nexport function sectionHeader(num: number, total: number, title: string, icon: string): void {\n console.log('');\n console.log(`${c.bgBlue}${c.white}${c.bold} ${icon} CHECK ${num}/${total} │ ${title} ${c.reset}`);\n divider();\n}\n\n// ─── Result Formatters ──────────────────────────────────────────────\n\nexport function result(type: ResultType, msg: string): void {\n const icons: Record<ResultType, string> = {\n pass: sym.pass,\n fail: sym.fail,\n warn: sym.warn,\n info: sym.info,\n };\n const colors: Record<ResultType, string> = {\n pass: c.green,\n fail: `${c.red}${c.bold}`,\n warn: c.yellow,\n info: c.dim,\n };\n console.log(` ${icons[type]} ${colors[type]}${msg}${c.reset}`);\n}\n\nexport function resultDetail(msg: string): void {\n console.log(` ${c.red}→ ${msg}${c.reset}`);\n}\n\n// ─── Spinner ────────────────────────────────────────────────────────\n\nconst SPIN_FRAMES = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n\nlet spinnerTimer: ReturnType<typeof setInterval> | null = null;\nlet spinnerFrame = 0;\n\nexport function spinnerStart(msg: string): void {\n spinnerFrame = 0;\n spinnerTimer = setInterval(() => {\n const frame = SPIN_FRAMES[spinnerFrame % SPIN_FRAMES.length];\n process.stdout.write(`\\r ${c.cyan}${frame}${c.reset} ${c.dim}${msg}${c.reset}`);\n spinnerFrame++;\n }, 80);\n}\n\nexport function spinnerStop(): void {\n if (spinnerTimer) {\n clearInterval(spinnerTimer);\n spinnerTimer = null;\n process.stdout.write('\\r\\x1b[K');\n }\n}\n\n// ─── Progress Bar ───────────────────────────────────────────────────\n\nexport function progressBar(current: number, total: number, label: string): void {\n const width = 30;\n const pct = Math.round((current / total) * 100);\n const filled = Math.round((current / total) * width);\n const empty = width - filled;\n const bar = `${c.green}${'█'.repeat(filled)}${c.reset}${c.dim}${'░'.repeat(empty)}${c.reset}`;\n const name = label.length > 25 ? label.slice(0, 22) + '...' : label;\n process.stdout.write(`\\r ${c.dim}[${c.reset}${bar}${c.dim}]${c.reset} ${c.white}${pct}%${c.reset} ${c.dim}${name.padEnd(25)}${c.reset}`);\n}\n\nexport function progressClear(): void {\n process.stdout.write('\\r\\x1b[K');\n}\n\n// ─── Rule List ──────────────────────────────────────────────────────\n\nexport const severityColors: Record<string, string> = {\n critical: c.red,\n high: c.yellow,\n medium: c.cyan,\n low: c.dim,\n};\n\nexport function printRuleList(rules: Rule[]): void {\n console.log('');\n console.log(` ${c.white}Available attack rules (${rules.length}):${c.reset}`);\n console.log('');\n\n for (const rule of rules) {\n const sc = severityColors[rule.severity] || c.dim;\n const pkgCount =\n Object.keys(rule.packages.compromised).length +\n Object.keys(rule.packages.malicious).length;\n console.log(\n ` ${c.dim}•${c.reset} ${c.white}${rule.id.padEnd(25)}${c.reset} ${sc}${rule.severity.padEnd(10)}${c.reset} ${c.dim}${rule.date}${c.reset} ${c.dim}(${pkgCount} pkgs)${c.reset}`\n );\n console.log(` ${c.dim}${rule.description}${c.reset}`);\n }\n console.log('');\n}\n\n// ─── Summary ────────────────────────────────────────────────────────\n\nexport function printSummary(summary: ScanSummary): void {\n console.log('');\n console.log(`${c.cyan}${'═'.repeat(60)}${c.reset}`);\n console.log(` ${sym.shield} ${c.white}${c.bold}SCAN COMPLETE${c.reset}`);\n console.log(`${c.cyan}${'═'.repeat(60)}${c.reset}`);\n console.log('');\n console.log(` ${c.dim}Projects scanned:${c.reset} ${c.white}${summary.projectsScanned}${c.reset}`);\n console.log(` ${c.dim}Rules checked:${c.reset} ${c.white}${summary.rulesChecked}${c.reset}`);\n console.log(` ${c.dim}Total checks:${c.reset} ${c.white}${summary.totalChecks}${c.reset}`);\n console.log(` ${c.green}Passed:${c.reset} ${c.green}${summary.passed}${c.reset}`);\n console.log(` ${c.yellow}Warnings:${c.reset} ${c.yellow}${summary.warnings}${c.reset}`);\n console.log(` ${c.red}Failed:${c.reset} ${c.red}${summary.failed}${c.reset}`);\n console.log('');\n divider();\n\n if (summary.failed > 0) {\n console.log('');\n console.log(` ${c.bgRed}${c.white}${c.bold} ⛔ COMPROMISE DETECTED ⛔ ${c.reset}`);\n console.log('');\n console.log(` ${c.red}${c.bold}Immediate Actions Required:${c.reset}`);\n console.log('');\n console.log(` ${c.red}1.${c.reset} Disconnect from the network`);\n console.log(` ${c.red}2.${c.reset} Rotate ALL credentials, tokens, API keys, SSH keys`);\n console.log(` ${c.red}3.${c.reset} Remove compromised packages: ${c.white}npm install <pkg>@latest${c.reset}`);\n console.log(` ${c.red}4.${c.reset} Clean npm cache: ${c.white}npm cache clean --force${c.reset}`);\n console.log(` ${c.red}5.${c.reset} Review system for backdoors and persistence mechanisms`);\n console.log(` ${c.red}6.${c.reset} Consider full system wipe if RAT/worm was executed`);\n console.log('');\n\n if (summary.compromisedProjects.length > 0) {\n console.log(` ${c.red}${c.bold}Compromised Projects:${c.reset}`);\n for (const proj of summary.compromisedProjects) {\n console.log(` ${c.red}${sym.skull} ${proj}${c.reset}`);\n }\n console.log('');\n }\n\n // Show failed results grouped by rule\n const failedByRule = new Map<string, CheckResult[]>();\n for (const r of summary.results.filter((r) => r.type === 'fail')) {\n const arr = failedByRule.get(r.rule) || [];\n arr.push(r);\n failedByRule.set(r.rule, arr);\n }\n for (const [rule, results] of failedByRule) {\n console.log(` ${c.red}${c.bold}${rule}:${c.reset}`);\n for (const r of results) {\n console.log(` ${c.red}→ ${r.message}${c.reset}`);\n if (r.details) console.log(` ${c.dim}${r.details}${c.reset}`);\n }\n console.log('');\n }\n } else if (summary.warnings > 0) {\n console.log('');\n console.log(` ${c.bgYellow}${c.white}${c.bold} ⚠ WARNINGS FOUND — REVIEW RECOMMENDED ⚠ ${c.reset}`);\n console.log('');\n console.log(` ${c.yellow}Some items need manual review. Check the warnings above.${c.reset}`);\n console.log('');\n } else {\n console.log('');\n console.log(` ${c.bgGreen}${c.white}${c.bold} ${sym.clean} ALL CLEAR — NO COMPROMISE DETECTED ${sym.clean} ${c.reset}`);\n console.log('');\n console.log(` ${c.green}Your system appears clean from all known supply chain attacks.${c.reset}`);\n console.log('');\n console.log(` ${c.dim}Prevention tips:${c.reset}`);\n console.log(` ${c.cyan}›${c.reset} Use lockfiles and verify package integrity`);\n console.log(` ${c.cyan}›${c.reset} Pin dependencies to exact versions`);\n console.log(` ${c.cyan}›${c.reset} Enable npm audit in your CI/CD pipeline`);\n console.log(` ${c.cyan}›${c.reset} Monitor for SLSA provenance on critical packages`);\n console.log('');\n }\n\n divider();\n console.log('');\n console.log(` ${c.dim}Scan completed at ${new Date().toISOString()}${c.reset}`);\n console.log(` ${c.dim}https://github.com/AsyrafHussin/supply-scan${c.reset}`);\n console.log('');\n}\n","import { join } from 'node:path';\nimport { readFileSync } from 'node:fs';\nimport type { Rule, CheckResult } from '../types.js';\nimport { dirExists, getPkgVersion, isVersionCompromised } from '../utils.js';\n\nexport function checkPackages(\n rules: Rule[],\n projectDirs: string[]\n): CheckResult[] {\n const results: CheckResult[] = [];\n\n for (const dir of projectDirs) {\n // Cache lockfile contents per project (read once, check all rules)\n const lockfileCache = readLockfiles(dir);\n\n for (const rule of rules) {\n // Check compromised packages in node_modules\n for (const [pkg, badVersions] of Object.entries(rule.packages.compromised)) {\n const version = getPkgVersion(join(dir, 'node_modules', pkg, 'package.json'));\n if (version && isVersionCompromised(version, badVersions)) {\n results.push({\n type: 'fail',\n rule: rule.id,\n check: 'packages',\n message: `${pkg}@${version} is COMPROMISED`,\n details: `${rule.name} — ${dir}`,\n });\n }\n }\n\n // Check malicious packages (should not exist at all)\n for (const [pkg] of Object.entries(rule.packages.malicious)) {\n const pkgDir = join(dir, 'node_modules', pkg);\n if (dirExists(pkgDir)) {\n const version = getPkgVersion(join(pkgDir, 'package.json'));\n results.push({\n type: 'fail',\n rule: rule.id,\n check: 'packages',\n message: `Malicious package ${pkg}${version ? `@${version}` : ''} found`,\n details: `${rule.name} — ${dir}`,\n });\n }\n }\n\n // Check lockfiles for references\n checkLockfilesForRule(lockfileCache, dir, rule, results);\n }\n }\n\n return results;\n}\n\nfunction readLockfiles(dir: string): Map<string, string> {\n const cache = new Map<string, string>();\n // All supported lockfile formats: npm, yarn, pnpm, bun\n const lockfiles = [\n 'package-lock.json',\n 'yarn.lock',\n 'pnpm-lock.yaml',\n 'bun.lock', // bun text-based lockfile (bun v1.2+)\n 'bun.lockb', // bun binary lockfile (older bun)\n ];\n for (const name of lockfiles) {\n try {\n // bun.lockb is binary — read as latin1 so string search still works on package names\n const encoding = name === 'bun.lockb' ? 'latin1' : 'utf-8';\n cache.set(name, readFileSync(join(dir, name), encoding));\n } catch {\n // File doesn't exist\n }\n }\n return cache;\n}\n\nfunction checkLockfilesForRule(\n lockfileCache: Map<string, string>,\n dir: string,\n rule: Rule,\n results: CheckResult[]\n): void {\n for (const [lockfile, content] of lockfileCache) {\n // Check for malicious package names\n for (const pkg of Object.keys(rule.packages.malicious)) {\n if (content.includes(pkg)) {\n results.push({\n type: 'fail',\n rule: rule.id,\n check: 'lockfile',\n message: `${lockfile} references malicious package \"${pkg}\"`,\n details: `${rule.name} — ${dir}`,\n });\n }\n }\n\n // Check for compromised versions using specific patterns\n for (const [pkg, versions] of Object.entries(rule.packages.compromised)) {\n for (const ver of versions) {\n // Only match patterns that tie the package name to the version\n const tied = [\n `\"${pkg}\": \"${ver}\"`, // package-lock.json / bun.lock\n `${pkg}@${ver}`, // yarn.lock / pnpm-lock.yaml\n `\"${pkg}\",\"${ver}\"`, // bun.lockb binary format\n ];\n if (tied.some((p) => content.includes(p))) {\n results.push({\n type: 'warn',\n rule: rule.id,\n check: 'lockfile',\n message: `${lockfile} may reference ${pkg}@${ver}`,\n details: `${rule.name} — ${dir}`,\n });\n }\n }\n }\n }\n}\n","import type { Rule, CheckResult } from '../types.js';\nimport { expandPath, fileExists, getOS } from '../utils.js';\n\n/**\n * Check for known malware artifacts on disk.\n * Checks OS-specific file paths from rule IOCs.\n */\nexport function checkFiles(rules: Rule[]): CheckResult[] {\n const results: CheckResult[] = [];\n const os = getOS();\n\n for (const rule of rules) {\n const filePaths = rule.ioc.files?.[os];\n if (!filePaths || filePaths.length === 0) continue;\n\n for (const rawPath of filePaths) {\n const path = expandPath(rawPath);\n if (fileExists(path)) {\n results.push({\n type: 'fail',\n rule: rule.id,\n check: 'files',\n message: `Malware artifact found: ${path}`,\n details: rule.name,\n });\n }\n }\n }\n\n return results;\n}\n","import { readFileSync } from 'node:fs';\nimport type { Rule, CheckResult } from '../types.js';\nimport { getOS, runCommand } from '../utils.js';\n\n/**\n * Check for active C2 connections and network IOCs.\n */\nexport function checkNetwork(rules: Rule[]): CheckResult[] {\n const results: CheckResult[] = [];\n const os = getOS();\n\n // Collect all C2 IPs and domains from rules\n const allIPs = new Set<string>();\n const allDomains = new Set<string>();\n const ruleByIOC = new Map<string, string>();\n\n for (const rule of rules) {\n for (const ip of rule.ioc.ips ?? []) {\n allIPs.add(ip);\n ruleByIOC.set(ip, rule.id);\n }\n for (const domain of rule.ioc.domains ?? []) {\n allDomains.add(domain);\n ruleByIOC.set(domain, rule.id);\n }\n }\n\n if (allIPs.size === 0 && allDomains.size === 0) return results;\n\n // Check active connections\n const connections = getActiveConnections(os);\n for (const ip of allIPs) {\n if (connections.includes(ip)) {\n results.push({\n type: 'fail',\n rule: ruleByIOC.get(ip) || 'unknown',\n check: 'network',\n message: `Active connection to C2 IP: ${ip}`,\n details: 'Disconnect from network immediately!',\n });\n }\n }\n\n for (const domain of allDomains) {\n if (connections.includes(domain)) {\n results.push({\n type: 'fail',\n rule: ruleByIOC.get(domain) || 'unknown',\n check: 'network',\n message: `Active connection to C2 domain: ${domain}`,\n details: 'Disconnect from network immediately!',\n });\n }\n }\n\n // Check /etc/hosts\n checkHostsFile(allIPs, allDomains, ruleByIOC, results);\n\n return results;\n}\n\nfunction getActiveConnections(os: string): string {\n if (os === 'darwin' || os === 'linux') {\n return runCommand('lsof -i -n -P 2>/dev/null || netstat -an 2>/dev/null');\n } else if (os === 'win32') {\n return runCommand('netstat -an');\n }\n return '';\n}\n\nfunction checkHostsFile(\n ips: Set<string>,\n domains: Set<string>,\n ruleByIOC: Map<string, string>,\n results: CheckResult[]\n): void {\n try {\n const hosts = readFileSync('/etc/hosts', 'utf-8');\n for (const ip of ips) {\n if (hosts.includes(ip)) {\n results.push({\n type: 'warn',\n rule: ruleByIOC.get(ip) || 'unknown',\n check: 'network',\n message: `C2 IP ${ip} found in /etc/hosts`,\n });\n }\n }\n for (const domain of domains) {\n if (hosts.includes(domain)) {\n results.push({\n type: 'warn',\n rule: ruleByIOC.get(domain) || 'unknown',\n check: 'network',\n message: `C2 domain ${domain} found in /etc/hosts`,\n });\n }\n }\n } catch {\n // Can't read hosts file\n }\n}\n","import type { Rule, CheckResult } from '../types.js';\nimport { getOS, runCommand, dirExists } from '../utils.js';\n\n/**\n * Check for known malicious processes running on the system.\n */\nexport function checkProcesses(rules: Rule[]): CheckResult[] {\n const results: CheckResult[] = [];\n const os = getOS();\n\n // Collect all process patterns from rules\n const processPatterns = new Map<string, string>(); // pattern -> ruleId\n\n for (const rule of rules) {\n for (const proc of rule.ioc.processes ?? []) {\n processPatterns.set(proc, rule.id);\n }\n }\n\n if (processPatterns.size === 0) return results;\n\n // Get running processes\n const processList = getProcessList(os);\n if (!processList) return results;\n\n for (const [pattern, ruleId] of processPatterns) {\n if (processList.includes(pattern)) {\n results.push({\n type: 'fail',\n rule: ruleId,\n check: 'processes',\n message: `Suspicious process running: ${pattern}`,\n details: 'This may indicate an active compromise',\n });\n }\n }\n\n // Check macOS persistence mechanisms\n if (os === 'darwin') {\n checkMacOSPersistence(rules, results);\n }\n\n return results;\n}\n\nfunction getProcessList(os: string): string {\n if (os === 'darwin' || os === 'linux') {\n return runCommand('ps aux 2>/dev/null');\n } else if (os === 'win32') {\n return runCommand('tasklist /v 2>nul');\n }\n return '';\n}\n\nfunction checkMacOSPersistence(rules: Rule[], results: CheckResult[]): void {\n const launchDirs = [\n `${process.env.HOME}/Library/LaunchAgents`,\n '/Library/LaunchAgents',\n '/Library/LaunchDaemons',\n ];\n\n // Collect all IOC strings to search for\n const searchStrings = new Map<string, string>();\n for (const rule of rules) {\n for (const s of rule.ioc.strings ?? []) {\n searchStrings.set(s, rule.id);\n }\n for (const domain of rule.ioc.domains ?? []) {\n searchStrings.set(domain, rule.id);\n }\n for (const ip of rule.ioc.ips ?? []) {\n searchStrings.set(ip, rule.id);\n }\n }\n\n const existingDirs = launchDirs.filter(dirExists);\n if (existingDirs.length === 0 || searchStrings.size === 0) return;\n\n const pattern = Array.from(searchStrings.keys()).join('|');\n const hits = runCommand(\n `grep -rlE \"${pattern}\" ${existingDirs.map((d) => `\"${d}\"`).join(' ')} 2>/dev/null`\n );\n\n if (hits) {\n for (const file of hits.split('\\n').filter(Boolean)) {\n // Find which rule matched\n const content = runCommand(`cat \"${file}\" 2>/dev/null`);\n let matchedRule = 'unknown';\n for (const [s, ruleId] of searchStrings) {\n if (content.includes(s)) { matchedRule = ruleId; break; }\n }\n results.push({\n type: 'fail',\n rule: matchedRule,\n check: 'processes',\n message: `Suspicious LaunchAgent/Daemon: ${file}`,\n details: 'May indicate malware persistence',\n });\n }\n }\n}\n","import { join } from 'node:path';\nimport { homedir } from 'node:os';\nimport type { Rule, CheckResult } from '../types.js';\nimport { dirExists, runCommand } from '../utils.js';\n\ninterface CacheLocation {\n name: string;\n dir: string;\n cleanCmd: string;\n}\n\nfunction detectCaches(): CacheLocation[] {\n const home = homedir();\n const caches: CacheLocation[] = [];\n\n // npm\n const npmCache = runCommand('npm config get cache') || join(home, '.npm');\n if (dirExists(npmCache)) {\n caches.push({ name: 'npm', dir: npmCache, cleanCmd: 'npm cache clean --force' });\n }\n\n // pnpm\n const pnpmStore = runCommand('pnpm store path 2>/dev/null');\n if (pnpmStore && dirExists(pnpmStore)) {\n caches.push({ name: 'pnpm', dir: pnpmStore, cleanCmd: 'pnpm store prune' });\n }\n\n // yarn v1\n const yarnCache = runCommand('yarn cache dir 2>/dev/null');\n if (yarnCache && dirExists(yarnCache)) {\n caches.push({ name: 'yarn', dir: yarnCache, cleanCmd: 'yarn cache clean' });\n }\n\n // yarn v2+ (berry) — project-local .yarn/cache\n const yarnBerryCache = join(process.cwd(), '.yarn', 'cache');\n if (dirExists(yarnBerryCache)) {\n caches.push({ name: 'yarn-berry', dir: yarnBerryCache, cleanCmd: 'yarn cache clean --all' });\n }\n\n // bun\n const bunCache = join(home, '.bun', 'install', 'cache');\n if (dirExists(bunCache)) {\n caches.push({ name: 'bun', dir: bunCache, cleanCmd: 'bun pm cache rm' });\n }\n\n return caches;\n}\n\n/**\n * Scan all package manager caches for malicious packages.\n * Supports: npm, pnpm, yarn (v1 & v2+), bun.\n */\nexport function checkCache(rules: Rule[]): CheckResult[] {\n const results: CheckResult[] = [];\n const caches = detectCaches();\n\n if (caches.length === 0) return results;\n\n // Collect all malicious package names and compromised tarballs\n const maliciousPkgs = new Map<string, string>();\n const compromisedTarballs = new Map<string, string>();\n\n for (const rule of rules) {\n for (const pkg of Object.keys(rule.packages.malicious)) {\n maliciousPkgs.set(pkg, rule.id);\n }\n for (const [pkg, versions] of Object.entries(rule.packages.compromised)) {\n for (const ver of versions) {\n compromisedTarballs.set(`${pkg}-${ver}.tgz`, rule.id);\n }\n }\n }\n\n if (maliciousPkgs.size === 0 && compromisedTarballs.size === 0) return results;\n\n for (const cache of caches) {\n scanCacheDir(cache, maliciousPkgs, compromisedTarballs, results);\n }\n\n return results;\n}\n\nfunction scanCacheDir(\n cache: CacheLocation,\n maliciousPkgs: Map<string, string>,\n compromisedTarballs: Map<string, string>,\n results: CheckResult[]\n): void {\n // Find malicious package directories\n if (maliciousPkgs.size > 0) {\n const nameArgs = Array.from(maliciousPkgs.keys())\n .map((n) => `-name \"${n}\"`)\n .join(' -o ');\n const found = runCommand(\n `find \"${cache.dir}\" -maxdepth 4 -type d \\\\( ${nameArgs} \\\\) 2>/dev/null`\n );\n if (found) {\n for (const line of found.split('\\n').filter(Boolean)) {\n const name = line.split('/').pop() || '';\n const ruleId = maliciousPkgs.get(name);\n if (ruleId) {\n results.push({\n type: 'fail',\n rule: ruleId,\n check: 'cache',\n message: `Malicious package \"${name}\" found in ${cache.name} cache`,\n details: `Run: ${cache.cleanCmd}`,\n });\n }\n }\n }\n }\n\n // Find compromised tarballs\n if (compromisedTarballs.size > 0) {\n const nameArgs = Array.from(compromisedTarballs.keys())\n .map((n) => `-name \"${n}\"`)\n .join(' -o ');\n const found = runCommand(\n `find \"${cache.dir}\" -maxdepth 4 -type f \\\\( ${nameArgs} \\\\) 2>/dev/null`\n );\n if (found) {\n for (const line of found.split('\\n').filter(Boolean)) {\n const name = line.split('/').pop() || '';\n const ruleId = compromisedTarballs.get(name);\n if (ruleId) {\n results.push({\n type: 'warn',\n rule: ruleId,\n check: 'cache',\n message: `Compromised tarball ${name} in ${cache.name} cache`,\n details: `Run: ${cache.cleanCmd}`,\n });\n }\n }\n }\n }\n}\n","import type { Rule, CheckResult, ScanSummary } from './types.js';\nimport { checkPackages } from './checks/packages.js';\nimport { checkFiles } from './checks/files.js';\nimport { checkNetwork } from './checks/network.js';\nimport { checkProcesses } from './checks/processes.js';\nimport { checkCache } from './checks/cache.js';\nimport * as ui from './ui.js';\n\ninterface ScanOptions {\n rules: Rule[];\n projectDirs: string[];\n ci: boolean;\n}\n\ninterface CheckDef {\n title: string;\n icon: string;\n passMessage: string;\n run: () => CheckResult[];\n}\n\nexport async function scan(options: ScanOptions): Promise<ScanSummary> {\n const { rules, projectDirs, ci } = options;\n const allResults: CheckResult[] = [];\n\n const checks: CheckDef[] = [\n {\n title: 'COMPROMISED PACKAGES',\n icon: '\\uD83D\\uDCE6',\n passMessage: `All ${projectDirs.length} projects clean — no compromised packages`,\n run: () => checkPackages(rules, projectDirs),\n },\n {\n title: 'MALWARE FILES',\n icon: '\\uD83D\\uDC80',\n passMessage: 'No malware artifacts found on disk',\n run: () => checkFiles(rules),\n },\n {\n title: 'NETWORK CONNECTIONS',\n icon: '\\uD83C\\uDF10',\n passMessage: 'No active C2 connections detected',\n run: () => checkNetwork(rules),\n },\n {\n title: 'SUSPICIOUS PROCESSES',\n icon: '\\u2699\\uFE0F ',\n passMessage: 'No suspicious processes detected',\n run: () => checkProcesses(rules),\n },\n {\n title: 'PACKAGE MANAGER CACHES',\n icon: '\\uD83D\\uDCC1',\n passMessage: 'All package manager caches are clean',\n run: () => checkCache(rules),\n },\n ];\n\n const totalChecks = checks.length;\n\n for (let i = 0; i < checks.length; i++) {\n const check = checks[i];\n\n if (!ci) ui.sectionHeader(i + 1, totalChecks, check.title, check.icon);\n if (!ci) ui.spinnerStart(`Running ${check.title.toLowerCase()} check...`);\n\n const results = check.run();\n allResults.push(...results);\n\n if (!ci) ui.spinnerStop();\n if (!ci) displayResults(results, check.passMessage);\n }\n\n return buildSummary(allResults, projectDirs.length, rules.length, totalChecks);\n}\n\nfunction displayResults(results: CheckResult[], passMessage: string): void {\n if (results.filter((r) => r.type === 'fail' || r.type === 'warn').length === 0) {\n ui.result('pass', passMessage);\n }\n for (const r of results) {\n ui.result(r.type, r.message);\n if (r.details) ui.resultDetail(r.details);\n }\n}\n\nfunction buildSummary(\n results: CheckResult[],\n projectsScanned: number,\n rulesChecked: number,\n totalChecks: number\n): ScanSummary {\n const compromisedProjects = new Set<string>();\n for (const r of results) {\n if (r.type === 'fail' && r.details) {\n const match = r.details.match(/— (.+)$/);\n if (match) compromisedProjects.add(match[1]);\n }\n }\n\n const failCount = results.filter((r) => r.type === 'fail').length;\n const warnCount = results.filter((r) => r.type === 'warn').length;\n\n return {\n projectsScanned,\n rulesChecked,\n totalChecks: results.length || totalChecks,\n passed: results.length === 0 ? totalChecks : results.length - failCount - warnCount,\n failed: failCount,\n warnings: warnCount,\n results,\n compromisedProjects: Array.from(compromisedProjects),\n };\n}\n"],"mappings":"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","names":["join","result","resolve","r","join","readFileSync","join","readFileSync","readFileSync","readFileSync","join","homedir","homedir","join","join"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "supply-scan",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Universal npm supply chain attack scanner. Detects compromised packages from 12+ known attacks.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"security",
|
|
7
|
+
"supply-chain",
|
|
8
|
+
"scanner",
|
|
9
|
+
"npm",
|
|
10
|
+
"vulnerability",
|
|
11
|
+
"malware",
|
|
12
|
+
"axios",
|
|
13
|
+
"chalk",
|
|
14
|
+
"supply-chain-attack"
|
|
15
|
+
],
|
|
16
|
+
"author": "Asyraf Hussin",
|
|
17
|
+
"license": "MIT",
|
|
18
|
+
"repository": {
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "git+https://github.com/AsyrafHussin/supply-scan.git"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/AsyrafHussin/supply-scan/issues"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://github.com/AsyrafHussin/supply-scan#readme",
|
|
26
|
+
"type": "module",
|
|
27
|
+
"bin": {
|
|
28
|
+
"supply-scan": "dist/index.js"
|
|
29
|
+
},
|
|
30
|
+
"main": "dist/index.js",
|
|
31
|
+
"files": [
|
|
32
|
+
"dist/",
|
|
33
|
+
"rules/"
|
|
34
|
+
],
|
|
35
|
+
"engines": {
|
|
36
|
+
"node": ">=16"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsup",
|
|
40
|
+
"dev": "node dist/index.js",
|
|
41
|
+
"typecheck": "tsc --noEmit",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"test:watch": "vitest",
|
|
44
|
+
"test:coverage": "vitest run --coverage",
|
|
45
|
+
"lint": "eslint .",
|
|
46
|
+
"format": "prettier --write --cache \"**/*.[tj]s?(x)\"",
|
|
47
|
+
"format:check": "prettier --check --cache \"**/*.[tj]s?(x)\"",
|
|
48
|
+
"version:patch": "npm version patch",
|
|
49
|
+
"version:minor": "npm version minor",
|
|
50
|
+
"version:major": "npm version major",
|
|
51
|
+
"prepublishOnly": "npm run build"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@eslint/js": "^10.0.1",
|
|
55
|
+
"@types/node": "^25.5.0",
|
|
56
|
+
"eslint": "^10.1.0",
|
|
57
|
+
"eslint-config-prettier": "^10.1.8",
|
|
58
|
+
"prettier": "^3.8.1",
|
|
59
|
+
"tsup": "^8.5.1",
|
|
60
|
+
"typescript": "^5.4.0",
|
|
61
|
+
"typescript-eslint": "^8.58.0",
|
|
62
|
+
"vitest": "^4.1.2"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "axios-2026",
|
|
3
|
+
"name": "Axios Supply Chain Attack",
|
|
4
|
+
"date": "2026-03-31",
|
|
5
|
+
"severity": "critical",
|
|
6
|
+
"description": "North Korea (UNC1069/Sapphire Sleet) compromised axios npm account, injected cross-platform RAT via plain-crypto-js",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://unit42.paloaltonetworks.com/axios-supply-chain-attack/",
|
|
9
|
+
"https://cloud.google.com/blog/topics/threat-intelligence/north-korea-threat-actor-targets-axios-npm-package"
|
|
10
|
+
],
|
|
11
|
+
"packages": {
|
|
12
|
+
"compromised": {
|
|
13
|
+
"axios": ["1.14.1", "0.30.4"]
|
|
14
|
+
},
|
|
15
|
+
"malicious": {
|
|
16
|
+
"plain-crypto-js": ["4.2.0", "4.2.1"]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"ioc": {
|
|
20
|
+
"files": {
|
|
21
|
+
"darwin": ["/Library/Caches/com.apple.act.mond", "/tmp/6202033"],
|
|
22
|
+
"linux": ["/tmp/ld.py"],
|
|
23
|
+
"win32": ["%PROGRAMDATA%\\wt.exe", "%PROGRAMDATA%\\system.bat"]
|
|
24
|
+
},
|
|
25
|
+
"domains": ["sfrclak.com", "callnrwise.com"],
|
|
26
|
+
"ips": ["142.11.206.73"],
|
|
27
|
+
"ports": [8000],
|
|
28
|
+
"processes": ["com.apple.act.mond", "wt.exe", "ld.py"],
|
|
29
|
+
"strings": ["OrDeR_7077", "6202033", "ifstap@proton", "nrwise@proton", "package.md"]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "chalk-debug-2025",
|
|
3
|
+
"name": "Chalk/Debug Mass Compromise",
|
|
4
|
+
"date": "2025-09-08",
|
|
5
|
+
"severity": "critical",
|
|
6
|
+
"description": "18 widely-used npm packages (2.6B weekly downloads) compromised via phishing — browser crypto wallet stealer",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://www.cisa.gov/news-events/alerts/2025/09/23/widespread-supply-chain-compromise-impacting-npm-ecosystem",
|
|
9
|
+
"https://semgrep.dev/blog/2025/chalk-debug-and-color-on-npm-compromised-in-new-supply-chain-attack/"
|
|
10
|
+
],
|
|
11
|
+
"packages": {
|
|
12
|
+
"compromised": {
|
|
13
|
+
"chalk": ["5.6.1"],
|
|
14
|
+
"debug": ["4.4.2"],
|
|
15
|
+
"color-name": ["2.0.1"],
|
|
16
|
+
"strip-ansi": ["7.1.1"],
|
|
17
|
+
"color": ["5.0.1"],
|
|
18
|
+
"color-convert": ["3.1.1"],
|
|
19
|
+
"color-string": ["2.1.1"],
|
|
20
|
+
"has-ansi": ["6.0.1"],
|
|
21
|
+
"ansi-styles": ["6.2.2"],
|
|
22
|
+
"ansi-regex": ["6.2.1"],
|
|
23
|
+
"supports-color": ["10.2.1"],
|
|
24
|
+
"backslash": ["0.2.1"],
|
|
25
|
+
"wrap-ansi": ["9.0.1"],
|
|
26
|
+
"is-arrayish": ["0.3.3"],
|
|
27
|
+
"error-ex": ["1.3.3"],
|
|
28
|
+
"slice-ansi": ["7.1.1"],
|
|
29
|
+
"simple-swizzle": ["0.2.3"],
|
|
30
|
+
"chalk-template": ["1.1.1"],
|
|
31
|
+
"supports-hyperlinks": ["4.1.1"]
|
|
32
|
+
},
|
|
33
|
+
"malicious": {}
|
|
34
|
+
},
|
|
35
|
+
"ioc": {
|
|
36
|
+
"domains": ["npmjs.help", "static-mw-host.b-cdn.net", "img-data-backup.b-cdn.net", "websocket-api2.publicvm.com"],
|
|
37
|
+
"ips": ["185.7.81.108"],
|
|
38
|
+
"strings": ["npmjs.help", "static-mw-host.b-cdn.net"]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "coa-rc-2021",
|
|
3
|
+
"name": "coa/rc Danabot Attack",
|
|
4
|
+
"date": "2021-11-04",
|
|
5
|
+
"severity": "critical",
|
|
6
|
+
"description": "Popular coa and rc packages hijacked to deploy Danabot password-stealing trojan",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://www.bleepingcomputer.com/news/security/popular-coa-npm-library-hijacked-to-steal-user-passwords/"
|
|
9
|
+
],
|
|
10
|
+
"packages": {
|
|
11
|
+
"compromised": {
|
|
12
|
+
"coa": ["2.0.3", "2.0.4", "2.1.1", "2.1.3", "3.0.1", "3.1.3"],
|
|
13
|
+
"rc": ["1.2.9", "1.3.9", "2.3.9"]
|
|
14
|
+
},
|
|
15
|
+
"malicious": {}
|
|
16
|
+
},
|
|
17
|
+
"ioc": {
|
|
18
|
+
"files": {
|
|
19
|
+
"win32": ["%TEMP%\\compile.js", "%TEMP%\\compile.bat", "%TEMP%\\sdd.dll"]
|
|
20
|
+
},
|
|
21
|
+
"domains": ["pastorcryptograph.at"],
|
|
22
|
+
"processes": ["compile.bat", "sdd.dll"],
|
|
23
|
+
"strings": ["pastorcryptograph.at", "compile.js", "compile.bat"]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "colors-faker-2022",
|
|
3
|
+
"name": "colors/faker Sabotage",
|
|
4
|
+
"date": "2022-01-09",
|
|
5
|
+
"severity": "medium",
|
|
6
|
+
"description": "Maintainer sabotage — colors prints infinite LIBERTY loop, faker exports purged. Denial of service.",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://www.sonatype.com/blog/npm-libraries-colors-and-faker-sabotaged-in-protest-by-their-maintainer-what-to-do-now"
|
|
9
|
+
],
|
|
10
|
+
"packages": {
|
|
11
|
+
"compromised": {
|
|
12
|
+
"colors": ["1.4.1", "1.4.2"],
|
|
13
|
+
"faker": ["6.6.6"]
|
|
14
|
+
},
|
|
15
|
+
"malicious": {}
|
|
16
|
+
},
|
|
17
|
+
"ioc": {
|
|
18
|
+
"strings": ["LIBERTY LIBERTY LIBERTY"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "eslint-scope-2018",
|
|
3
|
+
"name": "eslint-scope Token Stealer",
|
|
4
|
+
"date": "2018-07-12",
|
|
5
|
+
"severity": "high",
|
|
6
|
+
"description": "Malicious postinstall script in eslint-scope stole npm tokens from .npmrc files",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://eslint.org/blog/2018/07/postmortem-for-malicious-package-publishes/"
|
|
9
|
+
],
|
|
10
|
+
"packages": {
|
|
11
|
+
"compromised": {
|
|
12
|
+
"eslint-scope": ["3.7.2"],
|
|
13
|
+
"eslint-config-eslint": ["5.0.2"]
|
|
14
|
+
},
|
|
15
|
+
"malicious": {}
|
|
16
|
+
},
|
|
17
|
+
"ioc": {
|
|
18
|
+
"domains": ["pastebin.com"],
|
|
19
|
+
"strings": ["pastebin.com/raw/XLeVP82h", "_authToken"]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "event-stream-2018",
|
|
3
|
+
"name": "event-stream / flatmap-stream",
|
|
4
|
+
"date": "2018-11-26",
|
|
5
|
+
"severity": "high",
|
|
6
|
+
"description": "Targeted attack on Copay Bitcoin wallet via injected flatmap-stream dependency in event-stream",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://blog.npmjs.org/post/180565383195/details-about-the-event-stream-incident"
|
|
9
|
+
],
|
|
10
|
+
"packages": {
|
|
11
|
+
"compromised": {
|
|
12
|
+
"event-stream": ["3.3.6"]
|
|
13
|
+
},
|
|
14
|
+
"malicious": {
|
|
15
|
+
"flatmap-stream": ["0.1.1"]
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"ioc": {
|
|
19
|
+
"ips": ["111.90.151.134"],
|
|
20
|
+
"domains": ["copayapi.host"]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "glassworm-2026",
|
|
3
|
+
"name": "GlassWorm Campaign",
|
|
4
|
+
"date": "2026-03-18",
|
|
5
|
+
"severity": "critical",
|
|
6
|
+
"description": "433 components compromised across GitHub/npm/VSCode. Uses invisible Unicode and Solana blockchain as C2 dead-drop.",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://www.aikido.dev/blog/glassworm-returns-unicode-attack-github-npm-vscode",
|
|
9
|
+
"https://www.sonatype.com/blog/hijacked-npm-packages-deliver-malware-via-solana-linked-to-glassworm"
|
|
10
|
+
],
|
|
11
|
+
"packages": {
|
|
12
|
+
"compromised": {
|
|
13
|
+
"react-native-international-phone-number": ["0.12.1", "0.12.2", "0.12.3"],
|
|
14
|
+
"react-native-country-select": ["0.3.91"],
|
|
15
|
+
"@aifabrix/miso-client": ["4.7.2"]
|
|
16
|
+
},
|
|
17
|
+
"malicious": {
|
|
18
|
+
"@iflow-mcp/watercrawl-watercrawl-mcp": ["1.3.0", "1.3.1", "1.3.2", "1.3.3", "1.3.4"]
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"ioc": {
|
|
22
|
+
"ips": ["45.32.150.251"],
|
|
23
|
+
"strings": ["6YGcuyFRJKZtcaYCCFba9fScNUvPkGXodXE1mJiSzqDJ", "install.js"]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "lottie-player-2024",
|
|
3
|
+
"name": "Lottie Player Wallet Drainer",
|
|
4
|
+
"date": "2024-10-30",
|
|
5
|
+
"severity": "high",
|
|
6
|
+
"description": "Lottie Player npm package compromised — injected crypto wallet drainer popup into web pages",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://cycode.com/blog/lottie-web-player-malicious-package/"
|
|
9
|
+
],
|
|
10
|
+
"packages": {
|
|
11
|
+
"compromised": {
|
|
12
|
+
"@lottiefiles/lottie-player": ["2.0.5", "2.0.6", "2.0.7", "2.0.8"]
|
|
13
|
+
},
|
|
14
|
+
"malicious": {}
|
|
15
|
+
},
|
|
16
|
+
"ioc": {
|
|
17
|
+
"strings": ["connect wallet"]
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "node-ipc-2022",
|
|
3
|
+
"name": "node-ipc / peacenotwar Wiper",
|
|
4
|
+
"date": "2022-03-15",
|
|
5
|
+
"severity": "critical",
|
|
6
|
+
"description": "CVE-2022-23812 — Geotargeted file wiper (Russia/Belarus) + protest file dropper. CVSS 9.8.",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://snyk.io/blog/peacenotwar-malicious-npm-node-ipc-package-vulnerability/",
|
|
9
|
+
"https://github.com/advisories/GHSA-97m3-w2cp-4xx6"
|
|
10
|
+
],
|
|
11
|
+
"packages": {
|
|
12
|
+
"compromised": {
|
|
13
|
+
"node-ipc": ["10.1.1", "10.1.2", "9.2.2"]
|
|
14
|
+
},
|
|
15
|
+
"malicious": {
|
|
16
|
+
"peacenotwar": ["9.1.3", "9.1.5", "9.1.6"]
|
|
17
|
+
}
|
|
18
|
+
},
|
|
19
|
+
"ioc": {
|
|
20
|
+
"files": {
|
|
21
|
+
"darwin": ["~/Desktop/WITH-LOVE-FROM-AMERICA.txt"],
|
|
22
|
+
"linux": ["~/Desktop/WITH-LOVE-FROM-AMERICA.txt"],
|
|
23
|
+
"win32": ["%USERPROFILE%\\Desktop\\WITH-LOVE-FROM-AMERICA.txt"]
|
|
24
|
+
},
|
|
25
|
+
"domains": ["api.ipgeolocation.io"],
|
|
26
|
+
"strings": ["ssl-geospec.js", "WITH-LOVE-FROM-AMERICA", "peacenotwar"]
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "shai-hulud-2025",
|
|
3
|
+
"name": "Shai-Hulud 2.0 Worm",
|
|
4
|
+
"date": "2025-11-24",
|
|
5
|
+
"severity": "critical",
|
|
6
|
+
"description": "Self-replicating npm worm — credential harvester, secret scanner, dead man's switch wiper. 796 packages compromised.",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://unit42.paloaltonetworks.com/npm-supply-chain-attack/",
|
|
9
|
+
"https://securitylabs.datadoghq.com/articles/shai-hulud-2.0-npm-worm/"
|
|
10
|
+
],
|
|
11
|
+
"packages": {
|
|
12
|
+
"compromised": {
|
|
13
|
+
"@ctrl/tinycolor": ["4.1.1"],
|
|
14
|
+
"duckdb": ["1.3.3"],
|
|
15
|
+
"@duckdb/node-api": ["1.3.3"],
|
|
16
|
+
"@duckdb/node-bindings": ["1.3.3"],
|
|
17
|
+
"@duckdb/duckdb-wasm": ["1.29.2"]
|
|
18
|
+
},
|
|
19
|
+
"malicious": {}
|
|
20
|
+
},
|
|
21
|
+
"ioc": {
|
|
22
|
+
"domains": ["webhook.site"],
|
|
23
|
+
"strings": ["setup_bun.js", "bun_environment.js", "Sha1-Hulud", "discussion.yaml", "webhook.site"]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "solana-web3-2024",
|
|
3
|
+
"name": "@solana/web3.js Backdoor",
|
|
4
|
+
"date": "2024-12-03",
|
|
5
|
+
"severity": "critical",
|
|
6
|
+
"description": "Official Solana Web3.js library backdoored to steal private keys from Solana wallets",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://www.mend.io/blog/the-solana-web3-js-incident-another-wake-up-call-for-supply-chain-security/"
|
|
9
|
+
],
|
|
10
|
+
"packages": {
|
|
11
|
+
"compromised": {
|
|
12
|
+
"@solana/web3.js": ["1.95.6", "1.95.7"]
|
|
13
|
+
},
|
|
14
|
+
"malicious": {}
|
|
15
|
+
},
|
|
16
|
+
"ioc": {
|
|
17
|
+
"domains": ["sol-rpc.xyz"],
|
|
18
|
+
"strings": ["sol-rpc.xyz", "addToQueue"]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "ua-parser-js-2021",
|
|
3
|
+
"name": "ua-parser-js Hijack",
|
|
4
|
+
"date": "2021-10-22",
|
|
5
|
+
"severity": "critical",
|
|
6
|
+
"description": "Popular ua-parser-js hijacked to deploy XMRig cryptominer and Danabot password stealer",
|
|
7
|
+
"references": [
|
|
8
|
+
"https://github.com/nicedoc/ua-parser-js/issues/536"
|
|
9
|
+
],
|
|
10
|
+
"packages": {
|
|
11
|
+
"compromised": {
|
|
12
|
+
"ua-parser-js": ["0.7.29", "0.8.0", "1.0.0"]
|
|
13
|
+
},
|
|
14
|
+
"malicious": {}
|
|
15
|
+
},
|
|
16
|
+
"ioc": {
|
|
17
|
+
"files": {
|
|
18
|
+
"linux": ["/tmp/jsextension"],
|
|
19
|
+
"win32": ["%TEMP%\\jsextension.exe", "%TEMP%\\create.dll", "%TEMP%\\sdd.dll"]
|
|
20
|
+
},
|
|
21
|
+
"ips": ["159.148.186.228", "194.76.225.46", "185.158.250.216", "45.11.180.153"],
|
|
22
|
+
"processes": ["jsextension"]
|
|
23
|
+
}
|
|
24
|
+
}
|