cognium-dev 3.216.0 → 4.2.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/README.md +7 -1
- package/dist/cli.js +571 -18
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -65,7 +65,7 @@ Scan files or directories for security vulnerabilities.
|
|
|
65
65
|
cognium-dev scan <path> [options]
|
|
66
66
|
|
|
67
67
|
Options:
|
|
68
|
-
-l, --language <lang> Force language (java|javascript|typescript|python|go|rust|bash|html)
|
|
68
|
+
-l, --language <lang> Force language (java|javascript|typescript|python|go|rust|bash|html|csharp)
|
|
69
69
|
-f, --format <format> Output format (text|json|sarif) [default: text]
|
|
70
70
|
--threads <n> Parallel analysis threads [default: 4]
|
|
71
71
|
--severity <level> Filter by severity:
|
|
@@ -307,6 +307,12 @@ Filter to security findings only: `cognium-dev scan ./src --category security`
|
|
|
307
307
|
| Rust | `.rs` | Actix-web, Rocket, Axum |
|
|
308
308
|
| Bash | `.sh`, `.bash` | Shell scripts |
|
|
309
309
|
| HTML | `.html`, `.htm` | Web extraction preprocessor |
|
|
310
|
+
| C#/.NET _(experimental)_ | `.cs` | ASP.NET Core, ADO.NET, EF Core |
|
|
311
|
+
|
|
312
|
+
> **C#/.NET is experimental / preview** (since 4.0.0). Straight-line taint
|
|
313
|
+
> analysis across 10 CWE families (SQLi, command injection, path traversal,
|
|
314
|
+
> SSRF, code injection, XSS, deserialization, LDAP, XPath, XXE). Not yet
|
|
315
|
+
> benchmark-verified — expect gaps in branch/alias precision and detector breadth.
|
|
310
316
|
|
|
311
317
|
## Configuration
|
|
312
318
|
|
package/dist/cli.js
CHANGED
|
@@ -3627,8 +3627,87 @@ function extractTypes(tree, cache, language) {
|
|
|
3627
3627
|
if (isJavaScript) {
|
|
3628
3628
|
return extractJavaScriptTypes(tree, cache);
|
|
3629
3629
|
}
|
|
3630
|
+
if (effectiveLanguage === "csharp") {
|
|
3631
|
+
return extractCSharpTypes(tree, cache);
|
|
3632
|
+
}
|
|
3630
3633
|
return extractJavaTypes(tree, cache);
|
|
3631
3634
|
}
|
|
3635
|
+
function extractCSharpTypes(tree, cache) {
|
|
3636
|
+
const types = [];
|
|
3637
|
+
const KINDS = {
|
|
3638
|
+
class_declaration: "class",
|
|
3639
|
+
record_declaration: "class",
|
|
3640
|
+
struct_declaration: "class",
|
|
3641
|
+
interface_declaration: "interface",
|
|
3642
|
+
enum_declaration: "enum"
|
|
3643
|
+
};
|
|
3644
|
+
for (const kindNode of Object.keys(KINDS)) {
|
|
3645
|
+
for (const node of getNodesFromCache(tree.rootNode, kindNode, cache)) {
|
|
3646
|
+
const nameNode = node.childForFieldName("name");
|
|
3647
|
+
const body2 = node.childForFieldName("body");
|
|
3648
|
+
const methods = [];
|
|
3649
|
+
if (body2) {
|
|
3650
|
+
for (let i2 = 0;i2 < body2.childCount; i2++) {
|
|
3651
|
+
const m = body2.child(i2);
|
|
3652
|
+
if (!m || m.type !== "method_declaration")
|
|
3653
|
+
continue;
|
|
3654
|
+
const mName = m.childForFieldName("name");
|
|
3655
|
+
const paramList = m.childForFieldName("parameters");
|
|
3656
|
+
const parameters = [];
|
|
3657
|
+
if (paramList) {
|
|
3658
|
+
for (let j = 0;j < paramList.childCount; j++) {
|
|
3659
|
+
const pnode = paramList.child(j);
|
|
3660
|
+
if (!pnode || pnode.type !== "parameter")
|
|
3661
|
+
continue;
|
|
3662
|
+
const pName = pnode.childForFieldName("name");
|
|
3663
|
+
const pType = pnode.childForFieldName("type");
|
|
3664
|
+
if (pName) {
|
|
3665
|
+
parameters.push({
|
|
3666
|
+
name: getNodeText(pName),
|
|
3667
|
+
type: pType ? getNodeText(pType) : null,
|
|
3668
|
+
annotations: [],
|
|
3669
|
+
line: pnode.startPosition.row + 1
|
|
3670
|
+
});
|
|
3671
|
+
}
|
|
3672
|
+
}
|
|
3673
|
+
}
|
|
3674
|
+
const returns = m.childForFieldName("returns");
|
|
3675
|
+
methods.push({
|
|
3676
|
+
name: mName ? getNodeText(mName) : "unknown",
|
|
3677
|
+
return_type: returns ? getNodeText(returns) : null,
|
|
3678
|
+
parameters,
|
|
3679
|
+
annotations: [],
|
|
3680
|
+
modifiers: extractCSharpModifiers(m),
|
|
3681
|
+
start_line: m.startPosition.row + 1,
|
|
3682
|
+
end_line: m.endPosition.row + 1
|
|
3683
|
+
});
|
|
3684
|
+
}
|
|
3685
|
+
}
|
|
3686
|
+
types.push({
|
|
3687
|
+
name: nameNode ? getNodeText(nameNode) : "anonymous",
|
|
3688
|
+
kind: KINDS[kindNode],
|
|
3689
|
+
package: null,
|
|
3690
|
+
extends: null,
|
|
3691
|
+
implements: [],
|
|
3692
|
+
annotations: [],
|
|
3693
|
+
methods,
|
|
3694
|
+
fields: [],
|
|
3695
|
+
start_line: node.startPosition.row + 1,
|
|
3696
|
+
end_line: node.endPosition.row + 1
|
|
3697
|
+
});
|
|
3698
|
+
}
|
|
3699
|
+
}
|
|
3700
|
+
return types;
|
|
3701
|
+
}
|
|
3702
|
+
function extractCSharpModifiers(node) {
|
|
3703
|
+
const mods = [];
|
|
3704
|
+
for (let i2 = 0;i2 < node.childCount; i2++) {
|
|
3705
|
+
const c = node.child(i2);
|
|
3706
|
+
if (c && c.type === "modifier")
|
|
3707
|
+
mods.push(getNodeText(c));
|
|
3708
|
+
}
|
|
3709
|
+
return mods;
|
|
3710
|
+
}
|
|
3632
3711
|
function extractJavaTypes(tree, cache) {
|
|
3633
3712
|
const types = [];
|
|
3634
3713
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
@@ -5133,6 +5212,9 @@ function extractCalls(tree, cache, language) {
|
|
|
5133
5212
|
if (isJavaScript) {
|
|
5134
5213
|
return extractJavaScriptCalls(tree, cache);
|
|
5135
5214
|
}
|
|
5215
|
+
if (detectedLanguage === "csharp") {
|
|
5216
|
+
return extractCSharpCalls(tree, cache);
|
|
5217
|
+
}
|
|
5136
5218
|
const context = buildResolutionContext(tree, cache);
|
|
5137
5219
|
const invocations = getNodesFromCache(tree.rootNode, "method_invocation", cache);
|
|
5138
5220
|
for (const inv of invocations) {
|
|
@@ -5147,6 +5229,115 @@ function extractCalls(tree, cache, language) {
|
|
|
5147
5229
|
}
|
|
5148
5230
|
return calls;
|
|
5149
5231
|
}
|
|
5232
|
+
function buildCSharpReceiverTypeMap(tree, cache) {
|
|
5233
|
+
const map = new Map;
|
|
5234
|
+
const simple = (t) => t.replace(/<[^>]*>/g, "").split(".").pop()?.trim() ?? t;
|
|
5235
|
+
for (const vd of getNodesFromCache(tree.rootNode, "variable_declaration", cache)) {
|
|
5236
|
+
const typeNode = vd.childForFieldName("type");
|
|
5237
|
+
const declaredType = typeNode ? getNodeText(typeNode) : null;
|
|
5238
|
+
for (let i2 = 0;i2 < vd.childCount; i2++) {
|
|
5239
|
+
const decl = vd.child(i2);
|
|
5240
|
+
if (!decl || decl.type !== "variable_declarator")
|
|
5241
|
+
continue;
|
|
5242
|
+
const nameNode = decl.childForFieldName("name");
|
|
5243
|
+
if (nameNode?.type !== "identifier")
|
|
5244
|
+
continue;
|
|
5245
|
+
let t = declaredType && declaredType !== "var" ? declaredType : null;
|
|
5246
|
+
if (!t) {
|
|
5247
|
+
const oce = findFirstDescendant(decl, "object_creation_expression");
|
|
5248
|
+
const oceType = oce?.childForFieldName("type");
|
|
5249
|
+
if (oceType)
|
|
5250
|
+
t = getNodeText(oceType);
|
|
5251
|
+
}
|
|
5252
|
+
if (t && t !== "var")
|
|
5253
|
+
map.set(getNodeText(nameNode), simple(t));
|
|
5254
|
+
}
|
|
5255
|
+
}
|
|
5256
|
+
return map;
|
|
5257
|
+
}
|
|
5258
|
+
function findFirstDescendant(node, type) {
|
|
5259
|
+
for (let i2 = 0;i2 < node.childCount; i2++) {
|
|
5260
|
+
const c = node.child(i2);
|
|
5261
|
+
if (!c)
|
|
5262
|
+
continue;
|
|
5263
|
+
if (c.type === type)
|
|
5264
|
+
return c;
|
|
5265
|
+
const found = findFirstDescendant(c, type);
|
|
5266
|
+
if (found)
|
|
5267
|
+
return found;
|
|
5268
|
+
}
|
|
5269
|
+
return null;
|
|
5270
|
+
}
|
|
5271
|
+
function extractCSharpCalls(tree, cache) {
|
|
5272
|
+
const calls = [];
|
|
5273
|
+
const typeMap = buildCSharpReceiverTypeMap(tree, cache);
|
|
5274
|
+
const invocations = getNodesFromCache(tree.rootNode, "invocation_expression", cache);
|
|
5275
|
+
for (const inv of invocations) {
|
|
5276
|
+
const fn = inv.childForFieldName("function");
|
|
5277
|
+
let methodName = "unknown";
|
|
5278
|
+
let receiver = null;
|
|
5279
|
+
if (fn?.type === "member_access_expression") {
|
|
5280
|
+
const nameNode = fn.childForFieldName("name");
|
|
5281
|
+
const exprNode = fn.childForFieldName("expression");
|
|
5282
|
+
methodName = nameNode ? getNodeText(nameNode) : "unknown";
|
|
5283
|
+
receiver = exprNode ? getNodeText(exprNode) : null;
|
|
5284
|
+
} else if (fn) {
|
|
5285
|
+
methodName = getNodeText(fn);
|
|
5286
|
+
}
|
|
5287
|
+
const argsNode = inv.childForFieldName("arguments");
|
|
5288
|
+
calls.push({
|
|
5289
|
+
method_name: methodName,
|
|
5290
|
+
receiver,
|
|
5291
|
+
receiver_type: receiver ? typeMap.get(receiver) ?? null : null,
|
|
5292
|
+
receiver_type_fqn: null,
|
|
5293
|
+
arguments: argsNode ? extractCSharpArguments(argsNode) : [],
|
|
5294
|
+
location: { line: inv.startPosition.row + 1, column: inv.startPosition.column },
|
|
5295
|
+
in_method: findEnclosingMethod(inv)
|
|
5296
|
+
});
|
|
5297
|
+
}
|
|
5298
|
+
const objectCreations = getNodesFromCache(tree.rootNode, "object_creation_expression", cache);
|
|
5299
|
+
for (const creation of objectCreations) {
|
|
5300
|
+
const typeNode = creation.childForFieldName("type");
|
|
5301
|
+
const argsNode = creation.childForFieldName("arguments");
|
|
5302
|
+
calls.push({
|
|
5303
|
+
method_name: typeNode ? getNodeText(typeNode) : "unknown",
|
|
5304
|
+
receiver: null,
|
|
5305
|
+
receiver_type: null,
|
|
5306
|
+
receiver_type_fqn: null,
|
|
5307
|
+
arguments: argsNode ? extractCSharpArguments(argsNode) : [],
|
|
5308
|
+
location: { line: creation.startPosition.row + 1, column: creation.startPosition.column },
|
|
5309
|
+
in_method: findEnclosingMethod(creation),
|
|
5310
|
+
is_constructor: true
|
|
5311
|
+
});
|
|
5312
|
+
}
|
|
5313
|
+
return calls;
|
|
5314
|
+
}
|
|
5315
|
+
function extractCSharpArguments(argsNode) {
|
|
5316
|
+
const args2 = [];
|
|
5317
|
+
let position = 0;
|
|
5318
|
+
for (let i2 = 0;i2 < argsNode.childCount; i2++) {
|
|
5319
|
+
const child = argsNode.child(i2);
|
|
5320
|
+
if (!child || child.type !== "argument")
|
|
5321
|
+
continue;
|
|
5322
|
+
let expr = null;
|
|
5323
|
+
for (let j = child.childCount - 1;j >= 0; j--) {
|
|
5324
|
+
const c = child.child(j);
|
|
5325
|
+
if (c && c.isNamed) {
|
|
5326
|
+
expr = c;
|
|
5327
|
+
break;
|
|
5328
|
+
}
|
|
5329
|
+
}
|
|
5330
|
+
const text = expr ? getNodeText(expr) : getNodeText(child);
|
|
5331
|
+
args2.push({
|
|
5332
|
+
position: position++,
|
|
5333
|
+
expression: text,
|
|
5334
|
+
variable: expr?.type === "identifier" ? text : null,
|
|
5335
|
+
literal: expr?.type === "string_literal" ? text : null,
|
|
5336
|
+
value: null
|
|
5337
|
+
});
|
|
5338
|
+
}
|
|
5339
|
+
return args2;
|
|
5340
|
+
}
|
|
5150
5341
|
function extractJavaScriptCalls(tree, cache) {
|
|
5151
5342
|
const calls = [];
|
|
5152
5343
|
const context = buildJSResolutionContext(tree, cache);
|
|
@@ -8337,8 +8528,72 @@ function buildDFG(tree, cache, language) {
|
|
|
8337
8528
|
if (effectiveLanguage === "go") {
|
|
8338
8529
|
return buildGoDFG(tree);
|
|
8339
8530
|
}
|
|
8531
|
+
if (effectiveLanguage === "csharp") {
|
|
8532
|
+
return buildCSharpDFG(tree, cache);
|
|
8533
|
+
}
|
|
8340
8534
|
return buildJavaDFG(tree, cache);
|
|
8341
8535
|
}
|
|
8536
|
+
function buildCSharpDFG(tree, cache) {
|
|
8537
|
+
const defs = [];
|
|
8538
|
+
const uses = [];
|
|
8539
|
+
let defIdCounter = 1;
|
|
8540
|
+
let useIdCounter = 1;
|
|
8541
|
+
const scopeStack = [new Map];
|
|
8542
|
+
const methods = [
|
|
8543
|
+
...getNodesFromCache(tree.rootNode, "method_declaration", cache),
|
|
8544
|
+
...getNodesFromCache(tree.rootNode, "constructor_declaration", cache),
|
|
8545
|
+
...getNodesFromCache(tree.rootNode, "local_function_statement", cache)
|
|
8546
|
+
];
|
|
8547
|
+
for (const method of methods) {
|
|
8548
|
+
scopeStack.push(new Map);
|
|
8549
|
+
const params = method.childForFieldName("parameters");
|
|
8550
|
+
if (params) {
|
|
8551
|
+
for (const def of extractParameterDefs(params, defIdCounter)) {
|
|
8552
|
+
defs.push(def);
|
|
8553
|
+
currentScope(scopeStack).set(def.variable, def.id);
|
|
8554
|
+
defIdCounter++;
|
|
8555
|
+
}
|
|
8556
|
+
}
|
|
8557
|
+
const body2 = method.childForFieldName("body");
|
|
8558
|
+
if (body2) {
|
|
8559
|
+
for (const decl of findNodes(body2, "variable_declarator")) {
|
|
8560
|
+
const nameNode = decl.childForFieldName("name");
|
|
8561
|
+
if (nameNode?.type === "identifier") {
|
|
8562
|
+
const name2 = getNodeText(nameNode);
|
|
8563
|
+
const def = { id: defIdCounter++, variable: name2, line: decl.startPosition.row + 1, kind: "local" };
|
|
8564
|
+
defs.push(def);
|
|
8565
|
+
currentScope(scopeStack).set(name2, def.id);
|
|
8566
|
+
}
|
|
8567
|
+
}
|
|
8568
|
+
for (const asn of findNodes(body2, "assignment_expression")) {
|
|
8569
|
+
const left = asn.childForFieldName("left");
|
|
8570
|
+
if (left?.type === "identifier") {
|
|
8571
|
+
const name2 = getNodeText(left);
|
|
8572
|
+
const def = { id: defIdCounter++, variable: name2, line: asn.startPosition.row + 1, kind: "local" };
|
|
8573
|
+
defs.push(def);
|
|
8574
|
+
currentScope(scopeStack).set(name2, def.id);
|
|
8575
|
+
}
|
|
8576
|
+
}
|
|
8577
|
+
const bodyUses = extractUses(body2, useIdCounter, scopeStack, false);
|
|
8578
|
+
uses.push(...bodyUses.uses);
|
|
8579
|
+
useIdCounter = bodyUses.nextId;
|
|
8580
|
+
}
|
|
8581
|
+
scopeStack.pop();
|
|
8582
|
+
}
|
|
8583
|
+
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
8584
|
+
for (const cls of classes) {
|
|
8585
|
+
const body2 = cls.childForFieldName("body");
|
|
8586
|
+
if (body2) {
|
|
8587
|
+
for (const def of extractFieldDefs(body2, defIdCounter)) {
|
|
8588
|
+
defs.push(def);
|
|
8589
|
+
currentScope(scopeStack).set(def.variable, def.id);
|
|
8590
|
+
defIdCounter++;
|
|
8591
|
+
}
|
|
8592
|
+
}
|
|
8593
|
+
}
|
|
8594
|
+
const chains = computeChains(defs, uses);
|
|
8595
|
+
return { defs, uses, chains };
|
|
8596
|
+
}
|
|
8342
8597
|
function buildJavaDFG(tree, cache) {
|
|
8343
8598
|
const defs = [];
|
|
8344
8599
|
const uses = [];
|
|
@@ -8808,7 +9063,7 @@ function extractParameterDefs(params, startId) {
|
|
|
8808
9063
|
const param = params.child(i2);
|
|
8809
9064
|
if (!param)
|
|
8810
9065
|
continue;
|
|
8811
|
-
if (param.type === "formal_parameter" || param.type === "spread_parameter") {
|
|
9066
|
+
if (param.type === "formal_parameter" || param.type === "spread_parameter" || param.type === "parameter") {
|
|
8812
9067
|
const nameNode = param.childForFieldName("name");
|
|
8813
9068
|
if (nameNode) {
|
|
8814
9069
|
defs.push({
|
|
@@ -12020,6 +12275,55 @@ var DEFAULT_SINKS = [
|
|
|
12020
12275
|
{ method: "put", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
12021
12276
|
{ method: "patch", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
12022
12277
|
{ method: "head", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
12278
|
+
{ method: "SqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12279
|
+
{ method: "NpgsqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12280
|
+
{ method: "MySqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12281
|
+
{ method: "SqliteCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12282
|
+
{ method: "OracleCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12283
|
+
{ method: "ExecuteSqlRaw", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12284
|
+
{ method: "ExecuteSqlRawAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12285
|
+
{ method: "FromSqlRaw", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12286
|
+
{ method: "FromSqlRawAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12287
|
+
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12288
|
+
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
12289
|
+
{ method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12290
|
+
{ method: "ReadAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12291
|
+
{ method: "ReadAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12292
|
+
{ method: "WriteAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12293
|
+
{ method: "WriteAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12294
|
+
{ method: "AppendAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12295
|
+
{ method: "OpenRead", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12296
|
+
{ method: "OpenWrite", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12297
|
+
{ method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12298
|
+
{ method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12299
|
+
{ method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12300
|
+
{ method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12301
|
+
{ method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12302
|
+
{ method: "PutAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12303
|
+
{ method: "DeleteAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12304
|
+
{ method: "GetStringAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12305
|
+
{ method: "GetByteArrayAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12306
|
+
{ method: "GetStreamAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12307
|
+
{ method: "SendAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12308
|
+
{ method: "DownloadString", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12309
|
+
{ method: "DownloadData", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12310
|
+
{ method: "DownloadFile", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [1], languages: ["csharp"] },
|
|
12311
|
+
{ method: "Create", class: "WebRequest", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12312
|
+
{ method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12313
|
+
{ method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12314
|
+
{ method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12315
|
+
{ method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
12316
|
+
{ method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12317
|
+
{ method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12318
|
+
{ method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12319
|
+
{ method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12320
|
+
{ method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12321
|
+
{ method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12322
|
+
{ method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12323
|
+
{ method: "Compile", class: "XPathExpression", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12324
|
+
{ method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12325
|
+
{ method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12326
|
+
{ method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12023
12327
|
{ method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
12024
12328
|
{ method: "fetch", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
12025
12329
|
{ method: "fetchrow", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
@@ -12138,6 +12442,10 @@ var DEFAULT_SANITIZERS = [
|
|
|
12138
12442
|
{ method: "escapeEcmaScript", removes: ["xss"] },
|
|
12139
12443
|
{ method: "escapeCsv", removes: ["xss"] },
|
|
12140
12444
|
{ method: "sanitize", class: "PolicyFactory", removes: ["xss"] },
|
|
12445
|
+
{ method: "HtmlEncode", removes: ["xss"] },
|
|
12446
|
+
{ method: "JavaScriptStringEncode", removes: ["xss"] },
|
|
12447
|
+
{ method: "Encode", class: "HtmlEncoder", removes: ["xss"] },
|
|
12448
|
+
{ method: "GetFileName", class: "Path", removes: ["path_traversal"] },
|
|
12141
12449
|
{ method: "encodeToString", class: "Encoder", removes: ["sql_injection", "command_injection", "xss", "path_traversal", "code_injection"] },
|
|
12142
12450
|
{ method: "encode", class: "Encoder", removes: ["sql_injection", "command_injection", "xss", "path_traversal", "code_injection"] },
|
|
12143
12451
|
{ method: "encodeHexString", class: "Hex", removes: ["sql_injection", "command_injection", "xss", "path_traversal", "code_injection"] },
|
|
@@ -12790,7 +13098,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
12790
13098
|
if (skipMethods.includes(method.name))
|
|
12791
13099
|
continue;
|
|
12792
13100
|
for (const param of method.parameters) {
|
|
12793
|
-
const isTaintable = param.type ? isInterproceduralTaintableType(param.type) : true;
|
|
13101
|
+
const isTaintable = param.type ? isInterproceduralTaintableType(param.type, language) : true;
|
|
12794
13102
|
if (isTaintable) {
|
|
12795
13103
|
const paramLine = param.line ?? method.start_line;
|
|
12796
13104
|
sources.push({
|
|
@@ -12800,7 +13108,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
12800
13108
|
line: paramLine,
|
|
12801
13109
|
confidence: param.type ? 0.7 : 0.5,
|
|
12802
13110
|
in_method: method.name,
|
|
12803
|
-
...language === "java" ? { variable: param.name } : {}
|
|
13111
|
+
...language === "java" || language === "csharp" ? { variable: param.name } : {}
|
|
12804
13112
|
});
|
|
12805
13113
|
}
|
|
12806
13114
|
}
|
|
@@ -12903,7 +13211,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
12903
13211
|
}
|
|
12904
13212
|
return result;
|
|
12905
13213
|
}
|
|
12906
|
-
function isInterproceduralTaintableType(typeName) {
|
|
13214
|
+
function isInterproceduralTaintableType(typeName, language) {
|
|
12907
13215
|
const baseType = typeName.split("<")[0].trim();
|
|
12908
13216
|
const excludedTypes = [
|
|
12909
13217
|
"HttpServletRequest",
|
|
@@ -12966,6 +13274,26 @@ function isInterproceduralTaintableType(typeName) {
|
|
|
12966
13274
|
if (taintableTypes.includes(baseType)) {
|
|
12967
13275
|
return true;
|
|
12968
13276
|
}
|
|
13277
|
+
if (language === "csharp") {
|
|
13278
|
+
const csharpTaintable = [
|
|
13279
|
+
"string",
|
|
13280
|
+
"object",
|
|
13281
|
+
"IEnumerable",
|
|
13282
|
+
"ICollection",
|
|
13283
|
+
"IList",
|
|
13284
|
+
"Dictionary",
|
|
13285
|
+
"IDictionary",
|
|
13286
|
+
"IReadOnlyList",
|
|
13287
|
+
"IReadOnlyCollection",
|
|
13288
|
+
"Stream",
|
|
13289
|
+
"byte[]",
|
|
13290
|
+
"Byte[]",
|
|
13291
|
+
"MemoryStream"
|
|
13292
|
+
];
|
|
13293
|
+
const leaf = baseType.split(".").pop() ?? baseType;
|
|
13294
|
+
if (csharpTaintable.includes(baseType) || csharpTaintable.includes(leaf))
|
|
13295
|
+
return true;
|
|
13296
|
+
}
|
|
12969
13297
|
if (typeName.endsWith("[]")) {
|
|
12970
13298
|
const elementType = typeName.slice(0, -2);
|
|
12971
13299
|
if (elementType === "String" || elementType === "Object" || elementType === "byte") {
|
|
@@ -14549,19 +14877,19 @@ function walkBackwardDefs(startDefId, chainsByToDef, defById, options = {}) {
|
|
|
14549
14877
|
// ../circle-ir/dist/analysis/findings.js
|
|
14550
14878
|
function canSourceReachSink(sourceType, sinkType) {
|
|
14551
14879
|
const sourceToSinkMapping = {
|
|
14552
|
-
http_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "deserialization", "log_injection", "format_string", "nosql_injection"],
|
|
14553
|
-
http_body: ["sql_injection", "command_injection", "deserialization", "xxe", "xss", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection"],
|
|
14554
|
-
http_header: ["sql_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection"],
|
|
14555
|
-
http_cookie: ["sql_injection", "xss", "mybatis_mapper_call", "code_injection", "crlf", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection"],
|
|
14556
|
-
http_path: ["path_traversal", "sql_injection", "ssrf", "mybatis_mapper_call", "open_redirect", "trust_boundary", "xss", "log_injection", "format_string"],
|
|
14557
|
-
http_query: ["sql_injection", "command_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "deserialization", "log_injection", "format_string", "nosql_injection"],
|
|
14558
|
-
io_input: ["command_injection", "path_traversal", "deserialization", "xxe", "code_injection", "xss", "ssrf", "log_injection", "format_string"],
|
|
14880
|
+
http_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "deserialization", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
|
|
14881
|
+
http_body: ["sql_injection", "command_injection", "deserialization", "xxe", "xss", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
|
|
14882
|
+
http_header: ["sql_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
|
|
14883
|
+
http_cookie: ["sql_injection", "xss", "mybatis_mapper_call", "code_injection", "crlf", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
|
|
14884
|
+
http_path: ["path_traversal", "sql_injection", "ssrf", "mybatis_mapper_call", "open_redirect", "trust_boundary", "xss", "log_injection", "format_string", "prompt_injection"],
|
|
14885
|
+
http_query: ["sql_injection", "command_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "deserialization", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
|
|
14886
|
+
io_input: ["command_injection", "path_traversal", "deserialization", "xxe", "code_injection", "xss", "ssrf", "log_injection", "format_string", "prompt_injection"],
|
|
14559
14887
|
env_input: ["command_injection", "path_traversal"],
|
|
14560
14888
|
db_input: ["xss", "sql_injection", "log_injection"],
|
|
14561
14889
|
file_input: ["deserialization", "xxe", "path_traversal", "command_injection", "code_injection"],
|
|
14562
|
-
network_input: ["sql_injection", "command_injection", "xss", "ssrf", "log_injection", "format_string", "nosql_injection"],
|
|
14890
|
+
network_input: ["sql_injection", "command_injection", "xss", "ssrf", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
|
|
14563
14891
|
config_param: ["sql_injection", "command_injection", "path_traversal", "xss", "ssrf", "log_injection", "format_string"],
|
|
14564
|
-
interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection"],
|
|
14892
|
+
interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
|
|
14565
14893
|
plugin_param: ["sql_injection", "command_injection", "path_traversal", "xss", "code_injection", "log_injection", "format_string"]
|
|
14566
14894
|
};
|
|
14567
14895
|
const validSinks = sourceToSinkMapping[sourceType];
|
|
@@ -19694,6 +20022,10 @@ function applyLibraryApiSurfaceDowngrade(findings) {
|
|
|
19694
20022
|
}
|
|
19695
20023
|
|
|
19696
20024
|
// ../circle-ir/dist/analysis/note-coalescer.js
|
|
20025
|
+
var CLICKJACKING_PAIR = new Set(["missing-x-frame-options", "missing-csp-frame-ancestors"]);
|
|
20026
|
+
function levelRank(level) {
|
|
20027
|
+
return level === "error" ? 0 : level === "warning" ? 1 : 2;
|
|
20028
|
+
}
|
|
19697
20029
|
function coalesceNoteLevelFindings(findings) {
|
|
19698
20030
|
if (findings.length < 2)
|
|
19699
20031
|
return [...findings];
|
|
@@ -19711,11 +20043,27 @@ function coalesceNoteLevelFindings(findings) {
|
|
|
19711
20043
|
}
|
|
19712
20044
|
const out2 = [];
|
|
19713
20045
|
for (const key of order) {
|
|
19714
|
-
|
|
20046
|
+
let bucket = groups.get(key);
|
|
19715
20047
|
if (bucket.length === 1) {
|
|
19716
20048
|
out2.push(bucket[0]);
|
|
19717
20049
|
continue;
|
|
19718
20050
|
}
|
|
20051
|
+
const cj = bucket.filter((f) => CLICKJACKING_PAIR.has(f.rule_id));
|
|
20052
|
+
if (new Set(cj.map((f) => f.rule_id)).size === CLICKJACKING_PAIR.size) {
|
|
20053
|
+
const primary2 = [...cj].sort((a, b) => levelRank(a.level) - levelRank(b.level) || a.rule_id.localeCompare(b.rule_id))[0];
|
|
20054
|
+
const labels = Array.from(new Set([
|
|
20055
|
+
...primary2.labels ?? [],
|
|
20056
|
+
...cj.flatMap((f) => [f.rule_id, ...f.labels ?? []])
|
|
20057
|
+
])).filter((l) => l !== primary2.rule_id);
|
|
20058
|
+
out2.push({ ...primary2, labels });
|
|
20059
|
+
bucket = bucket.filter((f) => !CLICKJACKING_PAIR.has(f.rule_id));
|
|
20060
|
+
if (bucket.length === 0)
|
|
20061
|
+
continue;
|
|
20062
|
+
if (bucket.length === 1) {
|
|
20063
|
+
out2.push(bucket[0]);
|
|
20064
|
+
continue;
|
|
20065
|
+
}
|
|
20066
|
+
}
|
|
19719
20067
|
const allNote = bucket.every((f) => f.level === "note");
|
|
19720
20068
|
if (!allNote) {
|
|
19721
20069
|
for (const f of bucket)
|
|
@@ -23377,6 +23725,65 @@ class GoPlugin extends BaseLanguagePlugin {
|
|
|
23377
23725
|
}
|
|
23378
23726
|
}
|
|
23379
23727
|
|
|
23728
|
+
// ../circle-ir/dist/languages/plugins/csharp.js
|
|
23729
|
+
class CSharpPlugin extends BaseLanguagePlugin {
|
|
23730
|
+
id = "csharp";
|
|
23731
|
+
name = "C#";
|
|
23732
|
+
extensions = [".cs"];
|
|
23733
|
+
wasmPath = "tree-sitter-csharp.wasm";
|
|
23734
|
+
nodeTypes = {
|
|
23735
|
+
classDeclaration: ["class_declaration", "record_declaration", "struct_declaration"],
|
|
23736
|
+
interfaceDeclaration: ["interface_declaration"],
|
|
23737
|
+
enumDeclaration: ["enum_declaration"],
|
|
23738
|
+
functionDeclaration: [],
|
|
23739
|
+
methodDeclaration: ["method_declaration", "constructor_declaration", "local_function_statement"],
|
|
23740
|
+
methodCall: ["invocation_expression"],
|
|
23741
|
+
functionCall: [],
|
|
23742
|
+
assignment: ["assignment_expression"],
|
|
23743
|
+
variableDeclaration: ["local_declaration_statement", "field_declaration", "variable_declaration"],
|
|
23744
|
+
parameter: ["parameter"],
|
|
23745
|
+
argument: ["argument_list"],
|
|
23746
|
+
annotation: ["attribute", "attribute_list"],
|
|
23747
|
+
decorator: [],
|
|
23748
|
+
importStatement: ["using_directive"],
|
|
23749
|
+
ifStatement: ["if_statement"],
|
|
23750
|
+
forStatement: ["for_statement", "for_each_statement"],
|
|
23751
|
+
whileStatement: ["while_statement"],
|
|
23752
|
+
tryStatement: ["try_statement"],
|
|
23753
|
+
returnStatement: ["return_statement"]
|
|
23754
|
+
};
|
|
23755
|
+
detectFramework(context) {
|
|
23756
|
+
for (const imp of context.imports) {
|
|
23757
|
+
const path = imp.from_package || imp.imported_name;
|
|
23758
|
+
if (path.startsWith("Microsoft.AspNetCore") || path.startsWith("Microsoft.Extensions")) {
|
|
23759
|
+
return { name: "aspnetcore", confidence: 0.9, indicators: [`using: ${path}`] };
|
|
23760
|
+
}
|
|
23761
|
+
if (path.startsWith("Microsoft.EntityFrameworkCore")) {
|
|
23762
|
+
return { name: "efcore", confidence: 0.9, indicators: [`using: ${path}`] };
|
|
23763
|
+
}
|
|
23764
|
+
}
|
|
23765
|
+
return;
|
|
23766
|
+
}
|
|
23767
|
+
extractTypes(_context) {
|
|
23768
|
+
return [];
|
|
23769
|
+
}
|
|
23770
|
+
extractCalls(_context) {
|
|
23771
|
+
return [];
|
|
23772
|
+
}
|
|
23773
|
+
extractImports(_context) {
|
|
23774
|
+
return [];
|
|
23775
|
+
}
|
|
23776
|
+
extractPackage(_context) {
|
|
23777
|
+
return;
|
|
23778
|
+
}
|
|
23779
|
+
getBuiltinSources() {
|
|
23780
|
+
return [];
|
|
23781
|
+
}
|
|
23782
|
+
getBuiltinSinks() {
|
|
23783
|
+
return [];
|
|
23784
|
+
}
|
|
23785
|
+
}
|
|
23786
|
+
|
|
23380
23787
|
// ../circle-ir/dist/languages/plugins/index.js
|
|
23381
23788
|
function registerBuiltinPlugins() {
|
|
23382
23789
|
registerLanguage(new JavaPlugin);
|
|
@@ -23387,6 +23794,7 @@ function registerBuiltinPlugins() {
|
|
|
23387
23794
|
registerLanguage(new HtmlPlugin);
|
|
23388
23795
|
registerLanguage(new VuePlugin);
|
|
23389
23796
|
registerLanguage(new GoPlugin);
|
|
23797
|
+
registerLanguage(new CSharpPlugin);
|
|
23390
23798
|
}
|
|
23391
23799
|
// ../circle-ir/dist/analysis/passes/cross-file-pass.js
|
|
23392
23800
|
class CrossFilePass {
|
|
@@ -24570,6 +24978,7 @@ class LanguageSourcesPass {
|
|
|
24570
24978
|
additionalSources.push(...findStaticFieldSources(types, code, language));
|
|
24571
24979
|
additionalSources.push(...findSetterChainSources(types, code, language));
|
|
24572
24980
|
additionalSources.push(...findJavaScriptAssignmentSources(code, language));
|
|
24981
|
+
additionalSources.push(...findCSharpRequestSources(code, language));
|
|
24573
24982
|
const jsDOMSinks = findJavaScriptDOMSinks(code, language);
|
|
24574
24983
|
for (const s of jsDOMSinks) {
|
|
24575
24984
|
const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
|
|
@@ -25172,12 +25581,43 @@ function findSetterChainSources(types, sourceCode, language) {
|
|
|
25172
25581
|
}
|
|
25173
25582
|
return sources;
|
|
25174
25583
|
}
|
|
25584
|
+
function findCSharpRequestSources(sourceCode, language) {
|
|
25585
|
+
if (language !== "csharp")
|
|
25586
|
+
return [];
|
|
25587
|
+
const sources = [];
|
|
25588
|
+
const lines = sourceCode.split(`
|
|
25589
|
+
`);
|
|
25590
|
+
const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
|
|
25591
|
+
const requestReadRe = /\bRequest\s*\.\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\b/;
|
|
25592
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
25593
|
+
const m = assignRe.exec(lines[i2]);
|
|
25594
|
+
if (!m)
|
|
25595
|
+
continue;
|
|
25596
|
+
const [, varName, rhs] = m;
|
|
25597
|
+
if (!requestReadRe.test(rhs))
|
|
25598
|
+
continue;
|
|
25599
|
+
const lineNumber = i2 + 1;
|
|
25600
|
+
if (sources.some((s) => s.line === lineNumber && s.variable === varName))
|
|
25601
|
+
continue;
|
|
25602
|
+
sources.push({
|
|
25603
|
+
type: "http_param",
|
|
25604
|
+
location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
|
|
25605
|
+
severity: "high",
|
|
25606
|
+
line: lineNumber,
|
|
25607
|
+
confidence: 1,
|
|
25608
|
+
variable: varName
|
|
25609
|
+
});
|
|
25610
|
+
}
|
|
25611
|
+
return sources;
|
|
25612
|
+
}
|
|
25175
25613
|
function findJavaScriptAssignmentSources(sourceCode, language) {
|
|
25176
25614
|
if (!["javascript", "typescript"].includes(language))
|
|
25177
25615
|
return [];
|
|
25178
25616
|
const sources = [];
|
|
25179
25617
|
const lines = sourceCode.split(`
|
|
25180
25618
|
`);
|
|
25619
|
+
const isRouteHandler = /export\s+(?:async\s+)?(?:function\s+(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS|loader|action)\b|const\s+(?:GET|POST|PUT|PATCH|DELETE|loader|action)\b)/.test(sourceCode) && /\{[^}]*\bparams\b[^}]*\}/.test(sourceCode);
|
|
25620
|
+
const patterns = isRouteHandler ? [...JS_TAINTED_PATTERNS, { pattern: /\bparams\s*[.[]/, type: "http_path" }] : JS_TAINTED_PATTERNS;
|
|
25181
25621
|
for (let lineNum = 0;lineNum < lines.length; lineNum++) {
|
|
25182
25622
|
const line = lines[lineNum];
|
|
25183
25623
|
const lineNumber = lineNum + 1;
|
|
@@ -25185,7 +25625,7 @@ function findJavaScriptAssignmentSources(sourceCode, language) {
|
|
|
25185
25625
|
if (!assignmentMatch)
|
|
25186
25626
|
continue;
|
|
25187
25627
|
const [, varName, rhs] = assignmentMatch;
|
|
25188
|
-
for (const { pattern, type } of
|
|
25628
|
+
for (const { pattern, type } of patterns) {
|
|
25189
25629
|
if (pattern.test(rhs)) {
|
|
25190
25630
|
const alreadyExists = sources.some((s) => s.line === lineNumber && s.type === type);
|
|
25191
25631
|
if (!alreadyExists) {
|
|
@@ -31857,6 +32297,13 @@ var REPLACE_ALL_TO_PLACEHOLDER_RE = /\.replaceAll\s*\([\s\S]*?,\s*"[\s,?]*\?[\s,
|
|
|
31857
32297
|
var HTML_CONTENT_TYPE_RE = /text\/html|TEXT_HTML/;
|
|
31858
32298
|
var FETCH_GLOBAL_RECEIVER_RE = /(?:^|[^.\w])(?:window|globalThis|self|global)\s*\.\s*fetch\s*\(/;
|
|
31859
32299
|
var FETCH_MEMBER_RECEIVER_RE = /[\w$)\]]\s*\.\s*fetch\s*\(/;
|
|
32300
|
+
var NOSQL_ARRAY_METHODS = new Set(["find", "filter", "some", "every", "findIndex", "findLast"]);
|
|
32301
|
+
var FUNCTION_FIRST_ARG_RE = /\.\s*(?:find|filter|some|every|findIndex|findLast)\s*\(\s*(?:async\s+)?(?:function\b|(?:\([^()]*\)|[A-Za-z_$][\w$]*)\s*=>)/;
|
|
32302
|
+
var ORM_BUILDER_KEY_RE = /[{,]\s*(?:where|select|populate|include|orderBy|relations|attributes)\s*:/;
|
|
32303
|
+
var BROWSER_COMPONENT_EXT_RE = /\.(?:jsx|tsx|vue|svelte)$/;
|
|
32304
|
+
var CLIENT_SIGNAL_RE = /["']use client["']|\buse(?:State|Effect|Ref|Callback|Memo|Context|Reducer|LayoutEffect)\s*\(|\b(?:window|document|localStorage|sessionStorage|navigator)\s*\.|\son(?:Click|Change|Submit|Input|KeyDown|KeyUp|MouseOver|Focus|Blur)\s*=/;
|
|
32305
|
+
var SERVER_SIGNAL_RE = /["']use server["']|\b(?:getServerSideProps|getStaticProps|getStaticPaths)\b|from\s+["']next\/server["']|\bNext(?:Api)?(?:Request|Response)\b|from\s+["'](?:node:)?(?:fs|child_process|http|https|net|dns|dgram)["']|\bexport\s+(?:default\s+)?(?:async\s+)?function\s+handler\s*\(/;
|
|
32306
|
+
var FIXED_HOST_TEMPLATE_RE = /`https?:\/\/[^/${}\s`]+[/`]/;
|
|
31860
32307
|
function javaTraversalRejectGuardedLines(code) {
|
|
31861
32308
|
const covered = new Set;
|
|
31862
32309
|
const lines = code.split(`
|
|
@@ -31971,6 +32418,53 @@ function jsSsrfHostGuardedLines(code) {
|
|
|
31971
32418
|
}
|
|
31972
32419
|
return covered;
|
|
31973
32420
|
}
|
|
32421
|
+
var CSHARP_SANITIZER_RES = [
|
|
32422
|
+
{ re: /\b(?:HtmlEncode|JavaScriptStringEncode)\s*\(/, type: "xss" },
|
|
32423
|
+
{ re: /\bHtmlEncoder\s*\.\s*Encode\s*\(/, type: "xss" },
|
|
32424
|
+
{ re: /\bPath\s*\.\s*GetFileName\s*\(/, type: "path_traversal" }
|
|
32425
|
+
];
|
|
32426
|
+
function csharpSanitizedVarsByType(code) {
|
|
32427
|
+
const lines = code.split(`
|
|
32428
|
+
`);
|
|
32429
|
+
const byType = new Map;
|
|
32430
|
+
const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
|
|
32431
|
+
for (const { type } of CSHARP_SANITIZER_RES) {
|
|
32432
|
+
if (!byType.has(type))
|
|
32433
|
+
byType.set(type, new Set);
|
|
32434
|
+
}
|
|
32435
|
+
for (const line of lines) {
|
|
32436
|
+
const m = assignRe.exec(line);
|
|
32437
|
+
if (!m)
|
|
32438
|
+
continue;
|
|
32439
|
+
const [, lhs, rhs] = m;
|
|
32440
|
+
for (const { re, type } of CSHARP_SANITIZER_RES) {
|
|
32441
|
+
if (re.test(rhs))
|
|
32442
|
+
byType.get(type).add(lhs);
|
|
32443
|
+
}
|
|
32444
|
+
}
|
|
32445
|
+
let changed = true;
|
|
32446
|
+
let guard = 0;
|
|
32447
|
+
while (changed && guard < lines.length + 2) {
|
|
32448
|
+
changed = false;
|
|
32449
|
+
guard++;
|
|
32450
|
+
for (const line of lines) {
|
|
32451
|
+
const m = assignRe.exec(line);
|
|
32452
|
+
if (!m)
|
|
32453
|
+
continue;
|
|
32454
|
+
const [, lhs, rhs] = m;
|
|
32455
|
+
for (const [type, set] of byType) {
|
|
32456
|
+
if (set.has(lhs))
|
|
32457
|
+
continue;
|
|
32458
|
+
const refsSanitized = [...set].some((v) => new RegExp(`(?<![\\w])${escapeRegex(v)}(?![\\w])`).test(rhs));
|
|
32459
|
+
if (refsSanitized) {
|
|
32460
|
+
set.add(lhs);
|
|
32461
|
+
changed = true;
|
|
32462
|
+
}
|
|
32463
|
+
}
|
|
32464
|
+
}
|
|
32465
|
+
}
|
|
32466
|
+
return byType;
|
|
32467
|
+
}
|
|
31974
32468
|
var JAVA_INLINE_MATCHES_RE = /\.\s*matches\s*\(\s*"((?:[^"\\]|\\.)*)"\s*\)/g;
|
|
31975
32469
|
function resolveJavaReceiverType(receiver, sinkLine, sourceLines) {
|
|
31976
32470
|
if (!receiver || !/^[A-Za-z_]\w*$/.test(receiver))
|
|
@@ -32768,12 +33262,70 @@ class SinkFilterPass {
|
|
|
32768
33262
|
return !FETCH_MEMBER_RECEIVER_RE.test(sinkLineText);
|
|
32769
33263
|
});
|
|
32770
33264
|
}
|
|
33265
|
+
if (["javascript", "typescript"].includes(language)) {
|
|
33266
|
+
const sourceLines = ctx.code.split(`
|
|
33267
|
+
`);
|
|
33268
|
+
filtered = filtered.filter((sink) => {
|
|
33269
|
+
if (sink.type !== "nosql_injection")
|
|
33270
|
+
return true;
|
|
33271
|
+
if (!NOSQL_ARRAY_METHODS.has(sink.method ?? ""))
|
|
33272
|
+
return true;
|
|
33273
|
+
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
33274
|
+
return !FUNCTION_FIRST_ARG_RE.test(sinkLineText);
|
|
33275
|
+
});
|
|
33276
|
+
}
|
|
33277
|
+
if (["javascript", "typescript"].includes(language)) {
|
|
33278
|
+
const sourceLines = ctx.code.split(`
|
|
33279
|
+
`);
|
|
33280
|
+
filtered = filtered.filter((sink) => {
|
|
33281
|
+
if (sink.type !== "sql_injection" && sink.type !== "nosql_injection")
|
|
33282
|
+
return true;
|
|
33283
|
+
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
33284
|
+
return !ORM_BUILDER_KEY_RE.test(sinkLineText);
|
|
33285
|
+
});
|
|
33286
|
+
}
|
|
33287
|
+
if (["javascript", "typescript", "tsx"].includes(language)) {
|
|
33288
|
+
const file = ctx.graph?.ir?.meta?.file ?? "";
|
|
33289
|
+
if (BROWSER_COMPONENT_EXT_RE.test(file) && CLIENT_SIGNAL_RE.test(ctx.code) && !SERVER_SIGNAL_RE.test(ctx.code)) {
|
|
33290
|
+
filtered = filtered.filter((sink) => sink.type !== "ssrf");
|
|
33291
|
+
}
|
|
33292
|
+
}
|
|
33293
|
+
if (["javascript", "typescript"].includes(language)) {
|
|
33294
|
+
const sourceLines = ctx.code.split(`
|
|
33295
|
+
`);
|
|
33296
|
+
filtered = filtered.filter((sink) => {
|
|
33297
|
+
if (sink.type !== "ssrf")
|
|
33298
|
+
return true;
|
|
33299
|
+
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
33300
|
+
return !FIXED_HOST_TEMPLATE_RE.test(sinkLineText);
|
|
33301
|
+
});
|
|
33302
|
+
}
|
|
32771
33303
|
if (["javascript", "typescript"].includes(language)) {
|
|
32772
33304
|
const guardedLines = jsSsrfHostGuardedLines(ctx.code);
|
|
32773
33305
|
if (guardedLines.size > 0) {
|
|
32774
33306
|
filtered = filtered.filter((sink) => !(sink.type === "ssrf" && guardedLines.has(sink.line)));
|
|
32775
33307
|
}
|
|
32776
33308
|
}
|
|
33309
|
+
if (language === "csharp") {
|
|
33310
|
+
const sourceLines = ctx.code.split(`
|
|
33311
|
+
`);
|
|
33312
|
+
const sanitizedByType = csharpSanitizedVarsByType(ctx.code);
|
|
33313
|
+
filtered = filtered.filter((sink) => {
|
|
33314
|
+
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
33315
|
+
for (const { re, type } of CSHARP_SANITIZER_RES) {
|
|
33316
|
+
if (type === sink.type && re.test(sinkLineText))
|
|
33317
|
+
return false;
|
|
33318
|
+
}
|
|
33319
|
+
const sanitizedVars = sanitizedByType.get(sink.type);
|
|
33320
|
+
if (sanitizedVars && sanitizedVars.size > 0) {
|
|
33321
|
+
for (const v of sanitizedVars) {
|
|
33322
|
+
if (new RegExp(`(?<![\\w])${escapeRegex(v)}(?![\\w])`).test(sinkLineText))
|
|
33323
|
+
return false;
|
|
33324
|
+
}
|
|
33325
|
+
}
|
|
33326
|
+
return true;
|
|
33327
|
+
});
|
|
33328
|
+
}
|
|
32777
33329
|
if (["javascript", "typescript"].includes(language)) {
|
|
32778
33330
|
const sourceLines = ctx.code.split(`
|
|
32779
33331
|
`);
|
|
@@ -35417,7 +35969,7 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
35417
35969
|
}
|
|
35418
35970
|
}
|
|
35419
35971
|
}
|
|
35420
|
-
if (language === "java" && typeof code === "string" && sourcesWithVar.length > 0) {
|
|
35972
|
+
if ((language === "java" || language === "csharp") && typeof code === "string" && sourcesWithVar.length > 0) {
|
|
35421
35973
|
const seedVars = new Set(sourcesWithVar.map((s) => s.variable));
|
|
35422
35974
|
const derived = buildJavaTaintedVars(code, seedVars);
|
|
35423
35975
|
if (derived.size > 0) {
|
|
@@ -47226,7 +47778,7 @@ var colors = {
|
|
|
47226
47778
|
};
|
|
47227
47779
|
|
|
47228
47780
|
// src/version.ts
|
|
47229
|
-
var version = "
|
|
47781
|
+
var version = "4.2.0";
|
|
47230
47782
|
|
|
47231
47783
|
// src/formatters.ts
|
|
47232
47784
|
var LIBRARY_API_SURFACE_TAG2 = "library-api-surface:caller-responsibility";
|
|
@@ -48108,6 +48660,7 @@ var LANG_MAP = {
|
|
|
48108
48660
|
".tsx": "typescript",
|
|
48109
48661
|
".py": "python",
|
|
48110
48662
|
".go": "go",
|
|
48663
|
+
".cs": "csharp",
|
|
48111
48664
|
".rs": "rust",
|
|
48112
48665
|
".sh": "bash",
|
|
48113
48666
|
".bash": "bash",
|
|
@@ -48866,7 +49419,7 @@ async function handleInit() {
|
|
|
48866
49419
|
}
|
|
48867
49420
|
const config = {
|
|
48868
49421
|
version: "1.0",
|
|
48869
|
-
include: ["src/**/*.java", "src/**/*.ts", "src/**/*.js", "src/**/*.py", "src/**/*.go"],
|
|
49422
|
+
include: ["src/**/*.java", "src/**/*.ts", "src/**/*.js", "src/**/*.py", "src/**/*.go", "src/**/*.cs"],
|
|
48870
49423
|
exclude: ["**/test/**", "**/tests/**", "**/node_modules/**", "**/dist/**"],
|
|
48871
49424
|
passes: {
|
|
48872
49425
|
"naming-convention": false,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "4.2.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": "^
|
|
69
|
+
"circle-ir": "^4.2.0"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
72
|
"@types/node": "^25.5.0",
|