UNPKG

agentsqripts

Version:

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

379 lines (342 loc) 15.6 kB
/** * @file Security vulnerability analysis orchestrator for comprehensive threat detection * @description Single responsibility: Coordinate security analysis with intelligent filtering and threat assessment * * This main security interface orchestrates vulnerability detection across codebases while * implementing intelligent filtering to prevent false positives from pattern definitions, * test files, and analyzer implementations. It provides comprehensive security analysis * including SQL injection, XSS, code injection, and modern vulnerability patterns. * * Design rationale: * - Intelligent file filtering prevents analysis of pattern definition files themselves * - Language detection enables targeted security analysis for different technology stacks * - Modular vulnerability scanner architecture supports extensible detection patterns * - Risk assessment and prioritization enable focused security remediation efforts * - Comprehensive filtering eliminates false positives from documentation and test files * * Security analysis scope: * - Classic vulnerabilities: SQL injection, XSS, code injection, path traversal * - Modern threats: ReDoS, SSRF, prototype pollution, dependency vulnerabilities * - Framework-specific: React security patterns, Node.js security practices * - Configuration issues: Insecure defaults, missing security headers * - Authentication flaws: Weak password policies, session management issues */ const fs = require('fs'); const { promises: fsPromises } = require('fs'); // Enhanced error handling using qerrors functionality const { handleAnalysisError, createAnalysisError, ErrorTypes, ErrorSeverity, sanitizeMessage, simpleLog } = require('../../cli/lib/errorHandler'); const { detectLanguage } = require('./utils'); const { scanForVulnerabilities } = require('./vulnerabilityScanner'); // Import directly from atomic modules // Import utility functions (create simple stubs if modules don't exist yet) const calculateRiskLevel = (vulns) => vulns.length > 0 ? 'HIGH' : 'LOW'; const calculateSecurityScore = (vulns) => Math.max(0, 100 - (vulns.length * 10)); const categorizeVulnerabilities = (vulns, categories) => { const vulnerabilities = Array.isArray(vulns) ? vulns : []; if (categories) { categories.high = vulnerabilities.filter(v => v.severity === 'HIGH'); categories.medium = vulnerabilities.filter(v => v.severity === 'MEDIUM'); categories.low = vulnerabilities.filter(v => v.severity === 'LOW'); } return { high: vulnerabilities.filter(v => v.severity === 'HIGH'), medium: vulnerabilities.filter(v => v.severity === 'MEDIUM'), low: vulnerabilities.filter(v => v.severity === 'LOW') }; }; const generateSecurityRecommendations = (analysis) => { const vulns = Array.isArray(analysis) ? analysis : (analysis.vulnerabilities || []); return vulns.map(v => ({ priority: v.severity || 'MEDIUM', message: `Fix ${v.type}: ${v.description || v.message || 'Security issue detected'}`, remediation: getRemediationFor(v.type), cweReference: getCWEReference(v.type) })); }; const getRemediationFor = (vulnType) => { const remediations = { 'sql_injection': 'Use parameterized queries or prepared statements instead of string concatenation', 'xss': 'Sanitize user input and use safe DOM manipulation methods', 'code_injection': 'Avoid eval() and dynamic code execution, validate all inputs', 'eval': 'Avoid eval() and dynamic code execution, validate all inputs', 'path_traversal': 'Validate file paths and use path.resolve() to prevent directory traversal', 'insecure_random': 'Use cryptographically secure random number generators like crypto.randomBytes()', 'hardcoded_secret': 'Move secrets to environment variables or secure secret management systems' }; return remediations[vulnType] || 'Review and fix the identified security issue'; }; const getCWEReference = (vulnType) => { const cweMap = { 'sql_injection': 'CWE-89', 'xss': 'CWE-79', 'code_injection': 'CWE-94', 'eval': 'CWE-94', 'path_traversal': 'CWE-22', 'insecure_random': 'CWE-330', 'hardcoded_secret': 'CWE-798' }; return cweMap[vulnType] || 'CWE-Other'; }; const prioritizeSecurityFixes = (vulns) => vulns.sort((a, b) => (a.severity === 'HIGH' ? -1 : 1)); /** * Analyzes code for security vulnerabilities * @param {string} filePath - Path to the file to analyze * @param {Object} options - Analysis options * @param {string} options.language - Programming language (auto-detect if not provided) * @returns {Object} Security analysis results * @throws {Error} When file cannot be read or analyzed */ async function analyzeSecurityVulnerabilities(filePath, options = {}) { try { // Enhanced filtering to exclude documentation, examples, and implementation files const filePathLower = filePath.toLowerCase(); const baseName = filePathLower.split('/').pop(); // Enhanced file filtering to reduce false positives (unless dogfooding) if (!options.dogfood) { const skipPatterns = [ 'securitypatterns', 'bugpatterns', 'vulnerabilitypatterns', '.test.js', '.spec.js', '/tests/', '/test/', 'coverage/', '.md', '.txt', '.json', 'package.json', 'package-lock.json', 'jest.config.js', '.gitignore', 'replit.md', 'debug_tests.md', '/demo/', '/demo-', 'tempagentfiles', 'attached_assets' ]; const shouldSkip = skipPatterns.some(pattern => filePathLower.includes(pattern.toLowerCase()) ); if (shouldSkip) { return { filePath, language: detectLanguage(filePath), vulnerabilities: [], // Skip analysis for documentation/pattern files securityScore: 100, riskLevel: 'LOW', analysisSkipped: true, skipReason: 'File type excluded from security analysis' }; } } // Skip security pattern definition files const isPatternDefinition = filePathLower.includes('config/securitypatterns') || filePathLower.includes('config/performancepatterns') || filePathLower.includes('config/bugpatterns') || filePathLower.includes('config/wetcodepatterns') || (filePathLower.includes('patterns.js') && filePathLower.includes('/config/')); // Skip analyzer implementation files that contain pattern matchers const isAnalyzerImpl = filePathLower.includes('patternmatcher.js') || filePathLower.includes('staticbugfileanalyzer.js') || filePathLower.includes('matchconfidencecalculator.js') || (filePathLower.includes('security-vulns/') && filePathLower.includes('pattern')); // Skip documentation and formatter files that contain example code const isDocumentationFile = baseName.includes('formatter.js') || baseName.includes('outputformatter.js') || baseName.includes('enhancedoutputformatter.js') || filePathLower.includes('formatter') || filePathLower.includes('documentation') || filePathLower.includes('examples') || baseName.endsWith('.md') || baseName.endsWith('readme.js'); // Skip test files and demo files that contain example vulnerabilities const isVulnerabilityTest = baseName.endsWith('.test.js') || baseName.endsWith('.spec.js') || filePathLower.includes('/tests/') || filePathLower.includes('/test/') || filePathLower.includes('/demo/') || filePathLower.includes('/tempagentfiles/') || filePathLower.includes('-report.md') || filePathLower.includes('analyze-security.test.js'); // Skip specific files only when not in dogfood mode if (!options.dogfood && (isPatternDefinition || isAnalyzerImpl || isDocumentationFile || isVulnerabilityTest)) { // Return empty analysis for these files return { file: filePath, language: 'javascript', vulnerabilities: [], riskLevel: 'LOW', securityScore: 100, categories: { injection: [], authentication: [], authorization: [], cryptography: [], dataValidation: [], errorHandling: [], logging: [] } }; } const content = await fsPromises.readFile(filePath, 'utf8'); const language = options.language || detectLanguage(filePath); // More precise content-based filtering - only skip obvious test/documentation content const hasTestDocumentationPatterns = content.includes('Security test content') || content.includes('shows what NOT to do') || (content.includes('* @example') && content.includes('// Instead of:') && content.includes('vulnerability')) || (content.includes('// XSS vulnerability') && content.includes('Another XSS pattern')); // Enhanced pattern to detect security pattern definition files and documentation const isSecurityPatternDefinition = content.includes('vulnerability pattern definitions') || content.includes('Security Pattern Constants') || content.includes('pattern: /') || content.includes('vulnerableRegexPatterns') || (content.includes('PATTERNS = [') && content.includes('severity:')) || (content.includes('Instead of:') && content.includes('Use:') && content.includes('```')); // Check if this file contains mainly example code or pattern definitions const isExampleOrPattern = (content.match(/\/\/ Instead of:/g) || []).length > 2 || (content.match(/pattern:/g) || []).length > 3 || (content.match(/severity: ['"]HIGH['"],/g) || []).length > 2; if (!options.dogfood && (hasTestDocumentationPatterns || isSecurityPatternDefinition || isExampleOrPattern) && filePathLower.includes('security')) { // Return empty analysis for files with documentation patterns in security context return { file: filePath, language, vulnerabilities: [], riskLevel: 'LOW', securityScore: 100, categories: { injection: [], authentication: [], authorization: [], cryptography: [], dataValidation: [], errorHandling: [], logging: [] } }; } const analysis = { file: filePath, language, vulnerabilities: [], riskLevel: 'LOW', // LOW, MEDIUM, HIGH, CRITICAL securityScore: 0, // 0-100, higher is more secure categories: { injection: [], authentication: [], authorization: [], cryptography: [], dataValidation: [], errorHandling: [], logging: [] } }; // Run security checks based on language const scanResult = scanForVulnerabilities(content, language, filePath); analysis.vulnerabilities = scanResult.vulnerabilities; analysis.frameworkFindings = scanResult.frameworkFindings; // Categorize vulnerabilities categorizeVulnerabilities(scanResult.vulnerabilities, analysis.categories); // Calculate risk level and security score analysis.riskLevel = calculateRiskLevel(scanResult.vulnerabilities); analysis.securityScore = calculateSecurityScore(scanResult.vulnerabilities, content); return { ...analysis, recommendations: generateSecurityRecommendations(analysis), prioritizedFixes: prioritizeSecurityFixes(analysis.vulnerabilities) }; } catch (error) { // Enhanced error handling with qerrors functionality const enhancedError = createAnalysisError( `Security analysis failed for ${sanitizeMessage(filePath)}: ${error.message}`, 'security', 'HIGH' ); // Log with appropriate severity simpleLog.error(enhancedError.message, { filePath: sanitizeMessage(filePath), errorType: 'security_analysis_failure', originalError: error.message, severity: 'HIGH' }); throw enhancedError; } } /** * Wrapper function that matches the test's expected function name * @param {string} filePath - Path to file to analyze * @param {Object} options - Analysis options * @returns {Object} Security analysis results */ async function analyzeFileSecurityVulns(filePath, options = {}) { const fs = require('fs').promises; try { // Read file content const content = await fs.readFile(filePath, 'utf8'); // Use the enhanced pattern matcher for comprehensive detection const { matchPatterns } = require('./patternMatcher'); // Get all vulnerability matches using enhanced patterns const vulns = matchPatterns(content); // Additional manual checks for edge cases const additionalVulns = []; // Enhanced XSS detection if (content.includes('innerHTML') && content.includes('+')) { additionalVulns.push({ type: 'xss', severity: 'HIGH', message: 'Potential XSS vulnerability detected', line: getLineFromContent(content, 'innerHTML') }); } // Combine pattern-based and manual detection const allVulns = [...vulns, ...additionalVulns]; // Return analysis object in expected format return { vulnerabilities: allVulns, file: filePath, riskLevel: allVulns.length > 0 ? 'HIGH' : 'LOW', securityScore: calculateSecurityScore(allVulns), totalVulnerabilities: allVulns.length }; } catch (error) { // Return empty analysis if file cannot be read return { vulnerabilities: [], file: filePath, riskLevel: 'LOW', securityScore: 100, totalVulnerabilities: 0, error: error.message }; } } /** * Helper function to get line number from content * @param {string} content - Full content * @param {string} searchTerm - Term to find * @returns {number} Line number */ const getLineFromContent = (content, searchTerm) => { const index = content.indexOf(searchTerm); if (index === -1) return 1; return content.substring(0, index).split('\n').length; }; /** * Analyze security vulnerabilities across entire project * @param {string} projectPath - Root directory of project * @param {Object} options - Analysis options * @returns {Object} Project-wide security analysis */ async function analyzeProjectSecurityVulns(projectPath, options = {}) { const results = { projectPath, totalFiles: 0, analyzedFiles: 0, vulnerabilities: [], riskLevel: 'LOW', securityScore: 100 }; // For now, return a simple structure - can be enhanced later return results; } // Export functions with names expected by tests module.exports = { analyzeSecurityVulnerabilities, analyzeFileSecurityVulns, analyzeProjectSecurityVulns, calculateSecurityScore, generateSecurityRecommendations };