circle-ir 3.57.0 → 3.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/configs/sinks/golang.json +61 -0
- package/configs/sinks/nodejs.json +11 -6
- package/configs/sinks/python.json +24 -0
- package/configs/sinks/rust.json +30 -0
- package/configs/sinks/sql.yaml +53 -0
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +57 -9
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/constant-propagation/patterns.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/patterns.js +12 -0
- package/dist/analysis/constant-propagation/patterns.js.map +1 -1
- package/dist/analysis/constant-propagation/propagator.d.ts +62 -0
- package/dist/analysis/constant-propagation/propagator.d.ts.map +1 -1
- package/dist/analysis/constant-propagation/propagator.js +275 -7
- package/dist/analysis/constant-propagation/propagator.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +226 -14
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/security-headers-pass.d.ts.map +1 -1
- package/dist/analysis/passes/security-headers-pass.js +93 -0
- package/dist/analysis/passes/security-headers-pass.js.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +16 -1
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.d.ts.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.js +153 -9
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +116 -2
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/analysis/taint-propagation.d.ts.map +1 -1
- package/dist/analysis/taint-propagation.js +25 -1
- package/dist/analysis/taint-propagation.js.map +1 -1
- package/dist/browser/circle-ir.js +610 -45
- package/dist/core/circle-ir-core.cjs +368 -21
- package/dist/core/circle-ir-core.js +368 -21
- package/dist/types/config.d.ts +7 -0
- package/dist/types/config.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../src/analysis/constant-propagation/patterns.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,eAAO,MAAM,cAAc,UAgE1B,CAAC;AAGF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AAMF,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../src/analysis/constant-propagation/patterns.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,eAAO,MAAM,cAAc,UAgE1B,CAAC;AAGF,eAAO,MAAM,mBAAmB,QAE/B,CAAC;AAMF,eAAO,MAAM,iBAAiB,aAqD5B,CAAC;AAOH,eAAO,MAAM,sBAAsB,aA4BjC,CAAC;AAOH,eAAO,MAAM,kBAAkB,aA8B7B,CAAC"}
|
|
@@ -99,6 +99,18 @@ export const SANITIZER_METHODS = new Set([
|
|
|
99
99
|
// General
|
|
100
100
|
'sanitize', 'encode', 'escape', 'clean', 'filter', 'validate', 'validatePath',
|
|
101
101
|
'validateCityName', 'validateInput', 'sanitizeInput',
|
|
102
|
+
// Type-cast barriers (#57) — numeric/boolean casts cannot carry a string
|
|
103
|
+
// injection payload. Conservative whitelist; ambiguous names like `valueOf`,
|
|
104
|
+
// `Parse`, `fromString` are intentionally excluded.
|
|
105
|
+
// Java
|
|
106
|
+
'parseInt', 'parseLong', 'parseFloat', 'parseDouble', 'parseShort', 'parseByte',
|
|
107
|
+
'fromString', // UUID.fromString — parses strict UUID format, rejects injection
|
|
108
|
+
// JS/TS (parseInt/parseFloat covered above)
|
|
109
|
+
'Number', 'BigInt',
|
|
110
|
+
// Go
|
|
111
|
+
'Atoi', 'ParseInt', 'ParseFloat', 'ParseUint', 'ParseBool',
|
|
112
|
+
// Python
|
|
113
|
+
'int', 'float', 'bool',
|
|
102
114
|
]);
|
|
103
115
|
// =============================================================================
|
|
104
116
|
// Anti-Sanitizer Methods
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../src/analysis/constant-propagation/patterns.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../../src/analysis/constant-propagation/patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -46,6 +46,7 @@ export declare class ConstantPropagator {
|
|
|
46
46
|
private currentClassName;
|
|
47
47
|
private inConstructor;
|
|
48
48
|
private constructorParamPositions;
|
|
49
|
+
private safePatternFieldsCache;
|
|
49
50
|
/**
|
|
50
51
|
* Analyze source code and build constant propagation state.
|
|
51
52
|
*/
|
|
@@ -80,6 +81,58 @@ export declare class ConstantPropagator {
|
|
|
80
81
|
* These are variables declared directly in the class body, not inside methods.
|
|
81
82
|
*/
|
|
82
83
|
private collectClassFields;
|
|
84
|
+
/**
|
|
85
|
+
* Sprint 9 #55 — seed the symbol table with Python module-level constant
|
|
86
|
+
* assignments. Walks only direct children of the `module` root and adds
|
|
87
|
+
* `IDENT = <primitive literal>` to `symbols` so `if IDENT:` guards inside
|
|
88
|
+
* downstream functions can be folded to dead code.
|
|
89
|
+
*
|
|
90
|
+
* Recognized literal RHS kinds: `true`/`false` (booleans), integer/float
|
|
91
|
+
* literals, string literals. The ExpressionEvaluator already understands
|
|
92
|
+
* each via the same lookup callback; we just need the symbol present.
|
|
93
|
+
*/
|
|
94
|
+
/**
|
|
95
|
+
* Sprint 9 #55 — gate `field_declaration` folding to primitive literals.
|
|
96
|
+
*
|
|
97
|
+
* The deep-nesting regression (cognium-ai#88) constructs a Java
|
|
98
|
+
* `static final String hyphenData = "a" + "b" + ... (10k segments)` at the
|
|
99
|
+
* class level. `handleVariableDeclaration` would otherwise dispatch
|
|
100
|
+
* `evaluateExpression` on the deeply nested binary AST and blow the V8
|
|
101
|
+
* stack. The dead-code-by-const-guard pattern (`if (DEBUG)`) only requires
|
|
102
|
+
* `boolean`/`integer`/`string` (single-literal) RHS folding, so restrict
|
|
103
|
+
* to those node types.
|
|
104
|
+
*/
|
|
105
|
+
private fieldDeclHasPrimitiveLiteralValue;
|
|
106
|
+
/**
|
|
107
|
+
* Sprint 9 #58.1 — collect the set of class-level `Pattern` field names
|
|
108
|
+
* whose compiled regex is strict-anchored, i.e. provably matches a
|
|
109
|
+
* bounded character set with no wildcard escape. A subsequent
|
|
110
|
+
* `if (!FIELD.matcher(var).matches()) throw ...;` guard then proves
|
|
111
|
+
* `var` is sanitized after the if.
|
|
112
|
+
*
|
|
113
|
+
* Recognized initializer shapes (scanned via source-text regex to avoid
|
|
114
|
+
* threading another AST walk):
|
|
115
|
+
* `static final Pattern FIELD = Pattern.compile("regex");`
|
|
116
|
+
*
|
|
117
|
+
* Strict-anchored regex criteria:
|
|
118
|
+
* - starts with `^` and ends with `$`
|
|
119
|
+
* - after stripping `[...]` character classes, must not contain `.` or
|
|
120
|
+
* `|` (a `.` could match anything; `|` admits an arbitrary alternative)
|
|
121
|
+
*/
|
|
122
|
+
private getSafePatternFields;
|
|
123
|
+
private isStrictAnchoredRegex;
|
|
124
|
+
/**
|
|
125
|
+
* Sprint 9 #58.1 — detect the regex-allowlist guard pattern.
|
|
126
|
+
*
|
|
127
|
+
* if (!SAFE_NAME.matcher(var).matches()) { throw ...; }
|
|
128
|
+
*
|
|
129
|
+
* Returns the guarded variable name if the pattern matches AND
|
|
130
|
+
* `SAFE_NAME` is a recognized strict-anchored Pattern field, otherwise
|
|
131
|
+
* null. Caller drops the variable from `tainted` after the if-block.
|
|
132
|
+
*/
|
|
133
|
+
private detectRegexAllowlistGuard;
|
|
134
|
+
private consequenceContainsThrow;
|
|
135
|
+
private seedPythonModuleConstants;
|
|
83
136
|
private findAllMethods;
|
|
84
137
|
private getMethodName;
|
|
85
138
|
private refineTaintFromConstants;
|
|
@@ -149,8 +202,17 @@ export declare class ConstantPropagator {
|
|
|
149
202
|
/**
|
|
150
203
|
* Check if an expression is a call to a sanitizer method.
|
|
151
204
|
* This includes both built-in sanitizers and @sanitizer annotated methods.
|
|
205
|
+
* Handles Java (`method_invocation`), Go/JS/TS (`call_expression`), and
|
|
206
|
+
* Python (`call`) AST shapes.
|
|
152
207
|
*/
|
|
153
208
|
isSanitizerMethodCall(node: Node): boolean;
|
|
209
|
+
/**
|
|
210
|
+
* Extract the trailing method/function name from any call node shape:
|
|
211
|
+
* Java `method_invocation` — name field
|
|
212
|
+
* Go/JS `call_expression` — function field (identifier or selector/member)
|
|
213
|
+
* Python `call` — function field (identifier or attribute)
|
|
214
|
+
*/
|
|
215
|
+
private extractCallName;
|
|
154
216
|
/**
|
|
155
217
|
* Check if an expression is a call to an anti-sanitizer method.
|
|
156
218
|
* Anti-sanitizers reverse the effect of sanitization (e.g., URLDecoder.decode reverses URLEncoder.encode).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"propagator.d.ts","sourceRoot":"","sources":["../../../src/analysis/constant-propagation/propagator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,aAAa,EAAE,wBAAwB,EAAE,gBAAgB,EAAkB,MAAM,YAAY,CAAC;AAK5G;;;;;;;;GAQG;AACH,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,OAAO,CAAyC;IACxD,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,gBAAgB,CAA0B;IAClD,OAAO,CAAC,kBAAkB,CAAuC;IAEjE,OAAO,CAAC,aAAa,CAA0B;IAC/C,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,SAAS,CAAuB;IAGxC,OAAO,CAAC,eAAe,CAAgC;IAEvD,OAAO,CAAC,mBAAmB,CAAkB;IAE7C,OAAO,CAAC,qBAAqB,CAA0B;IAEvD,OAAO,CAAC,sBAAsB,CAA0B;IAExD,OAAO,CAAC,sBAAsB,CAAkC;IAEhE,OAAO,CAAC,sBAAsB,CAA0B;IAExD,OAAO,CAAC,uBAAuB,CAAgB;IAE/C,OAAO,CAAC,YAAY,CAA6C;IAEjE,OAAO,CAAC,aAAa,CAA0B;IAE/C,OAAO,CAAC,oBAAoB,CAAuC;IAEnE,OAAO,CAAC,aAAa,CAAuB;IAG5C,OAAO,CAAC,iBAAiB,CAAuC;IAEhE,OAAO,CAAC,cAAc,CAAgB;IAEtC,OAAO,CAAC,cAAc,CAAkC;IAExD,OAAO,CAAC,iBAAiB,CAA0B;IAEnD,OAAO,CAAC,mBAAmB,CAAkB;IAE7C,OAAO,CAAC,eAAe,CAAkC;IAEzD,OAAO,CAAC,WAAW,CAA0B;IAE7C,OAAO,CAAC,qBAAqB,CAA0B;IAEvD,OAAO,CAAC,kBAAkB,CAA0C;IAEpE,OAAO,CAAC,gBAAgB,CAAuB;IAE/C,OAAO,CAAC,aAAa,CAAkB;IAEvC,OAAO,CAAC,yBAAyB,CAAkC;
|
|
1
|
+
{"version":3,"file":"propagator.d.ts","sourceRoot":"","sources":["../../../src/analysis/constant-propagation/propagator.ts"],"names":[],"mappings":"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"}
|
|
@@ -72,6 +72,10 @@ export class ConstantPropagator {
|
|
|
72
72
|
inConstructor = false;
|
|
73
73
|
// Map constructor parameter names to their positions (0-indexed)
|
|
74
74
|
constructorParamPositions = new Map();
|
|
75
|
+
// Sprint 9 #58.1 — names of `static final Pattern` fields whose compiled
|
|
76
|
+
// regex is strict-anchored (provably matches a bounded character set).
|
|
77
|
+
// Populated lazily on first access via `getSafePatternFields()`.
|
|
78
|
+
safePatternFieldsCache = null;
|
|
75
79
|
/**
|
|
76
80
|
* Analyze source code and build constant propagation state.
|
|
77
81
|
*/
|
|
@@ -105,6 +109,7 @@ export class ConstantPropagator {
|
|
|
105
109
|
this.currentClassName = null;
|
|
106
110
|
this.inConstructor = false;
|
|
107
111
|
this.constructorParamPositions.clear();
|
|
112
|
+
this.safePatternFieldsCache = null;
|
|
108
113
|
// Pre-pass: identify class fields
|
|
109
114
|
this.collectClassFields(tree.rootNode);
|
|
110
115
|
// Pre-populate methodReturnsSanitized with methods marked with @sanitizer annotation
|
|
@@ -115,6 +120,14 @@ export class ConstantPropagator {
|
|
|
115
120
|
this.evaluator = new ExpressionEvaluator(this.source, (name) => this.lookupSymbol(name));
|
|
116
121
|
// Pre-pass: identify methods that always return constants or sanitized values
|
|
117
122
|
this.analyzeMethodReturns(tree.rootNode);
|
|
123
|
+
// Sprint 9 #55 — Pre-pass: fold Python module-level constant assignments
|
|
124
|
+
// (`DEBUG = False`, `FOO = "bar"`) into the symbol table so dead-code
|
|
125
|
+
// detection of `if DEBUG:` guards inside functions can mark the body
|
|
126
|
+
// unreachable. We do this only for top-level `assignment` nodes whose
|
|
127
|
+
// RHS is a primitive literal — keeps the change narrow and avoids the
|
|
128
|
+
// recursion hazards of dispatching the full `handleAssignment` path
|
|
129
|
+
// on Python ASTs.
|
|
130
|
+
this.seedPythonModuleConstants(tree.rootNode);
|
|
118
131
|
// First pass: collect symbols, taint, and unreachable lines
|
|
119
132
|
this.visit(tree.rootNode);
|
|
120
133
|
// Second pass: refine taint for variables derived from constants
|
|
@@ -505,6 +518,203 @@ export class ConstantPropagator {
|
|
|
505
518
|
}
|
|
506
519
|
}
|
|
507
520
|
}
|
|
521
|
+
/**
|
|
522
|
+
* Sprint 9 #55 — seed the symbol table with Python module-level constant
|
|
523
|
+
* assignments. Walks only direct children of the `module` root and adds
|
|
524
|
+
* `IDENT = <primitive literal>` to `symbols` so `if IDENT:` guards inside
|
|
525
|
+
* downstream functions can be folded to dead code.
|
|
526
|
+
*
|
|
527
|
+
* Recognized literal RHS kinds: `true`/`false` (booleans), integer/float
|
|
528
|
+
* literals, string literals. The ExpressionEvaluator already understands
|
|
529
|
+
* each via the same lookup callback; we just need the symbol present.
|
|
530
|
+
*/
|
|
531
|
+
/**
|
|
532
|
+
* Sprint 9 #55 — gate `field_declaration` folding to primitive literals.
|
|
533
|
+
*
|
|
534
|
+
* The deep-nesting regression (cognium-ai#88) constructs a Java
|
|
535
|
+
* `static final String hyphenData = "a" + "b" + ... (10k segments)` at the
|
|
536
|
+
* class level. `handleVariableDeclaration` would otherwise dispatch
|
|
537
|
+
* `evaluateExpression` on the deeply nested binary AST and blow the V8
|
|
538
|
+
* stack. The dead-code-by-const-guard pattern (`if (DEBUG)`) only requires
|
|
539
|
+
* `boolean`/`integer`/`string` (single-literal) RHS folding, so restrict
|
|
540
|
+
* to those node types.
|
|
541
|
+
*/
|
|
542
|
+
fieldDeclHasPrimitiveLiteralValue(node) {
|
|
543
|
+
const primitive = new Set([
|
|
544
|
+
// Java literal node types
|
|
545
|
+
'true', 'false', 'null_literal',
|
|
546
|
+
'decimal_integer_literal', 'hex_integer_literal',
|
|
547
|
+
'octal_integer_literal', 'binary_integer_literal',
|
|
548
|
+
'decimal_floating_point_literal',
|
|
549
|
+
'hex_floating_point_literal',
|
|
550
|
+
'character_literal', 'string_literal',
|
|
551
|
+
// JS/TS literal node types (defensive, in case other langs reuse it)
|
|
552
|
+
'number', 'string',
|
|
553
|
+
]);
|
|
554
|
+
for (const child of node.children) {
|
|
555
|
+
if (child.type !== 'variable_declarator')
|
|
556
|
+
continue;
|
|
557
|
+
const value = child.childForFieldName('value');
|
|
558
|
+
if (!value)
|
|
559
|
+
continue;
|
|
560
|
+
if (!primitive.has(value.type))
|
|
561
|
+
return false;
|
|
562
|
+
}
|
|
563
|
+
return true;
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Sprint 9 #58.1 — collect the set of class-level `Pattern` field names
|
|
567
|
+
* whose compiled regex is strict-anchored, i.e. provably matches a
|
|
568
|
+
* bounded character set with no wildcard escape. A subsequent
|
|
569
|
+
* `if (!FIELD.matcher(var).matches()) throw ...;` guard then proves
|
|
570
|
+
* `var` is sanitized after the if.
|
|
571
|
+
*
|
|
572
|
+
* Recognized initializer shapes (scanned via source-text regex to avoid
|
|
573
|
+
* threading another AST walk):
|
|
574
|
+
* `static final Pattern FIELD = Pattern.compile("regex");`
|
|
575
|
+
*
|
|
576
|
+
* Strict-anchored regex criteria:
|
|
577
|
+
* - starts with `^` and ends with `$`
|
|
578
|
+
* - after stripping `[...]` character classes, must not contain `.` or
|
|
579
|
+
* `|` (a `.` could match anything; `|` admits an arbitrary alternative)
|
|
580
|
+
*/
|
|
581
|
+
getSafePatternFields() {
|
|
582
|
+
if (this.safePatternFieldsCache !== null)
|
|
583
|
+
return this.safePatternFieldsCache;
|
|
584
|
+
const set = new Set();
|
|
585
|
+
// Match `static final Pattern NAME = Pattern.compile("REGEX")` allowing
|
|
586
|
+
// arbitrary modifier order and optional `public/private/protected`.
|
|
587
|
+
const re = /\b(?:public\s+|private\s+|protected\s+)?(?:static\s+final|final\s+static)\s+(?:java\.util\.regex\.)?Pattern\s+(\w+)\s*=\s*(?:java\.util\.regex\.)?Pattern\s*\.\s*compile\s*\(\s*"((?:[^"\\]|\\.)*)"/g;
|
|
588
|
+
let m;
|
|
589
|
+
while ((m = re.exec(this.source)) !== null) {
|
|
590
|
+
const name = m[1];
|
|
591
|
+
const regex = m[2];
|
|
592
|
+
if (this.isStrictAnchoredRegex(regex))
|
|
593
|
+
set.add(name);
|
|
594
|
+
}
|
|
595
|
+
this.safePatternFieldsCache = set;
|
|
596
|
+
return set;
|
|
597
|
+
}
|
|
598
|
+
isStrictAnchoredRegex(re) {
|
|
599
|
+
if (!re.startsWith('^') || !re.endsWith('$'))
|
|
600
|
+
return false;
|
|
601
|
+
// Strip [..] character classes (and escaped brackets) — wildcards inside
|
|
602
|
+
// a class are fine because they only match the listed characters.
|
|
603
|
+
const stripped = re.replace(/\[(?:[^\]\\]|\\.)*\]/g, '');
|
|
604
|
+
// Forbid bare `.` (any char) and `|` (alternation) outside classes.
|
|
605
|
+
// `\.` (escaped dot) is fine; same for `\|`.
|
|
606
|
+
const cleaned = stripped.replace(/\\./g, '');
|
|
607
|
+
if (cleaned.includes('.'))
|
|
608
|
+
return false;
|
|
609
|
+
if (cleaned.includes('|'))
|
|
610
|
+
return false;
|
|
611
|
+
return true;
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Sprint 9 #58.1 — detect the regex-allowlist guard pattern.
|
|
615
|
+
*
|
|
616
|
+
* if (!SAFE_NAME.matcher(var).matches()) { throw ...; }
|
|
617
|
+
*
|
|
618
|
+
* Returns the guarded variable name if the pattern matches AND
|
|
619
|
+
* `SAFE_NAME` is a recognized strict-anchored Pattern field, otherwise
|
|
620
|
+
* null. Caller drops the variable from `tainted` after the if-block.
|
|
621
|
+
*/
|
|
622
|
+
detectRegexAllowlistGuard(condition, consequence) {
|
|
623
|
+
if (!consequence)
|
|
624
|
+
return null;
|
|
625
|
+
let condText = getNodeText(condition, this.source).replace(/\s+/g, '');
|
|
626
|
+
// Strip balanced outer parens (Java's `if_statement` condition is a
|
|
627
|
+
// `parenthesized_expression`, so `(!SAFE.matcher(x).matches())` arrives
|
|
628
|
+
// wrapped). Repeat in case of redundant parens.
|
|
629
|
+
while (condText.startsWith('(') && condText.endsWith(')')) {
|
|
630
|
+
const inner = condText.slice(1, -1);
|
|
631
|
+
let depth = 0;
|
|
632
|
+
let balanced = true;
|
|
633
|
+
for (let i = 0; i < inner.length; i++) {
|
|
634
|
+
if (inner[i] === '(')
|
|
635
|
+
depth++;
|
|
636
|
+
else if (inner[i] === ')')
|
|
637
|
+
depth--;
|
|
638
|
+
if (depth < 0) {
|
|
639
|
+
balanced = false;
|
|
640
|
+
break;
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
if (!balanced || depth !== 0)
|
|
644
|
+
break;
|
|
645
|
+
condText = inner;
|
|
646
|
+
}
|
|
647
|
+
const m = condText.match(/^!(\w+)\.matcher\((\w+)\)\.matches\(\)$/);
|
|
648
|
+
if (!m)
|
|
649
|
+
return null;
|
|
650
|
+
const patternName = m[1];
|
|
651
|
+
const varName = m[2];
|
|
652
|
+
if (!this.getSafePatternFields().has(patternName))
|
|
653
|
+
return null;
|
|
654
|
+
if (!this.consequenceContainsThrow(consequence))
|
|
655
|
+
return null;
|
|
656
|
+
return varName;
|
|
657
|
+
}
|
|
658
|
+
consequenceContainsThrow(node) {
|
|
659
|
+
if (node.type === 'throw_statement')
|
|
660
|
+
return true;
|
|
661
|
+
// For block bodies, look for any throw_statement child.
|
|
662
|
+
const stack = [node];
|
|
663
|
+
while (stack.length > 0) {
|
|
664
|
+
const n = stack.pop();
|
|
665
|
+
if (!n)
|
|
666
|
+
continue;
|
|
667
|
+
if (n.type === 'throw_statement')
|
|
668
|
+
return true;
|
|
669
|
+
// Don't descend into nested control-flow that may not actually throw.
|
|
670
|
+
if (n.type === 'if_statement' || n.type === 'switch_statement')
|
|
671
|
+
continue;
|
|
672
|
+
for (const c of n.children)
|
|
673
|
+
stack.push(c);
|
|
674
|
+
}
|
|
675
|
+
return false;
|
|
676
|
+
}
|
|
677
|
+
seedPythonModuleConstants(root) {
|
|
678
|
+
if (root.type !== 'module')
|
|
679
|
+
return; // Python-only — Java/JS roots differ.
|
|
680
|
+
for (const child of root.children) {
|
|
681
|
+
// Top-level Python statements are wrapped in an `expression_statement`
|
|
682
|
+
// whose first child is the actual expression. Module-level `x = y`
|
|
683
|
+
// produces `expression_statement → assignment`.
|
|
684
|
+
const target = child.type === 'assignment'
|
|
685
|
+
? child
|
|
686
|
+
: child.type === 'expression_statement' && child.children.length > 0
|
|
687
|
+
? child.children[0]
|
|
688
|
+
: null;
|
|
689
|
+
if (!target || target.type !== 'assignment')
|
|
690
|
+
continue;
|
|
691
|
+
const left = target.childForFieldName('left');
|
|
692
|
+
const right = target.childForFieldName('right');
|
|
693
|
+
if (!left || !right)
|
|
694
|
+
continue;
|
|
695
|
+
if (left.type !== 'identifier')
|
|
696
|
+
continue;
|
|
697
|
+
// Constrain RHS to PRIMITIVE literal nodes only. Evaluating compound
|
|
698
|
+
// expressions (e.g. `"a" + "b" + ...`) via the full evaluator can
|
|
699
|
+
// recurse arbitrarily deep on pathological generated sources — the
|
|
700
|
+
// deep-nesting regression (cognium-ai#88) constructs 10k-segment
|
|
701
|
+
// string concatenations at module level. We don't need that here.
|
|
702
|
+
const allowed = new Set([
|
|
703
|
+
'true', 'false', 'none',
|
|
704
|
+
'integer', 'float',
|
|
705
|
+
'string',
|
|
706
|
+
]);
|
|
707
|
+
if (!allowed.has(right.type))
|
|
708
|
+
continue;
|
|
709
|
+
const name = getNodeText(left, this.source);
|
|
710
|
+
if (!name)
|
|
711
|
+
continue;
|
|
712
|
+
const value = this.evaluateExpression(right);
|
|
713
|
+
if (!isKnown(value))
|
|
714
|
+
continue;
|
|
715
|
+
this.symbols.set(name, value);
|
|
716
|
+
}
|
|
717
|
+
}
|
|
508
718
|
findAllMethods(node) {
|
|
509
719
|
// Iterative DFS — guards against stack overflow on deeply nested AST
|
|
510
720
|
// shapes (cognium-ai#88).
|
|
@@ -616,6 +826,24 @@ export class ConstantPropagator {
|
|
|
616
826
|
case 'local_variable_declaration':
|
|
617
827
|
this.handleVariableDeclaration(node);
|
|
618
828
|
return false;
|
|
829
|
+
case 'field_declaration':
|
|
830
|
+
// Sprint 9 #55 — fold class-level `static final` constants into the
|
|
831
|
+
// symbols table so `if (DEBUG) { ... }` style guards can be evaluated
|
|
832
|
+
// and the unreachable branch marked as dead code. Structurally
|
|
833
|
+
// identical to `local_variable_declaration` (both have
|
|
834
|
+
// `variable_declarator` children with `name`/`value`), so the same
|
|
835
|
+
// handler applies.
|
|
836
|
+
//
|
|
837
|
+
// Constrain to primitive-literal initializers only. Java
|
|
838
|
+
// `public static final String hyphenData = "a" + "b" + ...` at the
|
|
839
|
+
// class level can be arbitrarily deep (cognium-ai#88), and the
|
|
840
|
+
// evaluator's binary recursion blows the stack at ~5k segments. The
|
|
841
|
+
// dead-code-by-const-guard pattern (`if (DEBUG)`) only needs
|
|
842
|
+
// booleans/numbers/single strings folded; nothing else.
|
|
843
|
+
if (this.fieldDeclHasPrimitiveLiteralValue(node)) {
|
|
844
|
+
this.handleVariableDeclaration(node);
|
|
845
|
+
}
|
|
846
|
+
return false;
|
|
619
847
|
case 'assignment_expression':
|
|
620
848
|
this.handleAssignment(node);
|
|
621
849
|
return false;
|
|
@@ -1202,6 +1430,20 @@ export class ConstantPropagator {
|
|
|
1202
1430
|
}
|
|
1203
1431
|
this.inConditionalBranch = wasInConditional;
|
|
1204
1432
|
this.tainted = new Set([...taintedBefore, ...taintedAfterThen, ...taintedAfterElse]);
|
|
1433
|
+
// Sprint 9 #58.1 — regex-allowlist sanitizer.
|
|
1434
|
+
// if (!SAFE_NAME.matcher(var).matches()) throw ...;
|
|
1435
|
+
// After this if, `var` is provably one of the strict-anchored regex's
|
|
1436
|
+
// matches. Drop it from `tainted` (and from any scoped variant).
|
|
1437
|
+
const guardedVar = this.detectRegexAllowlistGuard(condition, consequence);
|
|
1438
|
+
if (guardedVar) {
|
|
1439
|
+
this.tainted.delete(guardedVar);
|
|
1440
|
+
this.sanitizedVars.add(guardedVar);
|
|
1441
|
+
const scoped = this.getScopedName(guardedVar);
|
|
1442
|
+
if (scoped !== guardedVar) {
|
|
1443
|
+
this.tainted.delete(scoped);
|
|
1444
|
+
this.sanitizedVars.add(scoped);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1205
1447
|
}
|
|
1206
1448
|
}
|
|
1207
1449
|
/**
|
|
@@ -1405,18 +1647,44 @@ export class ConstantPropagator {
|
|
|
1405
1647
|
/**
|
|
1406
1648
|
* Check if an expression is a call to a sanitizer method.
|
|
1407
1649
|
* This includes both built-in sanitizers and @sanitizer annotated methods.
|
|
1650
|
+
* Handles Java (`method_invocation`), Go/JS/TS (`call_expression`), and
|
|
1651
|
+
* Python (`call`) AST shapes.
|
|
1408
1652
|
*/
|
|
1409
1653
|
isSanitizerMethodCall(node) {
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
}
|
|
1413
|
-
const nameNode = node.childForFieldName('name');
|
|
1414
|
-
if (!nameNode) {
|
|
1654
|
+
const methodName = this.extractCallName(node);
|
|
1655
|
+
if (!methodName)
|
|
1415
1656
|
return false;
|
|
1416
|
-
}
|
|
1417
|
-
const methodName = getNodeText(nameNode, this.source);
|
|
1418
1657
|
return SANITIZER_METHODS.has(methodName) || this.methodReturnsSanitized.has(methodName);
|
|
1419
1658
|
}
|
|
1659
|
+
/**
|
|
1660
|
+
* Extract the trailing method/function name from any call node shape:
|
|
1661
|
+
* Java `method_invocation` — name field
|
|
1662
|
+
* Go/JS `call_expression` — function field (identifier or selector/member)
|
|
1663
|
+
* Python `call` — function field (identifier or attribute)
|
|
1664
|
+
*/
|
|
1665
|
+
extractCallName(node) {
|
|
1666
|
+
let fnNode = null;
|
|
1667
|
+
if (node.type === 'method_invocation') {
|
|
1668
|
+
fnNode = node.childForFieldName('name');
|
|
1669
|
+
}
|
|
1670
|
+
else if (node.type === 'call_expression' || node.type === 'call') {
|
|
1671
|
+
fnNode = node.childForFieldName('function');
|
|
1672
|
+
}
|
|
1673
|
+
if (!fnNode)
|
|
1674
|
+
return null;
|
|
1675
|
+
// For selector_expression (Go), member_expression (JS), attribute (Python),
|
|
1676
|
+
// take the trailing identifier.
|
|
1677
|
+
if (fnNode.type === 'selector_expression' ||
|
|
1678
|
+
fnNode.type === 'member_expression' ||
|
|
1679
|
+
fnNode.type === 'attribute') {
|
|
1680
|
+
const tail = fnNode.childForFieldName('field') ||
|
|
1681
|
+
fnNode.childForFieldName('property') ||
|
|
1682
|
+
fnNode.childForFieldName('attribute');
|
|
1683
|
+
if (tail)
|
|
1684
|
+
return getNodeText(tail, this.source);
|
|
1685
|
+
}
|
|
1686
|
+
return getNodeText(fnNode, this.source);
|
|
1687
|
+
}
|
|
1420
1688
|
/**
|
|
1421
1689
|
* Check if an expression is a call to an anti-sanitizer method.
|
|
1422
1690
|
* Anti-sanitizers reverse the effect of sanitization (e.g., URLDecoder.decode reverses URLEncoder.encode).
|