circle-ir 3.82.0 → 3.84.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.
@@ -10560,11 +10560,16 @@ var DEFAULT_SINKS = [
10560
10560
  { method: "setCommandline", class: "DefaultExecutor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10561
10561
  { method: "parse", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10562
10562
  { method: "addArgument", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10563
- // Process-related utilities
10564
- { method: "waitFor", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [] },
10565
- { method: "inheritIO", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [] },
10566
- { method: "redirectOutput", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [0] },
10567
- { method: "redirectInput", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [0] },
10563
+ // Process-related utilities — removed in 3.83.0 (#124):
10564
+ // - Process.waitFor() blocks on an already-spawned process; has no args,
10565
+ // no command string flows into it.
10566
+ // - ProcessBuilder.inheritIO() takes no args.
10567
+ // - ProcessBuilder.redirectOutput/redirectInput take a File destination/source,
10568
+ // not a command. If treated as sinks they would be path_traversal, not
10569
+ // command_injection — and even then the threat model is marginal.
10570
+ // The actual command-execution sinks (Runtime.exec, ProcessBuilder.start,
10571
+ // ProcessBuilder.command, ProcessBuilder(constructor)) remain configured
10572
+ // elsewhere in this file / in configs/sinks/command.yaml.
10568
10573
  // Path Traversal (CWE-22)
10569
10574
  // File: covers both File(String pathname) and File(parent, child). The 2-arg
10570
10575
  // overload's child argument carries CVE-2018-8041 (Camel mail Content-Disposition
@@ -10893,7 +10898,9 @@ var DEFAULT_SINKS = [
10893
10898
  { method: "ok", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
10894
10899
  // Code Injection (CWE-94)
10895
10900
  { method: "eval", class: "ScriptEngine", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
10896
- { method: "compile", class: "Pattern", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0] },
10901
+ // Pattern.compile removed in 3.83.0 (#124): regex compilation does not execute
10902
+ // code. The real risk from a tainted regex is ReDoS, covered by the
10903
+ // `Pattern.compile` -> `redos` rule below (line ~1945).
10897
10904
  // Expression Language injection (SpEL, OGNL, MVEL, EL)
10898
10905
  { method: "parseExpression", class: "ExpressionParser", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
10899
10906
  { method: "parseExpression", class: "SpelExpressionParser", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
@@ -11590,7 +11597,8 @@ var DEFAULT_SINKS = [
11590
11597
  // =========================================================================
11591
11598
  // Collection-based command injection (ProcessBuilder with List)
11592
11599
  { method: "command", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11593
- { method: "inheritIO", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [] },
11600
+ // ProcessBuilder.inheritIO removed in 3.83.0 (#124): no args, no command
11601
+ // string flows into it. See note above next to the Process-related cluster.
11594
11602
  // Jenkins DSL patterns
11595
11603
  { method: "step", class: "StepExecution", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11596
11604
  { method: "invokeMethod", class: "Script", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
@@ -12495,6 +12503,55 @@ function isSafeGoExecCommandCall(call, pattern, language) {
12495
12503
  if (SHELL_PROGRAMS.has(program)) return false;
12496
12504
  return true;
12497
12505
  }
12506
+ function isSafeRustCommandCall(call, pattern, language) {
12507
+ if (language !== "rust") return false;
12508
+ if (pattern.type !== "command_injection") return false;
12509
+ if (pattern.class !== void 0 && pattern.class !== "Command") return false;
12510
+ const SHELL_PROGRAMS = /* @__PURE__ */ new Set([
12511
+ "sh",
12512
+ "bash",
12513
+ "zsh",
12514
+ "dash",
12515
+ "ash",
12516
+ "ksh",
12517
+ "cmd",
12518
+ "cmd.exe",
12519
+ "powershell",
12520
+ "pwsh",
12521
+ "powershell.exe",
12522
+ "pwsh.exe"
12523
+ ]);
12524
+ const PROGRAM_RE = /\bCommand\s*::\s*new\s*\(\s*(?:r?"([^"]*)"|'([^']*)')/;
12525
+ const extractProgram = (text) => {
12526
+ const m = PROGRAM_RE.exec(text);
12527
+ if (!m) return null;
12528
+ const lit = m[1] ?? m[2] ?? "";
12529
+ return lit.split("/").pop() ?? lit;
12530
+ };
12531
+ if (pattern.method === "new") {
12532
+ const programArg = call.arguments.find((a) => a.position === 0);
12533
+ if (!programArg) return false;
12534
+ let program;
12535
+ if (programArg.literal !== null && programArg.literal !== void 0) {
12536
+ program = String(programArg.literal).split("/").pop() ?? String(programArg.literal);
12537
+ } else {
12538
+ const expr = (programArg.expression ?? "").trim();
12539
+ if (!(expr.startsWith('"') || expr.startsWith("'"))) {
12540
+ return false;
12541
+ }
12542
+ const stripped = expr.slice(1, -1);
12543
+ program = stripped.split("/").pop() ?? stripped;
12544
+ }
12545
+ return !SHELL_PROGRAMS.has(program);
12546
+ }
12547
+ if (pattern.method === "arg" || pattern.method === "args" || pattern.method === "spawn" || pattern.method === "output") {
12548
+ const receiverText = call.receiver ?? "";
12549
+ const program = extractProgram(receiverText);
12550
+ if (program === null) return false;
12551
+ return !SHELL_PROGRAMS.has(program);
12552
+ }
12553
+ return false;
12554
+ }
12498
12555
  var CLASS_LITERAL_RE = /^(?:[A-Za-z_][\w]*\.)*[A-Z][\w]*(?:\[\])*\.class$/;
12499
12556
  function argIsClassLiteral(call, position) {
12500
12557
  const arg = call.arguments.find((a) => a.position === position);
@@ -12517,6 +12574,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
12517
12574
  if (isSafeGoExecCommandCall(call, pattern, language)) {
12518
12575
  continue;
12519
12576
  }
12577
+ if (isSafeRustCommandCall(call, pattern, language)) {
12578
+ continue;
12579
+ }
12520
12580
  if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
12521
12581
  continue;
12522
12582
  }
@@ -10494,11 +10494,16 @@ var DEFAULT_SINKS = [
10494
10494
  { method: "setCommandline", class: "DefaultExecutor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10495
10495
  { method: "parse", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10496
10496
  { method: "addArgument", class: "CommandLine", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
10497
- // Process-related utilities
10498
- { method: "waitFor", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [] },
10499
- { method: "inheritIO", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [] },
10500
- { method: "redirectOutput", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [0] },
10501
- { method: "redirectInput", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "medium", arg_positions: [0] },
10497
+ // Process-related utilities — removed in 3.83.0 (#124):
10498
+ // - Process.waitFor() blocks on an already-spawned process; has no args,
10499
+ // no command string flows into it.
10500
+ // - ProcessBuilder.inheritIO() takes no args.
10501
+ // - ProcessBuilder.redirectOutput/redirectInput take a File destination/source,
10502
+ // not a command. If treated as sinks they would be path_traversal, not
10503
+ // command_injection — and even then the threat model is marginal.
10504
+ // The actual command-execution sinks (Runtime.exec, ProcessBuilder.start,
10505
+ // ProcessBuilder.command, ProcessBuilder(constructor)) remain configured
10506
+ // elsewhere in this file / in configs/sinks/command.yaml.
10502
10507
  // Path Traversal (CWE-22)
10503
10508
  // File: covers both File(String pathname) and File(parent, child). The 2-arg
10504
10509
  // overload's child argument carries CVE-2018-8041 (Camel mail Content-Disposition
@@ -10827,7 +10832,9 @@ var DEFAULT_SINKS = [
10827
10832
  { method: "ok", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
10828
10833
  // Code Injection (CWE-94)
10829
10834
  { method: "eval", class: "ScriptEngine", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
10830
- { method: "compile", class: "Pattern", type: "code_injection", cwe: "CWE-94", severity: "high", arg_positions: [0] },
10835
+ // Pattern.compile removed in 3.83.0 (#124): regex compilation does not execute
10836
+ // code. The real risk from a tainted regex is ReDoS, covered by the
10837
+ // `Pattern.compile` -> `redos` rule below (line ~1945).
10831
10838
  // Expression Language injection (SpEL, OGNL, MVEL, EL)
10832
10839
  { method: "parseExpression", class: "ExpressionParser", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
10833
10840
  { method: "parseExpression", class: "SpelExpressionParser", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0] },
@@ -11524,7 +11531,8 @@ var DEFAULT_SINKS = [
11524
11531
  // =========================================================================
11525
11532
  // Collection-based command injection (ProcessBuilder with List)
11526
11533
  { method: "command", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11527
- { method: "inheritIO", class: "ProcessBuilder", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [] },
11534
+ // ProcessBuilder.inheritIO removed in 3.83.0 (#124): no args, no command
11535
+ // string flows into it. See note above next to the Process-related cluster.
11528
11536
  // Jenkins DSL patterns
11529
11537
  { method: "step", class: "StepExecution", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
11530
11538
  { method: "invokeMethod", class: "Script", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
@@ -12429,6 +12437,55 @@ function isSafeGoExecCommandCall(call, pattern, language) {
12429
12437
  if (SHELL_PROGRAMS.has(program)) return false;
12430
12438
  return true;
12431
12439
  }
12440
+ function isSafeRustCommandCall(call, pattern, language) {
12441
+ if (language !== "rust") return false;
12442
+ if (pattern.type !== "command_injection") return false;
12443
+ if (pattern.class !== void 0 && pattern.class !== "Command") return false;
12444
+ const SHELL_PROGRAMS = /* @__PURE__ */ new Set([
12445
+ "sh",
12446
+ "bash",
12447
+ "zsh",
12448
+ "dash",
12449
+ "ash",
12450
+ "ksh",
12451
+ "cmd",
12452
+ "cmd.exe",
12453
+ "powershell",
12454
+ "pwsh",
12455
+ "powershell.exe",
12456
+ "pwsh.exe"
12457
+ ]);
12458
+ const PROGRAM_RE = /\bCommand\s*::\s*new\s*\(\s*(?:r?"([^"]*)"|'([^']*)')/;
12459
+ const extractProgram = (text) => {
12460
+ const m = PROGRAM_RE.exec(text);
12461
+ if (!m) return null;
12462
+ const lit = m[1] ?? m[2] ?? "";
12463
+ return lit.split("/").pop() ?? lit;
12464
+ };
12465
+ if (pattern.method === "new") {
12466
+ const programArg = call.arguments.find((a) => a.position === 0);
12467
+ if (!programArg) return false;
12468
+ let program;
12469
+ if (programArg.literal !== null && programArg.literal !== void 0) {
12470
+ program = String(programArg.literal).split("/").pop() ?? String(programArg.literal);
12471
+ } else {
12472
+ const expr = (programArg.expression ?? "").trim();
12473
+ if (!(expr.startsWith('"') || expr.startsWith("'"))) {
12474
+ return false;
12475
+ }
12476
+ const stripped = expr.slice(1, -1);
12477
+ program = stripped.split("/").pop() ?? stripped;
12478
+ }
12479
+ return !SHELL_PROGRAMS.has(program);
12480
+ }
12481
+ if (pattern.method === "arg" || pattern.method === "args" || pattern.method === "spawn" || pattern.method === "output") {
12482
+ const receiverText = call.receiver ?? "";
12483
+ const program = extractProgram(receiverText);
12484
+ if (program === null) return false;
12485
+ return !SHELL_PROGRAMS.has(program);
12486
+ }
12487
+ return false;
12488
+ }
12432
12489
  var CLASS_LITERAL_RE = /^(?:[A-Za-z_][\w]*\.)*[A-Z][\w]*(?:\[\])*\.class$/;
12433
12490
  function argIsClassLiteral(call, position) {
12434
12491
  const arg = call.arguments.find((a) => a.position === position);
@@ -12451,6 +12508,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
12451
12508
  if (isSafeGoExecCommandCall(call, pattern, language)) {
12452
12509
  continue;
12453
12510
  }
12511
+ if (isSafeRustCommandCall(call, pattern, language)) {
12512
+ continue;
12513
+ }
12454
12514
  if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
12455
12515
  continue;
12456
12516
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "circle-ir",
3
- "version": "3.82.0",
3
+ "version": "3.84.0",
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",