UNPKG

endpoint-sentinel

Version:

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

240 lines • 9.7 kB
"use strict"; /** * Interactive Setup Flow * Vercel-style progressive configuration for security scanning */ var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.InteractiveSetup = void 0; const inquirer_1 = __importDefault(require("inquirer")); const chalk_1 = __importDefault(require("chalk")); const config_manager_js_1 = require("./config-manager.js"); class InteractiveSetup { configManager; constructor() { this.configManager = new config_manager_js_1.ConfigManager(); } /** * Main interactive setup flow */ async setupDomain(url) { console.log(chalk_1.default.blue.bold('\nšŸ›”ļø Endpoint Sentinel - Security Scanner Setup\n')); const domain = new URL(url).hostname; console.log(chalk_1.default.gray(`Setting up scanning for: ${chalk_1.default.white(domain)}\n`)); const answers = await this.runSetupQuestions(url); if (!answers.hasPermission) { console.log(chalk_1.default.red('\nāŒ Cannot proceed without authorization')); process.exit(1); } const config = this.buildConfigFromAnswers(answers, domain); if (answers.saveConfig) { this.configManager.saveConfig(url, config); console.log(chalk_1.default.green(`\nāœ… Configuration saved for ${domain}`)); } return config; } /** * Ask if user wants to use existing config */ async shouldUseExistingConfig(url) { const existingConfig = this.configManager.getConfig(url); if (!existingConfig) return false; console.log(chalk_1.default.blue('\nšŸ” Found existing configuration:')); this.displayConfigSummary(existingConfig); const { useExisting } = await inquirer_1.default.prompt([ { type: 'confirm', name: 'useExisting', message: 'Use this configuration?', default: true } ]); return useExisting; } /** * Run the complete setup question flow */ async runSetupQuestions(url) { const questions = [ { type: 'confirm', name: 'hasPermission', message: 'Do you have explicit authorization to scan this domain?', default: false }, { type: 'confirm', name: 'isAuthenticated', message: 'Is this an authenticated application?', default: false, when: (answers) => answers.hasPermission }, { type: 'list', name: 'authType', message: 'What type of authentication does it use?', choices: [ { name: 'šŸŖ Session Cookie (sessionid, JSESSIONID, etc.)', value: 'session_cookie' }, { name: 'šŸŽ« JWT Token (JSON Web Token)', value: 'jwt' }, { name: 'šŸ”‘ Bearer Token (Authorization header)', value: 'bearer_token' }, { name: 'šŸ—ļø API Key', value: 'api_key' }, { name: 'šŸ” Basic Auth (username:password)', value: 'basic' }, { name: 'āš™ļø OAuth2', value: 'oauth2' }, { name: 'šŸ› ļø Other/Custom', value: 'custom' } ], when: (answers) => answers.isAuthenticated }, { type: 'input', name: 'token', message: 'Paste your authentication token:', validate: (input) => input.length > 0 || 'Token cannot be empty', when: (answers) => ['jwt', 'bearer_token', 'api_key'].includes(answers.authType) }, { type: 'input', name: 'cookie', message: 'Paste your session cookie (name=value format):', validate: (input) => input.includes('=') || 'Cookie must be in name=value format', when: (answers) => answers.authType === 'session_cookie' } ]; const basicAnswers = await inquirer_1.default.prompt(questions); // Additional questions based on auth type const additionalQuestions = [ { type: 'input', name: 'appName', message: 'Give this application a friendly name (optional):', default: new URL(url).hostname }, { type: 'checkbox', name: 'keywords', message: 'Select relevant keywords for endpoint discovery:', choices: [ { name: 'admin - Admin panels and management', value: 'admin' }, { name: 'api - REST API endpoints', value: 'api' }, { name: 'user - User management', value: 'user' }, { name: 'dashboard - User dashboards', value: 'dashboard' }, { name: 'auth - Authentication endpoints', value: 'auth' }, { name: 'v1, v2 - API versioning', value: 'v1,v2' }, { name: 'profile - User profiles', value: 'profile' }, { name: 'settings - Application settings', value: 'settings' } ] }, { type: 'list', name: 'rateLimit', message: 'Choose scanning speed:', choices: [ { name: '🐌 Conservative (1 req/s) - Respectful, slower', value: 1 }, { name: 'āš–ļø Balanced (2 req/s) - Good default', value: 2 }, { name: '⚔ Fast (5 req/s) - Quick results', value: 5 }, { name: 'šŸš€ Maximum (10 req/s) - Use carefully!', value: 10 } ], default: 2 }, { type: 'confirm', name: 'saveConfig', message: 'Save this configuration for future scans?', default: true }, { type: 'input', name: 'notes', message: 'Add any notes about this application (optional):', when: (answers) => answers.saveConfig } ]; const additionalAnswers = await inquirer_1.default.prompt(additionalQuestions); return { ...basicAnswers, ...additionalAnswers }; } /** * Build domain config from answers */ buildConfigFromAnswers(answers, domain) { const authType = answers.isAuthenticated ? answers.authType : 'none'; // Flatten keywords array if it contains comma-separated values const keywords = answers.keywords?.flatMap(k => k.split(',').map(s => s.trim())) || []; const config = { domain, authType, createdAt: new Date(), updatedAt: new Date() }; // Only add optional properties if they have values if (answers.appName) config.name = answers.appName; if (answers.token) config.token = answers.token; if (answers.cookie) config.cookie = answers.cookie; if (keywords.length > 0) config.defaultKeywords = keywords; if (answers.rateLimit) config.rateLimit = answers.rateLimit; if (answers.notes) config.notes = answers.notes; return config; } /** * Display configuration summary */ displayConfigSummary(config) { console.log(chalk_1.default.gray(` Name: ${config.name || 'Unnamed'}`)); console.log(chalk_1.default.gray(` Auth: ${this.getAuthDisplayName(config.authType)}`)); if (config.defaultKeywords && config.defaultKeywords.length > 0) { console.log(chalk_1.default.gray(` Keywords: ${config.defaultKeywords.join(', ')}`)); } console.log(chalk_1.default.gray(` Rate Limit: ${config.rateLimit || 2} req/s`)); console.log(chalk_1.default.gray(` Last Updated: ${config.updatedAt.toLocaleDateString()}`)); if (config.notes) { console.log(chalk_1.default.gray(` Notes: ${config.notes}`)); } } /** * 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; } /** * Quick setup for power users */ async quickSetup(url, authToken) { const domain = new URL(url).hostname; const config = { domain, name: domain, authType: authToken ? 'jwt' : 'none', defaultKeywords: ['api', 'admin', 'user'], rateLimit: 2, createdAt: new Date(), updatedAt: new Date() }; // Only add token if provided if (authToken) { config.token = authToken; } this.configManager.saveConfig(url, config); console.log(chalk_1.default.green(`āœ… Quick setup completed for ${domain}`)); return config; } } exports.InteractiveSetup = InteractiveSetup; //# sourceMappingURL=interactive-setup.js.map