/** * Main Constant Propagator class. * * Tracks constant values through variable assignments and evaluates expressions * to detect dead code and reduce false positives in taint analysis. */ import type { Node, Tree } from 'web-tree-sitter'; import type { ConstantValue, ConstantPropagatorResult, TaintedParameter } from './types.js'; /** * Constant Propagator for taint analysis. * * Key features: * - Tracks variable → constant value mappings * - Evaluates arithmetic, comparison, and string expressions * - Detects dead/unreachable code via if/switch/ternary evaluation * - Integrates with taint analysis to skip false positives */ export declare class ConstantPropagator { private symbols; private tainted; private unreachableLines; private taintedCollections; private sanitizedVars; private source; private evaluator; private definitionNodes; private inConditionalBranch; private methodReturnsConstant; private methodReturnsSanitized; private methodReturnsParameter; private methodReturnsSafeValue; private additionalTaintPatterns; private listElements; private loopVariables; private taintedArrayElements; private currentMethod; private conditionalTaints; private conditionStack; private lineConditions; private synchronizedLines; private inSynchronizedBlock; private iteratorSources; private classFields; private taintedParametersList; private instanceFieldTaint; private currentClassName; private inConstructor; private constructorParamPositions; private safePatternFieldsCache; private isTaintedExpressionCache; /** * Analyze source code and build constant propagation state. */ analyze(tree: Tree, sourceCode: string, additionalTaintPatterns?: string[], sanitizerMethods?: string[], taintedParameters?: TaintedParameter[]): ConstantPropagatorResult; /** * Evaluate an expression to determine its constant value. */ evaluateExpression(node: Node): ConstantValue; /** * Check if a variable has a known constant value. */ getValue(varName: string): ConstantValue | undefined; /** * Check if a variable is tainted. */ isTainted(varName: string): boolean; /** * Check if a line is reachable (not dead code). */ isLineReachable(line: number): boolean; /** * Pre-pass: Analyze all methods to detect those that always return constants or sanitized values. */ private analyzeMethodReturns; private findParameterSource; private extractSourceVariable; private getMethodParameters; private isSanitizerCall; private variableIsAssignedFromSanitizer; /** * Collect all class field names (instance/static variables declared at class level). * These are variables declared directly in the class body, not inside methods. */ private collectClassFieldsAndMethods; /** * Sprint 9 #55 — seed the symbol table with Python module-level constant * assignments. Walks only direct children of the `module` root and adds * `IDENT = ` to `symbols` so `if IDENT:` guards inside * downstream functions can be folded to dead code. * * Recognized literal RHS kinds: `true`/`false` (booleans), integer/float * literals, string literals. The ExpressionEvaluator already understands * each via the same lookup callback; we just need the symbol present. */ /** * Sprint 9 #55 — gate `field_declaration` folding to primitive literals. * * The deep-nesting regression (cognium-ai#88) constructs a Java * `static final String hyphenData = "a" + "b" + ... (10k segments)` at the * class level. `handleVariableDeclaration` would otherwise dispatch * `evaluateExpression` on the deeply nested binary AST and blow the V8 * stack. The dead-code-by-const-guard pattern (`if (DEBUG)`) only requires * `boolean`/`integer`/`string` (single-literal) RHS folding, so restrict * to those node types. */ private fieldDeclHasPrimitiveLiteralValue; /** * Sprint 9 #58.1 — collect the set of class-level `Pattern` field names * whose compiled regex is strict-anchored, i.e. provably matches a * bounded character set with no wildcard escape. A subsequent * `if (!FIELD.matcher(var).matches()) throw ...;` guard then proves * `var` is sanitized after the if. * * Recognized initializer shapes (scanned via source-text regex to avoid * threading another AST walk): * `static final Pattern FIELD = Pattern.compile("regex");` * * Strict-anchored regex criteria: * - starts with `^` and ends with `$` * - after stripping `[...]` character classes, must not contain `.` or * `|` (a `.` could match anything; `|` admits an arbitrary alternative) */ private getSafePatternFields; private isStrictAnchoredRegex; /** * Sprint 9 #58.1 — detect the regex-allowlist guard pattern. * * if (!SAFE_NAME.matcher(var).matches()) { throw ...; } * * Returns the guarded variable name if the pattern matches AND * `SAFE_NAME` is a recognized strict-anchored Pattern field, otherwise * null. Caller drops the variable from `tainted` after the if-block. */ private detectRegexAllowlistGuard; private consequenceContainsThrow; private seedPythonModuleConstants; private getMethodName; private refineTaintFromConstants; private visit; /** * Visit a single node. Returns true if the handler already descended into * children (and the caller should NOT push them), false to fall through to * the default pre-order descent. */ private visitOne; /** * Handle method declarations - scope local variables to this method. * This prevents local variables from one method bleeding into another. */ private handleMethodDeclaration; /** * Get the scoped name for a variable (includes method name if in a method). * This ensures local variables from different methods don't conflict. */ private getScopedName; /** * Look up a variable value, checking both scoped and unscoped names. * This handles cases where we need to find a variable that might be * either local (scoped) or global (unscoped, like class fields). */ private lookupSymbol; private handleLoopStatement; private collectLoopVariableNames; /** * Handle synchronized statements. * Operations inside synchronized blocks are atomic, so field strong updates are safe. */ private handleSynchronizedStatement; /** * Recursively collect line numbers that are inside a synchronized block. */ private collectSynchronizedLines; private markLoopVariables; private handleVariableDeclaration; private handleAssignment; private handleArrayElementAssignment; private handleUpdateExpression; private handleIfStatement; /** * Normalize a condition expression for comparison. * Strips parentheses and whitespace for consistent matching. */ private normalizeCondition; /** * Get the negated form of a condition expression. * "x" -> "!x" * "!x" -> "x" */ private getNegatedCondition; /** * Check if a variable's taint should be excluded in the current condition context. * Returns true if the variable was tainted under a condition that is mutually * exclusive with the current condition context. */ isExcludedByCondition(varName: string): boolean; private handleSwitch; private handleTernary; private handleExpressionStatement; private markUnreachable; private hasBreakStatement; private extractCaseValue; /** * Check if an expression is a call to a sanitizer method. * This includes both built-in sanitizers and @sanitizer annotated methods. * Handles Java (`method_invocation`), Go/JS/TS (`call_expression`), and * Python (`call`) AST shapes. */ isSanitizerMethodCall(node: Node): boolean; /** * Extract the trailing method/function name from any call node shape: * Java `method_invocation` — name field * Go/JS `call_expression` — function field (identifier or selector/member) * Python `call` — function field (identifier or attribute) */ private extractCallName; /** * Check if an expression is a call to an anti-sanitizer method. * Anti-sanitizers reverse the effect of sanitization (e.g., URLDecoder.decode reverses URLEncoder.encode). * If an argument to the anti-sanitizer was previously sanitized, the result is tainted again. */ isAntiSanitizerCall(node: Node): boolean; /** * Check if an anti-sanitizer call has a sanitized argument (which means the result should be tainted). * For example: URLDecoder.decode(sanitizedVar) should produce tainted output. */ antiSanitizerReintroducesTaint(node: Node): boolean; /** * Recursively track iterator assignments in a node (for handling for-loop init). */ private trackIteratorsInNode; /** * Track iterator assignments: when iter = collection.iterator() is called, * record that 'iter' was created from 'collection' so we can propagate taint * through iter.next() calls. */ private trackIteratorAssignment; /** * Check if a collection is tainted (has any tainted elements). */ private isCollectionTainted; /** * Get the taint type for a variable based on how it was tainted. * Returns the taint type (e.g., 'http_param', 'io_input') or null if not found. */ private getTaintTypeForVariable; isTaintedExpression(node: Node): boolean; private isTaintedExpressionImpl; private isTaintedExpressionStep; private checkCollectionTaint; } //# sourceMappingURL=propagator.d.ts.map