UNPKG

endpoint-sentinel

Version:

User-friendly security scanner with interactive setup that scales from beginner to expert

363 lines • 14.4 kB
"use strict"; /** * Semantic CLI Commands * User-friendly commands with progressive setup */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.SemanticCommands = void 0; const chalk_1 = __importDefault(require("chalk")); const config_manager_js_1 = require("../utils/config-manager.js"); const interactive_setup_js_1 = require("../utils/interactive-setup.js"); const scanner_js_1 = require("../core/scanner.js"); const logger_js_1 = require("../utils/logger.js"); const output_js_1 = require("./output.js"); class SemanticCommands { configManager; interactiveSetup; constructor() { this.configManager = new config_manager_js_1.ConfigManager(); this.interactiveSetup = new interactive_setup_js_1.InteractiveSetup(); } /** * Register all semantic commands */ registerCommands(program) { this.registerScanCommand(program); this.registerQuickCommand(program); this.registerConfigCommands(program); this.registerListCommand(program); } /** * Main scan command with progressive setup */ registerScanCommand(program) { program .command('scan <url>') .description('šŸŽÆ Scan a URL with automatic setup') .option('-f, --fresh', 'Force fresh setup (ignore existing config)') .option('-t, --token <token>', 'Quick auth token override') .option('-o, --output <file>', 'Output file for results') .option('--format <format>', 'Output format (json, csv, console)', 'console') .option('-v, --verbose', 'Verbose output') .option('-r, --rate-limit <number>', 'Requests per second override', parseFloat) .option('--non-interactive', 'Skip interactive prompts (for CI/CD)') .action(async (url, options) => { await this.handleSemanticScan(url, options); }); } /** * Quick command for power users */ registerQuickCommand(program) { program .command('quick <url>') .description('⚔ Quick scan with smart defaults') .option('-t, --token <token>', 'Authentication token') .option('-k, --keywords <keywords>', 'Comma-separated keywords') .option('-o, --output <file>', 'Output file') .option('--format <format>', 'Output format', 'console') .action(async (url, options) => { await this.handleQuickScan(url, options); }); } /** * Configuration management commands */ registerConfigCommands(program) { const configCmd = program .command('config') .description('šŸ”§ Manage configurations'); configCmd .command('list') .description('šŸ“‹ List saved configurations') .action(async () => { await this.handleConfigList(); }); configCmd .command('show <url>') .description('šŸ‘ļø Show configuration for URL') .action(async (url) => { await this.handleConfigShow(url); }); configCmd .command('edit <url>') .description('āœļø Edit configuration for URL') .action(async (url) => { await this.handleConfigEdit(url); }); configCmd .command('delete <url>') .description('šŸ—‘ļø Delete configuration for URL') .action(async (url) => { await this.handleConfigDelete(url); }); configCmd .command('reset') .description('šŸ”„ Reset all configurations') .option('--confirm', 'Skip confirmation prompt') .action(async (options) => { await this.handleConfigReset(options); }); } /** * List command for saved scans */ registerListCommand(program) { program .command('list') .alias('ls') .description('šŸ“œ List saved configurations and recent scans') .action(async () => { await this.handleList(); }); } /** * Handle semantic scan command */ async handleSemanticScan(url, options) { try { (0, output_js_1.displayBanner)(); let config; let useExistingConfig = false; // Check for existing config (unless --fresh is specified) if (!options.fresh && this.configManager.hasConfig(url)) { if (options.nonInteractive) { useExistingConfig = true; } else { useExistingConfig = await this.interactiveSetup.shouldUseExistingConfig(url); } } if (useExistingConfig) { // Use existing config with any CLI overrides config = this.configManager.buildScanConfig(url, this.extractOverrides(options)); console.log(chalk_1.default.green('āœ… Using saved configuration')); } else { if (options.nonInteractive) { // Non-interactive mode: use quick setup or fail if (options.token) { await this.interactiveSetup.quickSetup(url, options.token); config = this.configManager.buildScanConfig(url, this.extractOverrides(options)); } else { config = this.buildBasicConfig(url, options); } } else { // Interactive setup await this.interactiveSetup.setupDomain(url); config = this.configManager.buildScanConfig(url, this.extractOverrides(options)); } } // Execute the scan await this.executeScan(config); } catch (error) { console.error(chalk_1.default.red('āŒ Scan failed:'), error instanceof Error ? error.message : 'Unknown error'); process.exit(1); } } /** * Handle quick scan command */ async handleQuickScan(url, options) { try { (0, output_js_1.displayBanner)(); console.log(chalk_1.default.blue.bold('\n⚔ Quick Scan Mode\n')); // Build config with smart defaults const config = this.buildQuickConfig(url, options); // Auto-save if token provided if (options.token) { await this.interactiveSetup.quickSetup(url, options.token); } await this.executeScan(config); } catch (error) { console.error(chalk_1.default.red('āŒ Quick scan failed:'), error instanceof Error ? error.message : 'Unknown error'); process.exit(1); } } /** * Execute scan with config */ async executeScan(config) { console.log(chalk_1.default.blue('šŸŽÆ Target:'), chalk_1.default.white(config.target)); console.log(chalk_1.default.blue('⚔ Rate:'), chalk_1.default.white(`${config.rateLimit || 2} req/s`)); if (config.keywords && config.keywords.length > 0) { console.log(chalk_1.default.blue('šŸ” Keywords:'), chalk_1.default.white(config.keywords.join(', '))); } // Initialize scanner const logger = (0, logger_js_1.createLogger)({ level: config.verbose ? 'debug' : 'info', auditEnabled: true }); const scanner = new scanner_js_1.EndpointSentinel(logger); const progressDisplay = (0, output_js_1.displayProgress)(); try { const results = await scanner.scan(config); progressDisplay.stop(); await (0, output_js_1.displayResults)(results, config.output ? 'json' : 'console', config.output); const criticalFindings = results.findings.filter((f) => f.severity === 'critical').length; if (criticalFindings > 0) { console.log(chalk_1.default.red(`\nāš ļø Found ${criticalFindings} critical vulnerabilities!`)); } process.exit(criticalFindings > 0 ? 2 : 0); } catch (scanError) { progressDisplay.stop(); throw scanError; } } /** * Extract CLI overrides from options */ extractOverrides(options) { const overrides = {}; if (options.token) { overrides.cookie = `token=${options.token}`; } if (options.output) overrides.output = options.output; if (options.verbose) overrides.verbose = options.verbose; if (options.rateLimit) overrides.rateLimit = options.rateLimit; return overrides; } /** * Build basic config for non-interactive mode */ buildBasicConfig(url, options) { const config = { target: url, consent: true, rateLimit: options.rateLimit || 2 }; if (options.token) { config.cookie = `token=${options.token}`; } if (options.output) config.output = options.output; if (options.verbose) config.verbose = options.verbose; return config; } /** * Build quick config with smart defaults */ buildQuickConfig(url, options) { const config = { target: url, consent: true, rateLimit: 5, // Faster for quick scans keywords: ['api', 'admin', 'user'] // Smart defaults }; if (options.token) { config.cookie = `token=${options.token}`; } if (options.keywords) { config.keywords = options.keywords.split(',').map((k) => k.trim()); } if (options.output) config.output = options.output; return config; } /** * Configuration management handlers */ async handleConfigList() { const configs = this.configManager.listConfigs(); if (configs.length === 0) { console.log(chalk_1.default.yellow('šŸ“­ No saved configurations found')); console.log(chalk_1.default.gray('Run a scan to create your first configuration!')); return; } console.log(chalk_1.default.blue.bold('\nšŸ“‹ Saved Configurations\n')); configs.forEach((config, index) => { console.log(chalk_1.default.white(`${index + 1}. ${config.name || config.domain}`)); console.log(chalk_1.default.gray(` 🌐 ${config.domain}`)); console.log(chalk_1.default.gray(` šŸ” ${this.getAuthDisplayName(config.authType)}`)); if (config.defaultKeywords) { console.log(chalk_1.default.gray(` šŸ” ${config.defaultKeywords.join(', ')}`)); } console.log(chalk_1.default.gray(` šŸ“… ${config.updatedAt.toLocaleDateString()}\n`)); }); } async handleConfigShow(url) { const config = this.configManager.getConfig(url); if (!config) { console.log(chalk_1.default.red(`āŒ No configuration found for ${url}`)); return; } console.log(chalk_1.default.blue.bold(`\nšŸ” Configuration for ${config.domain}\n`)); console.log(chalk_1.default.white('Name:'), config.name || 'Unnamed'); console.log(chalk_1.default.white('Domain:'), config.domain); console.log(chalk_1.default.white('Auth Type:'), this.getAuthDisplayName(config.authType)); if (config.defaultKeywords) { console.log(chalk_1.default.white('Keywords:'), config.defaultKeywords.join(', ')); } console.log(chalk_1.default.white('Rate Limit:'), `${config.rateLimit || 2} req/s`); console.log(chalk_1.default.white('Created:'), config.createdAt.toLocaleString()); console.log(chalk_1.default.white('Updated:'), config.updatedAt.toLocaleString()); if (config.notes) { console.log(chalk_1.default.white('Notes:'), config.notes); } } async handleConfigEdit(url) { if (!this.configManager.hasConfig(url)) { console.log(chalk_1.default.red(`āŒ No configuration found for ${url}`)); console.log(chalk_1.default.gray('Run a scan first to create a configuration')); return; } console.log(chalk_1.default.blue('āœļø Editing configuration - running setup again...')); await this.interactiveSetup.setupDomain(url); } async handleConfigDelete(url) { if (!this.configManager.hasConfig(url)) { console.log(chalk_1.default.red(`āŒ No configuration found for ${url}`)); return; } const domain = new URL(url).hostname; const deleted = this.configManager.deleteConfig(url); if (deleted) { console.log(chalk_1.default.green(`āœ… Configuration deleted for ${domain}`)); } else { console.log(chalk_1.default.red(`āŒ Failed to delete configuration for ${domain}`)); } } async handleConfigReset(options) { if (!options.confirm) { console.log(chalk_1.default.yellow('āš ļø This will delete ALL saved configurations!')); console.log(chalk_1.default.gray('Use --confirm flag to proceed')); return; } this.configManager.reset(); console.log(chalk_1.default.green('āœ… All configurations reset')); } async handleList() { await this.handleConfigList(); } /** * Get display name for auth type */ getAuthDisplayName(authType) { const names = { 'none': 'No Authentication', 'session_cookie': 'Session Cookie', 'jwt': 'JWT Token', 'bearer_token': 'Bearer Token', 'api_key': 'API Key', 'basic': 'Basic Auth', 'oauth2': 'OAuth2', 'custom': 'Custom' }; return names[authType] || authType; } } exports.SemanticCommands = SemanticCommands; //# sourceMappingURL=semantic-commands.js.map