mirror of
https://github.com/affaan-m/everything-claude-code.git
synced 2026-05-16 01:12:13 +08:00
550 lines
18 KiB
JavaScript
Executable File
550 lines
18 KiB
JavaScript
Executable File
#!/usr/bin/env node
|
|
/**
|
|
* Scan dependency manifests, lockfiles, AI-tool configs, and installed package
|
|
* payload paths for active supply-chain incident indicators.
|
|
*/
|
|
|
|
const fs = require('fs');
|
|
const os = require('os');
|
|
const path = require('path');
|
|
|
|
const DEFAULT_ROOT = path.resolve(__dirname, '../..');
|
|
|
|
const MALICIOUS_PACKAGE_VERSIONS = {
|
|
'@beproduct/nestjs-auth': [
|
|
'0.1.2',
|
|
'0.1.3',
|
|
'0.1.4',
|
|
'0.1.5',
|
|
'0.1.6',
|
|
'0.1.7',
|
|
'0.1.8',
|
|
'0.1.9',
|
|
'0.1.10',
|
|
'0.1.11',
|
|
'0.1.12',
|
|
'0.1.13',
|
|
'0.1.14',
|
|
'0.1.15',
|
|
'0.1.16',
|
|
'0.1.17',
|
|
'0.1.18',
|
|
'0.1.19',
|
|
],
|
|
'@cap-js/db-service': ['2.10.1'],
|
|
'@cap-js/postgres': ['2.2.2'],
|
|
'@cap-js/sqlite': ['2.2.2'],
|
|
'@dirigible-ai/sdk': ['0.6.2', '0.6.3'],
|
|
'@draftauth/client': ['0.2.1', '0.2.2'],
|
|
'@draftauth/core': ['0.13.1', '0.13.2'],
|
|
'@draftlab/auth': ['0.24.1', '0.24.2'],
|
|
'@draftlab/auth-router': ['0.5.1', '0.5.2'],
|
|
'@draftlab/db': ['0.16.1', '0.16.2'],
|
|
'@mesadev/rest': ['0.28.3'],
|
|
'@mesadev/saguaro': ['0.4.22'],
|
|
'@mesadev/sdk': ['0.28.3'],
|
|
'@ml-toolkit-ts/preprocessing': ['1.0.2', '1.0.3'],
|
|
'@ml-toolkit-ts/xgboost': ['1.0.3', '1.0.4'],
|
|
'@mistralai/mistralai': ['2.2.2', '2.2.3', '2.2.4'],
|
|
'@mistralai/mistralai-azure': ['1.7.1', '1.7.2', '1.7.3'],
|
|
'@mistralai/mistralai-gcp': ['1.7.1', '1.7.2', '1.7.3'],
|
|
'@opensearch-project/opensearch': ['3.5.3', '3.6.2', '3.7.0', '3.8.0'],
|
|
'@squawk/airport-data': ['0.7.4', '0.7.5', '0.7.6', '0.7.7', '0.7.8'],
|
|
'@squawk/airports': ['0.6.2', '0.6.3', '0.6.4', '0.6.5', '0.6.6'],
|
|
'@squawk/airspace': ['0.8.1', '0.8.2', '0.8.3', '0.8.4', '0.8.5'],
|
|
'@squawk/airspace-data': ['0.5.3', '0.5.4', '0.5.5', '0.5.6', '0.5.7'],
|
|
'@squawk/airway-data': ['0.5.4', '0.5.5', '0.5.6', '0.5.7', '0.5.8'],
|
|
'@squawk/airways': ['0.4.2', '0.4.3', '0.4.4', '0.4.5', '0.4.6'],
|
|
'@squawk/fix-data': ['0.6.4', '0.6.5', '0.6.6', '0.6.7', '0.6.8'],
|
|
'@squawk/fixes': ['0.3.2', '0.3.3', '0.3.4', '0.3.5', '0.3.6'],
|
|
'@squawk/flight-math': ['0.5.4', '0.5.5', '0.5.6', '0.5.7', '0.5.8'],
|
|
'@squawk/flightplan': ['0.5.2', '0.5.3', '0.5.4', '0.5.5', '0.5.6'],
|
|
'@squawk/geo': ['0.4.4', '0.4.5', '0.4.6', '0.4.7', '0.4.8'],
|
|
'@squawk/icao-registry': ['0.5.2', '0.5.3', '0.5.4', '0.5.5', '0.5.6'],
|
|
'@squawk/icao-registry-data': ['0.8.4', '0.8.5', '0.8.6', '0.8.7', '0.8.8'],
|
|
'@squawk/mcp': ['0.9.1', '0.9.2', '0.9.3', '0.9.4', '0.9.5'],
|
|
'@squawk/navaid-data': ['0.6.4', '0.6.5', '0.6.6', '0.6.7', '0.6.8'],
|
|
'@squawk/navaids': ['0.4.2', '0.4.3', '0.4.4', '0.4.5', '0.4.6'],
|
|
'@squawk/notams': ['0.3.6', '0.3.7', '0.3.8', '0.3.9', '0.3.10'],
|
|
'@squawk/procedure-data': ['0.7.3', '0.7.4', '0.7.5', '0.7.6', '0.7.7'],
|
|
'@squawk/procedures': ['0.5.2', '0.5.3', '0.5.4', '0.5.5', '0.5.6'],
|
|
'@squawk/types': ['0.8.1', '0.8.2', '0.8.3', '0.8.4', '0.8.5'],
|
|
'@squawk/units': ['0.4.3', '0.4.4', '0.4.5', '0.4.6', '0.4.7'],
|
|
'@squawk/weather': ['0.5.6', '0.5.7', '0.5.8', '0.5.9', '0.5.10'],
|
|
'@supersurkhet/cli': ['0.0.2', '0.0.3', '0.0.4', '0.0.5', '0.0.6', '0.0.7'],
|
|
'@supersurkhet/sdk': ['0.0.2', '0.0.3', '0.0.4', '0.0.5', '0.0.6', '0.0.7'],
|
|
'@tallyui/components': ['1.0.1', '1.0.2', '1.0.3'],
|
|
'@tallyui/connector-medusa': ['1.0.1', '1.0.2', '1.0.3'],
|
|
'@tallyui/connector-shopify': ['1.0.1', '1.0.2', '1.0.3'],
|
|
'@tallyui/connector-vendure': ['1.0.1', '1.0.2', '1.0.3'],
|
|
'@tallyui/connector-woocommerce': ['1.0.1', '1.0.2', '1.0.3'],
|
|
'@tallyui/core': ['0.2.1', '0.2.2', '0.2.3'],
|
|
'@tallyui/database': ['1.0.1', '1.0.2', '1.0.3'],
|
|
'@tallyui/pos': ['0.1.1', '0.1.2', '0.1.3'],
|
|
'@tallyui/storage-sqlite': ['0.2.1', '0.2.2', '0.2.3'],
|
|
'@tallyui/theme': ['0.2.1', '0.2.2', '0.2.3'],
|
|
'@tanstack/arktype-adapter': ['1.166.12', '1.166.15'],
|
|
'@tanstack/eslint-plugin-router': ['1.161.9', '1.161.12'],
|
|
'@tanstack/eslint-plugin-start': ['0.0.4', '0.0.7'],
|
|
'@tanstack/history': ['1.161.9', '1.161.12'],
|
|
'@tanstack/nitro-v2-vite-plugin': ['1.154.12', '1.154.15'],
|
|
'@tanstack/react-router': ['1.169.5', '1.169.8'],
|
|
'@tanstack/react-router-devtools': ['1.166.16', '1.166.19'],
|
|
'@tanstack/react-router-ssr-query': ['1.166.15', '1.166.18'],
|
|
'@tanstack/react-start': ['1.167.68', '1.167.71'],
|
|
'@tanstack/react-start-client': ['1.166.51', '1.166.54'],
|
|
'@tanstack/react-start-rsc': ['0.0.47', '0.0.50'],
|
|
'@tanstack/react-start-server': ['1.166.55', '1.166.58'],
|
|
'@tanstack/router-cli': ['1.166.46', '1.166.49'],
|
|
'@tanstack/router-core': ['1.169.5', '1.169.8'],
|
|
'@tanstack/router-devtools': ['1.166.16', '1.166.19'],
|
|
'@tanstack/router-devtools-core': ['1.167.6', '1.167.9'],
|
|
'@tanstack/router-generator': ['1.166.45', '1.166.48'],
|
|
'@tanstack/router-plugin': ['1.167.38', '1.167.41'],
|
|
'@tanstack/router-ssr-query-core': ['1.168.3', '1.168.6'],
|
|
'@tanstack/router-utils': ['1.161.11', '1.161.14'],
|
|
'@tanstack/router-vite-plugin': ['1.166.53', '1.166.56'],
|
|
'@tanstack/solid-router': ['1.169.5', '1.169.8'],
|
|
'@tanstack/solid-router-devtools': ['1.166.16', '1.166.19'],
|
|
'@tanstack/solid-router-ssr-query': ['1.166.15', '1.166.18'],
|
|
'@tanstack/solid-start': ['1.167.65', '1.167.68'],
|
|
'@tanstack/solid-start-client': ['1.166.50', '1.166.53'],
|
|
'@tanstack/solid-start-server': ['1.166.54', '1.166.57'],
|
|
'@tanstack/start-client-core': ['1.168.5', '1.168.8'],
|
|
'@tanstack/start-fn-stubs': ['1.161.9', '1.161.12'],
|
|
'@tanstack/start-plugin-core': ['1.169.23', '1.169.26'],
|
|
'@tanstack/start-server-core': ['1.167.33', '1.167.36'],
|
|
'@tanstack/start-static-server-functions': ['1.166.44', '1.166.47'],
|
|
'@tanstack/start-storage-context': ['1.166.38', '1.166.41'],
|
|
'@tanstack/valibot-adapter': ['1.166.12', '1.166.15'],
|
|
'@tanstack/virtual-file-routes': ['1.161.10', '1.161.13'],
|
|
'@tanstack/vue-router': ['1.169.5', '1.169.8'],
|
|
'@tanstack/vue-router-devtools': ['1.166.16', '1.166.19'],
|
|
'@tanstack/vue-router-ssr-query': ['1.166.15', '1.166.18'],
|
|
'@tanstack/vue-start': ['1.167.61', '1.167.64'],
|
|
'@tanstack/vue-start-client': ['1.166.46', '1.166.49'],
|
|
'@tanstack/vue-start-server': ['1.166.50', '1.166.53'],
|
|
'@tanstack/zod-adapter': ['1.166.12', '1.166.15'],
|
|
'@taskflow-corp/cli': ['0.1.24', '0.1.25', '0.1.26', '0.1.27', '0.1.28', '0.1.29'],
|
|
'@tolka/cli': ['1.0.2', '1.0.3', '1.0.4', '1.0.5', '1.0.6'],
|
|
'@uipath/access-policy-sdk': ['0.3.1'],
|
|
'@uipath/access-policy-tool': ['0.3.1'],
|
|
'@uipath/agent.sdk': ['0.0.18'],
|
|
'@uipath/agent-sdk': ['1.0.2'],
|
|
'@uipath/agent-tool': ['1.0.1'],
|
|
'@uipath/admin-tool': ['0.1.1'],
|
|
'@uipath/aops-policy-tool': ['0.3.1'],
|
|
'@uipath/ap-chat': ['1.5.7'],
|
|
'@uipath/api-workflow-tool': ['1.0.1'],
|
|
'@uipath/apollo-core': ['5.9.2'],
|
|
'@uipath/apollo-react': ['4.24.5'],
|
|
'@uipath/apollo-wind': ['2.16.2'],
|
|
'@uipath/auth': ['1.0.1'],
|
|
'@uipath/case-tool': ['1.0.1'],
|
|
'@uipath/cli': ['1.0.1'],
|
|
'@uipath/codedagent-tool': ['1.0.1'],
|
|
'@uipath/codedagents-tool': ['0.1.12'],
|
|
'@uipath/codedapp-tool': ['1.0.1'],
|
|
'@uipath/common': ['1.0.1'],
|
|
'@uipath/context-grounding-tool': ['0.1.1'],
|
|
'@uipath/data-fabric-tool': ['1.0.2'],
|
|
'@uipath/docsai-tool': ['1.0.1'],
|
|
'@uipath/filesystem': ['1.0.1'],
|
|
'@uipath/flow-tool': ['1.0.2'],
|
|
'@uipath/functions-tool': ['1.0.1'],
|
|
'@uipath/gov-tool': ['0.3.1'],
|
|
'@uipath/identity-tool': ['0.1.1'],
|
|
'@uipath/insights-sdk': ['1.0.1'],
|
|
'@uipath/insights-tool': ['1.0.1'],
|
|
'@uipath/integrationservice-sdk': ['1.0.2'],
|
|
'@uipath/integrationservice-tool': ['1.0.2'],
|
|
'@uipath/llmgw-tool': ['1.0.1'],
|
|
'@uipath/maestro-sdk': ['1.0.1'],
|
|
'@uipath/maestro-tool': ['1.0.1'],
|
|
'@uipath/orchestrator-tool': ['1.0.1'],
|
|
'@uipath/packager-tool-apiworkflow': ['0.0.19'],
|
|
'@uipath/packager-tool-bpmn': ['0.0.9'],
|
|
'@uipath/packager-tool-case': ['0.0.9'],
|
|
'@uipath/packager-tool-connector': ['0.0.19'],
|
|
'@uipath/packager-tool-flow': ['0.0.19'],
|
|
'@uipath/packager-tool-functions': ['0.1.1'],
|
|
'@uipath/packager-tool-webapp': ['1.0.6'],
|
|
'@uipath/packager-tool-workflowcompiler': ['0.0.16'],
|
|
'@uipath/packager-tool-workflowcompiler-browser': ['0.0.34'],
|
|
'@uipath/platform-tool': ['1.0.1'],
|
|
'@uipath/project-packager': ['1.1.16'],
|
|
'@uipath/resource-tool': ['1.0.1'],
|
|
'@uipath/resourcecatalog-tool': ['0.1.1'],
|
|
'@uipath/resources-tool': ['0.1.11'],
|
|
'@uipath/robot': ['1.3.4'],
|
|
'@uipath/rpa-legacy-tool': ['1.0.1'],
|
|
'@uipath/rpa-tool': ['0.9.5'],
|
|
'@uipath/solution-packager': ['0.0.35'],
|
|
'@uipath/solution-tool': ['1.0.1'],
|
|
'@uipath/solutionpackager-sdk': ['1.0.11'],
|
|
'@uipath/solutionpackager-tool-core': ['0.0.34'],
|
|
'@uipath/tasks-tool': ['1.0.1'],
|
|
'@uipath/telemetry': ['0.0.7'],
|
|
'@uipath/test-manager-tool': ['1.0.2'],
|
|
'@uipath/tool-workflowcompiler': ['0.0.12'],
|
|
'@uipath/traces-tool': ['1.0.1'],
|
|
'@uipath/ui-widgets-multi-file-upload': ['1.0.1'],
|
|
'@uipath/uipath-python-bridge': ['1.0.1'],
|
|
'@uipath/vertical-solutions-tool': ['1.0.1'],
|
|
'@uipath/vss': ['0.1.6'],
|
|
'@uipath/widget.sdk': ['1.2.3'],
|
|
'agentwork-cli': ['0.1.4', '0.1.5'],
|
|
'cmux-agent-mcp': ['0.1.3', '0.1.4', '0.1.5', '0.1.6', '0.1.7', '0.1.8'],
|
|
'cross-stitch': ['1.1.3', '1.1.4', '1.1.5', '1.1.6', '1.1.7'],
|
|
'git-branch-selector': ['1.3.3', '1.3.4', '1.3.5', '1.3.6', '1.3.7'],
|
|
'git-git-git': ['1.0.8', '1.0.9', '1.0.10', '1.0.11', '1.0.12'],
|
|
'guardrails-ai': ['0.10.1'],
|
|
'intercom-client': ['7.0.4'],
|
|
'lightning': ['2.6.2', '2.6.3'],
|
|
'mbt': ['1.2.48'],
|
|
'mistralai': ['2.4.6'],
|
|
'ml-toolkit-ts': ['1.0.4', '1.0.5'],
|
|
'nextmove-mcp': ['0.1.3', '0.1.4', '0.1.5', '0.1.7'],
|
|
'safe-action': ['0.8.3', '0.8.4'],
|
|
'ts-dna': ['3.0.1', '3.0.2', '3.0.3', '3.0.4', '3.0.5'],
|
|
'wot-api': ['0.8.1', '0.8.2', '0.8.3', '0.8.4'],
|
|
};
|
|
|
|
const CRITICAL_TEXT_INDICATORS = [
|
|
'@tanstack/setup',
|
|
'github:tanstack/router#79ac49eedf774dd4b0cfa308722bc463cfe5885c',
|
|
'router_init.js',
|
|
'router_runtime.js',
|
|
'tanstack_runner.js',
|
|
'execution.js',
|
|
'transformers.pyz',
|
|
'pgmonitor.py',
|
|
'pgsql-monitor.service',
|
|
'gh-token-monitor',
|
|
'com.user.gh-token-monitor',
|
|
'IfYouRevokeThisTokenItWillWipeTheComputerOfTheOwner',
|
|
'filev2.getsession.org',
|
|
'seed1.getsession.org',
|
|
'seed2.getsession.org',
|
|
'seed3.getsession.org',
|
|
'git-tanstack.com',
|
|
'litter.catbox.moe/h8nc9u.js',
|
|
'litter.catbox.moe/7rrc6l.mjs',
|
|
'83.142.209.194',
|
|
'api.masscan.cloud',
|
|
'A Mini Shai-Hulud has Appeared',
|
|
'Shai-Hulud: Here We Go Again',
|
|
'PUSH UR T3MPRR',
|
|
'codeql_analysis.yml',
|
|
'shai-hulud-workflow.yml',
|
|
];
|
|
|
|
const DEPENDENCY_FILENAMES = new Set([
|
|
'package.json',
|
|
'package-lock.json',
|
|
'pnpm-lock.yaml',
|
|
'yarn.lock',
|
|
'bun.lock',
|
|
'pyproject.toml',
|
|
'poetry.lock',
|
|
'requirements.txt',
|
|
]);
|
|
|
|
const PERSISTENCE_FILENAMES = new Set([
|
|
'settings.json',
|
|
'tasks.json',
|
|
'router_runtime.js',
|
|
'setup.mjs',
|
|
'pgmonitor.py',
|
|
'gh-token-monitor.sh',
|
|
'com.user.gh-token-monitor.plist',
|
|
'gh-token-monitor.service',
|
|
'pgsql-monitor.service',
|
|
'codeql_analysis.yml',
|
|
'shai-hulud-workflow.yml',
|
|
]);
|
|
|
|
const PAYLOAD_FILENAMES = new Set([
|
|
'router_init.js',
|
|
'router_runtime.js',
|
|
'tanstack_runner.js',
|
|
'execution.js',
|
|
'transformers.pyz',
|
|
'pgmonitor.py',
|
|
'gh-token-monitor.sh',
|
|
'com.user.gh-token-monitor.plist',
|
|
'gh-token-monitor.service',
|
|
'pgsql-monitor.service',
|
|
'codeql_analysis.yml',
|
|
'shai-hulud-workflow.yml',
|
|
]);
|
|
|
|
const IGNORED_DIRS = new Set([
|
|
'.git',
|
|
'.next',
|
|
'.pytest_cache',
|
|
'__pycache__',
|
|
'coverage',
|
|
'dist',
|
|
'docs',
|
|
'target',
|
|
'tests',
|
|
]);
|
|
|
|
function normalizeForMatch(value) {
|
|
return value.toLowerCase();
|
|
}
|
|
|
|
function isInSpecialConfigPath(filePath) {
|
|
const normalized = filePath.split(path.sep).join('/');
|
|
return /\/\.claude\//.test(normalized)
|
|
|| /\/\.vscode\//.test(normalized)
|
|
|| /\/\.kiro\/settings\//.test(normalized)
|
|
|| /\/Library\/LaunchAgents\//.test(normalized)
|
|
|| /\/\.config\/systemd\/user\//.test(normalized)
|
|
|| /\/\.local\/bin\//.test(normalized)
|
|
|| /\/\.github\/workflows\//.test(normalized);
|
|
}
|
|
|
|
function shouldInspectFile(filePath) {
|
|
const base = path.basename(filePath);
|
|
if (DEPENDENCY_FILENAMES.has(base)) return true;
|
|
if (PERSISTENCE_FILENAMES.has(base) && isInSpecialConfigPath(filePath)) return true;
|
|
if (PAYLOAD_FILENAMES.has(base) && filePath.includes(`${path.sep}node_modules${path.sep}`)) return true;
|
|
return false;
|
|
}
|
|
|
|
function walkFiles(rootDir, files = []) {
|
|
if (!fs.existsSync(rootDir)) return files;
|
|
|
|
const stat = fs.statSync(rootDir);
|
|
if (stat.isFile()) {
|
|
if (shouldInspectFile(rootDir)) files.push(rootDir);
|
|
return files;
|
|
}
|
|
|
|
for (const entry of fs.readdirSync(rootDir, { withFileTypes: true })) {
|
|
const fullPath = path.join(rootDir, entry.name);
|
|
if (entry.isDirectory()) {
|
|
if (IGNORED_DIRS.has(entry.name) && entry.name !== 'node_modules') continue;
|
|
if (entry.name === 'node_modules') {
|
|
walkNodeModules(fullPath, files);
|
|
} else {
|
|
walkFiles(fullPath, files);
|
|
}
|
|
} else if (entry.isFile() && shouldInspectFile(fullPath)) {
|
|
files.push(fullPath);
|
|
}
|
|
}
|
|
|
|
return files;
|
|
}
|
|
|
|
function walkNodeModules(nodeModulesDir, files) {
|
|
if (!fs.existsSync(nodeModulesDir)) return;
|
|
|
|
for (const entry of fs.readdirSync(nodeModulesDir, { withFileTypes: true })) {
|
|
if (entry.name.startsWith('.')) continue;
|
|
const fullPath = path.join(nodeModulesDir, entry.name);
|
|
if (entry.isDirectory()) {
|
|
if (entry.name.startsWith('@')) {
|
|
for (const scopedEntry of fs.readdirSync(fullPath, { withFileTypes: true })) {
|
|
if (scopedEntry.isDirectory()) {
|
|
inspectPackageDir(path.join(fullPath, scopedEntry.name), files);
|
|
}
|
|
}
|
|
} else {
|
|
inspectPackageDir(fullPath, files);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function inspectPackageDir(packageDir, files) {
|
|
for (const filename of [...DEPENDENCY_FILENAMES, ...PAYLOAD_FILENAMES, 'setup.mjs', 'execution.js']) {
|
|
const candidate = path.join(packageDir, filename);
|
|
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
files.push(candidate);
|
|
}
|
|
}
|
|
}
|
|
|
|
function readText(filePath) {
|
|
try {
|
|
return fs.readFileSync(filePath, 'utf8');
|
|
} catch {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
function lineForIndex(text, index) {
|
|
return text.slice(0, index).split(/\r?\n/).length;
|
|
}
|
|
|
|
function escapeRegExp(value) {
|
|
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
}
|
|
|
|
function addFinding(findings, severity, filePath, line, indicator, message) {
|
|
findings.push({ severity, filePath, line, indicator, message });
|
|
}
|
|
|
|
function scanFile(filePath, rootDir, findings) {
|
|
const base = path.basename(filePath);
|
|
const relativePath = path.relative(rootDir, filePath) || filePath;
|
|
const text = readText(filePath);
|
|
const lowerText = normalizeForMatch(text);
|
|
|
|
if (PAYLOAD_FILENAMES.has(base)) {
|
|
addFinding(
|
|
findings,
|
|
'critical',
|
|
relativePath,
|
|
1,
|
|
base,
|
|
'Known Mini Shai-Hulud/TanStack payload or persistence filename is present',
|
|
);
|
|
}
|
|
|
|
for (const indicator of CRITICAL_TEXT_INDICATORS) {
|
|
const index = lowerText.indexOf(normalizeForMatch(indicator));
|
|
if (index !== -1) {
|
|
addFinding(
|
|
findings,
|
|
'critical',
|
|
relativePath,
|
|
lineForIndex(text, index),
|
|
indicator,
|
|
'Known active supply-chain IOC is present',
|
|
);
|
|
}
|
|
}
|
|
|
|
if (!DEPENDENCY_FILENAMES.has(base)) return;
|
|
|
|
for (const [packageName, versions] of Object.entries(MALICIOUS_PACKAGE_VERSIONS)) {
|
|
const packageIndex = lowerText.indexOf(normalizeForMatch(packageName));
|
|
if (packageIndex === -1) continue;
|
|
|
|
for (const version of versions) {
|
|
const versionPattern = new RegExp(`(^|[^0-9a-z.])${escapeRegExp(version)}([^0-9a-z.]|$)`, 'i');
|
|
if (versionPattern.test(text) || lowerText.includes(`@${version}`)) {
|
|
addFinding(
|
|
findings,
|
|
'critical',
|
|
relativePath,
|
|
lineForIndex(text, packageIndex),
|
|
`${packageName}@${version}`,
|
|
'Dependency manifest or lockfile references a known compromised package version',
|
|
);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function homeTargets(homeDir) {
|
|
return [
|
|
'.claude/settings.json',
|
|
'.claude/router_runtime.js',
|
|
'.claude/setup.mjs',
|
|
'.vscode/tasks.json',
|
|
'.vscode/setup.mjs',
|
|
'Library/LaunchAgents/com.user.gh-token-monitor.plist',
|
|
'.config/systemd/user/gh-token-monitor.service',
|
|
'.config/systemd/user/pgsql-monitor.service',
|
|
'.local/bin/gh-token-monitor.sh',
|
|
'.local/bin/pgmonitor.py',
|
|
].map(relativePath => path.join(homeDir, relativePath));
|
|
}
|
|
|
|
function runtimeTargets() {
|
|
return [
|
|
'/tmp/transformers.pyz',
|
|
'/tmp/pgmonitor.py',
|
|
'/private/tmp/transformers.pyz',
|
|
'/private/tmp/pgmonitor.py',
|
|
];
|
|
}
|
|
|
|
function scanSupplyChainIocs(options = {}) {
|
|
const rootDir = path.resolve(options.rootDir || DEFAULT_ROOT);
|
|
const files = walkFiles(rootDir);
|
|
const findings = [];
|
|
|
|
if (options.home) {
|
|
for (const target of homeTargets(options.homeDir || os.homedir())) {
|
|
if (fs.existsSync(target)) files.push(target);
|
|
}
|
|
for (const target of runtimeTargets()) {
|
|
if (fs.existsSync(target)) files.push(target);
|
|
}
|
|
}
|
|
|
|
for (const filePath of [...new Set(files)].sort()) {
|
|
scanFile(filePath, rootDir, findings);
|
|
}
|
|
|
|
return {
|
|
rootDir,
|
|
scannedFiles: files.length,
|
|
findings,
|
|
};
|
|
}
|
|
|
|
function parseArgs(argv) {
|
|
const options = {};
|
|
for (let i = 0; i < argv.length; i++) {
|
|
const arg = argv[i];
|
|
if (arg === '--root') {
|
|
options.rootDir = argv[++i];
|
|
} else if (arg === '--home') {
|
|
options.home = true;
|
|
} else if (arg === '--home-dir') {
|
|
options.home = true;
|
|
options.homeDir = argv[++i];
|
|
} else if (arg === '--json') {
|
|
options.json = true;
|
|
} else {
|
|
throw new Error(`Unknown argument: ${arg}`);
|
|
}
|
|
}
|
|
return options;
|
|
}
|
|
|
|
function printReport(result, json = false) {
|
|
if (json) {
|
|
console.log(JSON.stringify(result, null, 2));
|
|
return;
|
|
}
|
|
|
|
if (result.findings.length === 0) {
|
|
console.log(`Supply-chain IOC scan passed for ${result.rootDir} (${result.scannedFiles} files inspected)`);
|
|
return;
|
|
}
|
|
|
|
for (const finding of result.findings) {
|
|
console.error(
|
|
`${finding.severity.toUpperCase()}: ${finding.filePath}:${finding.line} ${finding.indicator}`,
|
|
);
|
|
console.error(` ${finding.message}`);
|
|
}
|
|
}
|
|
|
|
if (require.main === module) {
|
|
try {
|
|
const options = parseArgs(process.argv.slice(2));
|
|
const result = scanSupplyChainIocs(options);
|
|
printReport(result, options.json);
|
|
process.exit(result.findings.length > 0 ? 1 : 0);
|
|
} catch (error) {
|
|
console.error(error.message);
|
|
process.exit(2);
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
CRITICAL_TEXT_INDICATORS,
|
|
MALICIOUS_PACKAGE_VERSIONS,
|
|
scanSupplyChainIocs,
|
|
};
|