UNPKG

agentsqripts

Version:

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

225 lines (213 loc) 10.5 kB
/** * @file Security pattern matcher * @description Matches code patterns against known security vulnerability signatures * This module implements the core vulnerability detection logic using regular expression * patterns that identify potentially dangerous code constructs. The pattern-based approach * enables fast static analysis across large codebases while providing configurable * detection rules that can be updated as new vulnerability patterns emerge. */ // Vulnerability detection patterns organized by security issue type // Rationale: Regular expressions provide fast static analysis without requiring full // AST parsing, enabling efficient scanning of large codebases. Each pattern targets // specific vulnerability indicators based on common insecure coding patterns. const VULNERABILITY_PATTERNS = { 'sql_injection': [ /['"][^'"]*SELECT[^'"]*['"]\s*\+/i, // SELECT statement with string concatenation /['"][^'"]*INSERT[^'"]*['"]\s*\+/i, // INSERT statement with string concatenation /['"][^'"]*UPDATE[^'"]*['"]\s*\+/i, // UPDATE statement with string concatenation /['"][^'"]*DELETE[^'"]*['"]\s*\+/i, // DELETE statement with string concatenation /\.query\([^)]*\+[^)]*\)/i, // Database query method with concatenation /\.execute\([^)]*\+[^)]*\)/i, // Execute method with concatenation /\.exec\([^)]*\+[^)]*\)/i, // Exec method with concatenation /sql\s*=\s*[^;]*\+/i, // SQL variable assignment with concatenation /query\s*=\s*[^;]*\+/i, // Query variable assignment with concatenation /=\s*['"][^'"]*SQL[^'"]*['"]\s*\+/i, // Any SQL assignment with concatenation /WHERE\s+[^=]*=\s*[^+]*\+/i, // WHERE clause with concatenation /VALUES\s*\([^)]*\+[^)]*\)/i // VALUES clause with concatenation ], 'xss': [ /innerHTML\s*=\s*[^;]*[+]/i, // Dynamic HTML content creation with concatenation /document\.write\s*\(/i, // Direct DOM writing - classic XSS vector /eval\s*\(/i, // Code evaluation - enables script injection /outerHTML\s*=\s*[^;]*[+]/i, // Outer HTML manipulation /insertAdjacentHTML\s*\(/i, // Direct HTML insertion /\.html\([^)]*\+[^)]*\)/i // jQuery html() with concatenation ], 'code_injection': [ /eval\s*\(/i, // Direct code evaluation /Function\s*\(/i, // Dynamic function creation /setTimeout\s*\(\s*['"][^'"]*\+/i, // setTimeout with string concatenation /setInterval\s*\(\s*['"][^'"]*\+/i, // setInterval with string concatenation /new\s+Function\s*\(/i, // Constructor function creation /execSync\s*\([^)]*\+/i, // Synchronous command execution /exec\s*\([^)]*\+/i, // Command execution with concatenation /spawn\s*\([^)]*\+/i // Process spawning with concatenation ], 'path_traversal': [ /\.\.\/\.\.\//g, // Directory traversal patterns /\.\.\\\.\.\\/, // Windows directory traversal /['"][^'"]*\/[^'"]*['"]\s*\+/i, // String concatenation with path separators like './uploads/' + filename /\`[^`]*\/[^`]*\${[^}]+}\`/i, // Template literal path injection like `./files/${filename}` /readFile\s*\([^)]*\+[^)]*\)/i, // File reading with concatenation /readFileSync\s*\([^)]*\+[^)]*\)/i, // Sync file reading with concatenation /writeFile\s*\([^)]*\+[^)]*\)/i, // File writing with concatenation /require\s*\([^)]*\+[^)]*\)/i, // Dynamic require with concatenation /import\s*\([^)]*\+[^)]*\)/i, // Dynamic import with concatenation /fs\.[a-zA-Z]+\s*\([^)]*\+[^)]*\)/i, // File system operations with concatenation /path\.join\s*\([^)]*\+[^)]*\)/i, // Path joining with user input /sendFile\s*\([^)]*\+[^)]*\)/i // Express sendFile with concatenation ], 'insecure_random': [ /Math\.random\s*\(\s*\)/i, // Weak random number generation /new\s+Date\(\)\.getTime\(\)/i, // Time-based randomness /Date\.now\(\)/i, // Current time for randomness /Math\.floor\s*\(\s*Math\.random/i, // Common weak random pattern /parseInt\s*\(\s*Math\.random/i, // Parsing weak random numbers /random\s*\(\s*\)\s*\*\s*\d+/i // Scaling weak random numbers ], 'hardcoded_secret': [ /password\s*[=:]\s*['"][^'"]{6,}/i, // Password literals in code /api[_-]?key\s*[=:]\s*['"][^'"]+/i, // API key assignments /secret\s*[=:]\s*['"][^'"]+/i, // Generic secret assignments /token\s*[=:]\s*['"][^'"]{20,}/i, // Authentication tokens /private[_-]?key\s*[=:]\s*['"][^'"]+/i, // Private key assignments /access[_-]?key\s*[=:]\s*['"][^'"]+/i, // Access key assignments /auth[_-]?token\s*[=:]\s*['"][^'"]+/i, // Auth token assignments /client[_-]?secret\s*[=:]\s*['"][^'"]+/i // Client secret assignments ] }; /** * Match vulnerability patterns against source code content * @param {string} content - Source code content to analyze for vulnerabilities * @param {Object|string} patternOrType - Either a pattern object or a vulnerability type string * @returns {Array<Object>} Array of vulnerability matches with location and confidence data * * Rationale: Performs comprehensive pattern matching across all known vulnerability types * to identify potential security issues. Returns structured match data that includes * confidence scoring to help analysts prioritize investigation efforts on high-confidence * matches while not missing lower-confidence potential issues. */ function matchPatterns(content, patternOrType) { const matches = []; // If called with a pattern object (legacy compatibility) if (typeof patternOrType === 'object' && patternOrType.regex) { const found = content.match(patternOrType.regex); if (found) { matches.push({ type: patternOrType.type || 'unknown', pattern: patternOrType.regex.toString(), match: found[0], line: getLineNumber(content, found.index || 0), confidence: calculateConfidence(patternOrType.regex, found[0]), severity: patternOrType.severity || 'MEDIUM', message: patternOrType.message || `${patternOrType.type} vulnerability detected` }); } return matches; } // Check all vulnerability patterns against the content Object.entries(VULNERABILITY_PATTERNS).forEach(([type, patterns]) => { patterns.forEach(pattern => { let match; const regex = new RegExp(pattern.source, pattern.flags); if (pattern.global) { // Handle global patterns that can have multiple matches let execResult; while ((execResult = regex.exec(content)) !== null) { matches.push({ type, pattern: pattern.toString(), match: execResult[0], line: getLineNumber(content, execResult.index), confidence: calculateConfidence(pattern, execResult[0]), severity: getSeverityForType(type), message: `${type.replace('-', ' ')} vulnerability detected` }); } } else { // Handle non-global patterns match = content.match(pattern); if (match) { matches.push({ type, pattern: pattern.toString(), match: match[0], line: getLineNumber(content, match.index || 0), confidence: calculateConfidence(pattern, match[0]), severity: getSeverityForType(type), message: `${type.replace('-', ' ')} vulnerability detected` }); } } }); }); return matches; } /** * Get severity level for vulnerability type * @param {string} type - Vulnerability type * @returns {string} Severity level */ function getSeverityForType(type) { const severityMap = { 'sql-injection': 'HIGH', 'xss': 'HIGH', 'code-injection': 'HIGH', 'path-traversal': 'HIGH', 'insecure-randomness': 'MEDIUM', 'hardcoded-secrets': 'HIGH' }; return severityMap[type] || 'MEDIUM'; } /** * Calculate line number from character index in source content * @param {string} content - Full source code content * @param {number} index - Character index of the match within the content * @returns {number} Line number (1-based) where the match occurs * * Rationale: Provides precise location information for vulnerability matches, enabling * developers to quickly locate and fix identified issues. Line numbers are essential * for actionable security reports and integration with development tools. */ function getLineNumber(content, index) { return content.substring(0, index).split('\n').length; // Count newlines before match position } /** * Calculate confidence score for a pattern match based on specificity and context * @param {RegExp} pattern - The regular expression pattern that matched * @param {string} match - The actual text that was matched * @returns {number} Confidence score between 0.0 and 1.0 * * Rationale: Confidence scoring helps analysts prioritize investigation of matches. * Longer matches and more specific patterns typically indicate higher likelihood of * actual vulnerabilities rather than false positives. This reduces noise in security * reports and focuses attention on the most likely real issues. */ function calculateConfidence(pattern, match) { const baseConfidence = 0.5; // Starting confidence for any pattern match const lengthBonus = Math.min(match.length / 50, 0.3); // Longer matches are more specific const specificityBonus = pattern.source.includes('\\b') ? 0.2 : 0; // Word boundaries indicate more precise patterns // Cap confidence at 1.0 and combine all factors return Math.min(baseConfidence + lengthBonus + specificityBonus, 1.0); } /** * Add custom vulnerability detection pattern for project-specific security rules * @param {string} type - Vulnerability type category for the new pattern * @param {RegExp} pattern - Regular expression pattern to detect this vulnerability * * Rationale: Enables extensibility for organization-specific security requirements * or emerging vulnerability patterns not covered by the default rule set. This * allows the security scanner to evolve with changing threat landscapes and * custom security policies without requiring core system modifications. */ function addCustomPattern(type, pattern) { if (!VULNERABILITY_PATTERNS[type]) { VULNERABILITY_PATTERNS[type] = []; // Initialize new vulnerability type if needed } VULNERABILITY_PATTERNS[type].push(pattern); // Add pattern to existing type collection } module.exports = { matchPatterns, addCustomPattern, calculateConfidence };