circle-ir 4.9.28 → 4.10.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 (46) hide show
  1. package/dist/analysis/config-loader.d.ts.map +1 -1
  2. package/dist/analysis/config-loader.js +63 -2
  3. package/dist/analysis/config-loader.js.map +1 -1
  4. package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
  5. package/dist/analysis/passes/language-sources-pass.js +54 -7
  6. package/dist/analysis/passes/language-sources-pass.js.map +1 -1
  7. package/dist/analysis/passes/taint-propagation-pass.js +117 -12
  8. package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
  9. package/dist/analysis/taint-matcher.d.ts.map +1 -1
  10. package/dist/analysis/taint-matcher.js +41 -0
  11. package/dist/analysis/taint-matcher.js.map +1 -1
  12. package/dist/analyzer.d.ts +12 -0
  13. package/dist/analyzer.d.ts.map +1 -1
  14. package/dist/analyzer.js +1 -1
  15. package/dist/analyzer.js.map +1 -1
  16. package/dist/browser/circle-ir.js +260 -28
  17. package/dist/core/circle-ir-core.cjs +179 -12
  18. package/dist/core/circle-ir-core.js +179 -12
  19. package/dist/core/extractors/calls.js +29 -1
  20. package/dist/core/extractors/calls.js.map +1 -1
  21. package/dist/core/extractors/types.d.ts +28 -1
  22. package/dist/core/extractors/types.d.ts.map +1 -1
  23. package/dist/core/extractors/types.js +136 -10
  24. package/dist/core/extractors/types.js.map +1 -1
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.d.ts.map +1 -1
  27. package/dist/index.js +4 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/navigation/index.d.ts +8 -0
  30. package/dist/navigation/index.d.ts.map +1 -0
  31. package/dist/navigation/index.js +6 -0
  32. package/dist/navigation/index.js.map +1 -0
  33. package/dist/navigation/navigation-index.d.ts +208 -0
  34. package/dist/navigation/navigation-index.d.ts.map +1 -0
  35. package/dist/navigation/navigation-index.js +968 -0
  36. package/dist/navigation/navigation-index.js.map +1 -0
  37. package/dist/navigation/types.d.ts +160 -0
  38. package/dist/navigation/types.d.ts.map +1 -0
  39. package/dist/navigation/types.js +17 -0
  40. package/dist/navigation/types.js.map +1 -0
  41. package/dist/types/config.d.ts +14 -0
  42. package/dist/types/config.d.ts.map +1 -1
  43. package/dist/types/index.d.ts +19 -0
  44. package/dist/types/index.d.ts.map +1 -1
  45. package/dist/types/index.js.map +1 -1
  46. package/package.json +1 -1
@@ -4458,7 +4458,7 @@ function detectLanguage(tree) {
4458
4458
  if (pythonScore > jsScore && pythonScore > javaScore) return "python";
4459
4459
  return jsScore > javaScore ? "javascript" : "java";
4460
4460
  }
4461
- function extractTypes(tree, cache, language) {
4461
+ function extractTypes(tree, cache, language, options) {
4462
4462
  const effectiveLanguage = language ?? detectLanguage(tree);
4463
4463
  const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
4464
4464
  const isPython = effectiveLanguage === "python";
@@ -4478,7 +4478,22 @@ function extractTypes(tree, cache, language) {
4478
4478
  if (effectiveLanguage === "csharp") {
4479
4479
  return extractCSharpTypes(tree, cache);
4480
4480
  }
4481
- return extractJavaTypes(tree, cache);
4481
+ return extractJavaTypes(tree, cache, options);
4482
+ }
4483
+ function csharpMemberNodes(body2) {
4484
+ if (!body2) return [];
4485
+ const out2 = [];
4486
+ const visit = (parent, depth) => {
4487
+ if (depth > 32) return;
4488
+ for (let i2 = 0; i2 < parent.childCount; i2++) {
4489
+ const c = parent.child(i2);
4490
+ if (!c) continue;
4491
+ if (c.type.startsWith("preproc_")) visit(c, depth + 1);
4492
+ else out2.push(c);
4493
+ }
4494
+ };
4495
+ visit(body2, 0);
4496
+ return out2;
4482
4497
  }
4483
4498
  function extractCSharpTypes(tree, cache) {
4484
4499
  const types = [];
@@ -4496,9 +4511,8 @@ function extractCSharpTypes(tree, cache) {
4496
4511
  const methods = [];
4497
4512
  const fields = extractCSharpFields(body2);
4498
4513
  if (body2) {
4499
- for (let i2 = 0; i2 < body2.childCount; i2++) {
4500
- const m = body2.child(i2);
4501
- if (!m || m.type !== "method_declaration") continue;
4514
+ for (const m of csharpMemberNodes(body2)) {
4515
+ if (m.type !== "method_declaration") continue;
4502
4516
  const mName = m.childForFieldName("name");
4503
4517
  const paramList = m.childForFieldName("parameters");
4504
4518
  const parameters = [];
@@ -4549,9 +4563,7 @@ function extractCSharpTypes(tree, cache) {
4549
4563
  function extractCSharpFields(body2) {
4550
4564
  const fields = [];
4551
4565
  if (!body2) return fields;
4552
- for (let i2 = 0; i2 < body2.childCount; i2++) {
4553
- const c = body2.child(i2);
4554
- if (!c) continue;
4566
+ for (const c of csharpMemberNodes(body2)) {
4555
4567
  if (c.type === "field_declaration") {
4556
4568
  let varDecl = null;
4557
4569
  for (let k = 0; k < c.childCount; k++) {
@@ -4607,7 +4619,7 @@ function extractCSharpModifiers(node) {
4607
4619
  }
4608
4620
  return mods;
4609
4621
  }
4610
- function extractJavaTypes(tree, cache) {
4622
+ function extractJavaTypes(tree, cache, options) {
4611
4623
  const types = [];
4612
4624
  const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
4613
4625
  for (const cls of classes) {
@@ -4621,8 +4633,76 @@ function extractJavaTypes(tree, cache) {
4621
4633
  for (const enumDecl of enums) {
4622
4634
  types.push(extractEnumInfo(enumDecl));
4623
4635
  }
4636
+ if (!options?.navigationTypes) return types;
4637
+ for (const rec of getNodesFromCache(tree.rootNode, "record_declaration", cache)) {
4638
+ const info2 = extractClassInfo(rec);
4639
+ info2.is_record = true;
4640
+ for (const comp of recordComponents(rec)) {
4641
+ if (!info2.fields.some((f) => f.name === comp.name)) {
4642
+ info2.fields.push({
4643
+ name: comp.name,
4644
+ type: comp.type,
4645
+ modifiers: ["private", "final"],
4646
+ annotations: []
4647
+ });
4648
+ }
4649
+ if (!info2.methods.some((m) => m.name === comp.name)) {
4650
+ info2.methods.push({
4651
+ name: comp.name,
4652
+ return_type: comp.type,
4653
+ parameters: [],
4654
+ annotations: [],
4655
+ modifiers: ["public"],
4656
+ start_line: rec.startPosition.row + 1,
4657
+ end_line: rec.startPosition.row + 1
4658
+ });
4659
+ }
4660
+ }
4661
+ types.push(info2);
4662
+ }
4663
+ const declNodes = [
4664
+ ...classes,
4665
+ ...interfaces,
4666
+ ...enums,
4667
+ ...getNodesFromCache(tree.rootNode, "record_declaration", cache)
4668
+ ];
4669
+ for (const t of types) {
4670
+ const node = declNodes.find(
4671
+ (n) => n.startPosition.row + 1 === t.start_line && getIdentifier(n, "name") === t.name
4672
+ );
4673
+ const path = node ? enclosingTypePath(node) : [];
4674
+ if (path.length > 0) t.enclosing_type = path.join(".");
4675
+ }
4624
4676
  return types;
4625
4677
  }
4678
+ function recordComponents(node) {
4679
+ const out2 = [];
4680
+ const params = node.childForFieldName("parameters");
4681
+ if (!params) return out2;
4682
+ for (let i2 = 0; i2 < params.childCount; i2++) {
4683
+ const c = params.child(i2);
4684
+ if (!c || c.type !== "formal_parameter") continue;
4685
+ const name2 = c.childForFieldName("name");
4686
+ const type = c.childForFieldName("type");
4687
+ if (name2) out2.push({ name: getNodeText(name2), type: type ? getNodeText(type) : null });
4688
+ }
4689
+ return out2;
4690
+ }
4691
+ function enclosingTypePath(node) {
4692
+ const DECLS = /* @__PURE__ */ new Set([
4693
+ "class_declaration",
4694
+ "interface_declaration",
4695
+ "enum_declaration",
4696
+ "record_declaration"
4697
+ ]);
4698
+ const path = [];
4699
+ for (let p = node.parent; p; p = p.parent) {
4700
+ if (!DECLS.has(p.type)) continue;
4701
+ const n = getIdentifier(p, "name");
4702
+ if (n) path.unshift(n);
4703
+ }
4704
+ return path;
4705
+ }
4626
4706
  function extractJavaScriptTypes(tree, cache) {
4627
4707
  const types = [];
4628
4708
  const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
@@ -6085,6 +6165,13 @@ function extractCalls(tree, cache, language) {
6085
6165
  function buildCSharpReceiverTypeMap(tree, cache) {
6086
6166
  const map = /* @__PURE__ */ new Map();
6087
6167
  const simple = (t) => t.replace(/<[^>]*>/g, "").split(".").pop()?.trim() ?? t;
6168
+ for (const prm of getNodesFromCache(tree.rootNode, "parameter", cache)) {
6169
+ const nameNode = prm.childForFieldName("name");
6170
+ const typeNode = prm.childForFieldName("type");
6171
+ if (nameNode?.type !== "identifier" || !typeNode) continue;
6172
+ const t = getNodeText(typeNode);
6173
+ if (t && t !== "var") map.set(getNodeText(nameNode), simple(t));
6174
+ }
6088
6175
  for (const vd of getNodesFromCache(tree.rootNode, "variable_declaration", cache)) {
6089
6176
  const typeNode = vd.childForFieldName("type");
6090
6177
  const declaredType = typeNode ? getNodeText(typeNode) : null;
@@ -6114,6 +6201,7 @@ function findFirstDescendant(node, type) {
6114
6201
  }
6115
6202
  return null;
6116
6203
  }
6204
+ var CSHARP_RESPONSE_TYPES = /* @__PURE__ */ new Set(["HttpResponse", "HttpResponseBase"]);
6117
6205
  var CSHARP_COMMAND_EXECUTE_METHODS = /* @__PURE__ */ new Set([
6118
6206
  "ExecuteScalar",
6119
6207
  "ExecuteReader",
@@ -6266,7 +6354,8 @@ function extractCSharpCalls(tree, cache) {
6266
6354
  if (recvType !== "DirectorySearcher") continue;
6267
6355
  } else if (propName === "StatusDescription") {
6268
6356
  const recv = exprNode ? getNodeText(exprNode) : "";
6269
- if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv)) continue;
6357
+ const recvType = recv ? typeMap.get(recv) : void 0;
6358
+ if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv) && !(recvType && CSHARP_RESPONSE_TYPES.has(recvType))) continue;
6270
6359
  } else if (propName === "BaseAddress") {
6271
6360
  const recv = exprNode ? getNodeText(exprNode) : null;
6272
6361
  const recvType = recv ? typeMap.get(recv) : void 0;
@@ -11139,6 +11228,23 @@ var DEFAULT_SOURCES = [
11139
11228
  { method: "next", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11140
11229
  { method: "nextInt", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11141
11230
  // Database result sources
11231
+ // C# I/O, network and database reads (cognium-dev#502). Mirrors the Java
11232
+ // rows above one-for-one, including the severity split: console/file I/O is
11233
+ // `io_input` at high, a database row read is `db_input` at medium. There is
11234
+ // no separate confidence tier — `type` + `severity` IS the tier, exactly as
11235
+ // for `BufferedReader.readLine` vs `ResultSet.getString`.
11236
+ { method: "ReadLine", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11237
+ { method: "ReadLineAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11238
+ { method: "ReadToEnd", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11239
+ { method: "ReadToEndAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11240
+ // Socket and HTTP-client reads. `GetStream` / `AcceptTcpClient` /
11241
+ // `OpenRead` return the channel; the read off it is covered by the
11242
+ // StreamReader rows above, and seeding the channel too lets a flow form when
11243
+ // the read is inlined.
11244
+ { method: "GetStream", class: "TcpClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11245
+ { method: "AcceptTcpClient", class: "TcpListener", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11246
+ { method: "OpenRead", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11247
+ { method: "DownloadString", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11142
11248
  { method: "getString", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
11143
11249
  { method: "getObject", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
11144
11250
  { method: "getInt", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
@@ -13379,6 +13485,29 @@ var DEFAULT_SINKS = [
13379
13485
  { method: "deserialize", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
13380
13486
  { method: "from_str", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13381
13487
  { method: "from_str", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13488
+ // cognium-dev#541 — the crates the removed classless rows were silently
13489
+ // covering. #484 replaced a classless `from_str` / `from_slice` with
13490
+ // class-scoped rows, but only for the four formats that already had entries;
13491
+ // every other serde data format lost its sink, and `rmp_serde::from_slice`
13492
+ // on bytes read straight off a `TcpStream` stopped being reported. Each row
13493
+ // below is a serde *data format* entry point — a function whose whole job is
13494
+ // to build a typed value out of untrusted bytes or text.
13495
+ { method: "from_slice", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13496
+ { method: "from_read", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13497
+ { method: "from_read_ref", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13498
+ { method: "from_slice", class: "serde_yaml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13499
+ { method: "from_slice", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13500
+ { method: "from_reader", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13501
+ { method: "from_bytes", class: "postcard", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13502
+ { method: "from_slice", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13503
+ { method: "from_reader", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13504
+ { method: "from_reader", class: "ciborium", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13505
+ { method: "from_str", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13506
+ { method: "from_reader", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13507
+ { method: "from_str", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13508
+ { method: "from_reader", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13509
+ { method: "from_slice", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
13510
+ { method: "deserialize_from", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
13382
13511
  // #294/#484: no classless `from_str` / `from_slice` deserialization sink.
13383
13512
  // Those rows matched EVERY `T::from_str` / `from_slice` — `u32::from_str`,
13384
13513
  // `Url::from_str`, `PublicKey::from_slice` — i.e. ordinary `FromStr` / byte
@@ -13703,8 +13832,12 @@ var DEFAULT_SINKS = [
13703
13832
  { method: "OpenRead", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13704
13833
  { method: "OpenWrite", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13705
13834
  { method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13706
- { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13707
- { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13835
+ // #502: the `(Stream)` overload takes no path — `new StreamReader(tcp.GetStream())`
13836
+ // is not CWE-22. The `(string path)` overload still matches.
13837
+ { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
13838
+ // #502: the `(Stream)` overload takes no path — `new StreamWriter(tcp.GetStream())`
13839
+ // is not CWE-22. The `(string path)` overload still matches.
13840
+ { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
13708
13841
  // Remaining System.IO.File path APIs — class-scoped so they don't collide
13709
13842
  // with unrelated methods. Copy/Move take a source AND destination path (both
13710
13843
  // attacker-controllable → read-anywhere / write-anywhere), hence [0, 1].
@@ -13748,6 +13881,23 @@ var DEFAULT_SINKS = [
13748
13881
  // `new RestClient(userUrl)` points every later request at attacker-controlled
13749
13882
  // infrastructure. Ctor arg 0, taint-gated.
13750
13883
  { method: "RestClient", class: "constructor", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
13884
+ // C# code injection — CodeDOM runtime compilation (CWE-94), cognium-dev#503
13885
+ // part 2. `provider.CompileAssemblyFromSource(parameters, source)` compiles
13886
+ // and can then execute attacker-supplied C#, so the source argument is the
13887
+ // injection point. The signature is
13888
+ // `CompileAssemblyFromSource(CompilerParameters, params string[] sources)`,
13889
+ // so the sources begin at position 1 — position 0 is the options object.
13890
+ //
13891
+ // These rows are only useful together with the #530 StringBuilder
13892
+ // propagation fix: the Juliet CWE-94 corpus accumulates the compiled source
13893
+ // through `sb.Append(...)` and reads it back with `ToString()`, so before
13894
+ // that fix the sink registered but no taint ever reached it.
13895
+ { method: "CompileAssemblyFromSource", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
13896
+ { method: "CompileAssemblyFromSourceBatch", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
13897
+ // Roslyn's equivalent pair. `ParseText` turns text into a syntax tree and
13898
+ // `Create` compiles it; either is the point where attacker text becomes code.
13899
+ { method: "ParseText", class: "CSharpSyntaxTree", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13900
+ { method: "ParseText", class: "SyntaxFactory", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13751
13901
  // C# code injection — dynamic script/assembly loading (CWE-94).
13752
13902
  { method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13753
13903
  { method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
@@ -15529,6 +15679,20 @@ function escapeRe(s) {
15529
15679
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15530
15680
  }
15531
15681
  var TYPE_TOKEN_RE = /^new\s+(?:TypeReference|TypeToken)\s*<[\s\S]*>\s*\(\s*\)\s*\{\s*\}$/;
15682
+ function argIsStreamExpression(call, position) {
15683
+ const arg = call.arguments.find((a) => a.position === position);
15684
+ if (!arg) return false;
15685
+ const expr = (arg.expression ?? "").trim();
15686
+ if (!expr) return false;
15687
+ return (
15688
+ // `new MemoryStream(...)`, `new FileStream(...)`, `new GZipStream(...)`
15689
+ /^new\s+[A-Za-z_][\w.]*Stream\s*\(/.test(expr) || // `tcp.GetStream()`, `resp.GetResponseStream()`, `file.OpenReadStream()`
15690
+ /\.\s*Get(?:Response)?Stream\s*\(/.test(expr) || /\.\s*OpenReadStream\s*\(/.test(expr) || // `wc.OpenRead(url)` returns a Stream (WebClient), as does `File.OpenRead`
15691
+ /\.\s*OpenRead\s*\(/.test(expr) || // `Request.Body`, `x.BaseStream`
15692
+ /\.\s*(?:Body|BaseStream)\s*$/.test(expr) || // a local whose name is stream-ish and is not a path string
15693
+ /^(?:[A-Za-z_]\w*\.)*[A-Za-z_]*[sS]tream\d*$/.test(expr)
15694
+ );
15695
+ }
15532
15696
  function argIsStringLiteral(call, position) {
15533
15697
  const arg = call.arguments.find((a) => a.position === position);
15534
15698
  if (!arg) return false;
@@ -15766,6 +15930,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
15766
15930
  if (pattern.safe_if_string_literal_at !== void 0 && argIsStringLiteral(call, pattern.safe_if_string_literal_at)) {
15767
15931
  continue;
15768
15932
  }
15933
+ if (pattern.safe_if_stream_arg_at !== void 0 && argIsStreamExpression(call, pattern.safe_if_stream_arg_at)) {
15934
+ continue;
15935
+ }
15769
15936
  if (pattern.type === "command_injection") {
15770
15937
  if (call.is_constructor) {
15771
15938
  if (!CWE_78_RECEIVER_ALLOWLIST.has(call.method_name)) {
@@ -4392,7 +4392,7 @@ function detectLanguage(tree) {
4392
4392
  if (pythonScore > jsScore && pythonScore > javaScore) return "python";
4393
4393
  return jsScore > javaScore ? "javascript" : "java";
4394
4394
  }
4395
- function extractTypes(tree, cache, language) {
4395
+ function extractTypes(tree, cache, language, options) {
4396
4396
  const effectiveLanguage = language ?? detectLanguage(tree);
4397
4397
  const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
4398
4398
  const isPython = effectiveLanguage === "python";
@@ -4412,7 +4412,22 @@ function extractTypes(tree, cache, language) {
4412
4412
  if (effectiveLanguage === "csharp") {
4413
4413
  return extractCSharpTypes(tree, cache);
4414
4414
  }
4415
- return extractJavaTypes(tree, cache);
4415
+ return extractJavaTypes(tree, cache, options);
4416
+ }
4417
+ function csharpMemberNodes(body2) {
4418
+ if (!body2) return [];
4419
+ const out2 = [];
4420
+ const visit = (parent, depth) => {
4421
+ if (depth > 32) return;
4422
+ for (let i2 = 0; i2 < parent.childCount; i2++) {
4423
+ const c = parent.child(i2);
4424
+ if (!c) continue;
4425
+ if (c.type.startsWith("preproc_")) visit(c, depth + 1);
4426
+ else out2.push(c);
4427
+ }
4428
+ };
4429
+ visit(body2, 0);
4430
+ return out2;
4416
4431
  }
4417
4432
  function extractCSharpTypes(tree, cache) {
4418
4433
  const types = [];
@@ -4430,9 +4445,8 @@ function extractCSharpTypes(tree, cache) {
4430
4445
  const methods = [];
4431
4446
  const fields = extractCSharpFields(body2);
4432
4447
  if (body2) {
4433
- for (let i2 = 0; i2 < body2.childCount; i2++) {
4434
- const m = body2.child(i2);
4435
- if (!m || m.type !== "method_declaration") continue;
4448
+ for (const m of csharpMemberNodes(body2)) {
4449
+ if (m.type !== "method_declaration") continue;
4436
4450
  const mName = m.childForFieldName("name");
4437
4451
  const paramList = m.childForFieldName("parameters");
4438
4452
  const parameters = [];
@@ -4483,9 +4497,7 @@ function extractCSharpTypes(tree, cache) {
4483
4497
  function extractCSharpFields(body2) {
4484
4498
  const fields = [];
4485
4499
  if (!body2) return fields;
4486
- for (let i2 = 0; i2 < body2.childCount; i2++) {
4487
- const c = body2.child(i2);
4488
- if (!c) continue;
4500
+ for (const c of csharpMemberNodes(body2)) {
4489
4501
  if (c.type === "field_declaration") {
4490
4502
  let varDecl = null;
4491
4503
  for (let k = 0; k < c.childCount; k++) {
@@ -4541,7 +4553,7 @@ function extractCSharpModifiers(node) {
4541
4553
  }
4542
4554
  return mods;
4543
4555
  }
4544
- function extractJavaTypes(tree, cache) {
4556
+ function extractJavaTypes(tree, cache, options) {
4545
4557
  const types = [];
4546
4558
  const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
4547
4559
  for (const cls of classes) {
@@ -4555,8 +4567,76 @@ function extractJavaTypes(tree, cache) {
4555
4567
  for (const enumDecl of enums) {
4556
4568
  types.push(extractEnumInfo(enumDecl));
4557
4569
  }
4570
+ if (!options?.navigationTypes) return types;
4571
+ for (const rec of getNodesFromCache(tree.rootNode, "record_declaration", cache)) {
4572
+ const info2 = extractClassInfo(rec);
4573
+ info2.is_record = true;
4574
+ for (const comp of recordComponents(rec)) {
4575
+ if (!info2.fields.some((f) => f.name === comp.name)) {
4576
+ info2.fields.push({
4577
+ name: comp.name,
4578
+ type: comp.type,
4579
+ modifiers: ["private", "final"],
4580
+ annotations: []
4581
+ });
4582
+ }
4583
+ if (!info2.methods.some((m) => m.name === comp.name)) {
4584
+ info2.methods.push({
4585
+ name: comp.name,
4586
+ return_type: comp.type,
4587
+ parameters: [],
4588
+ annotations: [],
4589
+ modifiers: ["public"],
4590
+ start_line: rec.startPosition.row + 1,
4591
+ end_line: rec.startPosition.row + 1
4592
+ });
4593
+ }
4594
+ }
4595
+ types.push(info2);
4596
+ }
4597
+ const declNodes = [
4598
+ ...classes,
4599
+ ...interfaces,
4600
+ ...enums,
4601
+ ...getNodesFromCache(tree.rootNode, "record_declaration", cache)
4602
+ ];
4603
+ for (const t of types) {
4604
+ const node = declNodes.find(
4605
+ (n) => n.startPosition.row + 1 === t.start_line && getIdentifier(n, "name") === t.name
4606
+ );
4607
+ const path = node ? enclosingTypePath(node) : [];
4608
+ if (path.length > 0) t.enclosing_type = path.join(".");
4609
+ }
4558
4610
  return types;
4559
4611
  }
4612
+ function recordComponents(node) {
4613
+ const out2 = [];
4614
+ const params = node.childForFieldName("parameters");
4615
+ if (!params) return out2;
4616
+ for (let i2 = 0; i2 < params.childCount; i2++) {
4617
+ const c = params.child(i2);
4618
+ if (!c || c.type !== "formal_parameter") continue;
4619
+ const name2 = c.childForFieldName("name");
4620
+ const type = c.childForFieldName("type");
4621
+ if (name2) out2.push({ name: getNodeText(name2), type: type ? getNodeText(type) : null });
4622
+ }
4623
+ return out2;
4624
+ }
4625
+ function enclosingTypePath(node) {
4626
+ const DECLS = /* @__PURE__ */ new Set([
4627
+ "class_declaration",
4628
+ "interface_declaration",
4629
+ "enum_declaration",
4630
+ "record_declaration"
4631
+ ]);
4632
+ const path = [];
4633
+ for (let p = node.parent; p; p = p.parent) {
4634
+ if (!DECLS.has(p.type)) continue;
4635
+ const n = getIdentifier(p, "name");
4636
+ if (n) path.unshift(n);
4637
+ }
4638
+ return path;
4639
+ }
4560
4640
  function extractJavaScriptTypes(tree, cache) {
4561
4641
  const types = [];
4562
4642
  const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
@@ -6019,6 +6099,13 @@ function extractCalls(tree, cache, language) {
6019
6099
  function buildCSharpReceiverTypeMap(tree, cache) {
6020
6100
  const map = /* @__PURE__ */ new Map();
6021
6101
  const simple = (t) => t.replace(/<[^>]*>/g, "").split(".").pop()?.trim() ?? t;
6102
+ for (const prm of getNodesFromCache(tree.rootNode, "parameter", cache)) {
6103
+ const nameNode = prm.childForFieldName("name");
6104
+ const typeNode = prm.childForFieldName("type");
6105
+ if (nameNode?.type !== "identifier" || !typeNode) continue;
6106
+ const t = getNodeText(typeNode);
6107
+ if (t && t !== "var") map.set(getNodeText(nameNode), simple(t));
6108
+ }
6022
6109
  for (const vd of getNodesFromCache(tree.rootNode, "variable_declaration", cache)) {
6023
6110
  const typeNode = vd.childForFieldName("type");
6024
6111
  const declaredType = typeNode ? getNodeText(typeNode) : null;
@@ -6048,6 +6135,7 @@ function findFirstDescendant(node, type) {
6048
6135
  }
6049
6136
  return null;
6050
6137
  }
6138
+ var CSHARP_RESPONSE_TYPES = /* @__PURE__ */ new Set(["HttpResponse", "HttpResponseBase"]);
6051
6139
  var CSHARP_COMMAND_EXECUTE_METHODS = /* @__PURE__ */ new Set([
6052
6140
  "ExecuteScalar",
6053
6141
  "ExecuteReader",
@@ -6200,7 +6288,8 @@ function extractCSharpCalls(tree, cache) {
6200
6288
  if (recvType !== "DirectorySearcher") continue;
6201
6289
  } else if (propName === "StatusDescription") {
6202
6290
  const recv = exprNode ? getNodeText(exprNode) : "";
6203
- if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv)) continue;
6291
+ const recvType = recv ? typeMap.get(recv) : void 0;
6292
+ if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv) && !(recvType && CSHARP_RESPONSE_TYPES.has(recvType))) continue;
6204
6293
  } else if (propName === "BaseAddress") {
6205
6294
  const recv = exprNode ? getNodeText(exprNode) : null;
6206
6295
  const recvType = recv ? typeMap.get(recv) : void 0;
@@ -11073,6 +11162,23 @@ var DEFAULT_SOURCES = [
11073
11162
  { method: "next", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11074
11163
  { method: "nextInt", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
11075
11164
  // Database result sources
11165
+ // C# I/O, network and database reads (cognium-dev#502). Mirrors the Java
11166
+ // rows above one-for-one, including the severity split: console/file I/O is
11167
+ // `io_input` at high, a database row read is `db_input` at medium. There is
11168
+ // no separate confidence tier — `type` + `severity` IS the tier, exactly as
11169
+ // for `BufferedReader.readLine` vs `ResultSet.getString`.
11170
+ { method: "ReadLine", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11171
+ { method: "ReadLineAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11172
+ { method: "ReadToEnd", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11173
+ { method: "ReadToEndAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11174
+ // Socket and HTTP-client reads. `GetStream` / `AcceptTcpClient` /
11175
+ // `OpenRead` return the channel; the read off it is covered by the
11176
+ // StreamReader rows above, and seeding the channel too lets a flow form when
11177
+ // the read is inlined.
11178
+ { method: "GetStream", class: "TcpClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11179
+ { method: "AcceptTcpClient", class: "TcpListener", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11180
+ { method: "OpenRead", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11181
+ { method: "DownloadString", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
11076
11182
  { method: "getString", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
11077
11183
  { method: "getObject", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
11078
11184
  { method: "getInt", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
@@ -13313,6 +13419,29 @@ var DEFAULT_SINKS = [
13313
13419
  { method: "deserialize", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
13314
13420
  { method: "from_str", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13315
13421
  { method: "from_str", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13422
+ // cognium-dev#541 — the crates the removed classless rows were silently
13423
+ // covering. #484 replaced a classless `from_str` / `from_slice` with
13424
+ // class-scoped rows, but only for the four formats that already had entries;
13425
+ // every other serde data format lost its sink, and `rmp_serde::from_slice`
13426
+ // on bytes read straight off a `TcpStream` stopped being reported. Each row
13427
+ // below is a serde *data format* entry point — a function whose whole job is
13428
+ // to build a typed value out of untrusted bytes or text.
13429
+ { method: "from_slice", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13430
+ { method: "from_read", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13431
+ { method: "from_read_ref", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13432
+ { method: "from_slice", class: "serde_yaml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13433
+ { method: "from_slice", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13434
+ { method: "from_reader", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13435
+ { method: "from_bytes", class: "postcard", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13436
+ { method: "from_slice", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13437
+ { method: "from_reader", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13438
+ { method: "from_reader", class: "ciborium", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13439
+ { method: "from_str", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13440
+ { method: "from_reader", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13441
+ { method: "from_str", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13442
+ { method: "from_reader", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
13443
+ { method: "from_slice", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
13444
+ { method: "deserialize_from", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
13316
13445
  // #294/#484: no classless `from_str` / `from_slice` deserialization sink.
13317
13446
  // Those rows matched EVERY `T::from_str` / `from_slice` — `u32::from_str`,
13318
13447
  // `Url::from_str`, `PublicKey::from_slice` — i.e. ordinary `FromStr` / byte
@@ -13637,8 +13766,12 @@ var DEFAULT_SINKS = [
13637
13766
  { method: "OpenRead", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13638
13767
  { method: "OpenWrite", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13639
13768
  { method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13640
- { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13641
- { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13769
+ // #502: the `(Stream)` overload takes no path — `new StreamReader(tcp.GetStream())`
13770
+ // is not CWE-22. The `(string path)` overload still matches.
13771
+ { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
13772
+ // #502: the `(Stream)` overload takes no path — `new StreamWriter(tcp.GetStream())`
13773
+ // is not CWE-22. The `(string path)` overload still matches.
13774
+ { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
13642
13775
  // Remaining System.IO.File path APIs — class-scoped so they don't collide
13643
13776
  // with unrelated methods. Copy/Move take a source AND destination path (both
13644
13777
  // attacker-controllable → read-anywhere / write-anywhere), hence [0, 1].
@@ -13682,6 +13815,23 @@ var DEFAULT_SINKS = [
13682
13815
  // `new RestClient(userUrl)` points every later request at attacker-controlled
13683
13816
  // infrastructure. Ctor arg 0, taint-gated.
13684
13817
  { method: "RestClient", class: "constructor", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
13818
+ // C# code injection — CodeDOM runtime compilation (CWE-94), cognium-dev#503
13819
+ // part 2. `provider.CompileAssemblyFromSource(parameters, source)` compiles
13820
+ // and can then execute attacker-supplied C#, so the source argument is the
13821
+ // injection point. The signature is
13822
+ // `CompileAssemblyFromSource(CompilerParameters, params string[] sources)`,
13823
+ // so the sources begin at position 1 — position 0 is the options object.
13824
+ //
13825
+ // These rows are only useful together with the #530 StringBuilder
13826
+ // propagation fix: the Juliet CWE-94 corpus accumulates the compiled source
13827
+ // through `sb.Append(...)` and reads it back with `ToString()`, so before
13828
+ // that fix the sink registered but no taint ever reached it.
13829
+ { method: "CompileAssemblyFromSource", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
13830
+ { method: "CompileAssemblyFromSourceBatch", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
13831
+ // Roslyn's equivalent pair. `ParseText` turns text into a syntax tree and
13832
+ // `Create` compiles it; either is the point where attacker text becomes code.
13833
+ { method: "ParseText", class: "CSharpSyntaxTree", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13834
+ { method: "ParseText", class: "SyntaxFactory", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13685
13835
  // C# code injection — dynamic script/assembly loading (CWE-94).
13686
13836
  { method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13687
13837
  { method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
@@ -15463,6 +15613,20 @@ function escapeRe(s) {
15463
15613
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
15464
15614
  }
15465
15615
  var TYPE_TOKEN_RE = /^new\s+(?:TypeReference|TypeToken)\s*<[\s\S]*>\s*\(\s*\)\s*\{\s*\}$/;
15616
+ function argIsStreamExpression(call, position) {
15617
+ const arg = call.arguments.find((a) => a.position === position);
15618
+ if (!arg) return false;
15619
+ const expr = (arg.expression ?? "").trim();
15620
+ if (!expr) return false;
15621
+ return (
15622
+ // `new MemoryStream(...)`, `new FileStream(...)`, `new GZipStream(...)`
15623
+ /^new\s+[A-Za-z_][\w.]*Stream\s*\(/.test(expr) || // `tcp.GetStream()`, `resp.GetResponseStream()`, `file.OpenReadStream()`
15624
+ /\.\s*Get(?:Response)?Stream\s*\(/.test(expr) || /\.\s*OpenReadStream\s*\(/.test(expr) || // `wc.OpenRead(url)` returns a Stream (WebClient), as does `File.OpenRead`
15625
+ /\.\s*OpenRead\s*\(/.test(expr) || // `Request.Body`, `x.BaseStream`
15626
+ /\.\s*(?:Body|BaseStream)\s*$/.test(expr) || // a local whose name is stream-ish and is not a path string
15627
+ /^(?:[A-Za-z_]\w*\.)*[A-Za-z_]*[sS]tream\d*$/.test(expr)
15628
+ );
15629
+ }
15466
15630
  function argIsStringLiteral(call, position) {
15467
15631
  const arg = call.arguments.find((a) => a.position === position);
15468
15632
  if (!arg) return false;
@@ -15700,6 +15864,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
15700
15864
  if (pattern.safe_if_string_literal_at !== void 0 && argIsStringLiteral(call, pattern.safe_if_string_literal_at)) {
15701
15865
  continue;
15702
15866
  }
15867
+ if (pattern.safe_if_stream_arg_at !== void 0 && argIsStreamExpression(call, pattern.safe_if_stream_arg_at)) {
15868
+ continue;
15869
+ }
15703
15870
  if (pattern.type === "command_injection") {
15704
15871
  if (call.is_constructor) {
15705
15872
  if (!CWE_78_RECEIVER_ALLOWLIST.has(call.method_name)) {
@@ -118,6 +118,20 @@ export function extractCalls(tree, cache, language) {
118
118
  function buildCSharpReceiverTypeMap(tree, cache) {
119
119
  const map = new Map();
120
120
  const simple = (t) => t.replace(/<[^>]*>/g, '').split('.').pop()?.trim() ?? t;
121
+ // cognium-dev#542 — method/constructor parameters. A receiver is just as often a parameter as a
122
+ // local — `void Bad(HttpRequest req, HttpResponse resp)` is the canonical
123
+ // ASP.NET handler shape — and omitting them meant every receiver gate keyed on
124
+ // a declared type silently failed for parameters. Locals below win on a name
125
+ // clash, since an inner declaration shadows the parameter.
126
+ for (const prm of getNodesFromCache(tree.rootNode, 'parameter', cache)) {
127
+ const nameNode = prm.childForFieldName('name');
128
+ const typeNode = prm.childForFieldName('type');
129
+ if (nameNode?.type !== 'identifier' || !typeNode)
130
+ continue;
131
+ const t = getNodeText(typeNode);
132
+ if (t && t !== 'var')
133
+ map.set(getNodeText(nameNode), simple(t));
134
+ }
121
135
  for (const vd of getNodesFromCache(tree.rootNode, 'variable_declaration', cache)) {
122
136
  const typeNode = vd.childForFieldName('type');
123
137
  const declaredType = typeNode ? getNodeText(typeNode) : null;
@@ -155,6 +169,12 @@ function findFirstDescendant(node, type) {
155
169
  }
156
170
  return null;
157
171
  }
172
+ /**
173
+ * Declared types that make a receiver an HTTP response. `HttpResponse` is
174
+ * classic ASP.NET and ASP.NET Core; `HttpResponseBase` is the testable
175
+ * abstraction MVC hands controllers.
176
+ */
177
+ const CSHARP_RESPONSE_TYPES = new Set(['HttpResponse', 'HttpResponseBase']);
158
178
  /** ADO.NET command-execution methods whose taint rides the receiver object. */
159
179
  const CSHARP_COMMAND_EXECUTE_METHODS = new Set([
160
180
  'ExecuteScalar', 'ExecuteReader', 'ExecuteNonQuery',
@@ -381,7 +401,15 @@ function extractCSharpCalls(tree, cache) {
381
401
  // a Response-shaped name so an unrelated `.StatusDescription =` on some
382
402
  // other type is not surfaced.
383
403
  const recv = exprNode ? getNodeText(exprNode) : '';
384
- if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv))
404
+ const recvType = recv ? typeMap.get(recv) : undefined;
405
+ // cognium-dev#542 — two ways to be a response: named like the ASP.NET
406
+ // intrinsic (`Response`, `Context.Response`), which has no declaration to
407
+ // resolve, or *declared* as one. Juliet names the parameter `resp`, so the
408
+ // name test alone scored CWE-81 at 0/9 — the same defect #501 fixed on the
409
+ // source side, where `HttpRequest req` was missed because it was not
410
+ // spelled `Request`.
411
+ if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv)
412
+ && !(recvType && CSHARP_RESPONSE_TYPES.has(recvType)))
385
413
  continue;
386
414
  }
387
415
  else if (propName === 'BaseAddress') {