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