cognium-dev 3.181.0 → 3.185.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/cli.js +376 -11
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -9440,6 +9440,10 @@ function processGoBlock(node, defs, uses, scopeStack, counters) {
|
|
|
9440
9440
|
} else if (child.type === "for_statement") {
|
|
9441
9441
|
const rangeClause = findChildByTypeGo(child, "range_clause");
|
|
9442
9442
|
if (rangeClause) {
|
|
9443
|
+
const right = rangeClause.childForFieldName("right");
|
|
9444
|
+
if (right) {
|
|
9445
|
+
extractGoUses(right, uses, scopeStack);
|
|
9446
|
+
}
|
|
9443
9447
|
const left = rangeClause.childForFieldName("left");
|
|
9444
9448
|
if (left) {
|
|
9445
9449
|
extractGoLhsDefs(left, defs, scopeStack, child.startPosition.row + 1);
|
|
@@ -9447,6 +9451,7 @@ function processGoBlock(node, defs, uses, scopeStack, counters) {
|
|
|
9447
9451
|
}
|
|
9448
9452
|
} else if (child.type === "call_expression") {
|
|
9449
9453
|
extractGoUses(child, uses, scopeStack);
|
|
9454
|
+
recordGoOpaqueCodecDestDef(child, defs, scopeStack);
|
|
9450
9455
|
} else if (child.type === "return_statement") {
|
|
9451
9456
|
for (let i2 = 0;i2 < child.childCount; i2++) {
|
|
9452
9457
|
const expr = child.child(i2);
|
|
@@ -9546,6 +9551,70 @@ function findChildByTypeGo(node, type) {
|
|
|
9546
9551
|
}
|
|
9547
9552
|
return null;
|
|
9548
9553
|
}
|
|
9554
|
+
var GO_OPAQUE_CODEC_METHODS = new Set([
|
|
9555
|
+
"Unmarshal",
|
|
9556
|
+
"Decode",
|
|
9557
|
+
"NewDecoder",
|
|
9558
|
+
"UnmarshalYAML",
|
|
9559
|
+
"UnmarshalJSON",
|
|
9560
|
+
"UnmarshalText",
|
|
9561
|
+
"UnmarshalBinary"
|
|
9562
|
+
]);
|
|
9563
|
+
function recordGoOpaqueCodecDestDef(call, defs, scopeStack) {
|
|
9564
|
+
const fn = call.childForFieldName("function");
|
|
9565
|
+
if (!fn || fn.type !== "selector_expression")
|
|
9566
|
+
return;
|
|
9567
|
+
const fieldNode = fn.childForFieldName("field");
|
|
9568
|
+
if (!fieldNode)
|
|
9569
|
+
return;
|
|
9570
|
+
const method = getNodeText(fieldNode);
|
|
9571
|
+
if (!GO_OPAQUE_CODEC_METHODS.has(method))
|
|
9572
|
+
return;
|
|
9573
|
+
const argsNode = call.childForFieldName("arguments");
|
|
9574
|
+
if (!argsNode)
|
|
9575
|
+
return;
|
|
9576
|
+
const args2 = [];
|
|
9577
|
+
for (let i2 = 0;i2 < argsNode.childCount; i2++) {
|
|
9578
|
+
const c = argsNode.child(i2);
|
|
9579
|
+
if (!c)
|
|
9580
|
+
continue;
|
|
9581
|
+
if (c.type === "," || c.type === "(" || c.type === ")")
|
|
9582
|
+
continue;
|
|
9583
|
+
args2.push(c);
|
|
9584
|
+
}
|
|
9585
|
+
if (args2.length === 0)
|
|
9586
|
+
return;
|
|
9587
|
+
const destArg = args2.length >= 2 ? args2[1] : args2[0];
|
|
9588
|
+
const destName = extractGoAddressableVarName(destArg);
|
|
9589
|
+
if (!destName || destName === "_")
|
|
9590
|
+
return;
|
|
9591
|
+
const line = call.startPosition.row + 1;
|
|
9592
|
+
const def = {
|
|
9593
|
+
id: defs.length + 1,
|
|
9594
|
+
variable: destName,
|
|
9595
|
+
kind: "local",
|
|
9596
|
+
line
|
|
9597
|
+
};
|
|
9598
|
+
defs.push(def);
|
|
9599
|
+
currentScope(scopeStack).set(destName, def.id);
|
|
9600
|
+
}
|
|
9601
|
+
function extractGoAddressableVarName(node) {
|
|
9602
|
+
if (node.type === "unary_expression") {
|
|
9603
|
+
const operand = node.childForFieldName("operand") ?? node.child(1) ?? null;
|
|
9604
|
+
if (operand)
|
|
9605
|
+
return extractGoAddressableVarName(operand);
|
|
9606
|
+
return null;
|
|
9607
|
+
}
|
|
9608
|
+
if (node.type === "identifier") {
|
|
9609
|
+
return getNodeText(node);
|
|
9610
|
+
}
|
|
9611
|
+
if (node.type === "selector_expression") {
|
|
9612
|
+
const field = node.childForFieldName("field");
|
|
9613
|
+
if (field)
|
|
9614
|
+
return getNodeText(field);
|
|
9615
|
+
}
|
|
9616
|
+
return null;
|
|
9617
|
+
}
|
|
9549
9618
|
// ../circle-ir/dist/core/extractors/runtime-registrations.js
|
|
9550
9619
|
var HTTP_VERB_METHODS = new Set([
|
|
9551
9620
|
"get",
|
|
@@ -10622,6 +10691,19 @@ var DEFAULT_SOURCES = [
|
|
|
10622
10691
|
{ method: "getMap", class: "Metadata", type: "http_header", severity: "high", return_tainted: true, languages: ["javascript", "typescript"] },
|
|
10623
10692
|
{ method: "FromIncomingContext", class: "metadata", type: "http_header", severity: "high", return_tainted: true, languages: ["go"] },
|
|
10624
10693
|
{ method: "get", class: "Metadata", type: "http_header", severity: "high", return_tainted: true, languages: ["java"] },
|
|
10694
|
+
{ method: "receive_text", class: "WebSocket", type: "network_input", severity: "high", return_tainted: true, languages: ["python"] },
|
|
10695
|
+
{ method: "receive_bytes", class: "WebSocket", type: "network_input", severity: "high", return_tainted: true, languages: ["python"] },
|
|
10696
|
+
{ method: "receive_json", class: "WebSocket", type: "http_body", severity: "high", return_tainted: true, languages: ["python"] },
|
|
10697
|
+
{ method: "receive", class: "WebSocket", type: "network_input", severity: "high", return_tainted: true, languages: ["python"] },
|
|
10698
|
+
{ method: "receive_json", type: "http_body", severity: "high", return_tainted: true, languages: ["python"] },
|
|
10699
|
+
{ method: "receive_text", type: "network_input", severity: "high", return_tainted: true, languages: ["python"] },
|
|
10700
|
+
{ method: "receive_bytes", type: "network_input", severity: "high", return_tainted: true, languages: ["python"] },
|
|
10701
|
+
{ method: "ReadMessage", class: "Conn", type: "network_input", severity: "high", return_tainted: true, languages: ["go"] },
|
|
10702
|
+
{ method: "NextReader", class: "Conn", type: "network_input", severity: "high", return_tainted: true, languages: ["go"] },
|
|
10703
|
+
{ method: "Read", class: "Conn", type: "network_input", severity: "high", return_tainted: true, languages: ["go"] },
|
|
10704
|
+
{ annotation: "OnMessage", type: "network_input", severity: "high", param_tainted: true, languages: ["java"] },
|
|
10705
|
+
{ annotation: "MessageMapping", type: "http_body", severity: "high", param_tainted: true, languages: ["java"] },
|
|
10706
|
+
{ annotation: "SubscribeMapping", type: "http_body", severity: "high", param_tainted: true, languages: ["java"] },
|
|
10625
10707
|
{ method: "get", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["python"] },
|
|
10626
10708
|
{ method: "hget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["python"] },
|
|
10627
10709
|
{ method: "mget", class: "Redis", type: "db_input", severity: "medium", return_tainted: true, languages: ["python"] },
|
|
@@ -11913,6 +11995,26 @@ var DEFAULT_SANITIZERS = [
|
|
|
11913
11995
|
{ method: "resolve", class: "Path", removes: ["path_traversal"] },
|
|
11914
11996
|
{ method: "int", removes: ["sql_injection", "command_injection", "xss"] },
|
|
11915
11997
|
{ method: "float", removes: ["sql_injection", "command_injection"] },
|
|
11998
|
+
{ method: "quote", class: "urllib.parse", removes: ["ssrf", "open_redirect", "xss", "path_traversal"] },
|
|
11999
|
+
{ method: "quote_plus", class: "urllib.parse", removes: ["ssrf", "open_redirect", "xss", "path_traversal"] },
|
|
12000
|
+
{ method: "urlencode", class: "urllib.parse", removes: ["ssrf", "open_redirect", "xss"] },
|
|
12001
|
+
{ method: "quote_plus", removes: ["ssrf", "open_redirect", "xss", "path_traversal"] },
|
|
12002
|
+
{ method: "urlencode", removes: ["ssrf", "open_redirect", "xss"] },
|
|
12003
|
+
{ method: "linkify", class: "bleach", removes: ["xss"] },
|
|
12004
|
+
{ method: "linkify", removes: ["xss"] },
|
|
12005
|
+
{ method: "escape", class: "django.utils.html", removes: ["xss"] },
|
|
12006
|
+
{ method: "strip_tags", class: "django.utils.html", removes: ["xss"] },
|
|
12007
|
+
{ method: "escape", class: "jinja2", removes: ["xss"] },
|
|
12008
|
+
{ method: "escape", class: "flask", removes: ["xss"] },
|
|
12009
|
+
{ method: "escape", class: "saxutils", removes: ["xss"] },
|
|
12010
|
+
{ method: "escape", class: "xml.sax.saxutils", removes: ["xss"] },
|
|
12011
|
+
{ method: "quoteattr", class: "saxutils", removes: ["xss"] },
|
|
12012
|
+
{ method: "quoteattr", class: "xml.sax.saxutils", removes: ["xss"] },
|
|
12013
|
+
{ method: "escape", class: "re", removes: ["redos", "code_injection"] },
|
|
12014
|
+
{ method: "bindparams", removes: ["sql_injection"] },
|
|
12015
|
+
{ method: "Identifier", class: "sql", removes: ["sql_injection"] },
|
|
12016
|
+
{ method: "Literal", class: "sql", removes: ["sql_injection"] },
|
|
12017
|
+
{ method: "Placeholder", class: "sql", removes: ["sql_injection"] },
|
|
11916
12018
|
{ method: "query!", removes: ["sql_injection"] },
|
|
11917
12019
|
{ method: "query_as!", removes: ["sql_injection"] },
|
|
11918
12020
|
{ method: "query_scalar!", removes: ["sql_injection"] },
|
|
@@ -13836,7 +13938,13 @@ function matchesSanitizerPattern(call, pattern) {
|
|
|
13836
13938
|
return false;
|
|
13837
13939
|
}
|
|
13838
13940
|
if (pattern.class) {
|
|
13839
|
-
if (!call.receiver
|
|
13941
|
+
if (!call.receiver) {
|
|
13942
|
+
const target = call.resolution?.target;
|
|
13943
|
+
const expectedTail = `${pattern.class}.${pattern.method}`;
|
|
13944
|
+
if (target && (target === expectedTail || target.endsWith("." + expectedTail))) {} else {
|
|
13945
|
+
return false;
|
|
13946
|
+
}
|
|
13947
|
+
} else if (!receiverMightBeClass(call.receiver, pattern.class)) {
|
|
13840
13948
|
return false;
|
|
13841
13949
|
}
|
|
13842
13950
|
}
|
|
@@ -16432,15 +16540,23 @@ function propagateTaint(graphOrDfg, callsOrSources, sourcesOrSinks, sinksOrSanit
|
|
|
16432
16540
|
const callsAtSink = callsByLine.get(sink.line) ?? [];
|
|
16433
16541
|
for (const call of callsAtSink) {
|
|
16434
16542
|
for (const arg of call.arguments) {
|
|
16435
|
-
if (
|
|
16436
|
-
if (sink.argPositions
|
|
16437
|
-
|
|
16438
|
-
continue;
|
|
16439
|
-
}
|
|
16543
|
+
if (sink.argPositions && sink.argPositions.length > 0) {
|
|
16544
|
+
if (!sink.argPositions.includes(arg.position)) {
|
|
16545
|
+
continue;
|
|
16440
16546
|
}
|
|
16441
|
-
|
|
16442
|
-
|
|
16547
|
+
}
|
|
16548
|
+
const candidateUses = arg.variable ? usesAtSink.filter((u) => u.variable === arg.variable) : usesAtSink;
|
|
16549
|
+
{
|
|
16550
|
+
for (const use of candidateUses) {
|
|
16551
|
+
if (use.def_id !== null) {
|
|
16443
16552
|
if (allTaintedDefIds.has(use.def_id)) {
|
|
16553
|
+
if (!arg.variable) {
|
|
16554
|
+
if (typeof arg.expression !== "string" || arg.expression.length === 0)
|
|
16555
|
+
continue;
|
|
16556
|
+
const re = new RegExp(`(?:^|[^A-Za-z0-9_$])${use.variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:[^A-Za-z0-9_$]|$)`);
|
|
16557
|
+
if (!re.test(arg.expression))
|
|
16558
|
+
continue;
|
|
16559
|
+
}
|
|
16444
16560
|
const taintInfo = taintByDefId.get(use.def_id);
|
|
16445
16561
|
if (taintInfo) {
|
|
16446
16562
|
const isSanitized = checkSanitized(taintInfo.line, sink.line, sink.type, sanitizersByLine, {
|
|
@@ -16468,7 +16584,16 @@ function propagateTaint(graphOrDfg, callsOrSources, sourcesOrSinks, sinksOrSanit
|
|
|
16468
16584
|
}
|
|
16469
16585
|
}
|
|
16470
16586
|
}
|
|
16471
|
-
|
|
16587
|
+
const seen = new Set;
|
|
16588
|
+
const deduped = [];
|
|
16589
|
+
for (const f of flows) {
|
|
16590
|
+
const key = `${f.source.line}|${f.sink.line}|${f.sink.type}|${f.path.map((p) => p.variable).join(">")}`;
|
|
16591
|
+
if (seen.has(key))
|
|
16592
|
+
continue;
|
|
16593
|
+
seen.add(key);
|
|
16594
|
+
deduped.push(f);
|
|
16595
|
+
}
|
|
16596
|
+
return { taintedVars, flows: deduped, reachableSinks };
|
|
16472
16597
|
}
|
|
16473
16598
|
function findInitialTaint(sources, callsByLine, defsByLine) {
|
|
16474
16599
|
const tainted = [];
|
|
@@ -24602,7 +24727,10 @@ var PYTHON_TAINTED_PATTERNS2 = [
|
|
|
24602
24727
|
{ pattern: /\bget_query_parameter\s*\(/, type: "http_param" },
|
|
24603
24728
|
{ pattern: /\bget_header_value\s*\(/, type: "http_header" },
|
|
24604
24729
|
{ pattern: /\bget_cookie_value\s*\(/, type: "http_cookie" },
|
|
24605
|
-
{ pattern: /\binput\s*\(/, type: "io_input" }
|
|
24730
|
+
{ pattern: /\binput\s*\(/, type: "io_input" },
|
|
24731
|
+
{ pattern: /\.receive_text\s*\(/, type: "network_input" },
|
|
24732
|
+
{ pattern: /\.receive_bytes\s*\(/, type: "network_input" },
|
|
24733
|
+
{ pattern: /\.receive_json\s*\(/, type: "http_body" }
|
|
24606
24734
|
];
|
|
24607
24735
|
|
|
24608
24736
|
class LanguageSourcesPass {
|
|
@@ -25246,6 +25374,36 @@ function findJavaScriptAssignmentSources(sourceCode, language) {
|
|
|
25246
25374
|
}
|
|
25247
25375
|
}
|
|
25248
25376
|
}
|
|
25377
|
+
sources.push(...findJavaScriptCallbackParamSources(sourceCode));
|
|
25378
|
+
return sources;
|
|
25379
|
+
}
|
|
25380
|
+
function findJavaScriptCallbackParamSources(sourceCode) {
|
|
25381
|
+
const sources = [];
|
|
25382
|
+
const CB_EVENT_RE = /\.on\s*\(\s*['"](?:message|text|binary)['"]\s*,\s*(?:async\s+)?(?:function\s*\*?\s*(?:\w+\s*)?\(\s*(\w+)|\(\s*(\w+)\s*(?:[,):]|$)|(\w+)\s*=>)/;
|
|
25383
|
+
const lines = sourceCode.split(`
|
|
25384
|
+
`);
|
|
25385
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
25386
|
+
const line = lines[i2];
|
|
25387
|
+
const trimmed = line.trimStart();
|
|
25388
|
+
if (trimmed.startsWith("//") || trimmed.startsWith("*"))
|
|
25389
|
+
continue;
|
|
25390
|
+
const m = line.match(CB_EVENT_RE);
|
|
25391
|
+
if (!m)
|
|
25392
|
+
continue;
|
|
25393
|
+
const paramName = m[1] ?? m[2] ?? m[3];
|
|
25394
|
+
if (!paramName)
|
|
25395
|
+
continue;
|
|
25396
|
+
if (paramName === "err" || paramName === "error")
|
|
25397
|
+
continue;
|
|
25398
|
+
sources.push({
|
|
25399
|
+
type: "network_input",
|
|
25400
|
+
location: `WebSocket .on(...) callback param '${paramName}' at line ${i2 + 1}`,
|
|
25401
|
+
severity: "high",
|
|
25402
|
+
line: i2 + 1,
|
|
25403
|
+
confidence: 0.95,
|
|
25404
|
+
variable: paramName
|
|
25405
|
+
});
|
|
25406
|
+
}
|
|
25249
25407
|
return sources;
|
|
25250
25408
|
}
|
|
25251
25409
|
function findPythonAssignmentSources(sourceCode, language) {
|
|
@@ -25578,6 +25736,16 @@ function buildJavaScriptTaintedVars(sourceCode, language) {
|
|
|
25578
25736
|
const tainted = new Map;
|
|
25579
25737
|
const lines = sourceCode.split(`
|
|
25580
25738
|
`);
|
|
25739
|
+
const CB_EVENT_RE = /\.on\s*\(\s*['"](?:message|text|binary)['"]\s*,\s*(?:async\s+)?(?:function\s*\*?\s*(?:\w+\s*)?\(\s*(\w+)|\(\s*(\w+)\s*(?:[,):]|$)|(\w+)\s*=>)/;
|
|
25740
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
25741
|
+
const m = lines[i2].match(CB_EVENT_RE);
|
|
25742
|
+
if (!m)
|
|
25743
|
+
continue;
|
|
25744
|
+
const paramName = m[1] ?? m[2] ?? m[3];
|
|
25745
|
+
if (!paramName || paramName === "err" || paramName === "error")
|
|
25746
|
+
continue;
|
|
25747
|
+
tainted.set(paramName, i2 + 1);
|
|
25748
|
+
}
|
|
25581
25749
|
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
25582
25750
|
const line = lines[i2];
|
|
25583
25751
|
const trimmed = line.trimStart();
|
|
@@ -25784,6 +25952,90 @@ function findBashTaintSources(sourceCode, dfg) {
|
|
|
25784
25952
|
});
|
|
25785
25953
|
}
|
|
25786
25954
|
}
|
|
25955
|
+
const readMatch = /^read\s*\(/.test(trimmed) ? null : trimmed.match(/^read\b([^#(]*)$/);
|
|
25956
|
+
if (readMatch) {
|
|
25957
|
+
const argStr = readMatch[1].trim();
|
|
25958
|
+
const noRedirect = argStr.replace(/\s*<[^<].*$/, "").trim();
|
|
25959
|
+
const tokens = noRedirect.split(/\s+/);
|
|
25960
|
+
const varNames = [];
|
|
25961
|
+
for (let ti = 0;ti < tokens.length; ti++) {
|
|
25962
|
+
const tok = tokens[ti];
|
|
25963
|
+
if (!tok)
|
|
25964
|
+
continue;
|
|
25965
|
+
if (tok.startsWith("-")) {
|
|
25966
|
+
if (/^-[antpidu]$|^-N$/.test(tok))
|
|
25967
|
+
ti++;
|
|
25968
|
+
continue;
|
|
25969
|
+
}
|
|
25970
|
+
if (/^[A-Za-z_][\w]*$/.test(tok))
|
|
25971
|
+
varNames.push(tok);
|
|
25972
|
+
}
|
|
25973
|
+
if (varNames.length === 0)
|
|
25974
|
+
varNames.push("REPLY");
|
|
25975
|
+
for (const v of varNames) {
|
|
25976
|
+
const already = sources.some((s) => s.line === lineNumber && s.variable === v);
|
|
25977
|
+
if (already)
|
|
25978
|
+
continue;
|
|
25979
|
+
sources.push({
|
|
25980
|
+
type: "io_input",
|
|
25981
|
+
location: `read → $${v} (stdin)`,
|
|
25982
|
+
severity: "high",
|
|
25983
|
+
line: lineNumber,
|
|
25984
|
+
confidence: 0.9,
|
|
25985
|
+
variable: v
|
|
25986
|
+
});
|
|
25987
|
+
}
|
|
25988
|
+
}
|
|
25989
|
+
const mapfileMatch = /^(?:mapfile|readarray)\s*\(/.test(trimmed) ? null : trimmed.match(/^(?:mapfile|readarray)\b([^#(]*)$/);
|
|
25990
|
+
if (mapfileMatch) {
|
|
25991
|
+
const argStr = mapfileMatch[1].trim();
|
|
25992
|
+
const tokens = argStr.split(/\s+/);
|
|
25993
|
+
const varNames = [];
|
|
25994
|
+
for (let ti = 0;ti < tokens.length; ti++) {
|
|
25995
|
+
const tok = tokens[ti];
|
|
25996
|
+
if (!tok)
|
|
25997
|
+
continue;
|
|
25998
|
+
if (tok.startsWith("-")) {
|
|
25999
|
+
if (/^-[cCnOsud]$/.test(tok))
|
|
26000
|
+
ti++;
|
|
26001
|
+
continue;
|
|
26002
|
+
}
|
|
26003
|
+
if (/^[A-Za-z_][\w]*$/.test(tok))
|
|
26004
|
+
varNames.push(tok);
|
|
26005
|
+
}
|
|
26006
|
+
if (varNames.length === 0)
|
|
26007
|
+
varNames.push("MAPFILE");
|
|
26008
|
+
for (const v of varNames) {
|
|
26009
|
+
const already = sources.some((s) => s.line === lineNumber && s.variable === v);
|
|
26010
|
+
if (already)
|
|
26011
|
+
continue;
|
|
26012
|
+
sources.push({
|
|
26013
|
+
type: "io_input",
|
|
26014
|
+
location: `mapfile → $${v} (stdin array)`,
|
|
26015
|
+
severity: "high",
|
|
26016
|
+
line: lineNumber,
|
|
26017
|
+
confidence: 0.9,
|
|
26018
|
+
variable: v
|
|
26019
|
+
});
|
|
26020
|
+
}
|
|
26021
|
+
}
|
|
26022
|
+
const getoptsMatch = trimmed.match(/\bgetopts\s+["'][^"']+["']\s+(\w+)/);
|
|
26023
|
+
if (getoptsMatch) {
|
|
26024
|
+
const flagVar = getoptsMatch[1];
|
|
26025
|
+
for (const v of [flagVar, "OPTARG"]) {
|
|
26026
|
+
const already = sources.some((s) => s.line === lineNumber && s.variable === v);
|
|
26027
|
+
if (already)
|
|
26028
|
+
continue;
|
|
26029
|
+
sources.push({
|
|
26030
|
+
type: "io_input",
|
|
26031
|
+
location: `getopts → $${v} (CLI arg)`,
|
|
26032
|
+
severity: "high",
|
|
26033
|
+
line: lineNumber,
|
|
26034
|
+
confidence: 0.9,
|
|
26035
|
+
variable: v
|
|
26036
|
+
});
|
|
26037
|
+
}
|
|
26038
|
+
}
|
|
25787
26039
|
const envRe = /\$([A-Z][A-Z0-9_]{2,})|\$\{([A-Z][A-Z0-9_]{2,})\}/g;
|
|
25788
26040
|
let em;
|
|
25789
26041
|
while ((em = envRe.exec(line)) !== null) {
|
|
@@ -33393,6 +33645,12 @@ class TaintPropagationPass {
|
|
|
33393
33645
|
continue;
|
|
33394
33646
|
pushIfNew(f);
|
|
33395
33647
|
}
|
|
33648
|
+
if (ctx.language === "go" && typeof ctx.code === "string") {
|
|
33649
|
+
const goGlobalFlows = detectGoPackageGlobalFlows(ctx.code, calls, sources, sinks, constProp.unreachableLines) ?? [];
|
|
33650
|
+
for (const f of goGlobalFlows) {
|
|
33651
|
+
pushIfNew(f);
|
|
33652
|
+
}
|
|
33653
|
+
}
|
|
33396
33654
|
const sanitizedNames = constProp.sanitizedVars;
|
|
33397
33655
|
let finalFlows = sanitizedNames.size === 0 ? flows : flows.filter((f) => {
|
|
33398
33656
|
if (f.path.length === 0)
|
|
@@ -33649,6 +33907,113 @@ function isInJavaSanitizedMethod(code, types, sinkLine, sinkType) {
|
|
|
33649
33907
|
}
|
|
33650
33908
|
return false;
|
|
33651
33909
|
}
|
|
33910
|
+
function detectGoPackageGlobalFlows(code, _calls, sources, sinks, unreachableLines) {
|
|
33911
|
+
const flows = [];
|
|
33912
|
+
if (!code || sinks.length === 0)
|
|
33913
|
+
return flows;
|
|
33914
|
+
const lines = code.split(`
|
|
33915
|
+
`);
|
|
33916
|
+
const packageVars = new Set;
|
|
33917
|
+
let inVarBlock = false;
|
|
33918
|
+
for (const line of lines) {
|
|
33919
|
+
const stripped = line.replace(/\s+$/, "");
|
|
33920
|
+
if (!stripped)
|
|
33921
|
+
continue;
|
|
33922
|
+
if (/^[ \t]/.test(line)) {
|
|
33923
|
+
if (inVarBlock) {
|
|
33924
|
+
const m = stripped.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s+/);
|
|
33925
|
+
if (m)
|
|
33926
|
+
packageVars.add(m[1]);
|
|
33927
|
+
if (/^\s*\)/.test(stripped))
|
|
33928
|
+
inVarBlock = false;
|
|
33929
|
+
}
|
|
33930
|
+
continue;
|
|
33931
|
+
}
|
|
33932
|
+
if (/^var\s*\(/.test(stripped)) {
|
|
33933
|
+
inVarBlock = true;
|
|
33934
|
+
continue;
|
|
33935
|
+
}
|
|
33936
|
+
if (inVarBlock && /^\)/.test(stripped)) {
|
|
33937
|
+
inVarBlock = false;
|
|
33938
|
+
continue;
|
|
33939
|
+
}
|
|
33940
|
+
const varOne = stripped.match(/^var\s+([A-Za-z_][A-Za-z0-9_]*)\b/);
|
|
33941
|
+
if (varOne)
|
|
33942
|
+
packageVars.add(varOne[1]);
|
|
33943
|
+
}
|
|
33944
|
+
if (packageVars.size === 0)
|
|
33945
|
+
return flows;
|
|
33946
|
+
const sourceVarNames = new Set;
|
|
33947
|
+
for (const s of sources) {
|
|
33948
|
+
if (typeof s.variable === "string" && s.variable.length > 0) {
|
|
33949
|
+
sourceVarNames.add(s.variable);
|
|
33950
|
+
}
|
|
33951
|
+
}
|
|
33952
|
+
const GO_SOURCE_RE = /\b(r\.(?:URL\.Query|Form|PostForm|Header|Cookie|Body)|mux\.Vars|c\.(?:Query|Param|PostForm|GetHeader|Cookie)|ctx\.(?:Query|Param|PostForm|GetHeader|Cookie))\b/;
|
|
33953
|
+
const writes = [];
|
|
33954
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
33955
|
+
const line = lines[i2];
|
|
33956
|
+
if (unreachableLines.has(i2 + 1))
|
|
33957
|
+
continue;
|
|
33958
|
+
if (/:=/.test(line))
|
|
33959
|
+
continue;
|
|
33960
|
+
const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.+)$/);
|
|
33961
|
+
if (!m)
|
|
33962
|
+
continue;
|
|
33963
|
+
const [, lhs, rhs] = m;
|
|
33964
|
+
if (!packageVars.has(lhs))
|
|
33965
|
+
continue;
|
|
33966
|
+
let taintedBy = null;
|
|
33967
|
+
if (GO_SOURCE_RE.test(rhs)) {
|
|
33968
|
+
const src = sources.find((s) => s.line === i2 + 1);
|
|
33969
|
+
taintedBy = src ? { line: src.line, type: src.type } : { line: i2 + 1, type: "http_param" };
|
|
33970
|
+
} else {
|
|
33971
|
+
for (const v of sourceVarNames) {
|
|
33972
|
+
const re = new RegExp(`(?<![A-Za-z0-9_$])${v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_$])`);
|
|
33973
|
+
if (re.test(rhs)) {
|
|
33974
|
+
const src = sources.find((s) => s.variable === v);
|
|
33975
|
+
if (src) {
|
|
33976
|
+
taintedBy = { line: src.line, type: src.type };
|
|
33977
|
+
break;
|
|
33978
|
+
}
|
|
33979
|
+
}
|
|
33980
|
+
}
|
|
33981
|
+
}
|
|
33982
|
+
if (!taintedBy)
|
|
33983
|
+
continue;
|
|
33984
|
+
writes.push({ varName: lhs, line: i2 + 1, sourceLine: taintedBy.line, sourceType: taintedBy.type });
|
|
33985
|
+
}
|
|
33986
|
+
if (writes.length === 0)
|
|
33987
|
+
return flows;
|
|
33988
|
+
for (const sink of sinks) {
|
|
33989
|
+
if (unreachableLines.has(sink.line))
|
|
33990
|
+
continue;
|
|
33991
|
+
const sinkLineText = lines[sink.line - 1] ?? "";
|
|
33992
|
+
if (!sinkLineText)
|
|
33993
|
+
continue;
|
|
33994
|
+
for (const w of writes) {
|
|
33995
|
+
if (w.line >= sink.line)
|
|
33996
|
+
continue;
|
|
33997
|
+
const re = new RegExp(`(?<![A-Za-z0-9_$])${w.varName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![A-Za-z0-9_$])`);
|
|
33998
|
+
if (!re.test(sinkLineText))
|
|
33999
|
+
continue;
|
|
34000
|
+
flows.push({
|
|
34001
|
+
source_line: w.sourceLine,
|
|
34002
|
+
sink_line: sink.line,
|
|
34003
|
+
source_type: w.sourceType,
|
|
34004
|
+
sink_type: sink.type,
|
|
34005
|
+
path: [
|
|
34006
|
+
{ variable: w.varName, line: w.sourceLine, type: "source" },
|
|
34007
|
+
{ variable: w.varName, line: w.line, type: "assignment" },
|
|
34008
|
+
{ variable: w.varName, line: sink.line, type: "sink" }
|
|
34009
|
+
],
|
|
34010
|
+
confidence: 0.9,
|
|
34011
|
+
sanitized: false
|
|
34012
|
+
});
|
|
34013
|
+
}
|
|
34014
|
+
}
|
|
34015
|
+
return flows;
|
|
34016
|
+
}
|
|
33652
34017
|
function detectCollectionFlows(calls, sources, sinks, taintedVars, unreachableLines, code, types) {
|
|
33653
34018
|
const flows = [];
|
|
33654
34019
|
const callsByLine = new Map;
|
|
@@ -45451,7 +45816,7 @@ var colors = {
|
|
|
45451
45816
|
};
|
|
45452
45817
|
|
|
45453
45818
|
// src/version.ts
|
|
45454
|
-
var version = "3.
|
|
45819
|
+
var version = "3.185.0";
|
|
45455
45820
|
|
|
45456
45821
|
// src/formatters.ts
|
|
45457
45822
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.185.0",
|
|
4
4
|
"description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
68
|
"@cognium/project-profile-detect": "^1.1.0",
|
|
69
|
-
"circle-ir": "^3.
|
|
69
|
+
"circle-ir": "^3.185.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.5.0",
|