agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
339 lines (290 loc) ⢠10.6 kB
JavaScript
/**
* @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);
const context = [];
for (let i = startLine; i < endLine; i++) {
const lineNum = i + 1;
const isVulnLine = lineNum === lineNumber;
context.push({
number: lineNum,
content: lines[i],
isVulnerability: isVulnLine
});
}
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');
codeContext.context.forEach(line => {
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'];
frameworks.forEach(fw => {
output.push(`š¦ ${fw.framework} Security:`);
// Show positive findings
const positiveRecs = fw.recommendations.filter(r => r.positive);
if (positiveRecs.length > 0) {
output.push(' ā
Good practices found:');
positiveRecs.forEach(rec => {
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
const issues = fw.recommendations.filter(r => !r.positive);
if (issues.length > 0) {
output.push(' ā ļø Security recommendations:');
issues.forEach(rec => {
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));
// Group by severity
const bySeverity = {};
analysis.vulnerabilities.forEach(vuln => {
const sev = vuln.severity || 'UNKNOWN';
if (!bySeverity[sev]) bySeverity[sev] = [];
bySeverity[sev].push(vuln);
});
// Output in severity order
['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO'].forEach(severity => {
if (bySeverity[severity]) {
bySeverity[severity].forEach(vuln => {
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;
// Check for critical vulnerabilities
if (analysis.vulnerabilities) {
const critical = analysis.vulnerabilities.filter(v => v.severity === 'CRITICAL');
if (critical.length > 0) {
actions.push(`${priority++}. š“ Fix ${critical.length} CRITICAL vulnerabilities immediately`);
critical.slice(0, 3).forEach(v => {
actions.push(` - ${v.type} in ${path.basename(v.file)}:${v.line}`);
});
}
const high = analysis.vulnerabilities.filter(v => v.severity === 'HIGH');
if (high.length > 0) {
actions.push(`${priority++}. š Address ${high.length} HIGH severity issues`);
}
}
// Framework recommendations
if (analysis.frameworkFindings) {
analysis.frameworkFindings.forEach(fw => {
const criticalRecs = fw.recommendations.filter(r =>
r.severity === 'HIGH' || r.severity === 'CRITICAL' && !r.positive
);
if (criticalRecs.length > 0) {
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
};