circle-ir 3.110.0 → 3.111.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/configs/sinks/nodejs.json +18 -0
- package/dist/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +14 -2
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.d.ts.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.js +26 -2
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +134 -1
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/browser/circle-ir.js +103 -5
- package/dist/core/circle-ir-core.cjs +93 -3
- package/dist/core/circle-ir-core.js +93 -3
- package/package.json +1 -1
|
@@ -10485,10 +10485,20 @@ var DEFAULT_SINKS = [
|
|
|
10485
10485
|
{ method: "bash", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10486
10486
|
{ method: "shell", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10487
10487
|
{ method: "sh", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10488
|
-
|
|
10488
|
+
// cognium-dev #187 Sprint 54: arg_positions [0, 1] so the JS shell-mode shape
|
|
10489
|
+
// `spawn('sh', ['-c', tainted])` / `execFile('/bin/sh', ['-c', tainted])`
|
|
10490
|
+
// surfaces taint at the argv-array position (arg[1]).
|
|
10491
|
+
{ method: "spawn", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
|
|
10492
|
+
{ method: "spawnSync", languages: ["javascript", "typescript"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
|
|
10493
|
+
{ method: "execFile", languages: ["javascript", "typescript"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
|
|
10494
|
+
{ method: "execFileSync", languages: ["javascript", "typescript"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1] },
|
|
10489
10495
|
{ method: "fork", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10490
10496
|
{ method: "popen", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10491
10497
|
{ method: "system", languages: ["java", "javascript", "typescript", "python", "go", "rust"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10498
|
+
// execa (npm) — parses tainted shell-style strings into program+argv;
|
|
10499
|
+
// arg[0] is shell-injectable. cognium-dev #187 Sprint 54.
|
|
10500
|
+
{ method: "command", class: "execa", languages: ["javascript", "typescript"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10501
|
+
{ method: "commandSync", class: "execa", languages: ["javascript", "typescript"], type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
10492
10502
|
// Apache Commons Exec
|
|
10493
10503
|
// Note: bare class 'Executor' removed (see comment above) — DefaultExecutor matched explicitly.
|
|
10494
10504
|
{ method: "setCommandline", class: "DefaultExecutor", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0] },
|
|
@@ -11499,7 +11509,9 @@ var DEFAULT_SINKS = [
|
|
|
11499
11509
|
{ method: "aggregate", class: "Collection", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0] },
|
|
11500
11510
|
// pymongo dynamic attribute-access pattern: `db.users.find({...})` — receiver
|
|
11501
11511
|
// class isn't statically known. Method-only entries restricted to Python.
|
|
11502
|
-
// cognium-dev#104 Sprint 22
|
|
11512
|
+
// cognium-dev#104 Sprint 22, #194 Sprint 54 added `find`/`aggregate`.
|
|
11513
|
+
{ method: "find", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
11514
|
+
{ method: "aggregate", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
11503
11515
|
{ method: "find_one", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0], languages: ["python"] },
|
|
11504
11516
|
{ method: "update_one", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0, 1], languages: ["python"] },
|
|
11505
11517
|
{ method: "update_many", type: "nosql_injection", cwe: "CWE-943", severity: "critical", arg_positions: [0, 1], languages: ["python"] },
|
|
@@ -12079,10 +12091,50 @@ var PYTHON_TAINTED_PATTERNS = [
|
|
|
12079
12091
|
function analyzeTaint(calls, types, config = getDefaultConfig(), typeHierarchy, language, code) {
|
|
12080
12092
|
const sourceLines = code !== void 0 ? code.split("\n") : void 0;
|
|
12081
12093
|
const sources = findSources(calls, types, config.sources, sourceLines, language);
|
|
12082
|
-
const
|
|
12094
|
+
const sinkPatterns = expandPromisifyAliases(config.sinks, sourceLines, language);
|
|
12095
|
+
const sinks = findSinks(calls, sinkPatterns, typeHierarchy, language, sourceLines);
|
|
12083
12096
|
const sanitizers = findSanitizers(calls, types, config.sanitizers, sourceLines);
|
|
12084
12097
|
return { sources, sinks, sanitizers };
|
|
12085
12098
|
}
|
|
12099
|
+
function expandPromisifyAliases(patterns, sourceLines, language) {
|
|
12100
|
+
if (!sourceLines || sourceLines.length === 0) return patterns;
|
|
12101
|
+
if (language !== "javascript" && language !== "typescript") return patterns;
|
|
12102
|
+
const PROMISIFY_RE = /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*=\s*(?:util\s*\.\s*)?promisify\s*\(\s*([A-Za-z_$][\w$]*)\s*\)/;
|
|
12103
|
+
const innerToPattern = /* @__PURE__ */ new Map();
|
|
12104
|
+
for (const p of patterns) {
|
|
12105
|
+
if (p.languages && !p.languages.includes(language)) continue;
|
|
12106
|
+
if (p.class !== void 0) continue;
|
|
12107
|
+
if (!innerToPattern.has(p.method)) innerToPattern.set(p.method, p);
|
|
12108
|
+
}
|
|
12109
|
+
for (const p of patterns) {
|
|
12110
|
+
if (p.languages && !p.languages.includes(language)) continue;
|
|
12111
|
+
if (innerToPattern.has(p.method)) continue;
|
|
12112
|
+
innerToPattern.set(p.method, p);
|
|
12113
|
+
}
|
|
12114
|
+
const aliasPatterns = [];
|
|
12115
|
+
const seenAliases = /* @__PURE__ */ new Set();
|
|
12116
|
+
for (const line of sourceLines) {
|
|
12117
|
+
const m = PROMISIFY_RE.exec(line);
|
|
12118
|
+
if (!m) continue;
|
|
12119
|
+
const aliasName = m[1];
|
|
12120
|
+
const innerName = m[2];
|
|
12121
|
+
if (!aliasName || !innerName) continue;
|
|
12122
|
+
if (seenAliases.has(aliasName)) continue;
|
|
12123
|
+
const template = innerToPattern.get(innerName);
|
|
12124
|
+
if (!template) continue;
|
|
12125
|
+
seenAliases.add(aliasName);
|
|
12126
|
+
aliasPatterns.push({
|
|
12127
|
+
method: aliasName,
|
|
12128
|
+
type: template.type,
|
|
12129
|
+
cwe: template.cwe,
|
|
12130
|
+
severity: template.severity,
|
|
12131
|
+
arg_positions: template.arg_positions,
|
|
12132
|
+
languages: [language],
|
|
12133
|
+
note: `util.promisify alias of ${innerName} \u2014 cognium-dev #187`
|
|
12134
|
+
});
|
|
12135
|
+
}
|
|
12136
|
+
return aliasPatterns.length === 0 ? patterns : patterns.concat(aliasPatterns);
|
|
12137
|
+
}
|
|
12086
12138
|
function attachSourceLineCode(sources, sinks, code) {
|
|
12087
12139
|
const lines = code.split("\n");
|
|
12088
12140
|
for (const s of sources) {
|
|
@@ -12448,6 +12500,41 @@ function isSafeGoExecCommandCall(call, pattern, language) {
|
|
|
12448
12500
|
if (SHELL_PROGRAMS.has(program)) return false;
|
|
12449
12501
|
return true;
|
|
12450
12502
|
}
|
|
12503
|
+
function isSafeJSChildProcessCall(call, pattern, language) {
|
|
12504
|
+
if (language !== "javascript" && language !== "typescript") return false;
|
|
12505
|
+
if (pattern.type !== "command_injection") return false;
|
|
12506
|
+
const m = call.method_name;
|
|
12507
|
+
if (m !== "spawn" && m !== "spawnSync" && m !== "execFile" && m !== "execFileSync") return false;
|
|
12508
|
+
const programArg = call.arguments.find((a) => a.position === 0);
|
|
12509
|
+
if (!programArg) return false;
|
|
12510
|
+
let program;
|
|
12511
|
+
if (programArg.literal !== null && programArg.literal !== void 0) {
|
|
12512
|
+
program = String(programArg.literal).split("/").pop() ?? String(programArg.literal);
|
|
12513
|
+
} else {
|
|
12514
|
+
const expr = (programArg.expression ?? "").trim();
|
|
12515
|
+
if (!(expr.startsWith('"') || expr.startsWith("`") || expr.startsWith("'"))) {
|
|
12516
|
+
return false;
|
|
12517
|
+
}
|
|
12518
|
+
const stripped = expr.slice(1, -1);
|
|
12519
|
+
program = stripped.split("/").pop() ?? stripped;
|
|
12520
|
+
}
|
|
12521
|
+
const SHELL_PROGRAMS = /* @__PURE__ */ new Set([
|
|
12522
|
+
"sh",
|
|
12523
|
+
"bash",
|
|
12524
|
+
"zsh",
|
|
12525
|
+
"dash",
|
|
12526
|
+
"ash",
|
|
12527
|
+
"ksh",
|
|
12528
|
+
"cmd",
|
|
12529
|
+
"cmd.exe",
|
|
12530
|
+
"powershell",
|
|
12531
|
+
"pwsh",
|
|
12532
|
+
"powershell.exe",
|
|
12533
|
+
"pwsh.exe"
|
|
12534
|
+
]);
|
|
12535
|
+
if (SHELL_PROGRAMS.has(program)) return false;
|
|
12536
|
+
return true;
|
|
12537
|
+
}
|
|
12451
12538
|
function isSafeRustCommandCall(call, pattern, language) {
|
|
12452
12539
|
if (language !== "rust") return false;
|
|
12453
12540
|
if (pattern.type !== "command_injection") return false;
|
|
@@ -12662,6 +12749,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines) {
|
|
|
12662
12749
|
if (isSafeRustCommandCall(call, pattern, language)) {
|
|
12663
12750
|
continue;
|
|
12664
12751
|
}
|
|
12752
|
+
if (isSafeJSChildProcessCall(call, pattern, language)) {
|
|
12753
|
+
continue;
|
|
12754
|
+
}
|
|
12665
12755
|
if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at)) {
|
|
12666
12756
|
continue;
|
|
12667
12757
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circle-ir",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.111.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",
|