UNPKG

supamend

Version:

Pluggable DevSecOps Security Scanner with 10+ scanners and multiple reporting channels

234 lines • 10.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ConsoleReporter = void 0; const errors_1 = require("../core/errors"); const chalk_1 = __importDefault(require("chalk")); class ConsoleReporter { constructor() { this.name = 'console'; this.description = 'Display security scan results in the console with rich formatting'; this.version = '1.0.0'; } async init(config) { this.config = { colorize: config?.colorize !== false, // Default to true showDetails: config?.showDetails !== false, // Default to true maxIssues: config?.maxIssues || 50, format: config?.format || 'table' }; } async report(results, options) { try { const config = { ...this.config, ...options }; console.log('\n' + chalk_1.default.bold.blue('šŸ”’ Security Scan Results')); console.log(chalk_1.default.gray('='.repeat(50)) + '\n'); // Summary this.printSummary(results, config); if (results.length > 0) { console.log('\n' + chalk_1.default.bold('Detailed Results:')); console.log(chalk_1.default.gray('-'.repeat(50))); switch (config.format) { case 'table': this.printTable(results, config); break; case 'list': this.printList(results, config); break; case 'summary': this.printSummaryOnly(results, config); break; default: this.printTable(results, config); } } else { console.log(chalk_1.default.green.bold('āœ… No security issues found!')); } console.log('\n' + chalk_1.default.gray('='.repeat(50))); console.log(chalk_1.default.gray(`Scan completed at ${new Date().toLocaleString()}`)); } catch (error) { const reporterError = error instanceof Error ? error : new Error(String(error)); throw new errors_1.ReporterError(`Failed to display console report: ${reporterError.message}`, this.name, { recoverable: false, retryable: false, cause: reporterError, context: { operation: 'report' } }); } } async isAvailable() { return true; // Always available } getConfigSchema() { return { type: 'object', properties: { colorize: { type: 'boolean', description: 'Enable colored output' }, showDetails: { type: 'boolean', description: 'Show detailed issue information' }, maxIssues: { type: 'number', description: 'Maximum number of issues to display' }, format: { type: 'string', enum: ['table', 'list', 'summary'], description: 'Output format' } } }; } printSummary(results, config) { const summary = this.generateSummary(results); const colorize = config.colorize !== false; console.log(chalk_1.default.bold('šŸ“Š Summary:')); // Total issues const totalText = `Total Issues: ${results.length}`; console.log(colorize ? chalk_1.default.bold(totalText) : totalText); // Severity breakdown if (Object.keys(summary.severity).length > 0) { console.log('\nSeverity Breakdown:'); Object.entries(summary.severity).forEach(([severity, count]) => { const severityText = `${severity.toUpperCase()}: ${count}`; if (colorize) { const color = this.getSeverityColor(severity); console.log(` ${color(severityText)}`); } else { console.log(` ${severityText}`); } }); } // Scanner breakdown if (Object.keys(summary.scanners).length > 0) { console.log('\nScanners Used:'); Object.entries(summary.scanners).forEach(([scanner, count]) => { const scannerText = `${scanner}: ${count} issues`; console.log(` ${colorize ? chalk_1.default.cyan(scannerText) : scannerText}`); }); } } printTable(results, config) { const colorize = config.colorize !== false; const maxIssues = config.maxIssues || 50; const displayResults = results.slice(0, maxIssues); if (displayResults.length === 0) return; // Table header with better spacing const header = ['Severity', 'Scanner', 'Type', 'Title', 'File', 'Rule']; const headerText = header.map(h => colorize ? chalk_1.default.bold(h) : h).join(' | '); console.log(headerText); console.log(chalk_1.default.gray('-'.repeat(Math.max(headerText.length, 120)))); // Table rows displayResults.forEach(result => { const severity = colorize ? this.getSeverityColor(result.severity)(result.severity.toUpperCase().padEnd(8)) : result.severity.toUpperCase().padEnd(8); const scanner = (colorize ? chalk_1.default.cyan(result.scanner) : result.scanner).padEnd(15); const type = (colorize ? chalk_1.default.yellow(result.type) : result.type).padEnd(12); const title = result.title.length > 35 ? result.title.substring(0, 32) + '...' : result.title.padEnd(35); const file = result.file ? (result.line ? `${result.file}:${result.line}` : result.file) : 'N/A'; const fileFormatted = file.length > 25 ? file.substring(0, 22) + '...' : file.padEnd(25); const rule = result.rule ? (result.rule.length > 15 ? result.rule.substring(0, 12) + '...' : result.rule) : ''; const row = [severity, scanner, type, title, colorize ? chalk_1.default.blue(fileFormatted) : fileFormatted, colorize ? chalk_1.default.gray(rule) : rule].join(' | '); console.log(row); }); if (results.length > maxIssues) { console.log(chalk_1.default.gray(`\n... and ${results.length - maxIssues} more issues (use --format list for full details)`)); } } printList(results, config) { const colorize = config.colorize !== false; const maxIssues = config.maxIssues || 50; const displayResults = results.slice(0, maxIssues); if (displayResults.length === 0) return; displayResults.forEach((result, index) => { const severity = colorize ? this.getSeverityColor(result.severity)(result.severity.toUpperCase()) : result.severity.toUpperCase(); const scanner = colorize ? chalk_1.default.cyan(result.scanner) : result.scanner; console.log(`\n${index + 1}. ${severity} - ${result.title}`); console.log(` šŸ” Scanner: ${scanner}`); console.log(` šŸ·ļø Type: ${result.type}`); if (result.description) { const desc = result.description.length > 100 ? result.description.substring(0, 97) + '...' : result.description; console.log(` šŸ“ Description: ${desc}`); } if (result.file) { const fileInfo = result.line ? `${result.file}:${result.line}` : result.file; console.log(` šŸ“ File: ${colorize ? chalk_1.default.blue(fileInfo) : fileInfo}`); } if (result.rule) { console.log(` šŸ“‹ Rule: ${colorize ? chalk_1.default.gray(result.rule) : result.rule}`); } if (result.column) { console.log(` šŸ“ Position: Line ${result.line || 'N/A'}, Column ${result.column}`); } }); if (results.length > maxIssues) { console.log(chalk_1.default.gray(`\n... and ${results.length - maxIssues} more issues`)); } } printSummaryOnly(results, config) { const colorize = config.colorize !== false; const criticalIssues = results.filter(r => r.severity === 'critical'); const highIssues = results.filter(r => r.severity === 'high'); if (criticalIssues.length > 0) { console.log(chalk_1.default.bold.red('\n🚨 Critical Issues:')); criticalIssues.slice(0, 5).forEach(issue => { const title = colorize ? chalk_1.default.red(issue.title) : issue.title; console.log(` • ${title} (${issue.scanner})`); }); if (criticalIssues.length > 5) { console.log(chalk_1.default.gray(` ... and ${criticalIssues.length - 5} more`)); } } if (highIssues.length > 0) { console.log(chalk_1.default.bold.yellow('\nāš ļø High Priority Issues:')); highIssues.slice(0, 5).forEach(issue => { const title = colorize ? chalk_1.default.yellow(issue.title) : issue.title; console.log(` • ${title} (${issue.scanner})`); }); if (highIssues.length > 5) { console.log(chalk_1.default.gray(` ... and ${highIssues.length - 5} more`)); } } } generateSummary(results) { const severityCounts = results.reduce((acc, result) => { acc[result.severity] = (acc[result.severity] || 0) + 1; return acc; }, {}); const scannerCounts = results.reduce((acc, result) => { acc[result.scanner] = (acc[result.scanner] || 0) + 1; return acc; }, {}); return { severity: severityCounts, scanners: scannerCounts }; } getSeverityColor(severity) { switch (severity) { case 'critical': return chalk_1.default.red; case 'high': return chalk_1.default.yellow; case 'medium': return chalk_1.default.magenta; case 'low': return chalk_1.default.green; default: return chalk_1.default.white; } } } exports.ConsoleReporter = ConsoleReporter; exports.default = new ConsoleReporter(); //# sourceMappingURL=console.js.map