UNPKG

agentsqripts

Version:

Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems

384 lines (329 loc) • 12.2 kB
/** * @file Enhanced security output formatter * @description Provides detailed, actionable output with code context */ const { promises: fsPromises } = require('fs'); const path = require('path'); /** * Gets code context around a vulnerability (async version for better scalability) * @param {string} filePath - Path to the file * @param {number} lineNumber - Line number of vulnerability * @param {number} contextLines - Number of lines before/after to show * @returns {Promise<Object>} Code context with highlighted line */ async function getCodeContext(filePath, lineNumber, contextLines = 3) { try { const content = await fsPromises.readFile(filePath, 'utf8'); const lines = content.split('\n'); const startLine = Math.max(0, lineNumber - contextLines - 1); const endLine = Math.min(lines.length, lineNumber + contextLines); // SCALABILITY FIX: Use efficient slice and for...of loop instead of map/forEach const contextLines = lines.slice(startLine, endLine); const context = []; for (let i = 0; i < contextLines.length; i++) { const lineNum = startLine + i + 1; context.push({ number: lineNum, content: contextLines[i], isVulnerability: lineNum === lineNumber }); } return { file: filePath, line: lineNumber, context: context }; } catch (error) { return null; } } /** * Formats a single vulnerability with full context (async version for better scalability) * @param {Object} vuln - Vulnerability object * @param {string} filePath - Full file path * @returns {Promise<string>} Formatted vulnerability output */ async function formatVulnerability(vuln, filePath) { const output = []; // Header with severity badge const severityColors = { 'CRITICAL': 'šŸ”“', 'HIGH': '🟠', 'MEDIUM': '🟔', 'LOW': 'šŸ”µ', 'INFO': '🟢' }; output.push(`\n${severityColors[vuln.severity] || '⚪'} [${vuln.severity}] ${vuln.name || vuln.type}`); output.push(`šŸ“ File: ${filePath}:${vuln.line}`); output.push(`šŸ“ ${vuln.description}`); // Add code context const codeContext = await getCodeContext(filePath, vuln.line); if (codeContext) { output.push('\nšŸ“„ Code Context:'); output.push('```javascript'); // SCALABILITY FIX: Replace forEach with efficient for...of loop for (const line of codeContext.context) { const prefix = line.isVulnerability ? '>>> ' : ' '; const lineNumStr = String(line.number).padStart(4, ' '); output.push(`${lineNumStr} ${prefix}${line.content}`); } output.push('```'); } // Add remediation suggestions if (vuln.remediation) { output.push('\nšŸ’” Remediation:'); output.push(vuln.remediation); } else { output.push('\nšŸ’” Suggested Fix:'); output.push(getRemediationSuggestion(vuln)); } // Add confidence level if available if (vuln.confidence) { output.push(`\nšŸŽÆ Confidence: ${vuln.confidence}`); } return output.join('\n'); } /** * Gets remediation suggestions based on vulnerability type * @param {Object} vuln - Vulnerability object * @returns {string} Remediation suggestion */ function getRemediationSuggestion(vuln) { const suggestions = { 'SQL_INJECTION': 'Use parameterized queries or prepared statements:\n' + '```javascript\n' + '// Instead of: query = "SELECT * FROM users WHERE id = " + userId\n' + '// Use: query = "SELECT * FROM users WHERE id = ?"\n' + '// db.query(query, [userId])\n' + '```', 'XSS': 'Sanitize user input and use safe rendering methods:\n' + '```javascript\n' + '// Instead of: element.innerHTML = userInput\n' + '// Use: element.textContent = userInput\n' + '// Or use a sanitization library like DOMPurify\n' + '```', 'EVAL_USAGE': 'Avoid eval() and use safer alternatives:\n' + '```javascript\n' + '// Instead of: eval(userCode)\n' + '// Use: JSON.parse() for JSON data\n' + '// Or use Function constructor with strict validation\n' + '```', 'PROTOTYPE_POLLUTION': 'Validate object keys and use Object.create(null):\n' + '```javascript\n' + '// Create objects without prototype\n' + 'const obj = Object.create(null);\n' + '// Or validate keys: if (key === "__proto__") return;\n' + '```', 'REDOS': 'Simplify regex or use alternative parsing methods:\n' + '```javascript\n' + '// Avoid nested quantifiers like (a+)+\n' + '// Use atomic groups or possessive quantifiers\n' + '// Consider using a parser library instead\n' + '```', 'SSRF': 'Validate and whitelist URLs:\n' + '```javascript\n' + '// Validate URL protocol and domain\n' + 'const url = new URL(userInput);\n' + 'if (!allowedDomains.includes(url.hostname)) {\n' + ' throw new Error("Invalid domain");\n' + '}\n' + '```', 'INSECURE_DESERIALIZATION': 'Use JSON.parse with validation:\n' + '```javascript\n' + '// Never use eval() or Function() with user input\n' + '// Use JSON.parse and validate the structure\n' + 'const data = JSON.parse(userInput);\n' + 'validateSchema(data);\n' + '```', 'JWT_NONE_ALGORITHM': 'Explicitly specify allowed algorithms:\n' + '```javascript\n' + '// Specify algorithms explicitly\n' + 'jwt.verify(token, secret, { algorithms: ["HS256"] });\n' + '// Never allow "none" algorithm\n' + '```', 'JWT_NO_VERIFICATION': 'Always verify JWT tokens:\n' + '```javascript\n' + '// Instead of: jwt.decode(token)\n' + '// Use: jwt.verify(token, secret)\n' + '```' }; return suggestions[vuln.type] || 'Review this code for security implications and apply appropriate validation/sanitization.'; } /** * Formats framework security findings * @param {Array} frameworks - Array of framework security objects * @returns {string} Formatted framework findings */ function formatFrameworkFindings(frameworks) { if (!frameworks || frameworks.length === 0) return ''; const output = ['\nšŸ›”ļø Framework Security Analysis:\n']; // SCALABILITY FIX: Replace nested forEach loops with efficient for...of loops for (const fw of frameworks) { output.push(`šŸ“¦ ${fw.framework} Security:`); // SCALABILITY FIX: Single pass through recommendations instead of multiple filter operations const positiveRecs = []; const issueRecs = []; for (const rec of fw.recommendations) { if (rec.positive) { positiveRecs.push(rec); } else { issueRecs.push(rec); } } // Show positive findings if (positiveRecs.length > 0) { output.push(' āœ… Good practices found:'); for (const rec of positiveRecs) { output.push(` • ${rec.message}`); } } // Show security configurations if (fw.framework === 'Express.js') { output.push(` ${fw.helmet ? 'āœ…' : 'āŒ'} Helmet middleware`); output.push(` ${fw.cors ? 'āœ…' : 'āŒ'} CORS configured`); output.push(` ${fw.rateLimit ? 'āœ…' : 'āŒ'} Rate limiting`); } // Show issues if (issueRecs.length > 0) { output.push(' āš ļø Security recommendations:'); for (const rec of issueRecs) { const icon = rec.severity === 'HIGH' || rec.severity === 'CRITICAL' ? 'šŸ”“' : '🟔'; output.push(` ${icon} ${rec.message}`); } } output.push(''); } return output.join('\n'); } /** * Generates an enhanced security report * @param {Object} analysis - Complete analysis results * @returns {string} Enhanced formatted report */ function generateEnhancedReport(analysis) { const output = []; // Summary section output.push('šŸ”’ ENHANCED SECURITY ANALYSIS REPORT'); output.push('=' .repeat(50)); if (analysis.projectAnalysis) { const proj = analysis.projectAnalysis; output.push(`\nšŸ“Š Summary:`); output.push(` Files analyzed: ${proj.filesAnalyzed}`); output.push(` Total vulnerabilities: ${proj.totalVulnerabilities}`); output.push(` Risk level: ${proj.overallRisk}`); output.push(` Security score: ${proj.overallScore}/100`); } // Detailed vulnerabilities if (analysis.vulnerabilities && analysis.vulnerabilities.length > 0) { output.push('\n🚨 Vulnerabilities Found:'); output.push('=' .repeat(50)); // SCALABILITY FIX: Optimize nested loops and add memory bounds const bySeverity = {}; const maxVulnsToProcess = 1000; // Prevent memory exhaustion const maxVulnsPerSeverity = 100; // Limit per severity for performance // Single pass grouping with early exit to prevent runaway processing let processedCount = 0; for (const vuln of analysis.vulnerabilities) { if (processedCount >= maxVulnsToProcess) { output.push(`\nāš ļø Warning: Showing first ${maxVulnsToProcess} vulnerabilities (${analysis.vulnerabilities.length} total)`); break; } const sev = vuln.severity || 'UNKNOWN'; if (!bySeverity[sev]) bySeverity[sev] = []; if (bySeverity[sev].length < maxVulnsPerSeverity) { bySeverity[sev].push(vuln); } processedCount++; } // SCALABILITY FIX: Replace nested forEach with efficient for...of loops for (const severity of ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO']) { if (bySeverity[severity]) { for (const vuln of bySeverity[severity]) { output.push(formatVulnerability(vuln, vuln.file)); } } } } // Framework findings if (analysis.frameworkFindings) { output.push(formatFrameworkFindings(analysis.frameworkFindings)); } // Recommendations summary output.push('\nšŸ“‹ Priority Actions:'); output.push('=' .repeat(50)); output.push(generatePriorityActions(analysis)); return output.join('\n'); } /** * Generates priority actions based on findings * @param {Object} analysis - Analysis results * @returns {string} Priority actions */ function generatePriorityActions(analysis) { const actions = []; let priority = 1; // SCALABILITY FIX: Single pass through vulnerabilities instead of multiple filter operations if (analysis.vulnerabilities) { let criticalCount = 0; let highCount = 0; const criticalExamples = []; // Single pass to count and collect examples for (const vuln of analysis.vulnerabilities) { if (vuln.severity === 'CRITICAL') { criticalCount++; if (criticalExamples.length < 3) { criticalExamples.push(vuln); } } else if (vuln.severity === 'HIGH') { highCount++; } } if (criticalCount > 0) { actions.push(`${priority++}. šŸ”“ Fix ${criticalCount} CRITICAL vulnerabilities immediately`); for (const v of criticalExamples) { actions.push(` - ${v.type} in ${path.basename(v.file)}:${v.line}`); } } if (highCount > 0) { actions.push(`${priority++}. 🟠 Address ${highCount} HIGH severity issues`); } } // SCALABILITY FIX: Optimize framework findings processing if (analysis.frameworkFindings) { for (const fw of analysis.frameworkFindings) { let hasCriticalRecs = false; // Single pass check instead of filter + length check for (const r of fw.recommendations) { if ((r.severity === 'HIGH' || r.severity === 'CRITICAL') && !r.positive) { hasCriticalRecs = true; break; // Early exit once we find one } } if (hasCriticalRecs) { actions.push(`${priority++}. šŸ›”ļø Fix ${fw.framework} security configurations`); } } } if (actions.length === 0) { actions.push('āœ… No critical security issues found!'); actions.push(' Continue following security best practices'); } return actions.join('\n'); } module.exports = { formatVulnerability, formatFrameworkFindings, generateEnhancedReport, getCodeContext, getRemediationSuggestion };