is-my-code-pwned
Version:
Advanced security scanner for detecting malicious npm packages and analyzing vulnerability risks in Node.js projects
190 lines (169 loc) • 6.15 kB
JavaScript
const fs = require('fs');
const path = require('path');
class CLIHandler {
constructor() {
this.options = {};
}
printBanner() {
console.log('');
console.log('🔍 is-my-code-pwned v2.1.1');
console.log(' 🛡️ Advanced Security Scanner for npm Packages');
console.log(' 🔎 Comprehensive malware detection & vulnerability analysis');
console.log('');
}
printUsage() {
this.printBanner();
console.log('USAGE:');
console.log(' is-my-code-pwned [OPTIONS] [PATH]');
console.log('');
console.log('OPTIONS:');
console.log(' -h, --help Show this help');
console.log(' -v, --verbose Show detailed scan log');
console.log(' --json JSON output format');
console.log(' --log-file FILE Save detailed report to file');
console.log(' --scan-caches Include package manager caches in scan');
console.log(' --deep-scan Maximum depth scanning (slower but thorough)');
console.log(' --config-only Only analyze configuration files');
console.log('');
console.log('EXAMPLES:');
console.log(' is-my-code-pwned # Quick scan current directory');
console.log(' is-my-code-pwned -v /path/to/project # Verbose scan specific path');
console.log(' is-my-code-pwned --deep-scan --scan-caches # Maximum security scan');
console.log(' is-my-code-pwned --json --log-file report.json');
console.log(' is-my-code-pwned --config-only # Fast config check only');
console.log('');
console.log('SECURITY FEATURES:');
console.log(' ✓ Detects known malicious npm packages');
console.log(' ✓ Scans nested node_modules recursively');
console.log(' ✓ Checks global packages across all managers');
console.log(' ✓ Analyzes package.json for vulnerable ranges');
console.log(' ✓ Validates lock files and integrity');
console.log(' ✓ Scans .npmrc and configuration files');
console.log(' ✓ Checks package manager caches');
console.log(' ✓ Provides actionable security recommendations');
console.log('');
}
parseArgs() {
const args = process.argv.slice(2);
const options = {
help: false,
verbose: false,
json: false,
logFile: null,
scanCaches: false,
deepScan: false,
configOnly: false,
targetPath: process.cwd()
};
for (let i = 0; i < args.length; i++) {
const arg = args[i];
switch (arg) {
case '-h':
case '--help':
options.help = true;
break;
case '-v':
case '--verbose':
options.verbose = true;
break;
case '--json':
options.json = true;
break;
case '--log-file':
options.logFile = args[++i];
if (!options.logFile) {
console.error('❌ --log-file requires a filename');
process.exit(1);
}
break;
case '--scan-caches':
options.scanCaches = true;
break;
case '--deep-scan':
options.deepScan = true;
options.scanCaches = true; // Deep scan includes caches
break;
case '--config-only':
options.configOnly = true;
break;
default:
if (!arg.startsWith('-')) {
const resolvedPath = path.resolve(arg);
if (fs.existsSync(resolvedPath)) {
options.targetPath = resolvedPath;
} else {
console.error(`❌ Path does not exist: ${resolvedPath}`);
process.exit(1);
}
} else {
console.error(`❌ Unknown option: ${arg}`);
console.error('Use --help for usage information');
process.exit(1);
}
break;
}
}
this.options = options;
return options;
}
validateOptions() {
// Validar path de destino
if (!fs.existsSync(this.options.targetPath)) {
console.error(`❌ Path does not exist: ${this.options.targetPath}`);
process.exit(1);
}
if (!fs.statSync(this.options.targetPath).isDirectory()) {
console.error(`❌ Path is not a directory: ${this.options.targetPath}`);
process.exit(1);
}
// Validar archivo de log
if (this.options.logFile) {
const logDir = path.dirname(path.resolve(this.options.logFile));
if (!fs.existsSync(logDir)) {
console.error(`❌ Log file directory does not exist: ${logDir}`);
process.exit(1);
}
}
return true;
}
showScanStartMessage() {
if (!this.options.json) {
console.log(`🔍 Starting ${this.options.deepScan ? 'deep ' : ''}security scan...`);
console.log(`📁 Target: ${this.options.targetPath}`);
if (this.options.configOnly) {
console.log('⚡ Mode: Configuration analysis only (fast)');
} else if (this.options.deepScan) {
console.log('🔬 Mode: Deep scan (comprehensive, may take longer)');
} else {
console.log('⚡ Mode: Standard scan (balanced speed and coverage)');
}
if (this.options.scanCaches) {
console.log('💾 Cache scanning: Enabled');
}
console.log('');
}
}
getExitCode(summary) {
if (summary.maliciousPackages > 0) {
return 2; // Malicious packages found
}
if (summary.risks.critical > 0) {
return 1; // Critical risks
}
if (summary.risks.high > 0) {
return 1; // High risks
}
return 0; // Safe
}
printSecurityAdvice() {
if (!this.options.json && !this.options.help) {
console.log('💡 SECURITY TIPS:');
console.log(' • Run this scanner regularly in your CI/CD pipeline');
console.log(' • Use --deep-scan for thorough pre-production checks');
console.log(' • Keep the malicious package database updated');
console.log(' • Report new malicious packages to the community');
console.log('');
}
}
}
module.exports = CLIHandler;