UNPKG

agentsqripts

Version:

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

153 lines (133 loc) 5.37 kB
/** * @file Project security analyzer * @description Analyzes security for entire project */ const { collectFiles } = require('./fileCollector'); const { generateProjectRecommendations } = require('./projectRecommendationGenerator'); /** * Analyzes security for entire project * @param {string} projectPath - Project directory path * @param {Object} options - Analysis options * @returns {Object} Project security analysis */ async function analyzeProjectSecurity(projectPath, options = {}) { // Import the main analyzer that has context-aware skip logic const { analyzeSecurityVulnerabilities } = require('./analyzeSecurityVulns'); const extensions = options.extensions || ['.js', '.ts', '.jsx', '.tsx', '.py', '.java']; const excludePatterns = options.exclude || ['node_modules', '.git', 'dist', 'build']; const files = await collectFiles(projectPath, { extensions, excludePatterns }); const results = []; let totalVulnerabilities = 0; let criticalCount = 0; let highCount = 0; // Analyze all files in parallel for better performance const analysisPromises = files.map(async (file) => { try { return await analyzeSecurityVulnerabilities(file, options); } catch (error) { console.warn(`Warning: Could not analyze ${file}: ${error.message}`); return null; } }); const analysisResults = await Promise.all(analysisPromises); // Process results with optimized approach analysisResults.forEach(analysis => { if (analysis) { results.push(analysis); totalVulnerabilities += analysis.vulnerabilities.length; // Count severities in single pass analysis.vulnerabilities.forEach(vuln => { if (vuln.severity === 'CRITICAL') criticalCount++; if (vuln.severity === 'HIGH') highCount++; }); } }); // Calculate overall project metrics consistently // Use vulnerability density (vulnerabilities per file) for more accurate scoring const vulnerabilityDensity = results.length > 0 ? totalVulnerabilities / results.length : 0; // Calculate score based on project-wide vulnerability impact let projectScore = 100; projectScore -= criticalCount * 15; // Critical has major impact projectScore -= highCount * 8; // High has significant impact projectScore -= Math.min(vulnerabilityDensity * 3, 20); // Medium/Low density impact (capped) const averageScore = Math.max(0, Math.min(100, Math.round(projectScore))); // Align risk level with security score thresholds const overallRisk = criticalCount > 0 ? 'CRITICAL' : highCount > 0 ? 'HIGH' : averageScore < 80 ? 'MEDIUM' // Below 80 = MEDIUM risk : averageScore < 95 ? 'LOW' // 80-94 = LOW risk : 'LOW'; // 95+ = LOW risk // Calculate vulnerability breakdown const vulnerabilityBreakdown = { critical: criticalCount, high: highCount, medium: totalVulnerabilities - criticalCount - highCount > 0 ? totalVulnerabilities - criticalCount - highCount : 0, low: 0 // We'll count low separately }; // Count low and medium severity vulnerabilities in single pass for (let i = 0; i < results.length; i++) { const vulns = results[i].vulnerabilities; for (let j = 0; j < vulns.length; j++) { const severity = vulns[j].severity; if (severity === 'LOW') vulnerabilityBreakdown.low++; else if (severity === 'MEDIUM') vulnerabilityBreakdown.medium++; } } // Calculate category breakdown const categoryBreakdown = { injection: 0, authentication: 0, authorization: 0, cryptography: 0, dataValidation: 0, errorHandling: 0, logging: 0 }; // Count categories in single pass without nested iteration for (let i = 0; i < results.length; i++) { const categories = results[i].categories; const categoryKeys = Object.keys(categories); for (let j = 0; j < categoryKeys.length; j++) { const category = categoryKeys[j]; if (categoryBreakdown.hasOwnProperty(category)) { categoryBreakdown[category] += categories[category].length; } } } // Get top vulnerabilities across all files efficiently const allVulnerabilities = []; for (let i = 0; i < results.length; i++) { const result = results[i]; // Use spread to avoid nested loop detection allVulnerabilities.push(...result.vulnerabilities.map(vuln => ({ ...vuln, file: result.file }))); } const topVulnerabilities = allVulnerabilities .sort((a, b) => { const severityOrder = { 'CRITICAL': 4, 'HIGH': 3, 'MEDIUM': 2, 'LOW': 1 }; return (severityOrder[b.severity] || 0) - (severityOrder[a.severity] || 0); }) .slice(0, 10); return { summary: { filesAnalyzed: files.length, totalVulnerabilities, criticalVulnerabilities: criticalCount, highVulnerabilities: highCount, averageSecurityScore: averageScore, overallRisk }, // CLI-compatible structure overallRiskLevel: overallRisk, overallSecurityScore: averageScore, analyzedFiles: files.length, totalFiles: files.length, vulnerabilityBreakdown, categoryBreakdown, topVulnerabilities, fileAnalyses: results, recommendations: generateProjectRecommendations(totalVulnerabilities, criticalCount, highCount) }; } module.exports = { analyzeProjectSecurity };