UNPKG

tops-bmad

Version:

CLI tool to install BMAD workflow files into any project with integrated Shai-Hulud 2.0 security scanning

228 lines (199 loc) 6.91 kB
#!/usr/bin/env node /** * Shai-Hulud 2.0 IOC Database Updater * * Fetches the latest compromised package data from the Datadog consolidated IOCs * which aggregates data from 7 security vendors: * - Datadog Security Labs * - HelixGuard * - Koi Security * - ReversingLabs * - Socket.dev * - StepSecurity * - Wiz * * Source: https://github.com/DataDog/indicators-of-compromise/tree/main/shai-hulud-2.0 */ const https = require('https'); const fs = require('fs'); const path = require('path'); const CONSOLIDATED_IOC_URL = 'https://raw.githubusercontent.com/DataDog/indicators-of-compromise/main/shai-hulud-2.0/consolidated_iocs.csv'; const OUTPUT_FILE = path.join(__dirname, '..', 'compromised-packages.json'); /** * Fetch URL content via HTTPS */ function fetchUrl(url) { return new Promise((resolve, reject) => { https.get(url, (res) => { if (res.statusCode === 301 || res.statusCode === 302) { return fetchUrl(res.headers.location).then(resolve).catch(reject); } if (res.statusCode !== 200) { reject(new Error(`HTTP ${res.statusCode}: ${res.statusMessage}`)); return; } let data = ''; res.on('data', chunk => data += chunk); res.on('end', () => resolve(data)); res.on('error', reject); }).on('error', reject); }); } /** * Parse CSV line handling quoted fields */ function parseCSVLine(line) { const result = []; let current = ''; let inQuotes = false; for (let i = 0; i < line.length; i++) { const char = line[i]; if (char === '"') { inQuotes = !inQuotes; } else if (char === ',' && !inQuotes) { result.push(current.trim()); current = ''; } else { current += char; } } result.push(current.trim()); return result; } /** * Parse the consolidated IOCs CSV */ function parseConsolidatedCSV(csvContent) { const lines = csvContent.trim().split('\n'); const packages = []; // Skip header for (let i = 1; i < lines.length; i++) { const line = lines[i].trim(); if (!line) continue; const [packageName, packageVersions, sources] = parseCSVLine(line); if (!packageName || !packageVersions) continue; // Parse versions (comma-separated) const versions = packageVersions .split(',') .map(v => v.trim()) .filter(v => v.length > 0); // Parse sources const sourceList = sources ? sources.split(',').map(s => s.trim()).filter(s => s.length > 0) : []; packages.push({ name: packageName, severity: 'critical', affectedVersions: versions, sources: sourceList }); } return packages; } /** * Load existing database to preserve metadata */ function loadExistingDatabase() { try { const content = fs.readFileSync(OUTPUT_FILE, 'utf-8'); return JSON.parse(content); } catch (err) { console.error('Warning: Could not load existing database:', err.message); return null; } } /** * Main update function */ async function updateDatabase() { console.log('Shai-Hulud 2.0 IOC Database Updater'); console.log('===================================\n'); // Fetch consolidated IOCs console.log(`Fetching: ${CONSOLIDATED_IOC_URL}`); const csvContent = await fetchUrl(CONSOLIDATED_IOC_URL); console.log(`Downloaded ${csvContent.length} bytes\n`); // Parse CSV const packages = parseConsolidatedCSV(csvContent); console.log(`Parsed ${packages.length} packages from consolidated IOCs\n`); // Load existing database const existing = loadExistingDatabase(); // Count sources const sourceCounts = {}; packages.forEach(pkg => { pkg.sources.forEach(source => { sourceCounts[source] = (sourceCounts[source] || 0) + 1; }); }); console.log('Sources aggregated:'); Object.entries(sourceCounts) .sort((a, b) => b[1] - a[1]) .forEach(([source, count]) => { console.log(` - ${source}: ${count} packages`); }); console.log(''); // Build updated database const now = new Date().toISOString(); const database = { version: existing?.version || '2.1.0', lastUpdated: now, dataSource: { url: 'https://github.com/DataDog/indicators-of-compromise/tree/main/shai-hulud-2.0', description: 'Consolidated IOCs from multiple security vendors', sources: Object.keys(sourceCounts).sort(), fetchedAt: now }, attackInfo: existing?.attackInfo || { name: 'Shai-Hulud 2.0', alias: 'The Second Coming', firstDetected: '2025-11-24T03:16:00Z', description: 'Self-replicating npm worm targeting credential theft and supply chain compromise.' }, indicators: existing?.indicators || {}, acknowledgements: existing?.acknowledgements || [], packages: packages.map(pkg => ({ name: pkg.name, severity: pkg.severity, affectedVersions: pkg.affectedVersions })) }; // Write updated database fs.writeFileSync(OUTPUT_FILE, JSON.stringify(database, null, 2) + '\n'); // Summary console.log('Database updated successfully!'); console.log(` - Total packages: ${packages.length}`); console.log(` - Output: ${OUTPUT_FILE}`); console.log(` - Last updated: ${now}`); // Compare with previous if (existing?.packages) { const oldCount = existing.packages.length; const newCount = packages.length; const diff = newCount - oldCount; if (diff > 0) { console.log(` - New packages added: +${diff}`); } else if (diff < 0) { console.log(` - Packages removed: ${diff}`); } else { console.log(` - Package count unchanged`); } // Count packages that now have specific versions vs wildcards const oldWildcards = existing.packages.filter(p => p.affectedVersions?.length === 1 && p.affectedVersions[0] === '*' ).length; console.log(` - Previous wildcards: ${oldWildcards}`); console.log(` - Now all packages have specific versions`); } return { success: true, packageCount: packages.length }; } // Run if executed directly if (require.main === module) { updateDatabase() .then(result => { console.log('\nDone!'); process.exit(0); }) .catch(err => { console.error('\nError:', err.message); process.exit(1); }); } module.exports = { updateDatabase, parseConsolidatedCSV, fetchUrl };