agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
105 lines (91 loc) • 3.86 kB
JavaScript
/**
* @file Unbounded array detector
* @description Detects unbounded array growth patterns
*/
/**
* Detects unbounded array growth patterns
* @param {string} content - File content
* @param {string} filePath - Path to the file
* @returns {Array} Array of unbounded array issues
*/
function detectUnboundedArrays(content, filePath) {
const issues = [];
const lines = content.split('\n');
const { PERFORMANCE_PATTERNS } = require('./performancePatterns');
// Pattern to detect truly unbounded array growth
const dangerousPatterns = [
// Infinite or very long-running loops with array operations
/while\s*\(\s*true\s*\)/,
/while\s*\(\s*1\s*\)/,
/while\s*\(.*\)\s*{[^}]*\.push\s*\(/m,
// Array growth without any break conditions
/for\s*\(\s*;\s*;\s*\)/, // infinite for loop
];
// Check for dangerous patterns first
let hasDangerousLoop = false;
for (const pattern of dangerousPatterns) {
if (pattern.test(content)) {
hasDangerousLoop = true;
break;
}
}
// Only proceed if we found potentially dangerous patterns
if (!hasDangerousLoop) {
return issues;
}
lines.forEach((line, index) => {
const lineNumber = index + 1;
// Only detect actual array growth operations
const growthPatterns = [
/\.push\s*\([^)]*\)/,
/\.unshift\s*\([^)]*\)/,
/\[\s*\w+\.length\s*\]\s*=/ // array[array.length] = value
];
growthPatterns.forEach(pattern => {
if (pattern.test(line)) {
// Get broader context (20 lines before and after)
const contextStart = Math.max(0, index - 20);
const contextEnd = Math.min(lines.length, index + 20);
const contextLines = lines.slice(contextStart, contextEnd).join('\n');
// Check for signs this is actually bounded
const boundedIndicators = [
/for\s*\([^;]+;\s*\w+\s*<\s*\w+\.(length|size)/, // for loop with length check
/\.slice\(\d+,\s*\d+\)/, // explicit slice bounds
/\.splice\(\d+,\s*\d+\)/, // removing elements
/if\s*\([^)]*\.(length|size)\s*[><=]/, // length check condition
/maxResults|limit|MAX_|LIMIT_/i, // limit constants
/break|return/, // exit conditions
/\w+\s*=\s*\[\]/, // array reset
];
const isBounded = boundedIndicators.some(indicator => indicator.test(contextLines));
// Check if it's in an infinite/dangerous loop
const infiniteLoopPattern = /while\s*\(\s*(true|1)\s*\)|for\s*\(\s*;\s*;\s*\)/;
const inInfiniteLoop = infiniteLoopPattern.test(contextLines);
// Only flag if truly unbounded
if (inInfiniteLoop && !isBounded) {
const patternInfo = PERFORMANCE_PATTERNS['unbounded_array'];
const operation = line.match(/\.(push|unshift)/)?.[1] || 'array growth';
issues.push({
type: 'unbounded_array',
severity: patternInfo.severity,
category: patternInfo.category,
location: `${filePath}:${lineNumber}`,
line: lineNumber,
code: line.trim(),
operation: operation,
description: `Unbounded array ${operation} in infinite loop — memory leak risk`,
summary: `Unbounded array ${operation} in infinite loop`,
recommendation: 'Add break conditions or size limits to prevent memory exhaustion',
effort: patternInfo.effort,
impact: patternInfo.impact,
estimatedSavings: 'prevents memory exhaustion and OOM errors'
});
}
}
});
});
return issues;
}
module.exports = {
detectUnboundedArrays
};