UNPKG

vibesec

Version:

Security scanner for AI-generated code - detects vulnerabilities in vibe-coded projects

284 lines 14 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.PlainLanguageReporter = void 0; const chalk_1 = __importDefault(require("chalk")); const types_1 = require("../scanner/core/types"); const security_score_1 = require("../lib/utils/security-score"); class PlainLanguageReporter { constructor() { this.analogies = { 'secrets': 'password written on a sticky note on your monitor', 'injection': 'unlocked front door that anyone can walk through', 'sql-injection': 'unlocked front door that anyone can walk through', 'xss': 'poisoned water supply affecting everyone who drinks', 'command-injection': 'blank check handed to a stranger', 'path-traversal': 'skeleton key that opens any room', 'auth': 'broken lock on your front door', 'incomplete': 'half-built security fence with gaps', 'ai-specific': 'AI system that can be tricked into misbehaving', }; this.severityMap = { [types_1.Severity.CRITICAL]: { emoji: '🚨', label: 'Urgent - Fix Today', timeframe: 'immediately', businessImpact: 'High risk of data breach, legal liability, and financial loss', }, [types_1.Severity.HIGH]: { emoji: '⚠️', label: 'Important - Fix This Week', timeframe: 'within 7 days', businessImpact: 'Moderate risk to data security and user trust', }, [types_1.Severity.MEDIUM]: { emoji: '📋', label: 'Notable - Fix Soon', timeframe: 'within 30 days', businessImpact: 'Could lead to security problems if not addressed', }, [types_1.Severity.LOW]: { emoji: 'ℹ️', label: 'Good to Know - Consider Fixing', timeframe: 'when convenient', businessImpact: 'Minimal risk, follows best practices', }, }; this.fixTimeEstimates = { 'secrets': '10-15 minutes', 'injection': '15-30 minutes', 'sql-injection': '15-30 minutes', 'xss': '20-40 minutes', 'command-injection': '15-30 minutes', 'auth': '1-2 hours', 'incomplete': '30 minutes - 2 hours', 'ai-specific': '1-4 hours', 'default': '30 minutes - 1 hour', }; this.whoCanFix = { 'secrets': 'Any developer', 'injection': 'Backend developer', 'sql-injection': 'Backend developer', 'xss': 'Frontend or full-stack developer', 'command-injection': 'Backend developer', 'auth': 'Senior developer or security engineer', 'incomplete': 'Original developer or tech lead', 'ai-specific': 'AI/ML engineer or senior developer', 'default': 'Any developer', }; } generate(result) { const lines = []; lines.push(chalk_1.default.bold('═'.repeat(70))); lines.push(chalk_1.default.bold.cyan(' VibeSec Security Scan - Plain Language Report')); lines.push(chalk_1.default.bold('═'.repeat(70))); lines.push(''); lines.push(chalk_1.default.gray(`📁 Scanned: ${result.scan.path}`)); lines.push(chalk_1.default.gray(`📊 Files checked: ${result.scan.filesScanned}`)); lines.push(chalk_1.default.gray(`⏱️ Time taken: ${result.scan.duration.toFixed(2)} seconds`)); lines.push(''); const securityScore = (0, security_score_1.calculateSecurityScore)(result); const scoreColor = this.getScoreColor(securityScore.score); lines.push(chalk_1.default.bold('Security Score:')); lines.push(''); lines.push(scoreColor(` ${securityScore.score}/100 (${securityScore.grade}) - ${securityScore.rating}`)); lines.push(chalk_1.default.gray(` ${(0, security_score_1.getBenchmarkComparison)(securityScore.score, result.scan.filesScanned)}`)); lines.push(''); lines.push(chalk_1.default.bold('Summary:')); lines.push(''); const { bySeverity } = result.summary; if (bySeverity.critical > 0) { lines.push(chalk_1.default.red.bold(`🚨 ${bySeverity.critical} urgent issue${bySeverity.critical > 1 ? 's' : ''} need${bySeverity.critical === 1 ? 's' : ''} immediate attention`)); } if (bySeverity.high > 0) { lines.push(chalk_1.default.yellow.bold(`⚠️ ${bySeverity.high} important issue${bySeverity.high > 1 ? 's' : ''} should be fixed this week`)); } if (bySeverity.medium > 0) { lines.push(chalk_1.default.blue(`📋 ${bySeverity.medium} issue${bySeverity.medium > 1 ? 's' : ''} should be addressed soon`)); } if (bySeverity.low > 0) { lines.push(chalk_1.default.gray(`ℹ️ ${bySeverity.low} minor issue${bySeverity.low > 1 ? 's' : ''} to consider`)); } lines.push(''); if (result.findings.length === 0) { lines.push(chalk_1.default.green.bold('✅ Great news! No security issues found.')); lines.push(''); lines.push('Your code looks secure. Keep up the good work!'); lines.push(''); } else { lines.push(chalk_1.default.bold('═'.repeat(70))); lines.push(chalk_1.default.bold('Detailed Findings:')); lines.push(chalk_1.default.bold('═'.repeat(70))); lines.push(''); const grouped = this.groupBySeverity(result.findings); const severityOrder = [ types_1.Severity.CRITICAL, types_1.Severity.HIGH, types_1.Severity.MEDIUM, types_1.Severity.LOW, ]; let findingNumber = 1; for (const severity of severityOrder) { const findings = grouped[severity] || []; if (findings.length === 0) continue; for (const finding of findings) { lines.push(this.formatFinding(finding, findingNumber)); lines.push(chalk_1.default.bold('─'.repeat(70))); lines.push(''); findingNumber++; } } } lines.push(chalk_1.default.bold('═'.repeat(70))); lines.push(''); if (result.summary.total > 0) { lines.push(chalk_1.default.bold('💡 What to do next:')); lines.push(''); if (bySeverity.critical > 0) { lines.push(chalk_1.default.red(` 1. Fix the ${bySeverity.critical} urgent issue${bySeverity.critical > 1 ? 's' : ''} TODAY`)); } if (bySeverity.high > 0) { lines.push(chalk_1.default.yellow(` ${bySeverity.critical > 0 ? '2' : '1'}. Address the ${bySeverity.high} important issue${bySeverity.high > 1 ? 's' : ''} this week`)); } const nextStep = bySeverity.critical > 0 && bySeverity.high > 0 ? '3' : bySeverity.critical > 0 || bySeverity.high > 0 ? '2' : '1'; lines.push(chalk_1.default.blue(` ${nextStep}. Run 'vibesec scan --explain' again after making fixes`)); lines.push(''); lines.push(chalk_1.default.gray('💭 Need help understanding these issues?')); lines.push(chalk_1.default.gray(' Ask your development team to review the findings with you.')); lines.push(''); } else { lines.push(chalk_1.default.green('✨ Your code is secure!')); lines.push(''); lines.push('Keep running scans regularly to catch issues early.'); lines.push(''); } lines.push(chalk_1.default.bold('═'.repeat(70))); return lines.join('\n'); } formatFinding(finding, number) { const lines = []; const plainSeverity = this.severityMap[finding.severity]; const color = this.getSeverityColor(finding.severity); lines.push(color.bold(`${plainSeverity.emoji} [${number}] ${plainSeverity.label}`)); lines.push(''); lines.push(chalk_1.default.bold('Found:')); lines.push(`${finding.title} in ${chalk_1.default.cyan(finding.location.file + ':' + finding.location.line)}`); lines.push(''); lines.push(chalk_1.default.bold('What this means:')); lines.push(this.explainInPlainLanguage(finding)); lines.push(''); lines.push(chalk_1.default.bold('Why it matters:')); lines.push(plainSeverity.businessImpact); lines.push(''); lines.push(this.describeRealWorldImpact(finding)); lines.push(''); if (finding.snippet && finding.snippet.trim()) { lines.push(chalk_1.default.bold('The code:')); lines.push(chalk_1.default.gray(finding.snippet)); lines.push(''); } lines.push(chalk_1.default.bold('How to fix:')); lines.push(finding.fix.recommendation); lines.push(''); if (finding.fix.before && finding.fix.after) { lines.push(chalk_1.default.gray('Before:')); lines.push(chalk_1.default.red(` ${finding.fix.before}`)); lines.push(''); lines.push(chalk_1.default.gray('After:')); lines.push(chalk_1.default.green(` ${finding.fix.after}`)); lines.push(''); } const fixTime = this.estimateFixTime(finding); const whoFixes = this.suggestWhoCanFix(finding); lines.push(chalk_1.default.bold('Practical details:')); lines.push(`• Time needed: ${chalk_1.default.cyan(fixTime)}`); lines.push(`• Who can fix: ${chalk_1.default.cyan(whoFixes)}`); lines.push(`• Priority: ${chalk_1.default.cyan(plainSeverity.timeframe)}`); lines.push(''); if (finding.fix.references && finding.fix.references.length > 0) { lines.push(chalk_1.default.bold('Learn more:')); finding.fix.references.forEach((ref) => { lines.push(chalk_1.default.gray(` • ${ref}`)); }); lines.push(''); } return lines.join('\n'); } explainInPlainLanguage(finding) { const analogy = this.getAnalogy(finding.category); let explanation = finding.description; if (analogy) { explanation += `\n\nThink of this like having ${analogy}.`; } return explanation; } getAnalogy(category) { const categoryKey = category.toLowerCase(); return this.analogies[categoryKey] || this.analogies['incomplete']; } describeRealWorldImpact(finding) { const impacts = { 'secrets': 'If this code is shared (GitHub, email, etc.), anyone who sees it can:\n • Use your API keys and credentials\n • Rack up charges on your accounts\n • Access your private data', 'injection': 'An attacker could:\n • Steal all your data\n • Delete your database\n • Take over user accounts', 'sql-injection': 'An attacker could:\n • Read all data in your database\n • Modify or delete records\n • Bypass authentication and access any account', 'xss': 'Attackers could:\n • Steal user session cookies\n • Redirect users to malicious sites\n • Deface your website', 'command-injection': 'An attacker could:\n • Execute commands on your server\n • Read sensitive files\n • Take complete control of your system', 'auth': 'Users could:\n • Access features they haven\'t paid for\n • View other users\' private data\n • Bypass security restrictions', 'default': 'This could lead to:\n • Security vulnerabilities\n • Data exposure\n • System compromise', }; const categoryKey = finding.category.toLowerCase(); const ruleKey = finding.rule.toLowerCase(); return impacts[ruleKey] || impacts[categoryKey] || impacts['default']; } estimateFixTime(finding) { const categoryKey = finding.category.toLowerCase(); const ruleKey = finding.rule.toLowerCase(); return (this.fixTimeEstimates[ruleKey] || this.fixTimeEstimates[categoryKey] || this.fixTimeEstimates['default']); } suggestWhoCanFix(finding) { const categoryKey = finding.category.toLowerCase(); const ruleKey = finding.rule.toLowerCase(); return (this.whoCanFix[ruleKey] || this.whoCanFix[categoryKey] || this.whoCanFix['default']); } getSeverityColor(severity) { const colors = { [types_1.Severity.CRITICAL]: chalk_1.default.red, [types_1.Severity.HIGH]: chalk_1.default.yellow, [types_1.Severity.MEDIUM]: chalk_1.default.blue, [types_1.Severity.LOW]: chalk_1.default.gray, }; return colors[severity] || chalk_1.default.white; } getScoreColor(score) { if (score >= 90) return chalk_1.default.green.bold; if (score >= 80) return chalk_1.default.blue.bold; if (score >= 70) return chalk_1.default.yellow.bold; return chalk_1.default.red.bold; } groupBySeverity(findings) { const grouped = { [types_1.Severity.CRITICAL]: [], [types_1.Severity.HIGH]: [], [types_1.Severity.MEDIUM]: [], [types_1.Severity.LOW]: [], }; for (const finding of findings) { grouped[finding.severity].push(finding); } return grouped; } } exports.PlainLanguageReporter = PlainLanguageReporter; //# sourceMappingURL=plain-language.js.map