endpoint-sentinel
Version:
User-friendly security scanner with interactive setup that scales from beginner to expert
240 lines ⢠9.7 kB
JavaScript
;
/**
* 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