cognium-dev 4.9.28 → 4.10.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 +238 -33
- package/package.json +3 -3
package/dist/cli.js
CHANGED
|
@@ -3667,7 +3667,7 @@ function detectLanguage(tree) {
|
|
|
3667
3667
|
return "python";
|
|
3668
3668
|
return jsScore > javaScore ? "javascript" : "java";
|
|
3669
3669
|
}
|
|
3670
|
-
function extractTypes(tree, cache, language) {
|
|
3670
|
+
function extractTypes(tree, cache, language, options) {
|
|
3671
3671
|
const effectiveLanguage = language ?? detectLanguage(tree);
|
|
3672
3672
|
const isJavaScript = effectiveLanguage === "javascript" || effectiveLanguage === "typescript" || effectiveLanguage === "tsx";
|
|
3673
3673
|
const isPython = effectiveLanguage === "python";
|
|
@@ -3687,7 +3687,27 @@ function extractTypes(tree, cache, language) {
|
|
|
3687
3687
|
if (effectiveLanguage === "csharp") {
|
|
3688
3688
|
return extractCSharpTypes(tree, cache);
|
|
3689
3689
|
}
|
|
3690
|
-
return extractJavaTypes(tree, cache);
|
|
3690
|
+
return extractJavaTypes(tree, cache, options);
|
|
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;
|
|
3691
3711
|
}
|
|
3692
3712
|
function extractCSharpTypes(tree, cache) {
|
|
3693
3713
|
const types = [];
|
|
@@ -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++) {
|
|
@@ -3827,7 +3843,7 @@ function extractCSharpModifiers(node) {
|
|
|
3827
3843
|
}
|
|
3828
3844
|
return mods;
|
|
3829
3845
|
}
|
|
3830
|
-
function extractJavaTypes(tree, cache) {
|
|
3846
|
+
function extractJavaTypes(tree, cache, options) {
|
|
3831
3847
|
const types = [];
|
|
3832
3848
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
3833
3849
|
for (const cls of classes) {
|
|
@@ -3841,8 +3857,81 @@ function extractJavaTypes(tree, cache) {
|
|
|
3841
3857
|
for (const enumDecl of enums) {
|
|
3842
3858
|
types.push(extractEnumInfo(enumDecl));
|
|
3843
3859
|
}
|
|
3860
|
+
if (!options?.navigationTypes)
|
|
3861
|
+
return types;
|
|
3862
|
+
for (const rec of getNodesFromCache(tree.rootNode, "record_declaration", cache)) {
|
|
3863
|
+
const info2 = extractClassInfo(rec);
|
|
3864
|
+
info2.is_record = true;
|
|
3865
|
+
for (const comp of recordComponents(rec)) {
|
|
3866
|
+
if (!info2.fields.some((f) => f.name === comp.name)) {
|
|
3867
|
+
info2.fields.push({
|
|
3868
|
+
name: comp.name,
|
|
3869
|
+
type: comp.type,
|
|
3870
|
+
modifiers: ["private", "final"],
|
|
3871
|
+
annotations: []
|
|
3872
|
+
});
|
|
3873
|
+
}
|
|
3874
|
+
if (!info2.methods.some((m) => m.name === comp.name)) {
|
|
3875
|
+
info2.methods.push({
|
|
3876
|
+
name: comp.name,
|
|
3877
|
+
return_type: comp.type,
|
|
3878
|
+
parameters: [],
|
|
3879
|
+
annotations: [],
|
|
3880
|
+
modifiers: ["public"],
|
|
3881
|
+
start_line: rec.startPosition.row + 1,
|
|
3882
|
+
end_line: rec.startPosition.row + 1
|
|
3883
|
+
});
|
|
3884
|
+
}
|
|
3885
|
+
}
|
|
3886
|
+
types.push(info2);
|
|
3887
|
+
}
|
|
3888
|
+
const declNodes = [
|
|
3889
|
+
...classes,
|
|
3890
|
+
...interfaces,
|
|
3891
|
+
...enums,
|
|
3892
|
+
...getNodesFromCache(tree.rootNode, "record_declaration", cache)
|
|
3893
|
+
];
|
|
3894
|
+
for (const t of types) {
|
|
3895
|
+
const node = declNodes.find((n) => n.startPosition.row + 1 === t.start_line && getIdentifier(n, "name") === t.name);
|
|
3896
|
+
const path = node ? enclosingTypePath(node) : [];
|
|
3897
|
+
if (path.length > 0)
|
|
3898
|
+
t.enclosing_type = path.join(".");
|
|
3899
|
+
}
|
|
3844
3900
|
return types;
|
|
3845
3901
|
}
|
|
3902
|
+
function recordComponents(node) {
|
|
3903
|
+
const out2 = [];
|
|
3904
|
+
const params = node.childForFieldName("parameters");
|
|
3905
|
+
if (!params)
|
|
3906
|
+
return out2;
|
|
3907
|
+
for (let i2 = 0;i2 < params.childCount; i2++) {
|
|
3908
|
+
const c = params.child(i2);
|
|
3909
|
+
if (!c || c.type !== "formal_parameter")
|
|
3910
|
+
continue;
|
|
3911
|
+
const name2 = c.childForFieldName("name");
|
|
3912
|
+
const type = c.childForFieldName("type");
|
|
3913
|
+
if (name2)
|
|
3914
|
+
out2.push({ name: getNodeText(name2), type: type ? getNodeText(type) : null });
|
|
3915
|
+
}
|
|
3916
|
+
return out2;
|
|
3917
|
+
}
|
|
3918
|
+
function enclosingTypePath(node) {
|
|
3919
|
+
const DECLS = new Set([
|
|
3920
|
+
"class_declaration",
|
|
3921
|
+
"interface_declaration",
|
|
3922
|
+
"enum_declaration",
|
|
3923
|
+
"record_declaration"
|
|
3924
|
+
]);
|
|
3925
|
+
const path = [];
|
|
3926
|
+
for (let p = node.parent;p; p = p.parent) {
|
|
3927
|
+
if (!DECLS.has(p.type))
|
|
3928
|
+
continue;
|
|
3929
|
+
const n = getIdentifier(p, "name");
|
|
3930
|
+
if (n)
|
|
3931
|
+
path.unshift(n);
|
|
3932
|
+
}
|
|
3933
|
+
return path;
|
|
3934
|
+
}
|
|
3846
3935
|
function extractJavaScriptTypes(tree, cache) {
|
|
3847
3936
|
const types = [];
|
|
3848
3937
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
@@ -5351,6 +5440,15 @@ function extractCalls(tree, cache, language) {
|
|
|
5351
5440
|
function buildCSharpReceiverTypeMap(tree, cache) {
|
|
5352
5441
|
const map = new Map;
|
|
5353
5442
|
const simple = (t) => t.replace(/<[^>]*>/g, "").split(".").pop()?.trim() ?? t;
|
|
5443
|
+
for (const prm of getNodesFromCache(tree.rootNode, "parameter", cache)) {
|
|
5444
|
+
const nameNode = prm.childForFieldName("name");
|
|
5445
|
+
const typeNode = prm.childForFieldName("type");
|
|
5446
|
+
if (nameNode?.type !== "identifier" || !typeNode)
|
|
5447
|
+
continue;
|
|
5448
|
+
const t = getNodeText(typeNode);
|
|
5449
|
+
if (t && t !== "var")
|
|
5450
|
+
map.set(getNodeText(nameNode), simple(t));
|
|
5451
|
+
}
|
|
5354
5452
|
for (const vd of getNodesFromCache(tree.rootNode, "variable_declaration", cache)) {
|
|
5355
5453
|
const typeNode = vd.childForFieldName("type");
|
|
5356
5454
|
const declaredType = typeNode ? getNodeText(typeNode) : null;
|
|
@@ -5387,6 +5485,7 @@ function findFirstDescendant(node, type) {
|
|
|
5387
5485
|
}
|
|
5388
5486
|
return null;
|
|
5389
5487
|
}
|
|
5488
|
+
var CSHARP_RESPONSE_TYPES = new Set(["HttpResponse", "HttpResponseBase"]);
|
|
5390
5489
|
var CSHARP_COMMAND_EXECUTE_METHODS = new Set([
|
|
5391
5490
|
"ExecuteScalar",
|
|
5392
5491
|
"ExecuteReader",
|
|
@@ -5547,7 +5646,8 @@ function extractCSharpCalls(tree, cache) {
|
|
|
5547
5646
|
continue;
|
|
5548
5647
|
} else if (propName === "StatusDescription") {
|
|
5549
5648
|
const recv = exprNode ? getNodeText(exprNode) : "";
|
|
5550
|
-
|
|
5649
|
+
const recvType = recv ? typeMap.get(recv) : undefined;
|
|
5650
|
+
if (!/(^|\.)(Response|HttpResponse|Context\.Response)$/.test(recv) && !(recvType && CSHARP_RESPONSE_TYPES.has(recvType)))
|
|
5551
5651
|
continue;
|
|
5552
5652
|
} else if (propName === "BaseAddress") {
|
|
5553
5653
|
const recv = exprNode ? getNodeText(exprNode) : null;
|
|
@@ -11207,6 +11307,14 @@ var DEFAULT_SOURCES = [
|
|
|
11207
11307
|
{ method: "nextLine", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
|
|
11208
11308
|
{ method: "next", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
|
|
11209
11309
|
{ method: "nextInt", class: "Scanner", type: "io_input", severity: "high", return_tainted: true },
|
|
11310
|
+
{ method: "ReadLine", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11311
|
+
{ method: "ReadLineAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11312
|
+
{ method: "ReadToEnd", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11313
|
+
{ method: "ReadToEndAsync", class: "StreamReader", type: "io_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11314
|
+
{ method: "GetStream", class: "TcpClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11315
|
+
{ method: "AcceptTcpClient", class: "TcpListener", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11316
|
+
{ method: "OpenRead", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11317
|
+
{ method: "DownloadString", class: "WebClient", type: "network_input", severity: "high", return_tainted: true, languages: ["csharp"] },
|
|
11210
11318
|
{ method: "getString", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
|
|
11211
11319
|
{ method: "getObject", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
|
|
11212
11320
|
{ method: "getInt", class: "ResultSet", type: "db_input", severity: "medium", return_tainted: true },
|
|
@@ -12556,6 +12664,22 @@ var DEFAULT_SINKS = [
|
|
|
12556
12664
|
{ method: "deserialize", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
|
|
12557
12665
|
{ method: "from_str", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12558
12666
|
{ method: "from_str", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12667
|
+
{ method: "from_slice", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12668
|
+
{ method: "from_read", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12669
|
+
{ method: "from_read_ref", class: "rmp_serde", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12670
|
+
{ method: "from_slice", class: "serde_yaml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12671
|
+
{ method: "from_slice", class: "toml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12672
|
+
{ method: "from_reader", class: "ron", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12673
|
+
{ method: "from_bytes", class: "postcard", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12674
|
+
{ method: "from_slice", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12675
|
+
{ method: "from_reader", class: "serde_cbor", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12676
|
+
{ method: "from_reader", class: "ciborium", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12677
|
+
{ method: "from_str", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12678
|
+
{ method: "from_reader", class: "serde_xml_rs", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12679
|
+
{ method: "from_str", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12680
|
+
{ method: "from_reader", class: "quick_xml", type: "deserialization", cwe: "CWE-502", severity: "high", arg_positions: [0] },
|
|
12681
|
+
{ method: "from_slice", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
|
|
12682
|
+
{ method: "deserialize_from", class: "bincode", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0] },
|
|
12559
12683
|
{ method: "body", class: "HttpResponseBuilder", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
12560
12684
|
{ method: "body", class: "HttpResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
12561
12685
|
{ method: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
@@ -12730,8 +12854,8 @@ var DEFAULT_SINKS = [
|
|
|
12730
12854
|
{ method: "OpenRead", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12731
12855
|
{ method: "OpenWrite", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12732
12856
|
{ method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12733
|
-
{ method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12734
|
-
{ method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12857
|
+
{ method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
|
|
12858
|
+
{ method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], safe_if_stream_arg_at: 0, languages: ["csharp"] },
|
|
12735
12859
|
{ method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
|
|
12736
12860
|
{ method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
|
|
12737
12861
|
{ method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -12761,6 +12885,10 @@ var DEFAULT_SINKS = [
|
|
|
12761
12885
|
{ method: "DownloadFile", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [1], languages: ["csharp"] },
|
|
12762
12886
|
{ method: "Create", class: "WebRequest", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12763
12887
|
{ method: "RestClient", class: "constructor", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12888
|
+
{ method: "CompileAssemblyFromSource", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
|
|
12889
|
+
{ method: "CompileAssemblyFromSourceBatch", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [1, 2], languages: ["csharp"] },
|
|
12890
|
+
{ method: "ParseText", class: "CSharpSyntaxTree", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12891
|
+
{ method: "ParseText", class: "SyntaxFactory", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12764
12892
|
{ method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12765
12893
|
{ method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12766
12894
|
{ method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -14348,6 +14476,15 @@ function escapeRe(s) {
|
|
|
14348
14476
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14349
14477
|
}
|
|
14350
14478
|
var TYPE_TOKEN_RE = /^new\s+(?:TypeReference|TypeToken)\s*<[\s\S]*>\s*\(\s*\)\s*\{\s*\}$/;
|
|
14479
|
+
function argIsStreamExpression(call, position) {
|
|
14480
|
+
const arg = call.arguments.find((a) => a.position === position);
|
|
14481
|
+
if (!arg)
|
|
14482
|
+
return false;
|
|
14483
|
+
const expr = (arg.expression ?? "").trim();
|
|
14484
|
+
if (!expr)
|
|
14485
|
+
return false;
|
|
14486
|
+
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);
|
|
14487
|
+
}
|
|
14351
14488
|
function argIsStringLiteral(call, position) {
|
|
14352
14489
|
const arg = call.arguments.find((a) => a.position === position);
|
|
14353
14490
|
if (!arg)
|
|
@@ -14618,6 +14755,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
14618
14755
|
if (pattern.safe_if_string_literal_at !== undefined && argIsStringLiteral(call, pattern.safe_if_string_literal_at)) {
|
|
14619
14756
|
continue;
|
|
14620
14757
|
}
|
|
14758
|
+
if (pattern.safe_if_stream_arg_at !== undefined && argIsStreamExpression(call, pattern.safe_if_stream_arg_at)) {
|
|
14759
|
+
continue;
|
|
14760
|
+
}
|
|
14621
14761
|
if (pattern.type === "command_injection") {
|
|
14622
14762
|
if (call.is_constructor) {
|
|
14623
14763
|
if (!CWE_78_RECEIVER_ALLOWLIST.has(call.method_name)) {
|
|
@@ -26866,6 +27006,10 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26866
27006
|
const formFileDeclRe = /\bIFormFile\s+([A-Za-z_]\w*)/g;
|
|
26867
27007
|
const httpRequestReceivers = new Set(["Request"]);
|
|
26868
27008
|
const httpRequestDeclRe = /\b(?:HttpRequest|HttpRequestBase)\??\s+([A-Za-z_]\w*)/g;
|
|
27009
|
+
const streamReaderVars = new Set;
|
|
27010
|
+
const streamReaderDeclRe = /\b(?:StreamReader|TextReader|BinaryReader)\??\s+([A-Za-z_]\w*)/g;
|
|
27011
|
+
const dbReaderVars = new Set;
|
|
27012
|
+
const dbReaderDeclRe = /\b(?:SqlDataReader|DbDataReader|IDataReader|OdbcDataReader|OleDbDataReader)\??\s+([A-Za-z_]\w*)/g;
|
|
26869
27013
|
for (const line of lines) {
|
|
26870
27014
|
const formRe = new RegExp(formFileDeclRe.source, "g");
|
|
26871
27015
|
let d;
|
|
@@ -26874,15 +27018,25 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26874
27018
|
const reqRe = new RegExp(httpRequestDeclRe.source, "g");
|
|
26875
27019
|
while ((d = reqRe.exec(line)) !== null)
|
|
26876
27020
|
httpRequestReceivers.add(d[1]);
|
|
26877
|
-
|
|
27021
|
+
const srRe = new RegExp(streamReaderDeclRe.source, "g");
|
|
27022
|
+
while ((d = srRe.exec(line)) !== null)
|
|
27023
|
+
streamReaderVars.add(d[1]);
|
|
27024
|
+
const dbRe = new RegExp(dbReaderDeclRe.source, "g");
|
|
27025
|
+
while ((d = dbRe.exec(line)) !== null)
|
|
27026
|
+
dbReaderVars.add(d[1]);
|
|
27027
|
+
}
|
|
27028
|
+
const streamReadRe = streamReaderVars.size > 0 ? new RegExp(`\\b(?:${[...streamReaderVars].join("|")})\\s*\\.\\s*(?:ReadLine|ReadToEnd|ReadLineAsync|ReadToEndAsync|ReadBlock|Read)\\s*\\(`) : null;
|
|
27029
|
+
const dbReadRe = dbReaderVars.size > 0 ? new RegExp(`\\b(?:${[...dbReaderVars].join("|")})\\s*\\.\\s*(?:GetString|GetValue|GetInt32|GetInt64|GetDecimal|GetDateTime|GetFieldValue|\\[)`) : null;
|
|
26878
27030
|
const formFileReadRe = formFileParams.size > 0 ? new RegExp(`\\b(?:${[...formFileParams].join("|")})\\s*\\.\\s*(?:FileName|ContentType)\\b`) : null;
|
|
26879
27031
|
const requestReadRe = new RegExp(`\\b(?:${[...httpRequestReceivers].join("|")})\\s*\\.\\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\\b`);
|
|
26880
|
-
const classify = (text) => requestReadRe.test(text) ? "http_param" : formFileReadRe?.test(text) ? "http_param" : consoleReadRe.test(text) ? "io_input" : envReadRe.test(text) ? "env_input" : null;
|
|
27032
|
+
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;
|
|
26881
27033
|
const inlineReads = [
|
|
26882
27034
|
[requestReadRe, "http_param"],
|
|
26883
27035
|
[formFileReadRe, "http_param"],
|
|
26884
27036
|
[consoleReadRe, "io_input"],
|
|
26885
|
-
[envReadRe, "env_input"]
|
|
27037
|
+
[envReadRe, "env_input"],
|
|
27038
|
+
[streamReadRe, "io_input"],
|
|
27039
|
+
[dbReadRe, "db_input"]
|
|
26886
27040
|
];
|
|
26887
27041
|
const inlineRead = (text) => {
|
|
26888
27042
|
const argsStart = text.indexOf("(");
|
|
@@ -26904,7 +27058,8 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26904
27058
|
const embeddedAssignRe = /(?:^|[{;])\s*(?:await\s+)?(?:using\s*\(?\s*)?(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=(?!=)\s*([^;{}]+)/;
|
|
26905
27059
|
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
26906
27060
|
const m = assignRe.exec(lines[i2]);
|
|
26907
|
-
|
|
27061
|
+
const rhsCrossesBlock = m !== null && m[2].includes("{");
|
|
27062
|
+
if (!m || rhsCrossesBlock) {
|
|
26908
27063
|
const stmt = lines[i2];
|
|
26909
27064
|
if (stmt.includes("{") && !/^\s*(?:\/\/|\/\*|\*)/.test(stmt)) {
|
|
26910
27065
|
const emb = new RegExp(embeddedAssignRe.source, "g");
|
|
@@ -26922,7 +27077,7 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26922
27077
|
sources.push({
|
|
26923
27078
|
type: type3,
|
|
26924
27079
|
location: `${varName2} = ${rhs2.trim().substring(0, 50)}${rhs2.length > 50 ? "..." : ""}`,
|
|
26925
|
-
severity: "high",
|
|
27080
|
+
severity: type3 === "db_input" ? "medium" : "high",
|
|
26926
27081
|
line: lineNumber3,
|
|
26927
27082
|
confidence: 1,
|
|
26928
27083
|
variable: varName2
|
|
@@ -26945,7 +27100,7 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26945
27100
|
sources.push({
|
|
26946
27101
|
type: type2,
|
|
26947
27102
|
location: `${read} in ${code.substring(0, 50)}${code.length > 50 ? "..." : ""}`,
|
|
26948
|
-
severity: "high",
|
|
27103
|
+
severity: type2 === "db_input" ? "medium" : "high",
|
|
26949
27104
|
line: lineNumber2,
|
|
26950
27105
|
confidence: 1,
|
|
26951
27106
|
variable: "<inline>",
|
|
@@ -26963,7 +27118,7 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
26963
27118
|
sources.push({
|
|
26964
27119
|
type,
|
|
26965
27120
|
location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
|
|
26966
|
-
severity: "high",
|
|
27121
|
+
severity: type === "db_input" ? "medium" : "high",
|
|
26967
27122
|
line: lineNumber,
|
|
26968
27123
|
confidence: 1,
|
|
26969
27124
|
variable: varName
|
|
@@ -27157,7 +27312,7 @@ function findPythonAssignmentSources(sourceCode, language) {
|
|
|
27157
27312
|
sources.push({
|
|
27158
27313
|
type,
|
|
27159
27314
|
location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
|
|
27160
|
-
severity: "high",
|
|
27315
|
+
severity: type === "db_input" ? "medium" : "high",
|
|
27161
27316
|
line: lineNumber,
|
|
27162
27317
|
confidence: 0.95,
|
|
27163
27318
|
variable: varName
|
|
@@ -37753,13 +37908,17 @@ function maskStringLiteralsForTokenScan(expr) {
|
|
|
37753
37908
|
}
|
|
37754
37909
|
return out2;
|
|
37755
37910
|
}
|
|
37911
|
+
function argsReferenceTaintedVar(expr, sources) {
|
|
37912
|
+
const codePart = expr.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ").replace(/\/\*[\s\S]*?\*\//g, " ").replace(/\/\/[^\n]*/g, " ");
|
|
37913
|
+
return sources.some((s) => new RegExp(`(?<![\\p{L}\\p{N}_])${s.variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?![\\p{L}\\p{N}_])`, "u").test(codePart));
|
|
37914
|
+
}
|
|
37756
37915
|
function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachableLines, tainted, code, language, types) {
|
|
37757
37916
|
const flows = [];
|
|
37758
|
-
const
|
|
37759
|
-
const
|
|
37917
|
+
const methodRanges = (langs) => language && langs.includes(language) && types ? types.flatMap((t) => t.methods.map((m) => [m.start_line, m.end_line])) : [];
|
|
37918
|
+
const enclosingStart = (ranges, line) => {
|
|
37760
37919
|
let best = -1;
|
|
37761
37920
|
let bestSpan = -1;
|
|
37762
|
-
for (const [start2, end] of
|
|
37921
|
+
for (const [start2, end] of ranges) {
|
|
37763
37922
|
if (line >= start2 && line <= end && end - start2 > bestSpan) {
|
|
37764
37923
|
best = start2;
|
|
37765
37924
|
bestSpan = end - start2;
|
|
@@ -37767,6 +37926,10 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
37767
37926
|
}
|
|
37768
37927
|
return best;
|
|
37769
37928
|
};
|
|
37929
|
+
const goFnRanges = methodRanges(["go"]);
|
|
37930
|
+
const goFnStart = (line) => enclosingStart(goFnRanges, line);
|
|
37931
|
+
const csFnRanges = methodRanges(["csharp"]);
|
|
37932
|
+
const csFnStart = (line) => enclosingStart(csFnRanges, line);
|
|
37770
37933
|
const sourcesWithVar = sources.filter((s) => typeof s.variable === "string" && s.variable.length > 0);
|
|
37771
37934
|
const aliasSanitizedFor = new Map;
|
|
37772
37935
|
if (language === "python" && typeof code === "string") {
|
|
@@ -38102,15 +38265,51 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
38102
38265
|
const [, obj, rhs] = m;
|
|
38103
38266
|
if (known.has(obj))
|
|
38104
38267
|
continue;
|
|
38105
|
-
|
|
38106
|
-
const rhsTainted = sourcesWithVar.some((s) => new RegExp(`(?<![\\p{L}\\p{N}_])${s.variable.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}(?![\\p{L}\\p{N}_])`, "u").test(codePart));
|
|
38107
|
-
if (!rhsTainted)
|
|
38268
|
+
if (!argsReferenceTaintedVar(rhs, sourcesWithVar))
|
|
38108
38269
|
continue;
|
|
38109
38270
|
sourcesWithVar.push({ ...anchor, variable: obj, line: i2 + 1 });
|
|
38110
38271
|
added = true;
|
|
38111
38272
|
}
|
|
38112
38273
|
}
|
|
38113
38274
|
}
|
|
38275
|
+
const builderSeeds = new Set;
|
|
38276
|
+
if (language === "csharp" && typeof code === "string" && sourcesWithVar.length > 0) {
|
|
38277
|
+
const lines = code.split(`
|
|
38278
|
+
`);
|
|
38279
|
+
const builders = new Set;
|
|
38280
|
+
for (const line of lines) {
|
|
38281
|
+
for (const m of line.matchAll(/\bStringBuilder\s+([\p{L}_][\p{L}\p{N}_]*)/gu))
|
|
38282
|
+
builders.add(m[1]);
|
|
38283
|
+
for (const m of line.matchAll(/\b([\p{L}_][\p{L}\p{N}_]*)\s*=\s*new\s+StringBuilder\b/gu))
|
|
38284
|
+
builders.add(m[1]);
|
|
38285
|
+
}
|
|
38286
|
+
if (builders.size > 0) {
|
|
38287
|
+
const appendRe = /([\p{L}_][\p{L}\p{N}_]*)\s*\.\s*(?:Append|AppendLine|AppendFormat|Insert)\s*\(([\s\S]*)/u;
|
|
38288
|
+
const anchor = sourcesWithVar.reduce((a, s) => s.line < a.line ? s : a, sourcesWithVar[0]);
|
|
38289
|
+
let added = true;
|
|
38290
|
+
let guard = 0;
|
|
38291
|
+
while (added && guard < lines.length + 2) {
|
|
38292
|
+
added = false;
|
|
38293
|
+
guard++;
|
|
38294
|
+
const known = new Set(sourcesWithVar.map((s) => s.variable));
|
|
38295
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
38296
|
+
const m = appendRe.exec(lines[i2]);
|
|
38297
|
+
if (!m)
|
|
38298
|
+
continue;
|
|
38299
|
+
const [, obj, args2] = m;
|
|
38300
|
+
if (!builders.has(obj) || known.has(obj))
|
|
38301
|
+
continue;
|
|
38302
|
+
const appendFn = csFnStart(i2 + 1);
|
|
38303
|
+
const reachable = sourcesWithVar.filter((sv) => appendFn === -1 || csFnStart(sv.line) === -1 || csFnStart(sv.line) === appendFn);
|
|
38304
|
+
if (reachable.length > 0 && argsReferenceTaintedVar(args2, reachable)) {
|
|
38305
|
+
sourcesWithVar.push({ ...anchor, variable: obj, line: i2 + 1 });
|
|
38306
|
+
builderSeeds.add(`${obj}@${i2 + 1}`);
|
|
38307
|
+
added = true;
|
|
38308
|
+
}
|
|
38309
|
+
}
|
|
38310
|
+
}
|
|
38311
|
+
}
|
|
38312
|
+
}
|
|
38114
38313
|
const SIMPLE_IDENT = /^[\p{L}\p{N}_]+$/u;
|
|
38115
38314
|
const reCache = new Map;
|
|
38116
38315
|
for (const s of sourcesWithVar) {
|
|
@@ -38181,6 +38380,12 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
38181
38380
|
if (srcFn !== -1 && sinkFn !== -1 && srcFn !== sinkFn)
|
|
38182
38381
|
continue;
|
|
38183
38382
|
}
|
|
38383
|
+
if (csFnRanges.length > 0 && builderSeeds.has(`${source.variable}@${source.line}`)) {
|
|
38384
|
+
const srcFn = csFnStart(source.line);
|
|
38385
|
+
const sinkFn = csFnStart(sink.line);
|
|
38386
|
+
if (srcFn !== sinkFn)
|
|
38387
|
+
continue;
|
|
38388
|
+
}
|
|
38184
38389
|
if (complex) {
|
|
38185
38390
|
const re = reCache.get(source.variable);
|
|
38186
38391
|
if (!re || !re.test(expr))
|
|
@@ -49154,7 +49359,7 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
49154
49359
|
};
|
|
49155
49360
|
}
|
|
49156
49361
|
const tTypes = phaseStart();
|
|
49157
|
-
const types = extractTypes(tree, nodeCache, language);
|
|
49362
|
+
const types = extractTypes(tree, nodeCache, language, { navigationTypes: options.navigationTypes });
|
|
49158
49363
|
phaseEnd("extractTypes", tTypes);
|
|
49159
49364
|
const tCalls = phaseStart();
|
|
49160
49365
|
const calls = extractCalls(tree, nodeCache, language);
|
|
@@ -50669,7 +50874,7 @@ var colors = {
|
|
|
50669
50874
|
};
|
|
50670
50875
|
|
|
50671
50876
|
// src/version.ts
|
|
50672
|
-
var version = "4.
|
|
50877
|
+
var version = "4.10.0";
|
|
50673
50878
|
|
|
50674
50879
|
// src/formatters.ts
|
|
50675
50880
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
|
@@ -51221,8 +51426,8 @@ function generateSarifResults(results, crossFileData) {
|
|
|
51221
51426
|
|
|
51222
51427
|
// src/build-info.ts
|
|
51223
51428
|
var buildInfo = {
|
|
51224
|
-
gitSha: "
|
|
51225
|
-
builtAt: "2026-10-
|
|
51429
|
+
gitSha: "598f8fc",
|
|
51430
|
+
builtAt: "2026-10-02T23:23:28.653Z"
|
|
51226
51431
|
};
|
|
51227
51432
|
|
|
51228
51433
|
// src/utils/args.ts
|
|
@@ -51850,7 +52055,7 @@ async function initWasm(spin) {
|
|
|
51850
52055
|
go: join3(wasmDir, "tree-sitter-go.wasm"),
|
|
51851
52056
|
java: join3(wasmDir, "tree-sitter-java.wasm"),
|
|
51852
52057
|
javascript: join3(wasmDir, "tree-sitter-javascript.wasm"),
|
|
51853
|
-
typescript: join3(wasmDir, "tree-sitter-
|
|
52058
|
+
typescript: join3(wasmDir, "tree-sitter-typescript.wasm"),
|
|
51854
52059
|
python: join3(wasmDir, "tree-sitter-python.wasm"),
|
|
51855
52060
|
rust: join3(wasmDir, "tree-sitter-rust.wasm"),
|
|
51856
52061
|
html: join3(wasmDir, "tree-sitter-html.wasm"),
|
|
@@ -51885,7 +52090,7 @@ Please ensure the wasm/ directory is located next to the binary or in your curre
|
|
|
51885
52090
|
go: wasmBasePath + "tree-sitter-go.wasm",
|
|
51886
52091
|
java: wasmBasePath + "tree-sitter-java.wasm",
|
|
51887
52092
|
javascript: wasmBasePath + "tree-sitter-javascript.wasm",
|
|
51888
|
-
typescript: wasmBasePath + "tree-sitter-
|
|
52093
|
+
typescript: wasmBasePath + "tree-sitter-typescript.wasm",
|
|
51889
52094
|
python: wasmBasePath + "tree-sitter-python.wasm",
|
|
51890
52095
|
rust: wasmBasePath + "tree-sitter-rust.wasm",
|
|
51891
52096
|
html: wasmBasePath + "tree-sitter-html.wasm",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.10.0",
|
|
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"
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"test": "bun test",
|
|
13
13
|
"clean": "rm -rf dist cognium-dev cognium-dev-* *.bun-build wasm",
|
|
14
14
|
"typecheck": "tsc --noEmit",
|
|
15
|
-
"version": "node -e \"const v = require('./package.json').version; require('fs').writeFileSync('src/version.ts', '/**\\n * Version information\\n *\\n * Kept in sync with package.json via the \\`version\\` npm lifecycle script.\\n * Do not edit manually
|
|
15
|
+
"version": "node -e \"const v = require('./package.json').version; require('fs').writeFileSync('src/version.ts', '/**\\n * Version information\\n *\\n * Kept in sync with package.json via the \\`version\\` npm lifecycle script.\\n * Do not edit manually — use \\`npm version patch|minor|major\\` instead.\\n */\\nexport const version = \\x27' + v + '\\x27;\\n')\" && git add src/version.ts",
|
|
16
16
|
"dogfood": "bun run src/cli.ts scan src/ -q",
|
|
17
17
|
"prepublishOnly": "bun run build",
|
|
18
18
|
"test:coverage": "bun test --coverage"
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
},
|
|
66
66
|
"dependencies": {
|
|
67
67
|
"@cognium/project-profile-detect": "1.1.2",
|
|
68
|
-
"circle-ir": "4.
|
|
68
|
+
"circle-ir": "4.10.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^26.6.2",
|