endpoint-sentinel
Version:
User-friendly security scanner with interactive setup that scales from beginner to expert
338 lines • 15.6 kB
JavaScript
;
/**
* CLI Output Utilities
* Rich terminal output with progress indicators and formatting
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.generateFilenameFromUrl = generateFilenameFromUrl;
exports.displayBanner = displayBanner;
exports.displayProgress = displayProgress;
exports.displayResults = displayResults;
exports.displayError = displayError;
exports.displayWarning = displayWarning;
exports.displaySuccess = displaySuccess;
const chalk_1 = __importDefault(require("chalk"));
const ora_1 = __importDefault(require("ora"));
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
/**
* Generate a safe filename from a URL
*/
function generateFilenameFromUrl(url) {
try {
const urlObj = new URL(url);
// Create base filename from hostname
let filename = urlObj.hostname.replace(/[^a-zA-Z0-9.-]/g, '-');
// Add path if it's not just root
if (urlObj.pathname && urlObj.pathname !== '/') {
const pathPart = urlObj.pathname
.replace(/^\/+|\/+$/g, '') // Remove leading/trailing slashes
.replace(/[^a-zA-Z0-9.-]/g, '-') // Replace invalid chars
.replace(/-+/g, '-') // Collapse multiple dashes
.substring(0, 50); // Limit length
if (pathPart) {
filename += `_${pathPart}`;
}
}
// Add timestamp to make unique and prevent conflicts
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
return `${filename}_${timestamp}.json`;
}
catch (error) {
// Fallback for invalid URLs
const safeName = url.replace(/[^a-zA-Z0-9]/g, '-').replace(/-+/g, '-');
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
return `scan_${safeName}_${timestamp}.json`;
}
}
/**
* Progress spinner utilities
*/
function displayBanner() {
// Line 1 - ENDPOINT - Normal start, glare begins at center (around the D)
console.log(chalk_1.default.blue.bold('███████╗███╗ ██╗██████╗ ') +
chalk_1.default.cyan.bold('██████╗ ██████╗ ██╗███╗ ██╗████████╗'));
// Line 2 - Glare moves left from center D
console.log(chalk_1.default.blue.bold('██╔════╝████╗ ██║██╔══██╗') +
chalk_1.default.cyan.bold('██╔══██╗██╔═══██╗██║████╗ ██║╚══██╔══╝'));
// Line 3 - Glare continues diagonal down-left
console.log(chalk_1.default.blue.bold('█████╗ ██╔██╗ ██║') +
chalk_1.default.cyan.bold('██║ ██║██████╔╝██║ ██║██║██╔██╗ ██║ ██║ '));
// Line 4 - Glare moving more left
console.log(chalk_1.default.blue.bold('██╔══╝ ') +
chalk_1.default.cyan.bold('██║╚██╗██║██║ ██║██╔═══╝ ██║ ██║██║██║╚██╗██║ ██║ '));
// Line 5 - Glare reaches left side
console.log(chalk_1.default.cyan.bold('███████╗██║ ╚████║██████╔╝██║ ╚██████╔╝██║██║ ╚████║ ██║ '));
// Line 6 - Separator
console.log(chalk_1.default.blue.bold('╚══════╝╚═╝ ╚═══╝╚═════╝ ╚═╝ ╚═════╝ ╚═╝╚═╝ ╚═══╝ ╚═╝ '));
console.log(' ');
// Line 8 - SENTINEL - Glare continues diagonal pattern (more on left)
console.log(chalk_1.default.cyan.bold('███████╗███████╗███╗ ██╗████████╗██╗███╗ ██╗') +
chalk_1.default.blue.bold('███████╗██╗ '));
// Line 9 - Glare reducing as it goes down-left
console.log(chalk_1.default.cyan.bold('██╔════╝██╔════╝████╗ ██║╚══██╔══╝██║████╗ ██║') +
chalk_1.default.blue.bold('██╔════╝██║ '));
// Line 10 - Glare more focused on left
console.log(chalk_1.default.cyan.bold('███████╗█████╗ ██╔██╗ ██║ ██║ ██║') +
chalk_1.default.blue.bold('██╔██╗ ██║█████╗ ██║ '));
// Line 11 - Glare mostly on left side
console.log(chalk_1.default.cyan.bold('╚════██║██╔══╝ ██║╚██╗██║ ██║ ') +
chalk_1.default.blue.bold('██║██║╚██╗██║██╔══╝ ██║ '));
// Line 12 - Final line - glare ends on left
console.log(chalk_1.default.cyan.bold('███████║███████╗██║ ╚████║ ██║ ') +
chalk_1.default.blue.bold('██║██║ ╚████║███████╗███████╗'));
// Line 13 - Bottom
console.log(chalk_1.default.blue.bold('╚══════╝╚══════╝╚═╝ ╚═══╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝'));
console.log(chalk_1.default.red.bold('\n🛡️ ADVANCED SECURITY RECONNAISSANCE TOOLKIT'));
console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
console.log(chalk_1.default.cyan(' Intelligent endpoint discovery • Vulnerability assessment • Security analysis'));
console.log(chalk_1.default.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
}
function displayProgress() {
const messages = [
'🔍 Initializing security scan...',
'🌐 Establishing connection...',
'🕷️ Discovering endpoints...',
'🔐 Analyzing authentication...',
'🛡️ Checking security headers...',
'📊 Generating report...'
];
let messageIndex = 0;
const spinner = (0, ora_1.default)(messages[0]).start();
const interval = setInterval(() => {
messageIndex = (messageIndex + 1) % messages.length;
spinner.text = messages[messageIndex] || '🔍 Processing...';
}, 2000);
const originalStop = spinner.stop.bind(spinner);
spinner.stop = () => {
clearInterval(interval);
return originalStop();
};
return spinner;
}
/**
* Displays scan results with formatting and automatic JSON output
*/
async function displayResults(results, format = 'console', outputFile) {
// Always create a JSON file - either custom name or auto-generated
let jsonFileName;
if (outputFile) {
jsonFileName = outputFile;
}
else {
jsonFileName = generateFilenameFromUrl(results.target);
}
switch (format.toLowerCase()) {
case 'json':
await displayJsonResults(results, jsonFileName);
break;
case 'csv':
await displayCsvResults(results, outputFile);
// Also create JSON for consistency
await displayJsonResults(results, jsonFileName);
break;
case 'console':
default:
displayConsoleResults(results);
// Always create JSON file for analysis
await displayJsonResults(results, jsonFileName);
break;
}
}
/**
* Displays results in console format with colors and formatting
*/
function displayConsoleResults(results) {
console.log(chalk_1.default.green.bold('\n🎯 SCAN COMPLETED SUCCESSFULLY\n'));
// Summary stats
console.log(chalk_1.default.blue('📊 SCAN SUMMARY'));
console.log(` Target: ${chalk_1.default.white(results.target)}`);
console.log(` Duration: ${chalk_1.default.white(Math.round(results.duration / 1000))}s`);
console.log(` Endpoints: ${chalk_1.default.white(results.endpointsDiscovered)}`);
console.log(` Findings: ${chalk_1.default.white(results.findings.length)}`);
if (results.summary) {
const summary = results.summary;
if (summary.criticalFindings > 0) {
console.log(` ${chalk_1.default.red.bold('Critical')}: ${chalk_1.default.red(summary.criticalFindings)}`);
}
if (summary.highFindings > 0) {
console.log(` ${chalk_1.default.red('High')}: ${chalk_1.default.red(summary.highFindings)}`);
}
if (summary.mediumFindings > 0) {
console.log(` ${chalk_1.default.yellow('Medium')}: ${chalk_1.default.yellow(summary.mediumFindings)}`);
}
if (summary.lowFindings > 0) {
console.log(` ${chalk_1.default.blue('Low')}: ${chalk_1.default.blue(summary.lowFindings)}`);
}
if (summary.infoFindings > 0) {
console.log(` ${chalk_1.default.gray('Info')}: ${chalk_1.default.gray(summary.infoFindings)}`);
}
}
// Sample findings
if (results.findings.length > 0) {
console.log(chalk_1.default.blue('\n🔍 TOP FINDINGS'));
const topFindings = results.findings
.sort((a, b) => {
const severityOrder = { critical: 5, high: 4, medium: 3, low: 2, info: 1 };
return (severityOrder[b.severity] || 0) -
(severityOrder[a.severity] || 0);
})
.slice(0, 5);
topFindings.forEach((finding, index) => {
const severityColor = {
critical: chalk_1.default.red.bold,
high: chalk_1.default.red,
medium: chalk_1.default.yellow,
low: chalk_1.default.blue,
info: chalk_1.default.gray
}[finding.severity] || chalk_1.default.gray;
console.log(`\n${index + 1}. ${severityColor(finding.severity.toUpperCase())} - ${finding.title}`);
console.log(` ${chalk_1.default.gray('Endpoint:')} ${finding.endpoint}`);
console.log(` ${chalk_1.default.gray('Impact:')} ${finding.description}`);
});
if (results.findings.length > 5) {
console.log(chalk_1.default.gray(`\n ... and ${results.findings.length - 5} more findings`));
}
}
displayFooter();
}
/**
* Displays findings section with severity-based coloring
*/
/**
* Groups findings by severity level
*/
/**
* Displays a group of findings for a specific severity
*/
/**
* Gets color function for severity level
*/
/**
* Gets icon for severity level
*/
/**
* Displays performance metrics
*/
/**
* Displays footer with recommendations
*/
function displayFooter() {
console.log(chalk_1.default.gray('\n📄 Detailed results saved to JSON file for analysis'));
console.log(chalk_1.default.gray('🛡️ Review findings and apply recommended remediations'));
console.log(chalk_1.default.gray('⚡ For help: es --help\n'));
}
/**
* Saves results in JSON format
*/
async function displayJsonResults(results, outputFile) {
const jsonOutput = JSON.stringify(results, null, 2);
if (outputFile) {
const resolvedPath = path.resolve(outputFile);
const dir = path.dirname(resolvedPath);
// Ensure directory exists
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(resolvedPath, jsonOutput, 'utf-8');
console.log(chalk_1.default.green(`📄 Results saved to: ${resolvedPath}`));
}
else {
console.log(jsonOutput);
}
}
/**
* Saves results in CSV format
*/
async function displayCsvResults(results, outputFile) {
const csvHeader = 'endpoint,type,severity,confidence,title,description,remediation\n';
const csvRows = results.findings.map(finding => {
const escapeCsv = (text) => `"${text.replace(/"/g, '""')}"`;
return [
escapeCsv(finding.endpoint),
escapeCsv(finding.type),
escapeCsv(finding.severity),
escapeCsv(finding.confidence),
escapeCsv(finding.title),
escapeCsv(finding.description),
escapeCsv(finding.remediation)
].join(',');
}).join('\n');
const csvOutput = csvHeader + csvRows;
if (outputFile) {
const resolvedPath = path.resolve(outputFile);
const dir = path.dirname(resolvedPath);
// Ensure directory exists
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
fs.writeFileSync(resolvedPath, csvOutput, 'utf-8');
console.log(chalk_1.default.green(`📊 CSV results saved to: ${resolvedPath}`));
}
else {
console.log(csvOutput);
}
}
/**
* Displays error message with formatting
*/
function displayError(message, error) {
console.log(chalk_1.default.red.bold('❌ ERROR'));
console.log(chalk_1.default.red(` ${message}`));
if (error && error.message) {
console.log(chalk_1.default.gray(` Details: ${error.message}`));
}
}
/**
* Displays warning message
*/
function displayWarning(message) {
console.log(chalk_1.default.yellow.bold('⚠️ WARNING'));
console.log(chalk_1.default.yellow(message));
console.log('');
}
/**
* Displays success message
*/
function displaySuccess(message) {
console.log(chalk_1.default.green.bold('✅ SUCCESS'));
console.log(chalk_1.default.green(message));
console.log('');
}
//# sourceMappingURL=output.js.map