agentsqripts
Version:
Comprehensive static code analysis toolkit for identifying technical debt, security vulnerabilities, performance issues, and code quality problems
207 lines (181 loc) • 7.42 kB
JavaScript
/**
* @file Individual file static bug analyzer with AST-based detection
* @description Single responsibility: Perform comprehensive static bug analysis on individual JavaScript files
*
* This analyzer implements sophisticated AST-based static analysis to detect real programming
* errors, logic bugs, and potential runtime issues in individual JavaScript files. It provides
* accurate bug detection through syntax tree analysis while minimizing false positives that
* plague regex-based detection systems.
*
* Design rationale:
* - AST-based analysis provides superior accuracy compared to regex pattern matching
* - Individual file focus enables detailed analysis with comprehensive error context
* - Modular pattern matching architecture supports extensible bug detection capabilities
* - Quality scoring provides quantitative assessment for tracking improvement progress
* - Context-aware detection considers code structure and intent to reduce false positives
*
* Analysis capabilities:
* - Logic errors: Unreachable code, infinite loops, incorrect conditionals, variable scope issues
* - Type errors: Undefined variables, incorrect method calls, type mismatches, null reference errors
* - Security vulnerabilities: Injection flaws, authentication bypasses, authorization issues
* - Performance issues: Inefficient algorithms, memory leaks, blocking operations
* - Best practice violations: Deprecated APIs, anti-patterns, maintainability issues
*/
const fs = require('fs');
// Enhanced error handling using qerrors functionality
const {
handleAnalysisError,
createAnalysisError,
ErrorTypes,
ErrorSeverity,
sanitizeMessage,
simpleLog
} = require('../../cli/lib/errorHandler');
// Import utilities
const { parseToAST } = require('./utils/astParser');
const { getFileContext, shouldIgnoreBug } = require('./utils/contextAnalyzer');
const { stripStringContent, stripComments } = require('../srp-violations/utils/stringStripper');
// Import detectors
const { detectAsyncPatterns } = require('./detectors/asyncPatternDetector');
const { detectNullSafetyPatterns } = require('./detectors/nullSafetyDetector');
const { detectReactPatterns } = require('./detectors/reactPatternDetector');
const { detectResourceLeaks } = require('./detectors/resourceLeakDetector');
const { analyzeDataFlow } = require('./detectors/dataFlowAnalyzer');
// Import legacy detectors
const { checkUnreachableCode } = require('./unreachableCodeChecker');
const { generateBugRecommendations } = require('./bugRecommendationGenerator');
/**
* Analyze static bugs in a file
* @param {string} filePath - File to analyze
* @param {Object} options - Analysis options
* @returns {Promise<Object>} Bug analysis results
*/
async function analyzeFileStaticBugs(filePath, options = {}) {
try {
const content = await fs.promises.readFile(filePath, 'utf8');
// Get file context for intelligent analysis
const context = getFileContext(filePath, content);
// Parse to AST using original content (not stripped)
const ast = parseToAST(content, filePath);
// Strip strings and comments only for string-based checks
const strippedContent = stripStringContent(content);
const codeContent = stripComments(strippedContent);
const bugs = [];
if (ast) {
// Run AST-based detectors
bugs.push(...detectAsyncPatterns(ast, filePath));
bugs.push(...detectNullSafetyPatterns(ast, filePath));
bugs.push(...analyzeDataFlow(ast, filePath));
bugs.push(...detectResourceLeaks(ast, filePath));
// Framework-specific detectors
if (context.framework === 'react') {
bugs.push(...detectReactPatterns(ast, filePath, content));
}
}
// Run legacy string-based detectors (still useful for some patterns)
bugs.push(...checkUnreachableCode(content, filePath));
bugs.push(...checkUnsafePatterns(codeContent, filePath, context));
// Filter bugs based on context
const filteredBugs = bugs.filter(bug => !shouldIgnoreBug(bug.type, context));
// Calculate metrics
const criticalBugs = filteredBugs.filter(b => b.severity === 'HIGH').length;
const mediumBugs = filteredBugs.filter(b => b.severity === 'MEDIUM').length;
const lowBugs = filteredBugs.filter(b => b.severity === 'LOW').length;
const totalBugs = filteredBugs.length;
// More nuanced scoring
const qualityScore = Math.max(0, 100 - (criticalBugs * 10) - (mediumBugs * 3) - (lowBugs * 1));
return {
file: filePath,
context,
issues: filteredBugs,
bugs: filteredBugs,
qualityScore,
qualityGrade: getQualityGrade(qualityScore),
totalBugs,
criticalBugs,
mediumBugs,
lowBugs,
summary: {
totalIssues: totalBugs,
highSeverity: criticalBugs,
mediumSeverity: mediumBugs,
lowSeverity: lowBugs,
qualityScore,
qualityGrade: getQualityGrade(qualityScore)
},
recommendations: generateBugRecommendations(filteredBugs)
};
} catch (error) {
// Enhanced error handling with qerrors functionality
const enhancedError = createAnalysisError(
`Static bug analysis failed for ${sanitizeMessage(filePath)}: ${error.message}`,
'static-bugs',
'MEDIUM'
);
// Log with appropriate severity
simpleLog.error(enhancedError.message, {
filePath: sanitizeMessage(filePath),
errorType: 'static_bug_analysis_failure',
originalError: error.message,
severity: 'MEDIUM'
});
throw enhancedError;
}
}
/**
* Check for unsafe patterns with context awareness
* @param {string} content - Code content (strings/comments stripped)
* @param {string} filePath - File path
* @param {Object} context - File context
* @returns {Array} Detected bugs
*/
function checkUnsafePatterns(content, filePath, context) {
const bugs = [];
const lines = content.split('\n');
lines.forEach((line, index) => {
// Check for eval() - but respect context
if (line.includes('eval(') && !context.rules.allowEval) {
bugs.push({
type: 'dangerous_eval',
severity: 'HIGH',
category: 'Security',
line: index + 1,
column: line.indexOf('eval('),
description: 'Use of eval() is dangerous and can lead to code injection',
recommendation: 'Replace with safer alternatives like JSON.parse() or Function constructor',
effort: 2,
impact: 'high',
file: filePath
});
}
// Check for console.log in production code
if (line.includes('console.log(') && !context.rules.allowConsoleLog) {
bugs.push({
type: 'console_log',
severity: 'LOW',
category: 'Code Quality',
line: index + 1,
column: line.indexOf('console.log('),
description: 'console.log() left in production code',
recommendation: 'Remove console.log() or use proper logging library',
effort: 1,
impact: 'low',
file: filePath
});
}
});
return bugs;
}
const { getLetterGrade } = require('../utils/gradeUtils');
/**
* Get quality grade from score
* @param {number} score - Quality score
* @returns {string} Grade (A-F)
*/
function getQualityGrade(score) {
return getLetterGrade(score);
}
module.exports = {
analyzeFileStaticBugs,
getQualityGrade
};