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
JavaScript
/**
* @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
};