circle-ir 4.14.2 → 4.15.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.
Files changed (78) hide show
  1. package/dist/analysis/const-table-lookup.d.ts +29 -0
  2. package/dist/analysis/const-table-lookup.d.ts.map +1 -0
  3. package/dist/analysis/const-table-lookup.js +145 -0
  4. package/dist/analysis/const-table-lookup.js.map +1 -0
  5. package/dist/analysis/findings.d.ts.map +1 -1
  6. package/dist/analysis/findings.js +15 -4
  7. package/dist/analysis/findings.js.map +1 -1
  8. package/dist/analysis/go-strings-replacer.d.ts +21 -0
  9. package/dist/analysis/go-strings-replacer.d.ts.map +1 -0
  10. package/dist/analysis/go-strings-replacer.js +145 -0
  11. package/dist/analysis/go-strings-replacer.js.map +1 -0
  12. package/dist/analysis/interprocedural.d.ts.map +1 -1
  13. package/dist/analysis/interprocedural.js +53 -0
  14. package/dist/analysis/interprocedural.js.map +1 -1
  15. package/dist/analysis/java-canonical-prefix.d.ts +42 -0
  16. package/dist/analysis/java-canonical-prefix.d.ts.map +1 -0
  17. package/dist/analysis/java-canonical-prefix.js +145 -0
  18. package/dist/analysis/java-canonical-prefix.js.map +1 -0
  19. package/dist/analysis/java-crlf-strip.d.ts +30 -0
  20. package/dist/analysis/java-crlf-strip.d.ts.map +1 -0
  21. package/dist/analysis/java-crlf-strip.js +195 -0
  22. package/dist/analysis/java-crlf-strip.js.map +1 -0
  23. package/dist/analysis/passes/cleartext-credential-transport-pass.d.ts +2 -0
  24. package/dist/analysis/passes/cleartext-credential-transport-pass.d.ts.map +1 -1
  25. package/dist/analysis/passes/cleartext-credential-transport-pass.js +64 -12
  26. package/dist/analysis/passes/cleartext-credential-transport-pass.js.map +1 -1
  27. package/dist/analysis/passes/cross-file-pass.d.ts.map +1 -1
  28. package/dist/analysis/passes/cross-file-pass.js +43 -0
  29. package/dist/analysis/passes/cross-file-pass.js.map +1 -1
  30. package/dist/analysis/passes/csharp-sanitizer-credit.d.ts.map +1 -1
  31. package/dist/analysis/passes/csharp-sanitizer-credit.js +18 -0
  32. package/dist/analysis/passes/csharp-sanitizer-credit.js.map +1 -1
  33. package/dist/analysis/passes/interprocedural-pass.d.ts +28 -0
  34. package/dist/analysis/passes/interprocedural-pass.d.ts.map +1 -1
  35. package/dist/analysis/passes/interprocedural-pass.js +20 -1
  36. package/dist/analysis/passes/interprocedural-pass.js.map +1 -1
  37. package/dist/analysis/passes/language-sources-pass.d.ts +41 -2
  38. package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
  39. package/dist/analysis/passes/language-sources-pass.js +842 -10
  40. package/dist/analysis/passes/language-sources-pass.js.map +1 -1
  41. package/dist/analysis/passes/param-source-flow-gate-pass.d.ts +8 -0
  42. package/dist/analysis/passes/param-source-flow-gate-pass.d.ts.map +1 -1
  43. package/dist/analysis/passes/param-source-flow-gate-pass.js +162 -6
  44. package/dist/analysis/passes/param-source-flow-gate-pass.js.map +1 -1
  45. package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
  46. package/dist/analysis/passes/sink-filter-pass.js +207 -1
  47. package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
  48. package/dist/analysis/passes/taint-propagation-pass.d.ts.map +1 -1
  49. package/dist/analysis/passes/taint-propagation-pass.js +257 -2
  50. package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
  51. package/dist/analysis/scalar-derivation.d.ts +25 -0
  52. package/dist/analysis/scalar-derivation.d.ts.map +1 -0
  53. package/dist/analysis/scalar-derivation.js +196 -0
  54. package/dist/analysis/scalar-derivation.js.map +1 -0
  55. package/dist/analysis/taint-matcher.d.ts.map +1 -1
  56. package/dist/analysis/taint-matcher.js +167 -6
  57. package/dist/analysis/taint-matcher.js.map +1 -1
  58. package/dist/analysis/taint-propagation.d.ts.map +1 -1
  59. package/dist/analysis/taint-propagation.js +149 -19
  60. package/dist/analysis/taint-propagation.js.map +1 -1
  61. package/dist/analyzer.d.ts.map +1 -1
  62. package/dist/analyzer.js +7 -0
  63. package/dist/analyzer.js.map +1 -1
  64. package/dist/browser/circle-ir.js +3140 -1186
  65. package/dist/core/circle-ir-core.cjs +485 -27
  66. package/dist/core/circle-ir-core.js +485 -27
  67. package/dist/core/extractors/dfg.js +240 -6
  68. package/dist/core/extractors/dfg.js.map +1 -1
  69. package/dist/graph/code-graph.d.ts +9 -0
  70. package/dist/graph/code-graph.d.ts.map +1 -1
  71. package/dist/graph/code-graph.js +11 -0
  72. package/dist/graph/code-graph.js.map +1 -1
  73. package/dist/resolution/cross-file.d.ts +7 -0
  74. package/dist/resolution/cross-file.d.ts.map +1 -1
  75. package/dist/resolution/cross-file.js +10 -1
  76. package/dist/resolution/cross-file.js.map +1 -1
  77. package/dist/wasm/web-tree-sitter.wasm +0 -0
  78. package/package.json +3 -3
@@ -27,6 +27,14 @@
27
27
  * `@router.get(...)`, `@Get()`), Rust route attributes (`#[get("/<x>")]`)
28
28
  * and Rust handlers returning a web response type (`HttpResponse`,
29
29
  * `impl Responder`, `impl warp::Reply`, ...).
30
+ * - A parameter that a real source in the same file feeds is kept
31
+ * (cognium-dev #684): `function grep(t) {…}` called as
32
+ * `grep(req.query.t)`, a class method called as `svc.run(req.body.c)`,
33
+ * a class constructed with `new C(req.query.u)`, or a function handed to
34
+ * an executor that runs it (`pool.submit(fn, q)`). A bare call never
35
+ * feeds a class method and a call through a receiver never feeds a
36
+ * module function. Arguments passed through a sanitizer do not count,
37
+ * and `external_taint_escape` stays gated.
30
38
  *
31
39
  * Only flows are gated; the source list is untouched, so passes and
32
40
  * consumers that read `taint.sources` (cross-file resolution, co-occurrence
@@ -1 +1 @@
1
- {"version":3,"file":"param-source-flow-gate-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/param-source-flow-gate-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG9E,OAAO,KAAK,EAAE,UAAU,EAAiB,MAAM,sBAAsB,CAAC;AAEtE,MAAM,WAAW,yBAAyB;IACxC,8DAA8D;IAC9D,OAAO,EAAE,OAAO,CAAC;IACjB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AA2CD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAOjG;AAED,qBAAa,uBAAwB,YAAW,YAAY,CAAC,yBAAyB,CAAC;IAIzE,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAHlD,QAAQ,CAAC,IAAI,4BAA4B;IACzC,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,YAA6B,qBAAqB,EAAE,OAAO,EAAI;IAE/D,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,yBAAyB,CA+C/C;CACF"}
1
+ {"version":3,"file":"param-source-flow-gate-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/param-source-flow-gate-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAG9E,OAAO,KAAK,EAAE,UAAU,EAA8B,MAAM,sBAAsB,CAAC;AAEnF,MAAM,WAAW,yBAAyB;IACxC,8DAA8D;IAC9D,OAAO,EAAE,OAAO,CAAC;IACjB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC3C;AA2CD,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAOjG;AAkHD,qBAAa,uBAAwB,YAAW,YAAY,CAAC,yBAAyB,CAAC;IAIzE,OAAO,CAAC,QAAQ,CAAC,qBAAqB;IAHlD,QAAQ,CAAC,IAAI,4BAA4B;IACzC,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,YAA6B,qBAAqB,EAAE,OAAO,EAAI;IAE/D,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,yBAAyB,CAwF/C;CACF"}
@@ -27,6 +27,14 @@
27
27
  * `@router.get(...)`, `@Get()`), Rust route attributes (`#[get("/<x>")]`)
28
28
  * and Rust handlers returning a web response type (`HttpResponse`,
29
29
  * `impl Responder`, `impl warp::Reply`, ...).
30
+ * - A parameter that a real source in the same file feeds is kept
31
+ * (cognium-dev #684): `function grep(t) {…}` called as
32
+ * `grep(req.query.t)`, a class method called as `svc.run(req.body.c)`,
33
+ * a class constructed with `new C(req.query.u)`, or a function handed to
34
+ * an executor that runs it (`pool.submit(fn, q)`). A bare call never
35
+ * feeds a class method and a call through a receiver never feeds a
36
+ * module function. Arguments passed through a sanitizer do not count,
37
+ * and `external_taint_escape` stays gated.
30
38
  *
31
39
  * Only flows are gated; the source list is untouched, so passes and
32
40
  * consumers that read `taint.sources` (cross-file resolution, co-occurrence
@@ -80,6 +88,108 @@ export function isFrameworkHandler(method, language, lines) {
80
88
  }
81
89
  return false;
82
90
  }
91
+ /** Sources that stand for a constructor-injected field or a getter over one (#78). */
92
+ const FIELD_CARRIED_SOURCE = /constructor-injected field|returns tainted field/;
93
+ /**
94
+ * Calls that invoke a function passed to them with the arguments that follow
95
+ * it, keyed by method name. The value is how many of those arguments are not
96
+ * forwarded (`setTimeout(fn, delay, a)` passes only `a`). Executor and
97
+ * scheduler shapes only: `pool.submit(fn, q)`,
98
+ * `loop.run_in_executor(None, fn, q)`, `asyncio.to_thread(fn, q)`,
99
+ * `loop.call_soon(fn, q)`, `setImmediate(fn, q)`, `process.nextTick(fn, q)`.
100
+ */
101
+ const EXECUTOR_CALLEES = new Map([
102
+ ['submit', 0], ['run_in_executor', 0], ['to_thread', 0], ['run_sync', 0],
103
+ ['call_soon', 0], ['call_soon_threadsafe', 0], ['call_later', 0], ['call_at', 0],
104
+ ['setImmediate', 0], ['nextTick', 0], ['queueMicrotask', 0],
105
+ ['setTimeout', 1], ['setInterval', 1],
106
+ ]);
107
+ /** `name` as a whole identifier (names may contain `$`). */
108
+ function identifierRe(name) {
109
+ return new RegExp(`(?:^|[^\\w$.])${name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?![\\w$])`);
110
+ }
111
+ function realFedParams(ctx, sources) {
112
+ const out = { params: new Set(), classes: new Set() };
113
+ const { graph } = ctx;
114
+ const seeds = new Set();
115
+ const allMethods = graph.ir.types.flatMap(t => t.methods);
116
+ // Variables a real source binds directly (`q = request.args.get("q")`),
117
+ // with the method that binds them: a `name` bound in one handler says
118
+ // nothing about a `name` local to another function. The Python DFG does
119
+ // not model every function body, so the binding is the fallback evidence.
120
+ const boundVars = new Map();
121
+ // Source reads written inline as an argument (`sink(request.args.get("q"))`),
122
+ // by line: the read's text as the source location names it.
123
+ const inlineReads = new Map();
124
+ for (const s of sources) {
125
+ if (s.type === 'interprocedural_param')
126
+ continue;
127
+ for (const d of graph.defsAtLine(s.line))
128
+ seeds.add(d.id);
129
+ if (s.variable) {
130
+ const where = boundVars.get(s.variable) ?? new Set();
131
+ where.add(enclosingMethod(allMethods, s.line));
132
+ boundVars.set(s.variable, where);
133
+ }
134
+ const read = /^([\w$.]+?)(?:\(\))? in /.exec(s.location ?? '')?.[1];
135
+ if (read && read.includes('.'))
136
+ inlineReads.set(s.line, [...(inlineReads.get(s.line) ?? []), read]);
137
+ }
138
+ const tainted = graph.propagateTaintedDefIds(seeds);
139
+ // Sanitizer calls by line, as the bare method name (`shlex.quote()` -> `quote`).
140
+ const sanitizerNames = new Map();
141
+ if (ctx.hasResult('sink-filter')) {
142
+ for (const sa of ctx.getResult('sink-filter').sanitizers) {
143
+ const name = /([\w$]+)\(?\)?$/.exec(sa.method)?.[1];
144
+ if (name)
145
+ sanitizerNames.set(sa.line, [...(sanitizerNames.get(sa.line) ?? []), name]);
146
+ }
147
+ }
148
+ const classNames = new Set(graph.ir.types.map(t => t.name));
149
+ const functionNames = new Set(graph.ir.types.flatMap(t => t.methods.map(m => m.name)));
150
+ for (const call of graph.ir.calls) {
151
+ const line = call.location.line;
152
+ const uses = graph.usesAtLine(line).filter(u => u.def_id !== null && tainted.has(u.def_id));
153
+ const reads = inlineReads.get(line) ?? [];
154
+ const sanitizersHere = sanitizerNames.get(line) ?? [];
155
+ const callMethod = enclosingMethod(allMethods, line);
156
+ const isFed = (expr, variable) =>
157
+ // An argument passed through a sanitizer is not the raw request value.
158
+ !sanitizersHere.some(n => expr.includes(`${n}(`)) && ((!!variable && !!boundVars.get(variable)?.has(callMethod)) ||
159
+ reads.some(r => expr.includes(r)) ||
160
+ // The argument's own identifiers, not those of a sibling argument.
161
+ uses.some(u => identifierRe(u.variable).test(expr)));
162
+ // A local function handed over by reference to a call that runs it
163
+ // receives the arguments that follow it (`pool.submit(render, q)`,
164
+ // `loop.run_in_executor(None, f, q)`). Only callees known to invoke their
165
+ // function argument count: `print(banner, name)` or
166
+ // `audit.record(banner, name)` only pass `banner` along.
167
+ const skipAfterCallback = EXECUTOR_CALLEES.get(call.method_name);
168
+ let callbackName = null;
169
+ let callbackPos = -1;
170
+ for (const arg of call.arguments) {
171
+ const expr = typeof arg.expression === 'string' ? arg.expression.trim() : '';
172
+ if (skipAfterCallback !== undefined && callbackName === null && functionNames.has(expr)) {
173
+ callbackName = expr;
174
+ callbackPos = arg.position;
175
+ continue;
176
+ }
177
+ if (!isFed(expr, arg.variable))
178
+ continue;
179
+ // `res.send(req.query.m)` is a call on another object; it feeds a
180
+ // class method named `send`, never a module-level `function send`.
181
+ out.params.add(`${call.receiver ? 'm' : 'f'}:${call.method_name}:${arg.position}`);
182
+ if (classNames.has(call.method_name))
183
+ out.classes.add(call.method_name);
184
+ if (callbackName !== null) {
185
+ const pos = arg.position - callbackPos - 1 - (skipAfterCallback ?? 0);
186
+ if (pos >= 0)
187
+ out.params.add(`f:${callbackName}:${pos}`);
188
+ }
189
+ }
190
+ }
191
+ return out;
192
+ }
83
193
  export class ParamSourceFlowGatePass {
84
194
  keepParamSourcedFlows;
85
195
  name = 'param-source-flow-gate';
@@ -114,13 +224,59 @@ export class ParamSourceFlowGatePass {
114
224
  // the surviving flow may cite the parameter while the evidence is the
115
225
  // real source. Those flows are kept.
116
226
  const realSourceLines = new Set();
117
- if (ctx.hasResult('sink-filter')) {
118
- const sources = ctx.getResult('sink-filter').sources;
227
+ const sources = ctx.hasResult('sink-filter') ? ctx.getResult('sink-filter').sources : [];
228
+ for (const s of sources) {
229
+ if (s.type !== 'interprocedural_param')
230
+ realSourceLines.add(s.line);
231
+ }
232
+ // Computed on first need: most files have no parameter-only flow.
233
+ let fed;
234
+ const fedByRealSource = (f, m) => {
235
+ // The CWE-668 escape fallback names no concrete sink; a fed parameter
236
+ // does not make it more than a hint, so it stays gated.
237
+ if (f.sink_type === 'external_taint_escape')
238
+ return false;
239
+ fed ??= realFedParams(ctx, sources);
240
+ if (fed.params.size === 0)
241
+ return false;
242
+ const owner = ctx.graph.ir.types.find(t => t.methods.includes(m));
243
+ // A constructor is invoked through its class name (`new C(…)`, `C(…)`).
244
+ const isCtor = (m.name === 'constructor' || m.name === '__init__') && !!owner;
245
+ const callee = isCtor && owner ? owner.name : m.name;
246
+ // A module-level function or a constructor is called without a
247
+ // receiver; a class method is reached through one (`svc.run(x)`). Each
248
+ // matches its own call shape only: a bare `send(req.query.m)` does not
249
+ // run `Mailer.prototype.send`, and `res.send(…)` does not run a
250
+ // module-level `function send`.
251
+ const viaReceiver = !isCtor && !!owner && owner.name !== '<module>';
252
+ const feeds = (pos) => fed.params.has(`${viaReceiver ? 'm' : 'f'}:${callee}:${pos}`);
253
+ // When the flow's first step names a parameter, only that parameter
254
+ // counts: every parameter shares the signature line, and `f(a, b)`
255
+ // called as `f(req.query.a, 'x')` does not feed `b`.
256
+ const head = f.path[0]?.variable;
257
+ const named = m.parameters.findIndex(p => p.name === head);
258
+ if (named >= 0)
259
+ return feeds(named);
119
260
  for (const s of sources) {
120
- if (s.type !== 'interprocedural_param')
121
- realSourceLines.add(s.line);
261
+ if (s.line !== f.source_line || s.type !== 'interprocedural_param')
262
+ continue;
263
+ if (FIELD_CARRIED_SOURCE.test(s.location ?? '')) {
264
+ if (owner && fed.classes.has(owner.name))
265
+ return true;
266
+ continue;
267
+ }
268
+ // The source names its parameter in `variable` (Java / C#) or in its
269
+ // location, `<type> <name> in <method>` (taint-matcher).
270
+ const pos = m.parameters.findIndex(p => s.variable
271
+ ? p.name === s.variable
272
+ : (s.location ?? '').endsWith(` ${p.name} in ${m.name}`));
273
+ if (pos < 0)
274
+ continue;
275
+ if (feeds(pos))
276
+ return true;
122
277
  }
123
- }
278
+ return false;
279
+ };
124
280
  // In-place filter: `additionalFlows` is the list the analyzer assigns to
125
281
  // `taint.flows` after the pipeline.
126
282
  let w = 0;
@@ -128,7 +284,7 @@ export class ParamSourceFlowGatePass {
128
284
  let keep = true;
129
285
  if (f.source_type === 'interprocedural_param' && !realSourceLines.has(f.source_line)) {
130
286
  const m = enclosingMethod(methods, f.source_line);
131
- keep = m !== undefined && isHandler(m);
287
+ keep = m !== undefined && (isHandler(m) || fedByRealSource(f, m));
132
288
  }
133
289
  if (keep)
134
290
  flows[w++] = f;
@@ -1 +1 @@
1
- {"version":3,"file":"param-source-flow-gate-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/param-source-flow-gate-pass.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"param-source-flow-gate-pass.js","sourceRoot":"","sources":["../../../src/analysis/passes/param-source-flow-gate-pass.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"sink-filter-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/sink-filter-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAwB,MAAM,sBAAsB,CAAC;AACzG,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AA89B9E,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,sBAAsB;IACtB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B;AA6BD,qBAAa,cAAe,YAAW,YAAY,CAAC,gBAAgB,CAAC;IACnE,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,gBAAgB,CAo0CtC;CACF;AAkRD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,KAAK,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AA+J1G,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EACjC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,EACxB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,OAAO,EAAE,OAAO,EAChB,GAAG,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,EACrB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAC3B,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAC/B,QAAQ,CAAC,EAAE,MAAM,GAChB,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CA2F5B;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EACjC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,EAC3C,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,EACxB,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,GACrC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAuD5B"}
1
+ {"version":3,"file":"sink-filter-pass.d.ts","sourceRoot":"","sources":["../../../src/analysis/passes/sink-filter-pass.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,cAAc,EAAkC,MAAM,sBAAsB,CAAC;AACnH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAmlC9E,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,sBAAsB;IACtB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,UAAU,EAAE,cAAc,EAAE,CAAC;CAC9B;AA6BD,qBAAa,cAAe,YAAW,YAAY,CAAC,gBAAgB,CAAC;IACnE,QAAQ,CAAC,IAAI,iBAAiB;IAC9B,QAAQ,CAAC,QAAQ,EAAG,UAAU,CAAU;IAExC,GAAG,CAAC,GAAG,EAAE,WAAW,GAAG,gBAAgB,CAq4CtC;CACF;AAkRD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErD,KAAK,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE;IAAE,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC;AA+J1G,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EACjC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,EACxB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,EACxB,OAAO,EAAE,OAAO,EAChB,GAAG,CAAC,EAAE,QAAQ,CAAC,KAAK,CAAC,EACrB,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAC3B,iBAAiB,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAC/B,QAAQ,CAAC,EAAE,MAAM,GAChB,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CA2F5B;AAED,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,EACjC,UAAU,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,YAAY,CAAC,EAC3C,KAAK,EAAE,QAAQ,CAAC,OAAO,CAAC,EACxB,OAAO,CAAC,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC,SAAS,CAAC,GACrC,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,CAuD5B"}
@@ -464,6 +464,110 @@ const SERVER_SIGNAL_RE = /["']use server["']|\b(?:getServerSideProps|getStaticPr
464
464
  // interpolated, the destination host is not attacker-controlled. Matches a
465
465
  // backtick + scheme + a literal host run (no `${` before the first `/`).
466
466
  const FIXED_HOST_TEMPLATE_RE = /`https?:\/\/[^/${}\s`]+[/`]/;
467
+ /** A bash URL word whose scheme and host are literal and closed by `/` (#495). */
468
+ const BASH_FIXED_HOST_URL_RE = /^(?:https?|ftp):\/\/[A-Za-z0-9.-]+(?::\d+)?\//;
469
+ /**
470
+ * True when the string literal that ended at `end` is the WHOLE initializer:
471
+ * the next code token is `;`, `,`, end of input, or (ASI) a token on a later
472
+ * line that cannot continue the expression. A same-line operator (`+`, `.`,
473
+ * `||` …) or a next-line `+` / `.` / `(` / `[` / template continuation means
474
+ * the literal is only the start of a larger expression, so it is not a
475
+ * constant. An optional TS `as const` / `as string` suffix is skipped (it may
476
+ * sit on the next line).
477
+ * (cognium-dev#680, #681)
478
+ */
479
+ function literalEndsInitializer(code, end) {
480
+ let i = end;
481
+ let crossedNewline = false;
482
+ const asConst = /^\s+as\s+(?:const|string)\b/.exec(code.slice(i, i + 64));
483
+ if (asConst) {
484
+ i += asConst[0].length;
485
+ crossedNewline = asConst[0].includes('\n');
486
+ }
487
+ for (;;) {
488
+ while (i < code.length && /\s/.test(code[i])) {
489
+ if (code[i] === '\n')
490
+ crossedNewline = true;
491
+ i++;
492
+ }
493
+ if (code.startsWith('//', i)) {
494
+ const nl = code.indexOf('\n', i);
495
+ i = nl === -1 ? code.length : nl;
496
+ }
497
+ else if (code.startsWith('/*', i)) {
498
+ const close = code.indexOf('*/', i + 2);
499
+ if (close === -1)
500
+ return false;
501
+ if (code.slice(i, close).includes('\n'))
502
+ crossedNewline = true;
503
+ i = close + 2;
504
+ }
505
+ else
506
+ break;
507
+ }
508
+ if (i >= code.length)
509
+ return true;
510
+ const c = code[i];
511
+ if (c === ';' || c === ',')
512
+ return true;
513
+ // ASI: a following line that begins a new statement terminates the initializer.
514
+ // A line that starts with `as` / `satisfies` / `in` / `instanceof` continues it.
515
+ if (/^(?:as|satisfies|in|instanceof)(?![\w$])/.test(code.slice(i, i + 12)))
516
+ return false;
517
+ return crossedNewline && !/[-+*/%.?:|&<>=!~^(\[`]/.test(c);
518
+ }
519
+ /** Decode `\xNN` / `\uNNNN` / `\u{…}` JS string escapes; other escapes are kept verbatim. */
520
+ function decodeJsHexEscapes(value) {
521
+ return value.replace(/\\(?:x([0-9a-fA-F]{2})|u([0-9a-fA-F]{4})|u\{([0-9a-fA-F]{1,6})\}|[\s\S])/g, (m, x, u4, ub) => {
522
+ const hex = x ?? u4 ?? ub;
523
+ if (hex === undefined)
524
+ return m;
525
+ const cp = parseInt(hex, 16);
526
+ return cp <= 0x10ffff ? String.fromCodePoint(cp) : m;
527
+ });
528
+ }
529
+ /**
530
+ * The value of `name` when it is bound exactly once in the file, by `const`,
531
+ * to a plain string literal (no interpolation) that is the whole initializer,
532
+ * and has no other `let` / `var` / `function` binding; otherwise null.
533
+ * (cognium-dev#681)
534
+ */
535
+ function constStringLiteralValue(code, name) {
536
+ const esc = name.replace(/\$/g, '\\$');
537
+ const decl = new RegExp(`(?:^|[^\\w$.])const\\s+${esc}\\s*(?::\\s*string\\s*)?=\\s*(?:'((?:\\\\.|[^'\\\\\\n])*)'|"((?:\\\\.|[^"\\\\\\n])*)"|\`([^\`$\\\\]*)\`)`, 'g');
538
+ const decls = [...code.matchAll(decl)];
539
+ if (decls.length !== 1)
540
+ return null;
541
+ if (!literalEndsInitializer(code, decls[0].index + decls[0][0].length))
542
+ return null;
543
+ const anyBinding = new RegExp(`(?:^|[^\\w$.])(?:const|let|var|function)\\s+${esc}\\b`, 'g');
544
+ if ([...code.matchAll(anyBinding)].length !== 1)
545
+ return null;
546
+ return decodeJsHexEscapes(decls[0][1] ?? decls[0][2] ?? decls[0][3] ?? '');
547
+ }
548
+ /** A group that holds an unbounded quantifier and is itself quantified: `(a+)+`, `(x*)*`, `(\w{2,})+`, `(a+){2,}`. */
549
+ const NESTED_QUANTIFIER_RE = /\([^()]*(?:[+*]|\{\d+,\d*\})[^()]*\)\s*[+*{]/;
550
+ /** A template literal that starts with `${IDENT}/` (cognium-dev#680). */
551
+ const CONST_BASE_TEMPLATE_RE = /`\$\{\s*([A-Za-z_$][\w$]*)\s*\}\//g;
552
+ /**
553
+ * True when `name` is bound exactly once in the file, by `const`, to a string
554
+ * literal http(s) origin with a literal host, and is never reassigned.
555
+ */
556
+ function isConstLiteralOrigin(code, name) {
557
+ const esc = name.replace(/\$/g, '\\$');
558
+ const decl = new RegExp(`(?:^|[^\\w$.])(const|let|var)\\s+${esc}\\s*(?::\\s*string\\s*)?=\\s*(['"\`])(https?://[^/'"\`\\s$]+(?:/[^'"\`$]*)?)\\2`, 'gm');
559
+ const decls = [...code.matchAll(decl)];
560
+ if (decls.length !== 1 || decls[0][1] !== 'const')
561
+ return false;
562
+ if (!literalEndsInitializer(code, decls[0].index + decls[0][0].length))
563
+ return false;
564
+ const anyBinding = new RegExp(`(?:^|[^\\w$.])(?:const|let|var)\\s+${esc}\\b`, 'g');
565
+ if ([...code.matchAll(anyBinding)].length !== 1)
566
+ return false;
567
+ const reassign = new RegExp(`(?:^|[^\\w$.])${esc}\\s*(?:[-+]?=)(?!=)`, 'g');
568
+ const rest = code.slice(0, decls[0].index) + code.slice(decls[0].index + decls[0][0].length);
569
+ return !reassign.test(rest);
570
+ }
467
571
  /**
468
572
  * Returns the 1-indexed line numbers that a Java `if (x.contains("..")) throw/
469
573
  * return` reject-guard makes path-traversal-safe (cognium-dev#269). A line is
@@ -656,6 +760,33 @@ function isUnwrittenLocalMapKeyRead(code, source) {
656
760
  return false;
657
761
  return true;
658
762
  }
763
+ /**
764
+ * Is this C# `Content(...)` call the controller helper writing an HTML body
765
+ * (cognium-dev#580)?
766
+ *
767
+ * - The helper is inherited (`ControllerBase.Content`, `PageModel.Content`),
768
+ * so it is called unqualified or on `this` / `base`. `note.Content(q)` is
769
+ * some other type's method.
770
+ * - The content type is argument 1. A literal or `MediaTypeNames` constant
771
+ * that is not HTML or a script-capable XML type serves the body as data. A computed content type is unknown and keeps the sink, as does the
772
+ * one-argument overload.
773
+ */
774
+ function csharpContentServesHtml(call) {
775
+ if (call.receiver !== null && call.receiver !== 'this' && call.receiver !== 'base')
776
+ return false;
777
+ const contentType = call.arguments.find(a => a.position === 1)?.expression.trim();
778
+ if (contentType === undefined)
779
+ return true;
780
+ const literal = /^@?"([^"]*)"$/.exec(contentType);
781
+ // XML types (`text/xml`, `application/xml`, `image/svg+xml`, any `+xml`)
782
+ // render XHTML / SVG markup and run its scripts, so they count as HTML.
783
+ if (literal)
784
+ return /^\s*(?:text\/html|text\/xml|application\/xml|[\w.-]+\/[\w.-]+\+xml)\b/i.test(literal[1]);
785
+ const named = /^(?:System\s*\.\s*Net\s*\.\s*Mime\s*\.\s*)?MediaTypeNames\s*\.\s*(Text|Application)\s*\.\s*(\w+)$/.exec(contentType);
786
+ if (named)
787
+ return named[2] === 'Html' || named[2] === 'Xml';
788
+ return true;
789
+ }
659
790
  /**
660
791
  * Compute, per sink type, the set of C# variables whose value has passed through
661
792
  * a sanitizer for that type (cognium-dev C#/.NET Phase-1). Seeds from
@@ -2042,7 +2173,51 @@ export class SinkFilterPass {
2042
2173
  if (sink.type !== 'ssrf')
2043
2174
  return true;
2044
2175
  const sinkLineText = sourceLines[sink.line - 1] ?? '';
2045
- return !FIXED_HOST_TEMPLATE_RE.test(sinkLineText);
2176
+ if (FIXED_HOST_TEMPLATE_RE.test(sinkLineText))
2177
+ return false;
2178
+ // `${API_BASE}/path` where API_BASE is bound once by `const` to a
2179
+ // literal origin: the authority is constant and closed by `/` (#680).
2180
+ for (const m of sinkLineText.matchAll(CONST_BASE_TEMPLATE_RE)) {
2181
+ if (isConstLiteralOrigin(ctx.code, m[1]))
2182
+ return false;
2183
+ }
2184
+ return true;
2185
+ });
2186
+ }
2187
+ // Stage 15k — JS/TS redos: `new RegExp(`…${CONST}…`)` whose every
2188
+ // interpolation is a module `const` bound to a string literal has a
2189
+ // constant pattern; a tainted subject alone is not ReDoS. (cognium-dev#681)
2190
+ if (['javascript', 'typescript'].includes(language)) {
2191
+ const sourceLines = ctx.code.split('\n');
2192
+ filtered = filtered.filter(sink => {
2193
+ if (sink.type !== 'redos')
2194
+ return true;
2195
+ const m = (sourceLines[sink.line - 1] ?? '').match(/new\s+RegExp\s*\(\s*`((?:\\.|[^`\\])*)`/);
2196
+ if (!m)
2197
+ return true;
2198
+ let pattern = m[1];
2199
+ let interpolations = 0;
2200
+ let allConst = true;
2201
+ // Replace each `${ NAME }` as written (whitespace inside the braces
2202
+ // included) by the const's literal value.
2203
+ pattern = pattern.replace(/\$\{([^}]*)\}/g, (_full, raw) => {
2204
+ interpolations++;
2205
+ const name = raw.trim();
2206
+ if (!/^[A-Za-z_$][\w$]*$/.test(name)) {
2207
+ allConst = false;
2208
+ return '';
2209
+ }
2210
+ const value = constStringLiteralValue(ctx.code, name);
2211
+ if (value === null) {
2212
+ allConst = false;
2213
+ return '';
2214
+ }
2215
+ return value;
2216
+ });
2217
+ if (interpolations === 0 || !allConst)
2218
+ return true;
2219
+ // A constant pattern can still backtrack on a tainted subject.
2220
+ return NESTED_QUANTIFIER_RE.test(pattern);
2046
2221
  });
2047
2222
  }
2048
2223
  // Stage 15f — JS/TS ssrf: anchored host allow-list reject-guard.
@@ -2118,6 +2293,37 @@ export class SinkFilterPass {
2118
2293
  return !covered || credit.rawTaintReaches(sink.type, sink.line, sanitizedVars, csSeeds);
2119
2294
  });
2120
2295
  }
2296
+ // Stage 15k' — C# `Content(body, contentType)` serves HTML only when the
2297
+ // content type is HTML (cognium-dev#580). The `Content` xss sink stays when
2298
+ // at least one `Content` call on its line is the controller helper with an
2299
+ // HTML or unknown content type.
2300
+ if (language === 'csharp') {
2301
+ filtered = filtered.filter(sink => {
2302
+ if (sink.type !== 'xss' || sink.method !== 'Content')
2303
+ return true;
2304
+ const contentCalls = callsByLine.get(sink.line)?.filter(c => c.method_name === 'Content') ?? [];
2305
+ return contentCalls.length === 0 || contentCalls.some(csharpContentServesHtml);
2306
+ });
2307
+ }
2308
+ // Stage 15m — bash ssrf: fixed-host URL word (cognium-dev#495, #669).
2309
+ // The bash analogue of Stage 15i: `curl -- "https://api.example.com/${p}"`
2310
+ // can only reach the literal host, whatever `$p` holds. Every word on the
2311
+ // line that expands a variable must be such a URL; a word like `"$url"`,
2312
+ // `"https://${host}/x"` or `"https://api.example.com${p}"` (no `/` to
2313
+ // close the authority, so `$p` may be `@evil.com`) keeps the sink.
2314
+ if (language === 'bash') {
2315
+ const sourceLines = ctx.code.split('\n');
2316
+ filtered = filtered.filter(sink => {
2317
+ if (sink.type !== 'ssrf')
2318
+ return true;
2319
+ const words = (sourceLines[sink.line - 1] ?? '').match(/"[^"]*"|'[^']*'|[^\s"']+/g) ?? [];
2320
+ const expanding = words.filter(w => !w.startsWith("'") && w.includes('$'));
2321
+ // Unquoted (`https://h/$p`), the expansion is word-split, so `$p`
2322
+ // can add a second URL or a flag: only a double-quoted word counts.
2323
+ return expanding.length === 0 ||
2324
+ !expanding.every(w => w.startsWith('"') && BASH_FIXED_HOST_URL_RE.test(w.slice(1, -1)));
2325
+ });
2326
+ }
2121
2327
  // Stage 15l — C# ssrf: HttpClient with a constant BaseAddress and a
2122
2328
  // relative request path. (cognium-dev#286 D)
2123
2329
  //