cognium-dev 4.9.27 → 4.9.29
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 +463 -35
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -3689,6 +3689,26 @@ function extractTypes(tree, cache, language) {
|
|
|
3689
3689
|
}
|
|
3690
3690
|
return extractJavaTypes(tree, cache);
|
|
3691
3691
|
}
|
|
3692
|
+
function csharpMemberNodes(body2) {
|
|
3693
|
+
if (!body2)
|
|
3694
|
+
return [];
|
|
3695
|
+
const out2 = [];
|
|
3696
|
+
const visit = (parent, depth) => {
|
|
3697
|
+
if (depth > 32)
|
|
3698
|
+
return;
|
|
3699
|
+
for (let i2 = 0;i2 < parent.childCount; i2++) {
|
|
3700
|
+
const c = parent.child(i2);
|
|
3701
|
+
if (!c)
|
|
3702
|
+
continue;
|
|
3703
|
+
if (c.type.startsWith("preproc_"))
|
|
3704
|
+
visit(c, depth + 1);
|
|
3705
|
+
else
|
|
3706
|
+
out2.push(c);
|
|
3707
|
+
}
|
|
3708
|
+
};
|
|
3709
|
+
visit(body2, 0);
|
|
3710
|
+
return out2;
|
|
3711
|
+
}
|
|
3692
3712
|
function extractCSharpTypes(tree, cache) {
|
|
3693
3713
|
const types = [];
|
|
3694
3714
|
const KINDS = {
|
|
@@ -3705,9 +3725,8 @@ function extractCSharpTypes(tree, cache) {
|
|
|
3705
3725
|
const methods = [];
|
|
3706
3726
|
const fields = extractCSharpFields(body2);
|
|
3707
3727
|
if (body2) {
|
|
3708
|
-
for (
|
|
3709
|
-
|
|
3710
|
-
if (!m || m.type !== "method_declaration")
|
|
3728
|
+
for (const m of csharpMemberNodes(body2)) {
|
|
3729
|
+
if (m.type !== "method_declaration")
|
|
3711
3730
|
continue;
|
|
3712
3731
|
const mName = m.childForFieldName("name");
|
|
3713
3732
|
const paramList = m.childForFieldName("parameters");
|
|
@@ -3761,10 +3780,7 @@ function extractCSharpFields(body2) {
|
|
|
3761
3780
|
const fields = [];
|
|
3762
3781
|
if (!body2)
|
|
3763
3782
|
return fields;
|
|
3764
|
-
for (
|
|
3765
|
-
const c = body2.child(i2);
|
|
3766
|
-
if (!c)
|
|
3767
|
-
continue;
|
|
3783
|
+
for (const c of csharpMemberNodes(body2)) {
|
|
3768
3784
|
if (c.type === "field_declaration") {
|
|
3769
3785
|
let varDecl = null;
|
|
3770
3786
|
for (let k = 0;k < c.childCount; k++) {
|
|
@@ -5351,6 +5367,15 @@ function extractCalls(tree, cache, language) {
|
|
|
5351
5367
|
function buildCSharpReceiverTypeMap(tree, cache) {
|
|
5352
5368
|
const map = new Map;
|
|
5353
5369
|
const simple = (t) => t.replace(/<[^>]*>/g, "").split(".").pop()?.trim() ?? t;
|
|
5370
|
+
for (const prm of getNodesFromCache(tree.rootNode, "parameter", cache)) {
|
|
5371
|
+
const nameNode = prm.childForFieldName("name");
|
|
5372
|
+
const typeNode = prm.childForFieldName("type");
|
|
5373
|
+
if (nameNode?.type !== "identifier" || !typeNode)
|
|
5374
|
+
continue;
|
|
5375
|
+
const t = getNodeText(typeNode);
|
|
5376
|
+
if (t && t !== "var")
|
|
5377
|
+
map.set(getNodeText(nameNode), simple(t));
|
|
5378
|
+
}
|
|
5354
5379
|
for (const vd of getNodesFromCache(tree.rootNode, "variable_declaration", cache)) {
|
|
5355
5380
|
const typeNode = vd.childForFieldName("type");
|
|
5356
5381
|
const declaredType = typeNode ? getNodeText(typeNode) : null;
|
|
@@ -5387,6 +5412,7 @@ function findFirstDescendant(node, type) {
|
|
|
5387
5412
|
}
|
|
5388
5413
|
return null;
|
|
5389
5414
|
}
|
|
5415
|
+
var CSHARP_RESPONSE_TYPES = new Set(["HttpResponse", "HttpResponseBase"]);
|
|
5390
5416
|
var CSHARP_COMMAND_EXECUTE_METHODS = new Set([
|
|
5391
5417
|
"ExecuteScalar",
|
|
5392
5418
|
"ExecuteReader",
|
|
@@ -5506,6 +5532,33 @@ function extractCSharpCalls(tree, cache) {
|
|
|
5506
5532
|
});
|
|
5507
5533
|
continue;
|
|
5508
5534
|
}
|
|
5535
|
+
if (left?.type === "identifier" && asn.parent?.type === "initializer_expression") {
|
|
5536
|
+
const creation = asn.parent.parent;
|
|
5537
|
+
if (creation?.type !== "object_creation_expression")
|
|
5538
|
+
continue;
|
|
5539
|
+
const ctorType = creation.childForFieldName("type");
|
|
5540
|
+
const right2 = asn.childForFieldName("right");
|
|
5541
|
+
if (!ctorType || !right2)
|
|
5542
|
+
continue;
|
|
5543
|
+
const typeName = csharpBareName(ctorType);
|
|
5544
|
+
const rhsText2 = getNodeText(right2);
|
|
5545
|
+
calls.push({
|
|
5546
|
+
method_name: getNodeText(left),
|
|
5547
|
+
receiver: typeName,
|
|
5548
|
+
receiver_type: typeName,
|
|
5549
|
+
receiver_type_fqn: null,
|
|
5550
|
+
arguments: [{
|
|
5551
|
+
position: 0,
|
|
5552
|
+
expression: rhsText2,
|
|
5553
|
+
variable: right2.type === "identifier" ? rhsText2 : null,
|
|
5554
|
+
literal: CSHARP_LITERAL_NODE_TYPES.has(right2.type) ? rhsText2 : null,
|
|
5555
|
+
value: null
|
|
5556
|
+
}],
|
|
5557
|
+
location: { line: asn.startPosition.row + 1, column: asn.startPosition.column },
|
|
5558
|
+
in_method: findEnclosingMethod(asn)
|
|
5559
|
+
});
|
|
5560
|
+
continue;
|
|
5561
|
+
}
|
|
5509
5562
|
if (left?.type !== "member_access_expression")
|
|
5510
5563
|
continue;
|
|
5511
5564
|
const nameNode = left.childForFieldName("name");
|
|
@@ -5518,6 +5571,16 @@ function extractCSharpCalls(tree, cache) {
|
|
|
5518
5571
|
const recvType = recv ? typeMap.get(recv) : undefined;
|
|
5519
5572
|
if (recvType !== "DirectorySearcher")
|
|
5520
5573
|
continue;
|
|
5574
|
+
} else if (propName === "StatusDescription") {
|
|
5575
|
+
const recv = exprNode ? getNodeText(exprNode) : "";
|
|
5576
|
+
const recvType = recv ? typeMap.get(recv) : undefined;
|
|
5577
|
+
if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv) && !(recvType && CSHARP_RESPONSE_TYPES.has(recvType)))
|
|
5578
|
+
continue;
|
|
5579
|
+
} else if (propName === "BaseAddress") {
|
|
5580
|
+
const recv = exprNode ? getNodeText(exprNode) : null;
|
|
5581
|
+
const recvType = recv ? typeMap.get(recv) : undefined;
|
|
5582
|
+
if (recvType !== "HttpClient")
|
|
5583
|
+
continue;
|
|
5521
5584
|
} else if (propName !== "CommandText") {
|
|
5522
5585
|
continue;
|
|
5523
5586
|
}
|
|
@@ -5541,8 +5604,36 @@ function extractCSharpCalls(tree, cache) {
|
|
|
5541
5604
|
in_method: findEnclosingMethod(asn)
|
|
5542
5605
|
});
|
|
5543
5606
|
}
|
|
5607
|
+
const casts = getNodesFromCache(tree.rootNode, "cast_expression", cache);
|
|
5608
|
+
for (const cast of casts) {
|
|
5609
|
+
const typeNode = cast.childForFieldName("type");
|
|
5610
|
+
const valueNode = cast.childForFieldName("value");
|
|
5611
|
+
if (!typeNode || !valueNode)
|
|
5612
|
+
continue;
|
|
5613
|
+
const typeName = csharpBareName(typeNode);
|
|
5614
|
+
if (!CSHARP_RAW_MARKUP_CAST_TYPES.has(typeName))
|
|
5615
|
+
continue;
|
|
5616
|
+
const valueText = getNodeText(valueNode);
|
|
5617
|
+
calls.push({
|
|
5618
|
+
method_name: typeName,
|
|
5619
|
+
receiver: null,
|
|
5620
|
+
receiver_type: null,
|
|
5621
|
+
receiver_type_fqn: null,
|
|
5622
|
+
arguments: [{
|
|
5623
|
+
position: 0,
|
|
5624
|
+
expression: valueText,
|
|
5625
|
+
variable: valueNode.type === "identifier" ? valueText : null,
|
|
5626
|
+
literal: CSHARP_LITERAL_NODE_TYPES.has(valueNode.type) ? valueText : null,
|
|
5627
|
+
value: null
|
|
5628
|
+
}],
|
|
5629
|
+
location: { line: cast.startPosition.row + 1, column: cast.startPosition.column },
|
|
5630
|
+
in_method: findEnclosingMethod(cast),
|
|
5631
|
+
is_constructor: true
|
|
5632
|
+
});
|
|
5633
|
+
}
|
|
5544
5634
|
return calls;
|
|
5545
5635
|
}
|
|
5636
|
+
var CSHARP_RAW_MARKUP_CAST_TYPES = new Set(["MarkupString"]);
|
|
5546
5637
|
function extractCSharpArguments(argsNode) {
|
|
5547
5638
|
const args2 = [];
|
|
5548
5639
|
let position = 0;
|
|
@@ -5563,12 +5654,22 @@ function extractCSharpArguments(argsNode) {
|
|
|
5563
5654
|
position: position++,
|
|
5564
5655
|
expression: text,
|
|
5565
5656
|
variable: expr?.type === "identifier" ? text : null,
|
|
5566
|
-
literal: expr
|
|
5657
|
+
literal: expr && CSHARP_LITERAL_NODE_TYPES.has(expr.type) ? text : null,
|
|
5567
5658
|
value: null
|
|
5568
5659
|
});
|
|
5569
5660
|
}
|
|
5570
5661
|
return args2;
|
|
5571
5662
|
}
|
|
5663
|
+
var CSHARP_LITERAL_NODE_TYPES = new Set([
|
|
5664
|
+
"string_literal",
|
|
5665
|
+
"verbatim_string_literal",
|
|
5666
|
+
"raw_string_literal",
|
|
5667
|
+
"character_literal",
|
|
5668
|
+
"integer_literal",
|
|
5669
|
+
"real_literal",
|
|
5670
|
+
"boolean_literal",
|
|
5671
|
+
"null_literal"
|
|
5672
|
+
]);
|
|
5572
5673
|
function extractJavaScriptCalls(tree, cache) {
|
|
5573
5674
|
const calls = [];
|
|
5574
5675
|
const context = buildJSResolutionContext(tree, cache);
|
|
@@ -11133,6 +11234,14 @@ var DEFAULT_SOURCES = [
|
|
|
11133
11234
|
{ method: "nextLine", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
|
|
11134
11235
|
{ method: "next", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
|
|
11135
11236
|
{ method: "nextInt", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
|
|
11237
|
+
{ method: "ReadLine", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11238
|
+
{ method: "ReadLineAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11239
|
+
{ method: "ReadToEnd", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11240
|
+
{ method: "ReadToEndAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11241
|
+
{ method: "GetStream", class: "TcpClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11242
|
+
{ method: "AcceptTcpClient", class: "TcpListener", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11243
|
+
{ method: "OpenRead", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11244
|
+
{ method: "DownloadString", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11136
11245
|
{ method: "getString", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
|
|
11137
11246
|
{ method: "getObject", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
|
|
11138
11247
|
{ method: "getInt", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
|
|
@@ -12482,8 +12591,6 @@ var DEFAULT_SINKS = [
|
|
|
12482
12591
|
{ method: "deserialize", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
|
|
12483
12592
|
{ method: "from_str", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12484
12593
|
{ method: "from_str", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12485
|
-
{ method: "from_str", type: "deserialization", cwe: "CWE-502", severity: "medium", arg_positions: [0] },
|
|
12486
|
-
{ method: "from_slice", type: "deserialization", cwe: "CWE-502", severity: "medium", arg_positions: [0] },
|
|
12487
12594
|
{ method: "body", class: "HttpResponseBuilder", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
12488
12595
|
{ method: "body", class: "HttpResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
12489
12596
|
{ method: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
@@ -12658,8 +12765,8 @@ var DEFAULT_SINKS = [
|
|
|
12658
12765
|
{ method: "OpenRead", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12659
12766
|
{ method: "OpenWrite", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12660
12767
|
{ method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12661
|
-
{ method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12662
|
-
{ method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12768
|
+
{ method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
|
|
12769
|
+
{ method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
|
|
12663
12770
|
{ method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
|
|
12664
12771
|
{ method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
|
|
12665
12772
|
{ method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -12689,6 +12796,10 @@ var DEFAULT_SINKS = [
|
|
|
12689
12796
|
{ method: "DownloadFile", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [1], languages: ["csharp"] },
|
|
12690
12797
|
{ method: "Create", class: "WebRequest", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12691
12798
|
{ method: "RestClient", class: "constructor", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12799
|
+
{ method: "CompileAssemblyFromSource", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
|
|
12800
|
+
{ method: "CompileAssemblyFromSourceBatch", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
|
|
12801
|
+
{ method: "ParseText", class: "CSharpSyntaxTree", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12802
|
+
{ method: "ParseText", class: "SyntaxFactory", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12692
12803
|
{ method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12693
12804
|
{ method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12694
12805
|
{ method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -12707,10 +12818,12 @@ var DEFAULT_SINKS = [
|
|
|
12707
12818
|
{ method: "WriteAsync", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12708
12819
|
{ method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12709
12820
|
{ method: "MarkupString", class: "constructor", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12821
|
+
{ method: "StatusDescription", class: "Response", type: "xss", cwe: "CWE-81", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
12710
12822
|
{ method: "Content", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12711
12823
|
{ method: "Format", class: "String", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
12712
12824
|
{ method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12713
12825
|
{ method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12826
|
+
{ method: "BaseAddress", class: "HttpClient", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12714
12827
|
{ method: "Redirect", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
12715
12828
|
{ method: "RedirectPermanent", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
12716
12829
|
{ method: "AddHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["csharp"] },
|
|
@@ -14274,6 +14387,15 @@ function escapeRe(s) {
|
|
|
14274
14387
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14275
14388
|
}
|
|
14276
14389
|
var TYPE_TOKEN_RE = /^new\s+(?:TypeReference|TypeToken)\s*<[\s\S]*>\s*\(\s*\)\s*\{\s*\}$/;
|
|
14390
|
+
function argIsStreamExpression(call, position) {
|
|
14391
|
+
const arg = call.arguments.find((a) => a.position === position);
|
|
14392
|
+
if (!arg)
|
|
14393
|
+
return false;
|
|
14394
|
+
const expr = (arg.expression ?? "").trim();
|
|
14395
|
+
if (!expr)
|
|
14396
|
+
return false;
|
|
14397
|
+
return /^new\s+[A-Za-z_][\w.]*Stream\s*\(/.test(expr) || /\.\s*Get(?:Response)?Stream\s*\(/.test(expr) || /\.\s*OpenReadStream\s*\(/.test(expr) || /\.\s*OpenRead\s*\(/.test(expr) || /\.\s*(?:Body|BaseStream)\s*$/.test(expr) || /^(?:[A-Za-z_]\w*\.)*[A-Za-z_]*[sS]tream\d*$/.test(expr);
|
|
14398
|
+
}
|
|
14277
14399
|
function argIsStringLiteral(call, position) {
|
|
14278
14400
|
const arg = call.arguments.find((a) => a.position === position);
|
|
14279
14401
|
if (!arg)
|
|
@@ -14544,6 +14666,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
14544
14666
|
if (pattern.safe_if_string_literal_at !== undefined && argIsStringLiteral(call, pattern.safe_if_string_literal_at)) {
|
|
14545
14667
|
continue;
|
|
14546
14668
|
}
|
|
14669
|
+
if (pattern.safe_if_stream_arg_at !== undefined && argIsStreamExpression(call, pattern.safe_if_stream_arg_at)) {
|
|
14670
|
+
continue;
|
|
14671
|
+
}
|
|
14547
14672
|
if (pattern.type === "command_injection") {
|
|
14548
14673
|
if (call.is_constructor) {
|
|
14549
14674
|
if (!CWE_78_RECEIVER_ALLOWLIST.has(call.method_name)) {
|
|
@@ -14920,8 +15045,9 @@ function matchesSinkPattern(call, pattern, typeHierarchy, language) {
|
|
|
14920
15045
|
return false;
|
|
14921
15046
|
} else if (!call.receiver && !call.receiver_type) {
|
|
14922
15047
|
const target = call.resolution?.target;
|
|
15048
|
+
const normalisedTarget = language === "rust" ? target?.replace(/::/g, ".") : target;
|
|
14923
15049
|
const expectedTail = `${pattern.class}.${pattern.method}`;
|
|
14924
|
-
if (
|
|
15050
|
+
if (normalisedTarget && (normalisedTarget === expectedTail || normalisedTarget.endsWith("." + expectedTail))) {} else {
|
|
14925
15051
|
return false;
|
|
14926
15052
|
}
|
|
14927
15053
|
}
|
|
@@ -26079,7 +26205,7 @@ class LanguageSourcesPass {
|
|
|
26079
26205
|
}
|
|
26080
26206
|
}
|
|
26081
26207
|
if (language === "csharp") {
|
|
26082
|
-
additionalSanitizers.push(...findCSharpFullPathStartsWithGuardSanitizers(code));
|
|
26208
|
+
additionalSanitizers.push(...findCSharpFullPathStartsWithGuardSanitizers(code), ...findCSharpEarlyReturnGuardSanitizers(code, language));
|
|
26083
26209
|
}
|
|
26084
26210
|
if (language === "java") {
|
|
26085
26211
|
additionalSanitizers.push(...findJavaSafeJsonParseSanitizers(code));
|
|
@@ -26791,6 +26917,10 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26791
26917
|
const formFileDeclRe = /\bIFormFile\s+([A-Za-z_]\w*)/g;
|
|
26792
26918
|
const httpRequestReceivers = new Set(["Request"]);
|
|
26793
26919
|
const httpRequestDeclRe = /\b(?:HttpRequest|HttpRequestBase)\??\s+([A-Za-z_]\w*)/g;
|
|
26920
|
+
const streamReaderVars = new Set;
|
|
26921
|
+
const streamReaderDeclRe = /\b(?:StreamReader|TextReader|BinaryReader)\??\s+([A-Za-z_]\w*)/g;
|
|
26922
|
+
const dbReaderVars = new Set;
|
|
26923
|
+
const dbReaderDeclRe = /\b(?:SqlDataReader|DbDataReader|IDataReader|OdbcDataReader|OleDbDataReader)\??\s+([A-Za-z_]\w*)/g;
|
|
26794
26924
|
for (const line of lines) {
|
|
26795
26925
|
const formRe = new RegExp(formFileDeclRe.source, "g");
|
|
26796
26926
|
let d;
|
|
@@ -26799,15 +26929,25 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26799
26929
|
const reqRe = new RegExp(httpRequestDeclRe.source, "g");
|
|
26800
26930
|
while ((d = reqRe.exec(line)) !== null)
|
|
26801
26931
|
httpRequestReceivers.add(d[1]);
|
|
26802
|
-
|
|
26932
|
+
const srRe = new RegExp(streamReaderDeclRe.source, "g");
|
|
26933
|
+
while ((d = srRe.exec(line)) !== null)
|
|
26934
|
+
streamReaderVars.add(d[1]);
|
|
26935
|
+
const dbRe = new RegExp(dbReaderDeclRe.source, "g");
|
|
26936
|
+
while ((d = dbRe.exec(line)) !== null)
|
|
26937
|
+
dbReaderVars.add(d[1]);
|
|
26938
|
+
}
|
|
26939
|
+
const streamReadRe = streamReaderVars.size > 0 ? new RegExp(`\\b(?:${[...streamReaderVars].join("|")})\\s*\\.\\s*(?:ReadLine|ReadToEnd|ReadLineAsync|ReadToEndAsync|ReadBlock|Read)\\s*\\(`) : null;
|
|
26940
|
+
const dbReadRe = dbReaderVars.size > 0 ? new RegExp(`\\b(?:${[...dbReaderVars].join("|")})\\s*\\.\\s*(?:GetString|GetValue|GetInt32|GetInt64|GetDecimal|GetDateTime|GetFieldValue|\\[)`) : null;
|
|
26803
26941
|
const formFileReadRe = formFileParams.size > 0 ? new RegExp(`\\b(?:${[...formFileParams].join("|")})\\s*\\.\\s*(?:FileName|ContentType)\\b`) : null;
|
|
26804
26942
|
const requestReadRe = new RegExp(`\\b(?:${[...httpRequestReceivers].join("|")})\\s*\\.\\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\\b`);
|
|
26805
|
-
const classify = (text) => requestReadRe.test(text) ? "http_param" : formFileReadRe?.test(text) ? "http_param" : consoleReadRe.test(text) ? "io_input" : envReadRe.test(text) ? "env_input" : null;
|
|
26943
|
+
const classify = (text) => requestReadRe.test(text) ? "http_param" : formFileReadRe?.test(text) ? "http_param" : consoleReadRe.test(text) ? "io_input" : envReadRe.test(text) ? "env_input" : streamReadRe?.test(text) ? "io_input" : dbReadRe?.test(text) ? "db_input" : null;
|
|
26806
26944
|
const inlineReads = [
|
|
26807
26945
|
[requestReadRe, "http_param"],
|
|
26808
26946
|
[formFileReadRe, "http_param"],
|
|
26809
26947
|
[consoleReadRe, "io_input"],
|
|
26810
|
-
[envReadRe, "env_input"]
|
|
26948
|
+
[envReadRe, "env_input"],
|
|
26949
|
+
[streamReadRe, "io_input"],
|
|
26950
|
+
[dbReadRe, "db_input"]
|
|
26811
26951
|
];
|
|
26812
26952
|
const inlineRead = (text) => {
|
|
26813
26953
|
const argsStart = text.indexOf("(");
|
|
@@ -26829,7 +26969,8 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26829
26969
|
const embeddedAssignRe = /(?:^|[{;])\s*(?:await\s+)?(?:using\s*\(?\s*)?(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*([^;{}]+)/;
|
|
26830
26970
|
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
26831
26971
|
const m = assignRe.exec(lines[i2]);
|
|
26832
|
-
|
|
26972
|
+
const rhsCrossesBlock = m !== null && m[2].includes("{");
|
|
26973
|
+
if (!m || rhsCrossesBlock) {
|
|
26833
26974
|
const stmt = lines[i2];
|
|
26834
26975
|
if (stmt.includes("{") && !/^\s*(?:\/\/|\/\*|\*)/.test(stmt)) {
|
|
26835
26976
|
const emb = new RegExp(embeddedAssignRe.source, "g");
|
|
@@ -26847,7 +26988,7 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26847
26988
|
sources.push({
|
|
26848
26989
|
type: type3,
|
|
26849
26990
|
location: `${varName2} = ${rhs2.trim().substring(0, 50)}${rhs2.length > 50 ? "..." : ""}`,
|
|
26850
|
-
severity: "high",
|
|
26991
|
+
severity: type3 === "db_input" ? "medium" : "high",
|
|
26851
26992
|
line: lineNumber3,
|
|
26852
26993
|
confidence: 1,
|
|
26853
26994
|
variable: varName2
|
|
@@ -26870,7 +27011,7 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26870
27011
|
sources.push({
|
|
26871
27012
|
type: type2,
|
|
26872
27013
|
location: `${read} in ${code.substring(0, 50)}${code.length > 50 ? "..." : ""}`,
|
|
26873
|
-
severity: "high",
|
|
27014
|
+
severity: type2 === "db_input" ? "medium" : "high",
|
|
26874
27015
|
line: lineNumber2,
|
|
26875
27016
|
confidence: 1,
|
|
26876
27017
|
variable: "<inline>",
|
|
@@ -26888,7 +27029,7 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26888
27029
|
sources.push({
|
|
26889
27030
|
type,
|
|
26890
27031
|
location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
|
|
26891
|
-
severity: "high",
|
|
27032
|
+
severity: type === "db_input" ? "medium" : "high",
|
|
26892
27033
|
line: lineNumber,
|
|
26893
27034
|
confidence: 1,
|
|
26894
27035
|
variable: varName
|
|
@@ -27082,7 +27223,7 @@ function findPythonAssignmentSources(sourceCode, language) {
|
|
|
27082
27223
|
sources.push({
|
|
27083
27224
|
type,
|
|
27084
27225
|
location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
|
|
27085
|
-
severity: "high",
|
|
27226
|
+
severity: type === "db_input" ? "medium" : "high",
|
|
27086
27227
|
line: lineNumber,
|
|
27087
27228
|
confidence: 0.95,
|
|
27088
27229
|
variable: varName
|
|
@@ -29574,6 +29715,76 @@ function guardRejects(lines, i2, terminatorRe, window2) {
|
|
|
29574
29715
|
}
|
|
29575
29716
|
return false;
|
|
29576
29717
|
}
|
|
29718
|
+
function findCSharpEarlyReturnGuardSanitizers(code, language) {
|
|
29719
|
+
if (language !== "csharp")
|
|
29720
|
+
return [];
|
|
29721
|
+
const sanitizers = [];
|
|
29722
|
+
const lines = code.split(`
|
|
29723
|
+
`);
|
|
29724
|
+
const terminatorRe = /\b(throw|return)\b/;
|
|
29725
|
+
const allowlistRes = [
|
|
29726
|
+
/\bif\s*\(\s*Array\s*\.\s*IndexOf\s*\([^,]+,\s*([A-Za-z_][\w.]*)\s*\)\s*<\s*0\s*\)/,
|
|
29727
|
+
/\bif\s*\(\s*!\s*[A-Za-z_][\w.]*\s*\.\s*Contains\s*\(\s*([A-Za-z_][\w.]*)\s*\)\s*\)/
|
|
29728
|
+
];
|
|
29729
|
+
const charClassRe = /\bif\s*\(\s*!\s*([A-Za-z_][\w.]*)\s*\.\s*All\s*\(\s*char\s*\.\s*Is[A-Za-z]+\s*\)\s*\)/;
|
|
29730
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
29731
|
+
let guardedExpr = null;
|
|
29732
|
+
for (const re of allowlistRes) {
|
|
29733
|
+
const m = re.exec(lines[i2]);
|
|
29734
|
+
if (m) {
|
|
29735
|
+
guardedExpr = m[1];
|
|
29736
|
+
break;
|
|
29737
|
+
}
|
|
29738
|
+
}
|
|
29739
|
+
if (!guardedExpr) {
|
|
29740
|
+
const m = charClassRe.exec(lines[i2]);
|
|
29741
|
+
if (m)
|
|
29742
|
+
guardedExpr = m[1];
|
|
29743
|
+
}
|
|
29744
|
+
if (!guardedExpr)
|
|
29745
|
+
continue;
|
|
29746
|
+
if (!guardRejects(lines, i2, terminatorRe, 6))
|
|
29747
|
+
continue;
|
|
29748
|
+
const root = guardedExpr.split(".")[0];
|
|
29749
|
+
const guarded = new Set([guardedExpr, root]);
|
|
29750
|
+
const assignRe = /^\s*(?:(?:var|string)\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*(.*)$/;
|
|
29751
|
+
for (let b = i2 - 1;b >= 0; b--) {
|
|
29752
|
+
const a = assignRe.exec(lines[b]);
|
|
29753
|
+
if (!a || !guarded.has(a[1]))
|
|
29754
|
+
continue;
|
|
29755
|
+
for (const id of a[2].match(/[A-Za-z_]\w*/g) ?? [])
|
|
29756
|
+
guarded.add(id);
|
|
29757
|
+
}
|
|
29758
|
+
const mentions = (t) => {
|
|
29759
|
+
for (const n of guarded)
|
|
29760
|
+
if (new RegExp(`\\b${n.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`).test(t))
|
|
29761
|
+
return true;
|
|
29762
|
+
return false;
|
|
29763
|
+
};
|
|
29764
|
+
for (let l = i2 + 1;l < lines.length; l++) {
|
|
29765
|
+
const a = assignRe.exec(lines[l]);
|
|
29766
|
+
if (a && mentions(a[2]))
|
|
29767
|
+
guarded.add(a[1]);
|
|
29768
|
+
if (mentions(lines[l])) {
|
|
29769
|
+
sanitizers.push({
|
|
29770
|
+
type: "csharp_early_return_guard",
|
|
29771
|
+
method: "guard",
|
|
29772
|
+
line: l + 1,
|
|
29773
|
+
sanitizes: [
|
|
29774
|
+
"ssrf",
|
|
29775
|
+
"xpath_injection",
|
|
29776
|
+
"ldap_injection",
|
|
29777
|
+
"path_traversal",
|
|
29778
|
+
"command_injection",
|
|
29779
|
+
"sql_injection",
|
|
29780
|
+
"external_taint_escape"
|
|
29781
|
+
]
|
|
29782
|
+
});
|
|
29783
|
+
}
|
|
29784
|
+
}
|
|
29785
|
+
}
|
|
29786
|
+
return sanitizers;
|
|
29787
|
+
}
|
|
29577
29788
|
function findCSharpFullPathStartsWithGuardSanitizers(code) {
|
|
29578
29789
|
const sanitizers = [];
|
|
29579
29790
|
const lines = code.split(`
|
|
@@ -36647,6 +36858,65 @@ class LibraryProfileXssGatePass {
|
|
|
36647
36858
|
}
|
|
36648
36859
|
|
|
36649
36860
|
// ../circle-ir/dist/analysis/passes/taint-propagation-pass.js
|
|
36861
|
+
var CONTAINER_READ_SOURCES = new Set(["plugin_param", "config_param"]);
|
|
36862
|
+
function makeConstPropVeto(constProp, graph, sources, code) {
|
|
36863
|
+
const lines = typeof code === "string" ? code.split(`
|
|
36864
|
+
`) : [];
|
|
36865
|
+
const selfReassign = new Map;
|
|
36866
|
+
const escape = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
36867
|
+
for (const [line, defs] of graph.defsByLine) {
|
|
36868
|
+
const text = lines[line - 1];
|
|
36869
|
+
if (!text)
|
|
36870
|
+
continue;
|
|
36871
|
+
for (const d of defs) {
|
|
36872
|
+
if (d.kind !== "local" && d.kind !== "param")
|
|
36873
|
+
continue;
|
|
36874
|
+
const v = escape(d.variable);
|
|
36875
|
+
const m = new RegExp(`(?:^|[^\\w.$])${v}\\s*(?:([-+*/%&|^]|<<|>>)=|=(?!=))(.*)$`).exec(text);
|
|
36876
|
+
if (!m)
|
|
36877
|
+
continue;
|
|
36878
|
+
if (m[1] || new RegExp(`(?:^|[^\\w.$])${v}\\b`).test(m[2])) {
|
|
36879
|
+
const arr = selfReassign.get(d.variable) ?? [];
|
|
36880
|
+
arr.push(line);
|
|
36881
|
+
selfReassign.set(d.variable, arr);
|
|
36882
|
+
}
|
|
36883
|
+
}
|
|
36884
|
+
}
|
|
36885
|
+
const reassignedAt = (v, line) => (selfReassign.get(v) ?? []).includes(line);
|
|
36886
|
+
const defAt = (v, line) => {
|
|
36887
|
+
const own = (graph.defsByLine.get(line) ?? []).find((d) => d.variable === v);
|
|
36888
|
+
if (own)
|
|
36889
|
+
return own;
|
|
36890
|
+
const use = (graph.usesByLine.get(line) ?? []).find((u) => u.variable === v && u.def_id != null);
|
|
36891
|
+
return use ? graph.defById.get(use.def_id) : undefined;
|
|
36892
|
+
};
|
|
36893
|
+
return (step, sourceLine, sinkLine) => {
|
|
36894
|
+
const r = isFalsePositive(constProp, step.line, step.variable);
|
|
36895
|
+
if (!r.isFalsePositive)
|
|
36896
|
+
return false;
|
|
36897
|
+
if (r.reason !== "variable_not_tainted")
|
|
36898
|
+
return true;
|
|
36899
|
+
const lo = Math.min(sourceLine, sinkLine);
|
|
36900
|
+
let d = defAt(step.variable, step.line);
|
|
36901
|
+
let selfHops = 0;
|
|
36902
|
+
for (let guard = 0;d && d.line > lo && reassignedAt(d.variable, d.line) && guard < 32; guard++) {
|
|
36903
|
+
const here = d.id;
|
|
36904
|
+
const prior = (graph.usesByLine.get(d.line) ?? []).find((u) => u.variable === d.variable && u.def_id != null && u.def_id !== here);
|
|
36905
|
+
if (!prior)
|
|
36906
|
+
break;
|
|
36907
|
+
selfHops++;
|
|
36908
|
+
d = graph.defById.get(prior.def_id);
|
|
36909
|
+
}
|
|
36910
|
+
if (!d)
|
|
36911
|
+
return true;
|
|
36912
|
+
if (d.line === sourceLine && sources.some((s) => s.line === sourceLine && !CONTAINER_READ_SOURCES.has(s.type)))
|
|
36913
|
+
return false;
|
|
36914
|
+
if (selfHops > 0 && d.line >= lo)
|
|
36915
|
+
return false;
|
|
36916
|
+
return true;
|
|
36917
|
+
};
|
|
36918
|
+
}
|
|
36919
|
+
|
|
36650
36920
|
class TaintPropagationPass {
|
|
36651
36921
|
name = "taint-propagation";
|
|
36652
36922
|
category = "security";
|
|
@@ -36664,12 +36934,12 @@ class TaintPropagationPass {
|
|
|
36664
36934
|
return { flows: [] };
|
|
36665
36935
|
}
|
|
36666
36936
|
const propagationResult = propagateTaint(graph, sources, sinks, sanitizers);
|
|
36937
|
+
const isVetoed = makeConstPropVeto(constProp, graph, sources, ctx.code);
|
|
36667
36938
|
const verifiedFlows = propagationResult.flows.filter((flow) => {
|
|
36668
36939
|
if (constProp.unreachableLines.has(flow.sink.line))
|
|
36669
36940
|
return false;
|
|
36670
36941
|
for (const step of flow.path) {
|
|
36671
|
-
|
|
36672
|
-
if (fpCheck.isFalsePositive)
|
|
36942
|
+
if (isVetoed(step, flow.source.line, flow.sink.line))
|
|
36673
36943
|
return false;
|
|
36674
36944
|
}
|
|
36675
36945
|
if (isCorrelatedPredicateFP(constProp, flow))
|
|
@@ -36775,7 +37045,7 @@ class TaintPropagationPass {
|
|
|
36775
37045
|
continue;
|
|
36776
37046
|
let isFP = false;
|
|
36777
37047
|
for (const step of f.path) {
|
|
36778
|
-
if (
|
|
37048
|
+
if (isVetoed(step, f.source_line, f.sink_line)) {
|
|
36779
37049
|
isFP = true;
|
|
36780
37050
|
break;
|
|
36781
37051
|
}
|
|
@@ -36801,7 +37071,7 @@ class TaintPropagationPass {
|
|
|
36801
37071
|
continue;
|
|
36802
37072
|
let isFP = false;
|
|
36803
37073
|
for (const step of f.path) {
|
|
36804
|
-
if (
|
|
37074
|
+
if (isVetoed(step, f.source_line, f.sink_line)) {
|
|
36805
37075
|
isFP = true;
|
|
36806
37076
|
break;
|
|
36807
37077
|
}
|
|
@@ -37549,13 +37819,17 @@ function maskStringLiteralsForTokenScan(expr) {
|
|
|
37549
37819
|
}
|
|
37550
37820
|
return out2;
|
|
37551
37821
|
}
|
|
37822
|
+
function argsReferenceTaintedVar(expr, sources) {
|
|
37823
|
+
const codePart = expr.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ").replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " ");
|
|
37824
|
+
return sources.some((s) => new RegExp(`(?<![\\p{L}\\p{N}_])${s.variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\p{L}\\p{N}_])`, "u").test(codePart));
|
|
37825
|
+
}
|
|
37552
37826
|
function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, tainted, code, language, types) {
|
|
37553
37827
|
const flows = [];
|
|
37554
|
-
const
|
|
37555
|
-
const
|
|
37828
|
+
const methodRanges = (langs) => language && langs.includes(language) && types ? types.flatMap((t) => t.methods.map((m) => [m.start_line, m.end_line])) : [];
|
|
37829
|
+
const enclosingStart = (ranges, line) => {
|
|
37556
37830
|
let best = -1;
|
|
37557
37831
|
let bestSpan = -1;
|
|
37558
|
-
for (const [start2, end] of
|
|
37832
|
+
for (const [start2, end] of ranges) {
|
|
37559
37833
|
if (line >= start2 && line <= end && end - start2 > bestSpan) {
|
|
37560
37834
|
best = start2;
|
|
37561
37835
|
bestSpan = end - start2;
|
|
@@ -37563,6 +37837,10 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
37563
37837
|
}
|
|
37564
37838
|
return best;
|
|
37565
37839
|
};
|
|
37840
|
+
const goFnRanges = methodRanges(["go"]);
|
|
37841
|
+
const goFnStart = (line) => enclosingStart(goFnRanges, line);
|
|
37842
|
+
const csFnRanges = methodRanges(["csharp"]);
|
|
37843
|
+
const csFnStart = (line) => enclosingStart(csFnRanges, line);
|
|
37566
37844
|
const sourcesWithVar = sources.filter((s) => typeof s.variable === "string" && s.variable.length > 0);
|
|
37567
37845
|
const aliasSanitizedFor = new Map;
|
|
37568
37846
|
if (language === "python" && typeof code === "string") {
|
|
@@ -37898,15 +38176,51 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
37898
38176
|
const [, obj, rhs] = m;
|
|
37899
38177
|
if (known.has(obj))
|
|
37900
38178
|
continue;
|
|
37901
|
-
|
|
37902
|
-
const rhsTainted = sourcesWithVar.some((s) => new RegExp(`(?<![\\p{L}\\p{N}_])${s.variable.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}(?![\\p{L}\\p{N}_])`, "u").test(codePart));
|
|
37903
|
-
if (!rhsTainted)
|
|
38179
|
+
if (!argsReferenceTaintedVar(rhs, sourcesWithVar))
|
|
37904
38180
|
continue;
|
|
37905
38181
|
sourcesWithVar.push({ ...anchor, variable: obj, line: i2 + 1 });
|
|
37906
38182
|
added = true;
|
|
37907
38183
|
}
|
|
37908
38184
|
}
|
|
37909
38185
|
}
|
|
38186
|
+
const builderSeeds = new Set;
|
|
38187
|
+
if (language === "csharp" && typeof code === "string" && sourcesWithVar.length > 0) {
|
|
38188
|
+
const lines = code.split(`
|
|
38189
|
+
`);
|
|
38190
|
+
const builders = new Set;
|
|
38191
|
+
for (const line of lines) {
|
|
38192
|
+
for (const m of line.matchAll(/\bStringBuilder\s+([\p{L}_][\p{L}\p{N}_]*)/gu))
|
|
38193
|
+
builders.add(m[1]);
|
|
38194
|
+
for (const m of line.matchAll(/\b([\p{L}_][\p{L}\p{N}_]*)\s*=\s*new\s+StringBuilder\b/gu))
|
|
38195
|
+
builders.add(m[1]);
|
|
38196
|
+
}
|
|
38197
|
+
if (builders.size > 0) {
|
|
38198
|
+
const appendRe = /([\p{L}_][\p{L}\p{N}_]*)\s*\.\s*(?:Append|AppendLine|AppendFormat|Insert)\s*\(([\s\S]*)/u;
|
|
38199
|
+
const anchor = sourcesWithVar.reduce((a, s) => s.line < a.line ? s : a, sourcesWithVar[0]);
|
|
38200
|
+
let added = true;
|
|
38201
|
+
let guard = 0;
|
|
38202
|
+
while (added && guard < lines.length + 2) {
|
|
38203
|
+
added = false;
|
|
38204
|
+
guard++;
|
|
38205
|
+
const known = new Set(sourcesWithVar.map((s) => s.variable));
|
|
38206
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
38207
|
+
const m = appendRe.exec(lines[i2]);
|
|
38208
|
+
if (!m)
|
|
38209
|
+
continue;
|
|
38210
|
+
const [, obj, args2] = m;
|
|
38211
|
+
if (!builders.has(obj) || known.has(obj))
|
|
38212
|
+
continue;
|
|
38213
|
+
const appendFn = csFnStart(i2 + 1);
|
|
38214
|
+
const reachable = sourcesWithVar.filter((sv) => appendFn === -1 || csFnStart(sv.line) === -1 || csFnStart(sv.line) === appendFn);
|
|
38215
|
+
if (reachable.length > 0 && argsReferenceTaintedVar(args2, reachable)) {
|
|
38216
|
+
sourcesWithVar.push({ ...anchor, variable: obj, line: i2 + 1 });
|
|
38217
|
+
builderSeeds.add(`${obj}@${i2 + 1}`);
|
|
38218
|
+
added = true;
|
|
38219
|
+
}
|
|
38220
|
+
}
|
|
38221
|
+
}
|
|
38222
|
+
}
|
|
38223
|
+
}
|
|
37910
38224
|
const SIMPLE_IDENT = /^[\p{L}\p{N}_]+$/u;
|
|
37911
38225
|
const reCache = new Map;
|
|
37912
38226
|
for (const s of sourcesWithVar) {
|
|
@@ -37977,6 +38291,12 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
37977
38291
|
if (srcFn !== -1 && sinkFn !== -1 && srcFn !== sinkFn)
|
|
37978
38292
|
continue;
|
|
37979
38293
|
}
|
|
38294
|
+
if (csFnRanges.length > 0 && builderSeeds.has(`${source.variable}@${source.line}`)) {
|
|
38295
|
+
const srcFn = csFnStart(source.line);
|
|
38296
|
+
const sinkFn = csFnStart(sink.line);
|
|
38297
|
+
if (srcFn !== sinkFn)
|
|
38298
|
+
continue;
|
|
38299
|
+
}
|
|
37980
38300
|
if (complex) {
|
|
37981
38301
|
const re = reCache.get(source.variable);
|
|
37982
38302
|
if (!re || !re.test(expr))
|
|
@@ -38205,6 +38525,109 @@ var GO_PURE_PACKAGES = new Set([
|
|
|
38205
38525
|
"hex",
|
|
38206
38526
|
"base64"
|
|
38207
38527
|
]);
|
|
38528
|
+
var CSHARP_PURE_STATIC_RECEIVERS = new Set([
|
|
38529
|
+
"string",
|
|
38530
|
+
"String",
|
|
38531
|
+
"Convert",
|
|
38532
|
+
"Encoding",
|
|
38533
|
+
"BitConverter",
|
|
38534
|
+
"Math",
|
|
38535
|
+
"Int16",
|
|
38536
|
+
"Int32",
|
|
38537
|
+
"Int64",
|
|
38538
|
+
"UInt16",
|
|
38539
|
+
"UInt32",
|
|
38540
|
+
"UInt64",
|
|
38541
|
+
"Double",
|
|
38542
|
+
"Single",
|
|
38543
|
+
"Decimal",
|
|
38544
|
+
"Boolean",
|
|
38545
|
+
"Byte",
|
|
38546
|
+
"SByte",
|
|
38547
|
+
"Char",
|
|
38548
|
+
"Guid",
|
|
38549
|
+
"DateTime",
|
|
38550
|
+
"DateTimeOffset",
|
|
38551
|
+
"TimeSpan",
|
|
38552
|
+
"Enum",
|
|
38553
|
+
"Array",
|
|
38554
|
+
"Buffer",
|
|
38555
|
+
"Nullable",
|
|
38556
|
+
"CultureInfo",
|
|
38557
|
+
"StringComparer",
|
|
38558
|
+
"Comparer",
|
|
38559
|
+
"EqualityComparer",
|
|
38560
|
+
"Console",
|
|
38561
|
+
"Debug",
|
|
38562
|
+
"Trace",
|
|
38563
|
+
"Logger",
|
|
38564
|
+
"logger",
|
|
38565
|
+
"_logger",
|
|
38566
|
+
"Log"
|
|
38567
|
+
]);
|
|
38568
|
+
var CSHARP_NON_ESCAPE_METHODS = new Set([
|
|
38569
|
+
"ToString",
|
|
38570
|
+
"ToArray",
|
|
38571
|
+
"ToList",
|
|
38572
|
+
"ToCharArray",
|
|
38573
|
+
"GetHashCode",
|
|
38574
|
+
"Equals",
|
|
38575
|
+
"CompareTo",
|
|
38576
|
+
"Trim",
|
|
38577
|
+
"TrimStart",
|
|
38578
|
+
"TrimEnd",
|
|
38579
|
+
"Substring",
|
|
38580
|
+
"Split",
|
|
38581
|
+
"Join",
|
|
38582
|
+
"Concat",
|
|
38583
|
+
"PadLeft",
|
|
38584
|
+
"PadRight",
|
|
38585
|
+
"ToLower",
|
|
38586
|
+
"ToUpper",
|
|
38587
|
+
"ToLowerInvariant",
|
|
38588
|
+
"ToUpperInvariant",
|
|
38589
|
+
"StartsWith",
|
|
38590
|
+
"EndsWith",
|
|
38591
|
+
"Contains",
|
|
38592
|
+
"IndexOf",
|
|
38593
|
+
"LastIndexOf",
|
|
38594
|
+
"Insert",
|
|
38595
|
+
"Remove",
|
|
38596
|
+
"Normalize",
|
|
38597
|
+
"Append",
|
|
38598
|
+
"AppendLine",
|
|
38599
|
+
"AppendFormat",
|
|
38600
|
+
"TryParse",
|
|
38601
|
+
"Parse",
|
|
38602
|
+
"LogInformation",
|
|
38603
|
+
"LogWarning",
|
|
38604
|
+
"LogError",
|
|
38605
|
+
"LogDebug",
|
|
38606
|
+
"LogTrace",
|
|
38607
|
+
"LogCritical",
|
|
38608
|
+
"Information",
|
|
38609
|
+
"Warning",
|
|
38610
|
+
"Verbose",
|
|
38611
|
+
"Fatal",
|
|
38612
|
+
"BeginScope",
|
|
38613
|
+
"Add",
|
|
38614
|
+
"AddLast",
|
|
38615
|
+
"AddFirst",
|
|
38616
|
+
"AddRange",
|
|
38617
|
+
"TryAdd",
|
|
38618
|
+
"Insert",
|
|
38619
|
+
"Push",
|
|
38620
|
+
"Enqueue"
|
|
38621
|
+
]);
|
|
38622
|
+
function isCSharpNonEscapeCall(call) {
|
|
38623
|
+
const name2 = call.method_name;
|
|
38624
|
+
const recv = (call.receiver ?? "").split(".").pop() ?? "";
|
|
38625
|
+
if (CSHARP_PURE_STATIC_RECEIVERS.has(recv))
|
|
38626
|
+
return true;
|
|
38627
|
+
if (CSHARP_NON_ESCAPE_METHODS.has(name2))
|
|
38628
|
+
return true;
|
|
38629
|
+
return false;
|
|
38630
|
+
}
|
|
38208
38631
|
function isGoNonEscapeCall(call) {
|
|
38209
38632
|
const name2 = call.method_name;
|
|
38210
38633
|
const recv = (call.receiver ?? "").split(".").pop() ?? "";
|
|
@@ -38452,6 +38875,9 @@ function analyzeInterprocedural2(graphOrTypes, callsOrSources, dfgOrSinks, sourc
|
|
|
38452
38875
|
if (graph.ir.meta.language === "go" && isGoNonEscapeCall(call)) {
|
|
38453
38876
|
continue;
|
|
38454
38877
|
}
|
|
38878
|
+
if (graph.ir.meta.language === "csharp" && isCSharpNonEscapeCall(call)) {
|
|
38879
|
+
continue;
|
|
38880
|
+
}
|
|
38455
38881
|
const bashSafeBuiltins = new Set([
|
|
38456
38882
|
"echo",
|
|
38457
38883
|
"printf",
|
|
@@ -40086,6 +40512,8 @@ class VariableShadowingPass {
|
|
|
40086
40512
|
const outerEntry = declLocals[0];
|
|
40087
40513
|
for (let i2 = 1;i2 < numDeclLocals; i2++) {
|
|
40088
40514
|
const inner = declLocals[i2];
|
|
40515
|
+
if (inner.def.line <= outerEntry.def.line)
|
|
40516
|
+
continue;
|
|
40089
40517
|
if (!isInNestedScope(codeLines, outerEntry.def.line, inner.def.line))
|
|
40090
40518
|
continue;
|
|
40091
40519
|
const key = `${variable}-${inner.def.line}`;
|
|
@@ -50357,7 +50785,7 @@ var colors = {
|
|
|
50357
50785
|
};
|
|
50358
50786
|
|
|
50359
50787
|
// src/version.ts
|
|
50360
|
-
var version = "4.9.
|
|
50788
|
+
var version = "4.9.29";
|
|
50361
50789
|
|
|
50362
50790
|
// src/formatters.ts
|
|
50363
50791
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
|
@@ -50909,8 +51337,8 @@ function generateSarifResults(results, crossFileData) {
|
|
|
50909
51337
|
|
|
50910
51338
|
// src/build-info.ts
|
|
50911
51339
|
var buildInfo = {
|
|
50912
|
-
gitSha: "
|
|
50913
|
-
builtAt: "2026-
|
|
51340
|
+
gitSha: "9d1d45a",
|
|
51341
|
+
builtAt: "2026-10-01T03:14:59.073Z"
|
|
50914
51342
|
};
|
|
50915
51343
|
|
|
50916
51344
|
// src/utils/args.ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.29",
|
|
4
4
|
"description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
|
|
5
5
|
"bin": {
|
|
6
6
|
"cognium-dev": "dist/cli.js"
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@cognium/project-profile-detect": "1.1.2",
|
|
68
|
-
"circle-ir": "4.9.
|
|
68
|
+
"circle-ir": "4.9.29"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^26.6.2",
|