UNPKG

agentsqripts

Version:

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

124 lines (113 loc) 4.66 kB
/** * @file Detect potential null/undefined access patterns using AST analysis * @description Single responsibility: Identify member access on potentially null values * * This detector uses Abstract Syntax Tree (AST) traversal to identify potential runtime * errors caused by accessing properties or methods on null or undefined values. It tracks * variable nullability through the code flow and reports unsafe access patterns. * * Design rationale: * - AST-based analysis provides precise source location information * - State tracking through NullabilityTracker maintains variable flow analysis * - Conservative approach flags potential issues to prevent runtime errors * - Separate handling for different types of unsafe access patterns */ const walk = require('acorn-walk'); const { getLineNumber, getVariableName } = require('../../../utils/astParser'); /** * Track variable nullability state through code execution flow * * This class maintains a stateful analysis of variable nullability as code is traversed. * It tracks variable declarations, assignments, and initialization status to determine * which variables may contain null or undefined values at access points. * * Implementation rationale: * - Map data structure provides O(1) variable lookup performance * - Separate tracking of nullable and initialized state handles different risk patterns * - Conservative default assumptions prevent false negatives in safety analysis * - Simple state model balances accuracy with performance for large codebases */ class NullabilityTracker { constructor() { this.variables = new Map(); // variable -> { nullable: boolean, initialized: boolean } } setVariable(name, nullable, initialized = true) { this.variables.set(name, { nullable, initialized }); } isNullable(name) { const info = this.variables.get(name); return info ? info.nullable : true; // Unknown variables are potentially nullable } isInitialized(name) { const info = this.variables.get(name); return info ? info.initialized : false; } } /** * Detect potential null/undefined access * @param {Object} ast - AST tree * @param {string} filePath - File path * @returns {Array} Detected bugs */ function detectNullAccess(ast, filePath) { const bugs = []; const tracker = new NullabilityTracker(); walk.simple(ast, { // Track variable declarations VariableDeclarator(node) { const name = node.id.name; if (!node.init) { // Uninitialized variable tracker.setVariable(name, true, false); } else if (node.init.type === 'Literal' && node.init.value === null) { tracker.setVariable(name, true, true); } else if (node.init.type === 'Identifier' && node.init.name === 'undefined') { tracker.setVariable(name, true, true); } else { tracker.setVariable(name, false, true); } }, // Check member access on potentially null values MemberExpression(node) { const objectName = getVariableName(node.object); if (objectName && tracker.isNullable(objectName)) { bugs.push({ type: 'potential_null_access', severity: 'MEDIUM', category: 'Null Safety', line: getLineNumber(node), column: node.loc ? node.loc.start.column : 0, description: `Potential null/undefined access on '${objectName}'`, recommendation: `Add null check before accessing properties: ${objectName}?.property or ${objectName} && ${objectName}.property`, effort: 1, impact: 'medium', file: filePath }); } }, // Check array access without bounds checking MemberExpression(node) { if (node.computed && node.property.type === 'Literal' && typeof node.property.value === 'number') { const objectName = getVariableName(node.object); if (objectName && objectName.includes('array') || objectName.includes('list')) { // This is a heuristic - could be improved with type tracking bugs.push({ type: 'array_bounds', severity: 'LOW', category: 'Null Safety', line: getLineNumber(node), column: node.loc ? node.loc.start.column : 0, description: `Array access without bounds checking: ${objectName}[${node.property.value}]`, recommendation: 'Consider checking array length before accessing by index', effort: 1, impact: 'low', file: filePath }); } } } }); return bugs; } module.exports = detectNullAccess;