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.
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +15 -7
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +291 -0
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +96 -0
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/browser/circle-ir.js +247 -7
- package/dist/core/circle-ir-core.cjs +67 -7
- package/dist/core/circle-ir-core.js +67 -7
- package/package.json +1 -1
|
@@ -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
|
-
|
|
10565
|
-
|
|
10566
|
-
|
|
10567
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
10499
|
-
|
|
10500
|
-
|
|
10501
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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",
|