// ============================================================================ // Agent 7: Security Auditor // Comprehensive security scanning - OWASP Top 10, Zero-Day patterns // ============================================================================ import * as path from 'path'; import { AgentState, SecurityReport, Vulnerability, VulnerabilityType, DependencyVulnerability, CodeSmell, OwaspCheck, } from '../types'; import { readFileContent, loadPackageJson } from '../utils/file-utils'; import { logger } from '../utils/logger'; export async function securityAgent(state: AgentState): Promise> { const startTime = Date.now(); logger.agentStart('security'); try { if (!state.codeAnalysis || !state.projectStructure) { throw new Error('Code analysis not available. Analyzer Agent must run first.'); } const vulnerabilities: Vulnerability[] = []; const codeSmells: CodeSmell[] = []; const sourceFiles = state.projectStructure.files.filter( f => f.type === 'source' && (f.language === 'typescript' || f.language === 'javascript') ); // 1. Scan source files for vulnerabilities logger.agent('security', `Scanning ${sourceFiles.length} files for security vulnerabilities...`); for (let i = 0; i < sourceFiles.length; i++) { const file = sourceFiles[i]; logger.progress(i + 1, sourceFiles.length, file.relativePath); const content = readFileContent(file.path); if (!content) continue; // Run all security checks vulnerabilities.push(...checkSqlInjection(content, file.path)); vulnerabilities.push(...checkXss(content, file.path)); vulnerabilities.push(...checkCommandInjection(content, file.path)); vulnerabilities.push(...checkPathTraversal(content, file.path)); vulnerabilities.push(...checkPrototypePollution(content, file.path)); vulnerabilities.push(...checkInsecureDeserialization(content, file.path)); vulnerabilities.push(...checkHardcodedSecrets(content, file.path)); vulnerabilities.push(...checkInsecureRandomness(content, file.path)); vulnerabilities.push(...checkSsrf(content, file.path)); vulnerabilities.push(...checkRegexDos(content, file.path)); vulnerabilities.push(...checkInformationDisclosure(content, file.path)); vulnerabilities.push(...checkBrokenAuth(content, file.path)); vulnerabilities.push(...checkCsrf(content, file.path)); vulnerabilities.push(...checkZeroDayPatterns(content, file.path)); // Code smells codeSmells.push(...detectCodeSmells(content, file.path)); } // 2. Check dependencies logger.agent('security', 'Checking dependencies for known vulnerabilities...'); const depVulns = checkDependencyVulnerabilities(state.projectPath); // 3. OWASP Top 10 checks logger.agent('security', 'Running OWASP Top 10 checks...'); const owaspChecks = performOwaspChecks(state, vulnerabilities); // 4. Calculate risk score const score = calculateSecurityScore(vulnerabilities, depVulns, owaspChecks); const overallRisk = getOverallRisk(score); // Report const criticalCount = vulnerabilities.filter(v => v.severity === 'critical').length; const highCount = vulnerabilities.filter(v => v.severity === 'high').length; const mediumCount = vulnerabilities.filter(v => v.severity === 'medium').length; const lowCount = vulnerabilities.filter(v => v.severity === 'low').length; logger.newline(); logger.table( ['Severity', 'Count'], [ ['CRITICAL', String(criticalCount)], ['HIGH', String(highCount)], ['MEDIUM', String(mediumCount)], ['LOW', String(lowCount)], ['Code Smells', String(codeSmells.length)], ['Dep. Vulns', String(depVulns.length)], ] ); // Show critical findings for (const vuln of vulnerabilities.filter(v => v.severity === 'critical' || v.severity === 'high')) { logger.securityFinding(vuln.severity, `${vuln.type}: ${vuln.description}`, path.relative(state.projectPath, vuln.filePath)); } logger.newline(); logger.stats('Security Score', `${score}/100`); logger.stats('Risk Assessment', overallRisk.toUpperCase()); // OWASP Summary const owaspPassed = owaspChecks.filter(c => c.passed).length; logger.stats('OWASP Top 10', `${owaspPassed}/${owaspChecks.length} passed`); const securityReport: SecurityReport = { vulnerabilities, dependencyAudit: depVulns, codeSmells, owaspChecks, overallRisk, score, }; logger.agentComplete('security', Date.now() - startTime); return { securityReport, agentLog: [ ...state.agentLog, { agent: 'security', timestamp: new Date().toISOString(), action: 'Security audit completed', details: `${vulnerabilities.length} vulnerabilities, Score: ${score}/100, Risk: ${overallRisk}`, duration: Date.now() - startTime, status: 'complete', }, ], }; } catch (error) { const errMsg = error instanceof Error ? error.message : String(error); logger.agentError('security', errMsg); return { errors: [...state.errors, `Security: ${errMsg}`], agentLog: [ ...state.agentLog, { agent: 'security', timestamp: new Date().toISOString(), action: 'Error', details: errMsg, status: 'error', }, ], }; } } // --- Vulnerability Checks --- function checkSqlInjection(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); const patterns = [ { regex: /\$\{.*\}.*(?:SELECT|INSERT|UPDATE|DELETE|FROM|WHERE)/i, desc: 'SQL Template Literal Injection' }, { regex: /['"].*\+.*(?:SELECT|INSERT|UPDATE|DELETE|FROM|WHERE)/i, desc: 'SQL String Concatenation' }, { regex: /query\s*\(\s*['"`].*\$\{/i, desc: 'Unsanitized SQL Query' }, { regex: /\.raw\s*\(\s*['"`].*\+/i, desc: 'Raw SQL with Concatenation' }, { regex: /exec(?:ute)?\s*\(\s*['"`].*\+/i, desc: 'SQL Execute with Concatenation' }, ]; for (let i = 0; i < lines.length; i++) { for (const pattern of patterns) { if (pattern.regex.test(lines[i])) { vulns.push(createVulnerability('sql-injection', 'high', filePath, i + 1, lines[i].trim(), pattern.desc, 'Use parameterized queries/prepared statements', 'CWE-89', 'A03:2021')); } } } return vulns; } function checkXss(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); const patterns = [ { regex: /innerHTML\s*=/, desc: 'innerHTML assignment (potential XSS)' }, { regex: /dangerouslySetInnerHTML/, desc: 'React dangerouslySetInnerHTML' }, { regex: /document\.write\s*\(/, desc: 'document.write (XSS risk)' }, { regex: /\.html\s*\(\s*(?:req\.|request\.|params\.|query\.|body\.)/, desc: 'Unsanitized user input in HTML' }, { regex: /res\.send\s*\(.*req\./, desc: 'Reflected user input in response' }, { regex: /eval\s*\(/, desc: 'eval() usage (code injection risk)' }, ]; for (let i = 0; i < lines.length; i++) { for (const pattern of patterns) { if (pattern.regex.test(lines[i])) { vulns.push(createVulnerability('xss', 'high', filePath, i + 1, lines[i].trim(), pattern.desc, 'Sanitize output, use Content Security Policy', 'CWE-79', 'A03:2021')); } } } return vulns; } function checkCommandInjection(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); const patterns = [ { regex: /exec\s*\(\s*['"`].*\$\{/, desc: 'Command injection via template literal' }, { regex: /exec\s*\(\s*.*\+\s*(?:req\.|params\.|query\.|body\.|input|user)/, desc: 'Command injection via user input' }, { regex: /execSync\s*\(\s*.*\+/, desc: 'Synchronous command injection' }, { regex: /spawn\s*\(\s*['"`](?:sh|bash|cmd)/, desc: 'Shell spawn with user input risk' }, { regex: /child_process/, desc: 'child_process usage (review for injection)' }, ]; for (let i = 0; i < lines.length; i++) { for (const pattern of patterns) { if (pattern.regex.test(lines[i])) { const severity = pattern.desc.includes('user input') ? 'critical' : 'medium'; vulns.push(createVulnerability('command-injection', severity, filePath, i + 1, lines[i].trim(), pattern.desc, 'Use allowlists, no shell interpolation with user input', 'CWE-78', 'A03:2021')); } } } return vulns; } function checkPathTraversal(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); const patterns = [ { regex: /(?:readFile|writeFile|createReadStream|createWriteStream|access)\s*\(\s*(?:req\.|params\.|query\.|body\.)/, desc: 'File operation with user input' }, { regex: /path\.join\s*\(.*(?:req\.|params\.|query\.|body\.)/, desc: 'Path join with user input' }, { regex: /path\.resolve\s*\(.*(?:req\.|params\.|query\.|body\.)/, desc: 'Path resolve with user input' }, { regex: /\.\.\/|\.\.\\/, desc: 'Path traversal pattern detected' }, ]; for (let i = 0; i < lines.length; i++) { for (const pattern of patterns) { if (pattern.regex.test(lines[i]) && !lines[i].trim().startsWith('//')) { vulns.push(createVulnerability('path-traversal', 'high', filePath, i + 1, lines[i].trim(), pattern.desc, 'Validate paths, use path.normalize and allowlists', 'CWE-22', 'A01:2021')); } } } return vulns; } function checkPrototypePollution(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); const patterns = [ { regex: /Object\.assign\s*\(\s*\{\}/, desc: 'Object.assign with potential prototype pollution' }, { regex: /\.\.\.(req\.body|params|query|input)/, desc: 'Spread of user input (prototype pollution risk)' }, { regex: /\[['"]?__proto__['"]?\]/, desc: '__proto__ access detected' }, { regex: /\[['"]?constructor['"]?\]/, desc: 'constructor access detected' }, { regex: /lodash\.merge|_.merge|deepmerge/, desc: 'Deep merge with potential pollution' }, ]; for (let i = 0; i < lines.length; i++) { for (const pattern of patterns) { if (pattern.regex.test(lines[i])) { vulns.push(createVulnerability('prototype-pollution', 'high', filePath, i + 1, lines[i].trim(), pattern.desc, 'Use Object.create(null), validate keys, block __proto__', 'CWE-1321', 'A03:2021')); } } } return vulns; } function checkInsecureDeserialization(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); const patterns = [ { regex: /JSON\.parse\s*\(\s*(?:req\.|body\.|input|data)/, desc: 'JSON.parse of untrusted input without validation' }, { regex: /yaml\.load\s*\(/, desc: 'YAML load (potential code execution)' }, { regex: /deserialize|unserialize|unpickle/, desc: 'Deserialization of untrusted data' }, ]; for (let i = 0; i < lines.length; i++) { for (const pattern of patterns) { if (pattern.regex.test(lines[i])) { vulns.push(createVulnerability('insecure-deserialization', 'medium', filePath, i + 1, lines[i].trim(), pattern.desc, 'Validate deserialized data with schema validation', 'CWE-502', 'A08:2021')); } } } return vulns; } function checkHardcodedSecrets(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); const patterns = [ { regex: /(?:password|passwd|pwd)\s*[:=]\s*['"][^'"]{3,}/i, desc: 'Hardcoded password' }, { regex: /(?:api[_-]?key|apikey|api[_-]?secret)\s*[:=]\s*['"][^'"]{8,}/i, desc: 'Hardcoded API key' }, { regex: /(?:secret|token|auth)\s*[:=]\s*['"][A-Za-z0-9+/=]{20,}/i, desc: 'Hardcoded secret/token' }, { regex: /(?:aws|gcp|azure)[_-]?(?:key|secret|token)\s*[:=]\s*['"][^'"]+/i, desc: 'Hardcoded cloud credentials' }, { regex: /-----BEGIN (?:RSA |EC )?PRIVATE KEY-----/, desc: 'Hardcoded private key' }, { regex: /mongodb(?:\+srv)?:\/\/[^:]+:[^@]+@/, desc: 'Hardcoded MongoDB connection string with credentials' }, { regex: /(?:mysql|postgres|postgresql):\/\/[^:]+:[^@]+@/, desc: 'Hardcoded database connection string' }, ]; for (let i = 0; i < lines.length; i++) { if (lines[i].trim().startsWith('//') || lines[i].trim().startsWith('*')) continue; for (const pattern of patterns) { if (pattern.regex.test(lines[i])) { vulns.push(createVulnerability('hardcoded-credentials', 'critical', filePath, i + 1, '[REDACTED]', pattern.desc, 'Use environment variables or a secret manager', 'CWE-798', 'A07:2021')); } } } return vulns; } function checkInsecureRandomness(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); for (let i = 0; i < lines.length; i++) { if (/Math\.random\s*\(\)/.test(lines[i]) && /(?:token|secret|key|password|session|nonce|salt|hash|id|uuid)/i.test(lines[i])) { vulns.push(createVulnerability('insecure-randomness', 'high', filePath, i + 1, lines[i].trim(), 'Math.random() for security-critical values', 'Use crypto.randomBytes() or crypto.randomUUID()', 'CWE-338', 'A02:2021')); } } return vulns; } function checkSsrf(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); const patterns = [ { regex: /(?:fetch|axios|got|request|http\.get|https\.get)\s*\(\s*(?:req\.|params\.|query\.|body\.|url|input)/, desc: 'HTTP request with user-controlled URL' }, { regex: /new\s+URL\s*\(\s*(?:req\.|params\.|query\.|body\.)/, desc: 'URL construction from user input' }, ]; for (let i = 0; i < lines.length; i++) { for (const pattern of patterns) { if (pattern.regex.test(lines[i])) { vulns.push(createVulnerability('ssrf', 'high', filePath, i + 1, lines[i].trim(), pattern.desc, 'Validate URLs, use allowlists, block internal IPs', 'CWE-918', 'A10:2021')); } } } return vulns; } function checkRegexDos(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); // Detect potentially catastrophic regex patterns const dangerousRegex = /new\s+RegExp\s*\(\s*(?:req\.|params\.|query\.|body\.|input|user)/; const nestedQuantifiers = /\/.*(?:\+|\*|\{)\s*(?:.*(?:\+|\*|\{))/; for (let i = 0; i < lines.length; i++) { if (dangerousRegex.test(lines[i])) { vulns.push(createVulnerability('regex-dos', 'high', filePath, i + 1, lines[i].trim(), 'RegExp with user input (ReDoS)', 'Use safe regex library or validate input length', 'CWE-1333', 'A03:2021')); } if (nestedQuantifiers.test(lines[i])) { vulns.push(createVulnerability('regex-dos', 'medium', filePath, i + 1, lines[i].trim(), 'Regex with nested quantifiers (ReDoS risk)', 'Simplify regex or set timeout', 'CWE-1333', 'A03:2021')); } } return vulns; } function checkInformationDisclosure(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); const patterns = [ { regex: /console\.(log|error|warn|debug)\s*\(.*(?:password|secret|token|key|credentials)/i, desc: 'Sensitive data in console output' }, { regex: /res\.(?:json|send)\s*\(\s*(?:err|error)(?:\s*\))/, desc: 'Error object sent to client (stack trace exposure)' }, { regex: /stack(?:Trace)?.*res\.(?:json|send)/, desc: 'Stack trace in response' }, { regex: /x-powered-by/i, desc: 'Server technology disclosure' }, ]; for (let i = 0; i < lines.length; i++) { for (const pattern of patterns) { if (pattern.regex.test(lines[i])) { vulns.push(createVulnerability('information-disclosure', 'medium', filePath, i + 1, lines[i].trim(), pattern.desc, 'Remove sensitive data from logs/responses', 'CWE-200', 'A01:2021')); } } } return vulns; } function checkBrokenAuth(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); const patterns = [ { regex: /(?:algorithm|alg)\s*[:=]\s*['"]none['"]/i, desc: 'JWT "none" algorithm vulnerability' }, { regex: /verify\s*:\s*false/, desc: 'TLS/JWT verification disabled' }, { regex: /(?:bcrypt|scrypt|argon|pbkdf).*rounds?\s*[:=]\s*[0-5]\b/, desc: 'Weak hashing rounds' }, { regex: /md5|sha1(?!-)|createHash\s*\(\s*['"](?:md5|sha1)['"]/, desc: 'Weak hash algorithm' }, { regex: /rejectUnauthorized\s*:\s*false/, desc: 'TLS certificate validation disabled' }, ]; for (let i = 0; i < lines.length; i++) { for (const pattern of patterns) { if (pattern.regex.test(lines[i])) { vulns.push(createVulnerability('broken-auth', 'critical', filePath, i + 1, lines[i].trim(), pattern.desc, 'Use secure algorithms and configurations', 'CWE-287', 'A07:2021')); } } } return vulns; } function checkCsrf(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; // Check if there's a POST/PUT/DELETE endpoint without CSRF protection if (/\.(post|put|delete|patch)\s*\(/.test(content) && !/csrf|csurf|csrfToken|_csrf|xsrf/.test(content)) { if (/cookie|session/.test(content)) { vulns.push(createVulnerability('csrf', 'high', filePath, 0, '', 'State-changing endpoints without CSRF protection', 'Implement CSRF token validation', 'CWE-352', 'A01:2021')); } } return vulns; } function checkZeroDayPatterns(content: string, filePath: string): Vulnerability[] { const vulns: Vulnerability[] = []; const lines = content.split('\n'); const patterns = [ { regex: /process\.binding\s*\(/, desc: 'process.binding() - internal API access' }, { regex: /require\s*\(\s*['"](?:vm|v8|inspector)['"]/, desc: 'Security-sensitive Node.js module' }, { regex: /Function\s*\(\s*(?:req\.|body\.|params\.|query\.|input)/, desc: 'Dynamic Function constructor with user input' }, { regex: /Proxy\s*\(\s*(?:req\.|body\.|params\.)/, desc: 'Proxy with user-controlled handler' }, { regex: /Reflect\.(?:construct|apply|defineProperty)\s*\(.*(?:req\.|body\.|params\.)/, desc: 'Reflect API with user input' }, { regex: /import\s*\(\s*(?:req\.|body\.|params\.|query\.)/, desc: 'Dynamic import with user input' }, { regex: /\.constructor\s*\[\s*['"]/, desc: 'Constructor bracket notation (sandbox escape)' }, { regex: /globalThis|global\[/, desc: 'Global scope access pattern' }, { regex: /Buffer\.allocUnsafe\s*\(/, desc: 'Uninitialized Buffer (memory leak risk)' }, { regex: /WeakRef|FinalizationRegistry/, desc: 'Advanced GC APIs (timing side-channel risk)' }, ]; for (let i = 0; i < lines.length; i++) { for (const pattern of patterns) { if (pattern.regex.test(lines[i]) && !lines[i].trim().startsWith('//')) { vulns.push(createVulnerability('zero-day-pattern', 'high', filePath, i + 1, lines[i].trim(), pattern.desc, 'Check if necessary - replace with secure alternative', 'CWE-94', 'A03:2021')); } } } return vulns; } // --- Helper Functions --- function createVulnerability( type: VulnerabilityType, severity: Vulnerability['severity'], filePath: string, line: number, code: string, description: string, recommendation: string, cwe: string, owasp: string ): Vulnerability { return { id: `${type}-${path.basename(filePath)}-${line}`, type, severity, filePath, line, code: code.substring(0, 200), description, recommendation, cwe, owasp, }; } function checkDependencyVulnerabilities(projectPath: string): DependencyVulnerability[] { const pkg = loadPackageJson(projectPath); if (!pkg) return []; const vulns: DependencyVulnerability[] = []; const allDeps = { ...pkg.dependencies, ...pkg.devDependencies }; // Known vulnerable packages (static check) const knownVulnerable: Record = { 'lodash': { severity: 'high', advisory: 'Prototype Pollution in versions < 4.17.21', fix: '4.17.21' }, 'minimist': { severity: 'high', advisory: 'Prototype Pollution in versions < 1.2.6', fix: '1.2.6' }, 'node-fetch': { severity: 'medium', advisory: 'Exposed headers in redirect for v2 < 2.6.7', fix: '2.6.7' }, 'axios': { severity: 'medium', advisory: 'SSRF vulnerability in versions < 1.6.0', fix: '1.6.0' }, 'express': { severity: 'medium', advisory: 'Various vulnerabilities in versions < 4.19.2', fix: '4.19.2' }, 'jsonwebtoken': { severity: 'high', advisory: 'Algorithm confusion in versions < 9.0.0', fix: '9.0.0' }, 'tar': { severity: 'high', advisory: 'Path traversal in versions < 6.2.1', fix: '6.2.1' }, 'glob-parent': { severity: 'high', advisory: 'ReDoS in versions < 5.1.2', fix: '5.1.2' }, 'trim-newlines': { severity: 'medium', advisory: 'ReDoS vulnerability', fix: '4.0.1' }, 'ansi-regex': { severity: 'medium', advisory: 'ReDoS in versions < 6.0.1', fix: '6.0.1' }, }; for (const [dep, version] of Object.entries(allDeps)) { if (knownVulnerable[dep]) { const vuln = knownVulnerable[dep]; vulns.push({ package: dep, version: version as string, severity: vuln.severity, advisory: vuln.advisory, fixVersion: vuln.fix, }); } } return vulns; } function performOwaspChecks(state: AgentState, vulns: Vulnerability[]): OwaspCheck[] { const checks: OwaspCheck[] = [ { category: 'A01:2021 - Broken Access Control', passed: !vulns.some(v => v.owasp === 'A01:2021'), findings: vulns.filter(v => v.owasp === 'A01:2021').map(v => v.description), recommendations: ['Implement RBAC', 'Validate permissions server-side'], }, { category: 'A02:2021 - Cryptographic Failures', passed: !vulns.some(v => v.owasp === 'A02:2021'), findings: vulns.filter(v => v.owasp === 'A02:2021').map(v => v.description), recommendations: ['Use strong encryption', 'No sensitive data in plaintext'], }, { category: 'A03:2021 - Injection', passed: !vulns.some(v => v.owasp === 'A03:2021'), findings: vulns.filter(v => v.owasp === 'A03:2021').map(v => v.description), recommendations: ['Use parameterized queries', 'Validate and sanitize input'], }, { category: 'A04:2021 - Insecure Design', passed: true, findings: [], recommendations: ['Use threat modeling', 'Implement defense in depth'], }, { category: 'A05:2021 - Security Misconfiguration', passed: !vulns.some(v => ['broken-auth', 'security-misconfiguration'].includes(v.type)), findings: vulns.filter(v => ['broken-auth', 'security-misconfiguration'].includes(v.type)).map(v => v.description), recommendations: ['Use security headers', 'Disable debug mode in production'], }, { category: 'A06:2021 - Vulnerable Components', passed: state.securityReport?.dependencyAudit?.length === 0, findings: [], recommendations: ['Update dependencies regularly', 'Use npm audit'], }, { category: 'A07:2021 - Auth Failures', passed: !vulns.some(v => v.owasp === 'A07:2021'), findings: vulns.filter(v => v.owasp === 'A07:2021').map(v => v.description), recommendations: ['Use secure auth patterns', 'Implement MFA'], }, { category: 'A08:2021 - Software Integrity', passed: !vulns.some(v => v.owasp === 'A08:2021'), findings: vulns.filter(v => v.owasp === 'A08:2021').map(v => v.description), recommendations: ['Validate data integrity', 'Use subresource integrity'], }, { category: 'A09:2021 - Logging Failures', passed: true, findings: [], recommendations: ['Implement audit logging', 'Monitor security-relevant events'], }, { category: 'A10:2021 - SSRF', passed: !vulns.some(v => v.owasp === 'A10:2021'), findings: vulns.filter(v => v.owasp === 'A10:2021').map(v => v.description), recommendations: ['Validate URLs', 'Block internal IP ranges'], }, ]; return checks; } function detectCodeSmells(content: string, filePath: string): CodeSmell[] { const smells: CodeSmell[] = []; const lines = content.split('\n'); for (let i = 0; i < lines.length; i++) { const line = lines[i]; // TODO comments if (/\/\/\s*TODO|\/\/\s*FIXME|\/\/\s*HACK|\/\/\s*XXX/.test(line)) { smells.push({ type: 'todo-comment', filePath, line: i + 1, description: 'TODO/FIXME comment found', suggestion: 'Implement or create issue' }); } // Empty catch blocks if (/catch\s*\([^)]*\)\s*\{\s*\}/.test(line)) { smells.push({ type: 'empty-catch', filePath, line: i + 1, description: 'Empty catch block', suggestion: 'Log or handle error' }); } // Console.log in production code if (/console\.(log|debug)\s*\(/.test(line) && !filePath.includes('test') && !filePath.includes('debug')) { smells.push({ type: 'console-in-prod', filePath, line: i + 1, description: 'console.log in production code', suggestion: 'Use a logger' }); } // Magic numbers if (/(?:===|!==|>|<|>=|<=)\s*(?!0|1|-1|null|undefined|true|false|'|")\d{2,}/.test(line) && !line.includes('port') && !line.includes('status')) { smells.push({ type: 'magic-number', filePath, line: i + 1, description: 'Magic number found', suggestion: 'Use named constant' }); } } return smells; } function calculateSecurityScore(vulns: Vulnerability[], deps: DependencyVulnerability[], owasp: OwaspCheck[]): number { let score = 100; for (const v of vulns) { switch (v.severity) { case 'critical': score -= 15; break; case 'high': score -= 10; break; case 'medium': score -= 5; break; case 'low': score -= 2; break; } } for (const d of deps) { switch (d.severity) { case 'critical': score -= 10; break; case 'high': score -= 7; break; case 'medium': score -= 3; break; case 'low': score -= 1; break; } } const owaspFailed = owasp.filter(c => !c.passed).length; score -= owaspFailed * 5; return Math.max(0, Math.min(100, score)); } function getOverallRisk(score: number): SecurityReport['overallRisk'] { if (score >= 90) return 'none'; if (score >= 70) return 'low'; if (score >= 50) return 'medium'; if (score >= 30) return 'high'; return 'critical'; }