circle-ir 4.7.1 → 4.7.2

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.
@@ -12258,7 +12258,11 @@ var DEFAULT_SINKS = [
12258
12258
  // File: covers both File(String pathname) and File(parent, child). The 2-arg
12259
12259
  // overload's child argument carries CVE-2018-8041 (Camel mail Content-Disposition
12260
12260
  // filename written to disk).
12261
- { method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
12261
+ // Java `new File(path)` idiom. Excluded for C#: there is no `new File()` in
12262
+ // .NET (it's `System.IO.File` statics + `FileStream`, covered separately), and
12263
+ // this over-matched ASP.NET `ControllerBase.File(stream|bytes, contentType)` —
12264
+ // a result helper that returns a file, not a path sink (cognium-ai#326 B).
12265
+ { method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], exclude_languages: ["csharp"] },
12262
12266
  { method: "FileInputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
12263
12267
  { method: "FileOutputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
12264
12268
  { method: "FileReader", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
@@ -13813,6 +13817,22 @@ var DEFAULT_SINKS = [
13813
13817
  { method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13814
13818
  { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13815
13819
  { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13820
+ // Remaining System.IO.File path APIs — class-scoped so they don't collide
13821
+ // with unrelated methods. Copy/Move take a source AND destination path (both
13822
+ // attacker-controllable → read-anywhere / write-anywhere), hence [0, 1].
13823
+ { method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
13824
+ { method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
13825
+ { method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13826
+ { method: "Open", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13827
+ { method: "OpenText", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13828
+ { method: "Create", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13829
+ { method: "WriteAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13830
+ { method: "AppendAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13831
+ // System.IO.Directory path APIs.
13832
+ { method: "CreateDirectory", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13833
+ { method: "Delete", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13834
+ { method: "GetFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13835
+ { method: "EnumerateFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13816
13836
  // C# SSRF — HttpClient / WebClient / WebRequest (CWE-918).
13817
13837
  { method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
13818
13838
  { method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13829,9 +13849,24 @@ var DEFAULT_SINKS = [
13829
13849
  // C# code injection — dynamic script/assembly loading (CWE-94).
13830
13850
  { method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13831
13851
  { method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13832
- // C# insecure deserialization BinaryFormatter et al. (CWE-502).
13852
+ // Loading an assembly from an attacker-controlled name/path is arbitrary code
13853
+ // execution. Class-scoped to `Assembly` (low FP — a tainted arg here is rare
13854
+ // in benign code). `Type.GetType`/`Activator.CreateInstance` are intentionally
13855
+ // NOT added — reflection with config-driven type names is common in DI, an FP
13856
+ // trap without the reflection-suppress machinery.
13857
+ { method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13858
+ { method: "LoadFrom", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13859
+ { method: "LoadFile", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13860
+ // C# insecure deserialization — the polymorphic BCL formatters that can
13861
+ // instantiate arbitrary types named in the payload (CWE-502, cognium-ai#318).
13862
+ // Each of these classes exists only to deserialize and is unsafe on untrusted
13863
+ // input; class-scoped, so no collision with safe JSON APIs (System.Text.Json /
13864
+ // Newtonsoft-default, which are intentionally NOT sinks).
13833
13865
  { method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13834
13866
  { method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13867
+ { method: "Deserialize", class: "SoapFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13868
+ { method: "Deserialize", class: "LosFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13869
+ { method: "Deserialize", class: "ObjectStateFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13835
13870
  // C# XSS — raw HTML output (CWE-79).
13836
13871
  { method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
13837
13872
  { method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13856,6 +13891,20 @@ var DEFAULT_SINKS = [
13856
13891
  // cognium-dev#273/#275). `BsonDocument.Parse(userJson)` deserializes an
13857
13892
  // attacker-controlled query document. Class-scoped (static receiver resolves).
13858
13893
  { method: "Parse", class: "BsonDocument", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["csharp"] },
13894
+ // C# log injection — Microsoft.Extensions.Logging `ILogger` (CWE-117,
13895
+ // cognium-ai#318). Only the message TEMPLATE (arg[0]) is the injectable
13896
+ // position; the structured form `LogInformation("… {Id}", id)` keeps taint in
13897
+ // later args (the logger renders them as data), so arg_positions:[0] does not
13898
+ // flag it. Mirrors the Java `Logger` sinks; severity low.
13899
+ { method: "LogInformation", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13900
+ { method: "LogWarning", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13901
+ { method: "LogError", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13902
+ { method: "LogDebug", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13903
+ { method: "LogCritical", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13904
+ { method: "LogTrace", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13905
+ // Generic `ILogger.Log(logLevel, message, …)` — class-scoped (`Log` alone is
13906
+ // too generic); the message is arg[1].
13907
+ { method: "Log", class: "ILogger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1], languages: ["csharp"] },
13859
13908
  // C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
13860
13909
  { method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
13861
13910
  { method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13864,6 +13913,11 @@ var DEFAULT_SINKS = [
13864
13913
  { method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13865
13914
  { method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13866
13915
  { method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13916
+ // `new XmlTextReader(input)` — its DtdProcessing defaults to Parse (resolves
13917
+ // external entities), unlike `XmlReader.Create` (Prohibit). CWE-611,
13918
+ // cognium-ai#318. The 4.7.0 XmlResolver=null / DtdProcessing=Prohibit
13919
+ // hardening still suppresses the safe shape.
13920
+ { method: "XmlTextReader", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13867
13921
  // Python SQLi — asyncpg Connection.*
13868
13922
  { method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
13869
13923
  { method: "fetch", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
@@ -15276,6 +15330,34 @@ function isSafeJSChildProcessCall(call, pattern, language) {
15276
15330
  if (SHELL_PROGRAMS.has(program)) return false;
15277
15331
  return true;
15278
15332
  }
15333
+ function isSafeCSharpProcessStartCall(call, pattern, language) {
15334
+ if (language !== "csharp") return false;
15335
+ if (pattern.type !== "command_injection") return false;
15336
+ if (call.method_name !== "Start") return false;
15337
+ if (call.arguments.length < 2) return false;
15338
+ const fileArg = call.arguments.find((a) => a.position === 0);
15339
+ if (!fileArg) return false;
15340
+ let raw;
15341
+ if (fileArg.literal !== null && fileArg.literal !== void 0) {
15342
+ raw = String(fileArg.literal).trim();
15343
+ } else {
15344
+ raw = (fileArg.expression ?? "").trim();
15345
+ }
15346
+ if (!/^@?"[^"]*"$/.test(raw)) return false;
15347
+ const program = (raw.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
15348
+ const SHELL_PROGRAMS = /* @__PURE__ */ new Set([
15349
+ "sh",
15350
+ "bash",
15351
+ "zsh",
15352
+ "dash",
15353
+ "ash",
15354
+ "ksh",
15355
+ "cmd",
15356
+ "powershell",
15357
+ "pwsh"
15358
+ ]);
15359
+ return !SHELL_PROGRAMS.has(program);
15360
+ }
15279
15361
  function isSafeRustCommandCall(call, pattern, language) {
15280
15362
  if (language !== "rust") return false;
15281
15363
  if (pattern.type !== "command_injection") return false;
@@ -15531,6 +15613,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
15531
15613
  if (isSafeJSChildProcessCall(call, pattern, language)) {
15532
15614
  continue;
15533
15615
  }
15616
+ if (isSafeCSharpProcessStartCall(call, pattern, language)) {
15617
+ continue;
15618
+ }
15534
15619
  if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at, types)) {
15535
15620
  continue;
15536
15621
  }
@@ -16761,8 +16846,8 @@ function canSourceReachSink(sourceType, sinkType) {
16761
16846
  network_input: ["sql_injection", "command_injection", "xss", "ssrf", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
16762
16847
  config_param: ["sql_injection", "command_injection", "path_traversal", "xss", "ssrf", "log_injection", "format_string"],
16763
16848
  // Servlet init params
16764
- interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
16765
- // Cross-method taint; Sprint 82 (#189) — open_redirect added; Sprint 91 (#117) — trust_boundary added; cognium-ai#129 — log_injection/format_string/nosql_injection added; cognium-ai#281 — prompt_injection added
16849
+ interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection", "xxe", "deserialization"],
16850
+ // Cross-method taint; Sprint 82 (#189) — open_redirect added; Sprint 91 (#117) — trust_boundary added; cognium-ai#129 — log_injection/format_string/nosql_injection added; cognium-ai#281 — prompt_injection added; cognium-ai#317 — xxe/deserialization added (C# xxe/deser sinks are reached via param-seeded sources and were silently dropped, 0% finding conversion)
16766
16851
  plugin_param: ["sql_injection", "command_injection", "path_traversal", "xss", "code_injection", "log_injection", "format_string"]
16767
16852
  // Plugin/config parameters
16768
16853
  };
@@ -27764,8 +27849,9 @@ function buildJavaTaintedVars(sourceCode, seedVars) {
27764
27849
  if (JAVA_KEYWORDS.has(lhs)) continue;
27765
27850
  if (knownTainted.has(lhs)) continue;
27766
27851
  const escaped = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
27852
+ const rhsCode = rhs.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ").replace(/'(?:[^'\\]|\\.)'/g, " ");
27767
27853
  const ref = [...knownTainted].some(
27768
- (v) => new RegExp(`(?<![\\p{L}\\p{N}_])${escaped(v)}(?![\\p{L}\\p{N}_])`, "u").test(rhs)
27854
+ (v) => new RegExp(`(?<![\\p{L}\\p{N}_])${escaped(v)}(?![\\p{L}\\p{N}_])`, "u").test(rhsCode)
27769
27855
  );
27770
27856
  if (ref) {
27771
27857
  derived.set(lhs, i2 + 1);
@@ -40790,6 +40876,8 @@ function isLikelyCredentialAssignment(line) {
40790
40876
  if (CRED_DYNAMIC_VALUE_RE.test(value)) return null;
40791
40877
  if (value.length < 3) return null;
40792
40878
  if (isAllSameChar(value)) return null;
40879
+ if (value.toLowerCase() === name2.toLowerCase()) return null;
40880
+ if (/^(?:https?:\/\/|urn:|xmlns)/i.test(value)) return null;
40793
40881
  if (value.length < 12) return null;
40794
40882
  if (shannonEntropy(value) < 3.5) return null;
40795
40883
  if (charClassDiversity(value) < 2) return null;
@@ -11653,7 +11653,11 @@ var DEFAULT_SINKS = [
11653
11653
  // File: covers both File(String pathname) and File(parent, child). The 2-arg
11654
11654
  // overload's child argument carries CVE-2018-8041 (Camel mail Content-Disposition
11655
11655
  // filename written to disk).
11656
- { method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
11656
+ // Java `new File(path)` idiom. Excluded for C#: there is no `new File()` in
11657
+ // .NET (it's `System.IO.File` statics + `FileStream`, covered separately), and
11658
+ // this over-matched ASP.NET `ControllerBase.File(stream|bytes, contentType)` —
11659
+ // a result helper that returns a file, not a path sink (cognium-ai#326 B).
11660
+ { method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], exclude_languages: ["csharp"] },
11657
11661
  { method: "FileInputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11658
11662
  { method: "FileOutputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11659
11663
  { method: "FileReader", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
@@ -13208,6 +13212,22 @@ var DEFAULT_SINKS = [
13208
13212
  { method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13209
13213
  { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13210
13214
  { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13215
+ // Remaining System.IO.File path APIs — class-scoped so they don't collide
13216
+ // with unrelated methods. Copy/Move take a source AND destination path (both
13217
+ // attacker-controllable → read-anywhere / write-anywhere), hence [0, 1].
13218
+ { method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
13219
+ { method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
13220
+ { method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13221
+ { method: "Open", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13222
+ { method: "OpenText", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13223
+ { method: "Create", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13224
+ { method: "WriteAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13225
+ { method: "AppendAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13226
+ // System.IO.Directory path APIs.
13227
+ { method: "CreateDirectory", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13228
+ { method: "Delete", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13229
+ { method: "GetFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13230
+ { method: "EnumerateFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13211
13231
  // C# SSRF — HttpClient / WebClient / WebRequest (CWE-918).
13212
13232
  { method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
13213
13233
  { method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13224,9 +13244,24 @@ var DEFAULT_SINKS = [
13224
13244
  // C# code injection — dynamic script/assembly loading (CWE-94).
13225
13245
  { method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13226
13246
  { method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13227
- // C# insecure deserialization BinaryFormatter et al. (CWE-502).
13247
+ // Loading an assembly from an attacker-controlled name/path is arbitrary code
13248
+ // execution. Class-scoped to `Assembly` (low FP — a tainted arg here is rare
13249
+ // in benign code). `Type.GetType`/`Activator.CreateInstance` are intentionally
13250
+ // NOT added — reflection with config-driven type names is common in DI, an FP
13251
+ // trap without the reflection-suppress machinery.
13252
+ { method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13253
+ { method: "LoadFrom", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13254
+ { method: "LoadFile", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13255
+ // C# insecure deserialization — the polymorphic BCL formatters that can
13256
+ // instantiate arbitrary types named in the payload (CWE-502, cognium-ai#318).
13257
+ // Each of these classes exists only to deserialize and is unsafe on untrusted
13258
+ // input; class-scoped, so no collision with safe JSON APIs (System.Text.Json /
13259
+ // Newtonsoft-default, which are intentionally NOT sinks).
13228
13260
  { method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13229
13261
  { method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13262
+ { method: "Deserialize", class: "SoapFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13263
+ { method: "Deserialize", class: "LosFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13264
+ { method: "Deserialize", class: "ObjectStateFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13230
13265
  // C# XSS — raw HTML output (CWE-79).
13231
13266
  { method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
13232
13267
  { method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13251,6 +13286,20 @@ var DEFAULT_SINKS = [
13251
13286
  // cognium-dev#273/#275). `BsonDocument.Parse(userJson)` deserializes an
13252
13287
  // attacker-controlled query document. Class-scoped (static receiver resolves).
13253
13288
  { method: "Parse", class: "BsonDocument", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["csharp"] },
13289
+ // C# log injection — Microsoft.Extensions.Logging `ILogger` (CWE-117,
13290
+ // cognium-ai#318). Only the message TEMPLATE (arg[0]) is the injectable
13291
+ // position; the structured form `LogInformation("… {Id}", id)` keeps taint in
13292
+ // later args (the logger renders them as data), so arg_positions:[0] does not
13293
+ // flag it. Mirrors the Java `Logger` sinks; severity low.
13294
+ { method: "LogInformation", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13295
+ { method: "LogWarning", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13296
+ { method: "LogError", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13297
+ { method: "LogDebug", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13298
+ { method: "LogCritical", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13299
+ { method: "LogTrace", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13300
+ // Generic `ILogger.Log(logLevel, message, …)` — class-scoped (`Log` alone is
13301
+ // too generic); the message is arg[1].
13302
+ { method: "Log", class: "ILogger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1], languages: ["csharp"] },
13254
13303
  // C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
13255
13304
  { method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
13256
13305
  { method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13259,6 +13308,11 @@ var DEFAULT_SINKS = [
13259
13308
  { method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13260
13309
  { method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13261
13310
  { method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13311
+ // `new XmlTextReader(input)` — its DtdProcessing defaults to Parse (resolves
13312
+ // external entities), unlike `XmlReader.Create` (Prohibit). CWE-611,
13313
+ // cognium-ai#318. The 4.7.0 XmlResolver=null / DtdProcessing=Prohibit
13314
+ // hardening still suppresses the safe shape.
13315
+ { method: "XmlTextReader", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13262
13316
  // Python SQLi — asyncpg Connection.*
13263
13317
  { method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
13264
13318
  { method: "fetch", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
@@ -14576,6 +14630,34 @@ function isSafeJSChildProcessCall(call, pattern, language) {
14576
14630
  if (SHELL_PROGRAMS.has(program)) return false;
14577
14631
  return true;
14578
14632
  }
14633
+ function isSafeCSharpProcessStartCall(call, pattern, language) {
14634
+ if (language !== "csharp") return false;
14635
+ if (pattern.type !== "command_injection") return false;
14636
+ if (call.method_name !== "Start") return false;
14637
+ if (call.arguments.length < 2) return false;
14638
+ const fileArg = call.arguments.find((a) => a.position === 0);
14639
+ if (!fileArg) return false;
14640
+ let raw;
14641
+ if (fileArg.literal !== null && fileArg.literal !== void 0) {
14642
+ raw = String(fileArg.literal).trim();
14643
+ } else {
14644
+ raw = (fileArg.expression ?? "").trim();
14645
+ }
14646
+ if (!/^@?"[^"]*"$/.test(raw)) return false;
14647
+ const program = (raw.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
14648
+ const SHELL_PROGRAMS = /* @__PURE__ */ new Set([
14649
+ "sh",
14650
+ "bash",
14651
+ "zsh",
14652
+ "dash",
14653
+ "ash",
14654
+ "ksh",
14655
+ "cmd",
14656
+ "powershell",
14657
+ "pwsh"
14658
+ ]);
14659
+ return !SHELL_PROGRAMS.has(program);
14660
+ }
14579
14661
  function isSafeRustCommandCall(call, pattern, language) {
14580
14662
  if (language !== "rust") return false;
14581
14663
  if (pattern.type !== "command_injection") return false;
@@ -14831,6 +14913,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
14831
14913
  if (isSafeJSChildProcessCall(call, pattern, language)) {
14832
14914
  continue;
14833
14915
  }
14916
+ if (isSafeCSharpProcessStartCall(call, pattern, language)) {
14917
+ continue;
14918
+ }
14834
14919
  if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at, types)) {
14835
14920
  continue;
14836
14921
  }
@@ -11587,7 +11587,11 @@ var DEFAULT_SINKS = [
11587
11587
  // File: covers both File(String pathname) and File(parent, child). The 2-arg
11588
11588
  // overload's child argument carries CVE-2018-8041 (Camel mail Content-Disposition
11589
11589
  // filename written to disk).
11590
- { method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1] },
11590
+ // Java `new File(path)` idiom. Excluded for C#: there is no `new File()` in
11591
+ // .NET (it's `System.IO.File` statics + `FileStream`, covered separately), and
11592
+ // this over-matched ASP.NET `ControllerBase.File(stream|bytes, contentType)` —
11593
+ // a result helper that returns a file, not a path sink (cognium-ai#326 B).
11594
+ { method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], exclude_languages: ["csharp"] },
11591
11595
  { method: "FileInputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11592
11596
  { method: "FileOutputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
11593
11597
  { method: "FileReader", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
@@ -13142,6 +13146,22 @@ var DEFAULT_SINKS = [
13142
13146
  { method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13143
13147
  { method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13144
13148
  { method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13149
+ // Remaining System.IO.File path APIs — class-scoped so they don't collide
13150
+ // with unrelated methods. Copy/Move take a source AND destination path (both
13151
+ // attacker-controllable → read-anywhere / write-anywhere), hence [0, 1].
13152
+ { method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
13153
+ { method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
13154
+ { method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13155
+ { method: "Open", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13156
+ { method: "OpenText", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13157
+ { method: "Create", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13158
+ { method: "WriteAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13159
+ { method: "AppendAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13160
+ // System.IO.Directory path APIs.
13161
+ { method: "CreateDirectory", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13162
+ { method: "Delete", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13163
+ { method: "GetFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13164
+ { method: "EnumerateFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
13145
13165
  // C# SSRF — HttpClient / WebClient / WebRequest (CWE-918).
13146
13166
  { method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
13147
13167
  { method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13158,9 +13178,24 @@ var DEFAULT_SINKS = [
13158
13178
  // C# code injection — dynamic script/assembly loading (CWE-94).
13159
13179
  { method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13160
13180
  { method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13161
- // C# insecure deserialization BinaryFormatter et al. (CWE-502).
13181
+ // Loading an assembly from an attacker-controlled name/path is arbitrary code
13182
+ // execution. Class-scoped to `Assembly` (low FP — a tainted arg here is rare
13183
+ // in benign code). `Type.GetType`/`Activator.CreateInstance` are intentionally
13184
+ // NOT added — reflection with config-driven type names is common in DI, an FP
13185
+ // trap without the reflection-suppress machinery.
13186
+ { method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13187
+ { method: "LoadFrom", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13188
+ { method: "LoadFile", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13189
+ // C# insecure deserialization — the polymorphic BCL formatters that can
13190
+ // instantiate arbitrary types named in the payload (CWE-502, cognium-ai#318).
13191
+ // Each of these classes exists only to deserialize and is unsafe on untrusted
13192
+ // input; class-scoped, so no collision with safe JSON APIs (System.Text.Json /
13193
+ // Newtonsoft-default, which are intentionally NOT sinks).
13162
13194
  { method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13163
13195
  { method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13196
+ { method: "Deserialize", class: "SoapFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13197
+ { method: "Deserialize", class: "LosFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13198
+ { method: "Deserialize", class: "ObjectStateFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
13164
13199
  // C# XSS — raw HTML output (CWE-79).
13165
13200
  { method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
13166
13201
  { method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13185,6 +13220,20 @@ var DEFAULT_SINKS = [
13185
13220
  // cognium-dev#273/#275). `BsonDocument.Parse(userJson)` deserializes an
13186
13221
  // attacker-controlled query document. Class-scoped (static receiver resolves).
13187
13222
  { method: "Parse", class: "BsonDocument", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["csharp"] },
13223
+ // C# log injection — Microsoft.Extensions.Logging `ILogger` (CWE-117,
13224
+ // cognium-ai#318). Only the message TEMPLATE (arg[0]) is the injectable
13225
+ // position; the structured form `LogInformation("… {Id}", id)` keeps taint in
13226
+ // later args (the logger renders them as data), so arg_positions:[0] does not
13227
+ // flag it. Mirrors the Java `Logger` sinks; severity low.
13228
+ { method: "LogInformation", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13229
+ { method: "LogWarning", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13230
+ { method: "LogError", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13231
+ { method: "LogDebug", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13232
+ { method: "LogCritical", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13233
+ { method: "LogTrace", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
13234
+ // Generic `ILogger.Log(logLevel, message, …)` — class-scoped (`Log` alone is
13235
+ // too generic); the message is arg[1].
13236
+ { method: "Log", class: "ILogger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1], languages: ["csharp"] },
13188
13237
  // C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
13189
13238
  { method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
13190
13239
  { method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
@@ -13193,6 +13242,11 @@ var DEFAULT_SINKS = [
13193
13242
  { method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13194
13243
  { method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13195
13244
  { method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13245
+ // `new XmlTextReader(input)` — its DtdProcessing defaults to Parse (resolves
13246
+ // external entities), unlike `XmlReader.Create` (Prohibit). CWE-611,
13247
+ // cognium-ai#318. The 4.7.0 XmlResolver=null / DtdProcessing=Prohibit
13248
+ // hardening still suppresses the safe shape.
13249
+ { method: "XmlTextReader", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
13196
13250
  // Python SQLi — asyncpg Connection.*
13197
13251
  { method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
13198
13252
  { method: "fetch", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
@@ -14510,6 +14564,34 @@ function isSafeJSChildProcessCall(call, pattern, language) {
14510
14564
  if (SHELL_PROGRAMS.has(program)) return false;
14511
14565
  return true;
14512
14566
  }
14567
+ function isSafeCSharpProcessStartCall(call, pattern, language) {
14568
+ if (language !== "csharp") return false;
14569
+ if (pattern.type !== "command_injection") return false;
14570
+ if (call.method_name !== "Start") return false;
14571
+ if (call.arguments.length < 2) return false;
14572
+ const fileArg = call.arguments.find((a) => a.position === 0);
14573
+ if (!fileArg) return false;
14574
+ let raw;
14575
+ if (fileArg.literal !== null && fileArg.literal !== void 0) {
14576
+ raw = String(fileArg.literal).trim();
14577
+ } else {
14578
+ raw = (fileArg.expression ?? "").trim();
14579
+ }
14580
+ if (!/^@?"[^"]*"$/.test(raw)) return false;
14581
+ const program = (raw.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
14582
+ const SHELL_PROGRAMS = /* @__PURE__ */ new Set([
14583
+ "sh",
14584
+ "bash",
14585
+ "zsh",
14586
+ "dash",
14587
+ "ash",
14588
+ "ksh",
14589
+ "cmd",
14590
+ "powershell",
14591
+ "pwsh"
14592
+ ]);
14593
+ return !SHELL_PROGRAMS.has(program);
14594
+ }
14513
14595
  function isSafeRustCommandCall(call, pattern, language) {
14514
14596
  if (language !== "rust") return false;
14515
14597
  if (pattern.type !== "command_injection") return false;
@@ -14765,6 +14847,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
14765
14847
  if (isSafeJSChildProcessCall(call, pattern, language)) {
14766
14848
  continue;
14767
14849
  }
14850
+ if (isSafeCSharpProcessStartCall(call, pattern, language)) {
14851
+ continue;
14852
+ }
14768
14853
  if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at, types)) {
14769
14854
  continue;
14770
14855
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "circle-ir",
3
- "version": "4.7.1",
3
+ "version": "4.7.2",
4
4
  "description": "High-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.js",