circle-ir 3.216.0 → 4.0.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/analysis/config-loader.d.ts.map +1 -1
- package/dist/analysis/config-loader.js +68 -0
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +42 -0
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.d.ts.map +1 -1
- package/dist/analysis/passes/sink-filter-pass.js +178 -0
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.js +1 -1
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +18 -3
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/browser/circle-ir.js +530 -5
- package/dist/core/circle-ir-core.cjs +338 -4
- package/dist/core/circle-ir-core.js +338 -4
- package/dist/core/extractors/calls.d.ts.map +1 -1
- package/dist/core/extractors/calls.js +133 -0
- package/dist/core/extractors/calls.js.map +1 -1
- package/dist/core/extractors/dfg.d.ts.map +1 -1
- package/dist/core/extractors/dfg.js +86 -1
- package/dist/core/extractors/dfg.js.map +1 -1
- package/dist/core/extractors/types.d.ts.map +1 -1
- package/dist/core/extractors/types.js +89 -0
- package/dist/core/extractors/types.js.map +1 -1
- package/dist/languages/plugins/csharp.d.ts +35 -0
- package/dist/languages/plugins/csharp.d.ts.map +1 -0
- package/dist/languages/plugins/csharp.js +72 -0
- package/dist/languages/plugins/csharp.js.map +1 -0
- package/dist/languages/plugins/index.d.ts +1 -0
- package/dist/languages/plugins/index.d.ts.map +1 -1
- package/dist/languages/plugins/index.js +3 -0
- package/dist/languages/plugins/index.js.map +1 -1
- package/dist/languages/types.d.ts +1 -1
- package/dist/languages/types.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/wasm/tree-sitter-csharp.wasm +0 -0
- package/package.json +1 -1
|
@@ -4356,8 +4356,84 @@ function extractTypes(tree, cache, language) {
|
|
|
4356
4356
|
if (isJavaScript) {
|
|
4357
4357
|
return extractJavaScriptTypes(tree, cache);
|
|
4358
4358
|
}
|
|
4359
|
+
if (effectiveLanguage === "csharp") {
|
|
4360
|
+
return extractCSharpTypes(tree, cache);
|
|
4361
|
+
}
|
|
4359
4362
|
return extractJavaTypes(tree, cache);
|
|
4360
4363
|
}
|
|
4364
|
+
function extractCSharpTypes(tree, cache) {
|
|
4365
|
+
const types = [];
|
|
4366
|
+
const KINDS = {
|
|
4367
|
+
class_declaration: "class",
|
|
4368
|
+
record_declaration: "class",
|
|
4369
|
+
struct_declaration: "class",
|
|
4370
|
+
interface_declaration: "interface",
|
|
4371
|
+
enum_declaration: "enum"
|
|
4372
|
+
};
|
|
4373
|
+
for (const kindNode of Object.keys(KINDS)) {
|
|
4374
|
+
for (const node of getNodesFromCache(tree.rootNode, kindNode, cache)) {
|
|
4375
|
+
const nameNode = node.childForFieldName("name");
|
|
4376
|
+
const body2 = node.childForFieldName("body");
|
|
4377
|
+
const methods = [];
|
|
4378
|
+
if (body2) {
|
|
4379
|
+
for (let i2 = 0; i2 < body2.childCount; i2++) {
|
|
4380
|
+
const m = body2.child(i2);
|
|
4381
|
+
if (!m || m.type !== "method_declaration") continue;
|
|
4382
|
+
const mName = m.childForFieldName("name");
|
|
4383
|
+
const paramList = m.childForFieldName("parameters");
|
|
4384
|
+
const parameters = [];
|
|
4385
|
+
if (paramList) {
|
|
4386
|
+
for (let j = 0; j < paramList.childCount; j++) {
|
|
4387
|
+
const pnode = paramList.child(j);
|
|
4388
|
+
if (!pnode || pnode.type !== "parameter") continue;
|
|
4389
|
+
const pName = pnode.childForFieldName("name");
|
|
4390
|
+
const pType = pnode.childForFieldName("type");
|
|
4391
|
+
if (pName) {
|
|
4392
|
+
parameters.push({
|
|
4393
|
+
name: getNodeText(pName),
|
|
4394
|
+
type: pType ? getNodeText(pType) : null,
|
|
4395
|
+
annotations: [],
|
|
4396
|
+
line: pnode.startPosition.row + 1
|
|
4397
|
+
});
|
|
4398
|
+
}
|
|
4399
|
+
}
|
|
4400
|
+
}
|
|
4401
|
+
const returns = m.childForFieldName("returns");
|
|
4402
|
+
methods.push({
|
|
4403
|
+
name: mName ? getNodeText(mName) : "unknown",
|
|
4404
|
+
return_type: returns ? getNodeText(returns) : null,
|
|
4405
|
+
parameters,
|
|
4406
|
+
annotations: [],
|
|
4407
|
+
modifiers: extractCSharpModifiers(m),
|
|
4408
|
+
start_line: m.startPosition.row + 1,
|
|
4409
|
+
end_line: m.endPosition.row + 1
|
|
4410
|
+
});
|
|
4411
|
+
}
|
|
4412
|
+
}
|
|
4413
|
+
types.push({
|
|
4414
|
+
name: nameNode ? getNodeText(nameNode) : "anonymous",
|
|
4415
|
+
kind: KINDS[kindNode],
|
|
4416
|
+
package: null,
|
|
4417
|
+
extends: null,
|
|
4418
|
+
implements: [],
|
|
4419
|
+
annotations: [],
|
|
4420
|
+
methods,
|
|
4421
|
+
fields: [],
|
|
4422
|
+
start_line: node.startPosition.row + 1,
|
|
4423
|
+
end_line: node.endPosition.row + 1
|
|
4424
|
+
});
|
|
4425
|
+
}
|
|
4426
|
+
}
|
|
4427
|
+
return types;
|
|
4428
|
+
}
|
|
4429
|
+
function extractCSharpModifiers(node) {
|
|
4430
|
+
const mods = [];
|
|
4431
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
4432
|
+
const c = node.child(i2);
|
|
4433
|
+
if (c && c.type === "modifier") mods.push(getNodeText(c));
|
|
4434
|
+
}
|
|
4435
|
+
return mods;
|
|
4436
|
+
}
|
|
4361
4437
|
function extractJavaTypes(tree, cache) {
|
|
4362
4438
|
const types = [];
|
|
4363
4439
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
@@ -5816,6 +5892,9 @@ function extractCalls(tree, cache, language) {
|
|
|
5816
5892
|
if (isJavaScript) {
|
|
5817
5893
|
return extractJavaScriptCalls(tree, cache);
|
|
5818
5894
|
}
|
|
5895
|
+
if (detectedLanguage === "csharp") {
|
|
5896
|
+
return extractCSharpCalls(tree, cache);
|
|
5897
|
+
}
|
|
5819
5898
|
const context = buildResolutionContext(tree, cache);
|
|
5820
5899
|
const invocations = getNodesFromCache(tree.rootNode, "method_invocation", cache);
|
|
5821
5900
|
for (const inv of invocations) {
|
|
@@ -5830,6 +5909,107 @@ function extractCalls(tree, cache, language) {
|
|
|
5830
5909
|
}
|
|
5831
5910
|
return calls;
|
|
5832
5911
|
}
|
|
5912
|
+
function buildCSharpReceiverTypeMap(tree, cache) {
|
|
5913
|
+
const map = /* @__PURE__ */ new Map();
|
|
5914
|
+
const simple = (t) => t.replace(/<[^>]*>/g, "").split(".").pop()?.trim() ?? t;
|
|
5915
|
+
for (const vd of getNodesFromCache(tree.rootNode, "variable_declaration", cache)) {
|
|
5916
|
+
const typeNode = vd.childForFieldName("type");
|
|
5917
|
+
const declaredType = typeNode ? getNodeText(typeNode) : null;
|
|
5918
|
+
for (let i2 = 0; i2 < vd.childCount; i2++) {
|
|
5919
|
+
const decl = vd.child(i2);
|
|
5920
|
+
if (!decl || decl.type !== "variable_declarator") continue;
|
|
5921
|
+
const nameNode = decl.childForFieldName("name");
|
|
5922
|
+
if (nameNode?.type !== "identifier") continue;
|
|
5923
|
+
let t = declaredType && declaredType !== "var" ? declaredType : null;
|
|
5924
|
+
if (!t) {
|
|
5925
|
+
const oce = findFirstDescendant(decl, "object_creation_expression");
|
|
5926
|
+
const oceType = oce?.childForFieldName("type");
|
|
5927
|
+
if (oceType) t = getNodeText(oceType);
|
|
5928
|
+
}
|
|
5929
|
+
if (t && t !== "var") map.set(getNodeText(nameNode), simple(t));
|
|
5930
|
+
}
|
|
5931
|
+
}
|
|
5932
|
+
return map;
|
|
5933
|
+
}
|
|
5934
|
+
function findFirstDescendant(node, type) {
|
|
5935
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
5936
|
+
const c = node.child(i2);
|
|
5937
|
+
if (!c) continue;
|
|
5938
|
+
if (c.type === type) return c;
|
|
5939
|
+
const found = findFirstDescendant(c, type);
|
|
5940
|
+
if (found) return found;
|
|
5941
|
+
}
|
|
5942
|
+
return null;
|
|
5943
|
+
}
|
|
5944
|
+
function extractCSharpCalls(tree, cache) {
|
|
5945
|
+
const calls = [];
|
|
5946
|
+
const typeMap = buildCSharpReceiverTypeMap(tree, cache);
|
|
5947
|
+
const invocations = getNodesFromCache(tree.rootNode, "invocation_expression", cache);
|
|
5948
|
+
for (const inv of invocations) {
|
|
5949
|
+
const fn = inv.childForFieldName("function");
|
|
5950
|
+
let methodName = "unknown";
|
|
5951
|
+
let receiver = null;
|
|
5952
|
+
if (fn?.type === "member_access_expression") {
|
|
5953
|
+
const nameNode = fn.childForFieldName("name");
|
|
5954
|
+
const exprNode = fn.childForFieldName("expression");
|
|
5955
|
+
methodName = nameNode ? getNodeText(nameNode) : "unknown";
|
|
5956
|
+
receiver = exprNode ? getNodeText(exprNode) : null;
|
|
5957
|
+
} else if (fn) {
|
|
5958
|
+
methodName = getNodeText(fn);
|
|
5959
|
+
}
|
|
5960
|
+
const argsNode = inv.childForFieldName("arguments");
|
|
5961
|
+
calls.push({
|
|
5962
|
+
method_name: methodName,
|
|
5963
|
+
receiver,
|
|
5964
|
+
receiver_type: receiver ? typeMap.get(receiver) ?? null : null,
|
|
5965
|
+
receiver_type_fqn: null,
|
|
5966
|
+
arguments: argsNode ? extractCSharpArguments(argsNode) : [],
|
|
5967
|
+
location: { line: inv.startPosition.row + 1, column: inv.startPosition.column },
|
|
5968
|
+
in_method: findEnclosingMethod(inv)
|
|
5969
|
+
});
|
|
5970
|
+
}
|
|
5971
|
+
const objectCreations = getNodesFromCache(tree.rootNode, "object_creation_expression", cache);
|
|
5972
|
+
for (const creation of objectCreations) {
|
|
5973
|
+
const typeNode = creation.childForFieldName("type");
|
|
5974
|
+
const argsNode = creation.childForFieldName("arguments");
|
|
5975
|
+
calls.push({
|
|
5976
|
+
method_name: typeNode ? getNodeText(typeNode) : "unknown",
|
|
5977
|
+
receiver: null,
|
|
5978
|
+
receiver_type: null,
|
|
5979
|
+
receiver_type_fqn: null,
|
|
5980
|
+
arguments: argsNode ? extractCSharpArguments(argsNode) : [],
|
|
5981
|
+
location: { line: creation.startPosition.row + 1, column: creation.startPosition.column },
|
|
5982
|
+
in_method: findEnclosingMethod(creation),
|
|
5983
|
+
is_constructor: true
|
|
5984
|
+
});
|
|
5985
|
+
}
|
|
5986
|
+
return calls;
|
|
5987
|
+
}
|
|
5988
|
+
function extractCSharpArguments(argsNode) {
|
|
5989
|
+
const args2 = [];
|
|
5990
|
+
let position = 0;
|
|
5991
|
+
for (let i2 = 0; i2 < argsNode.childCount; i2++) {
|
|
5992
|
+
const child = argsNode.child(i2);
|
|
5993
|
+
if (!child || child.type !== "argument") continue;
|
|
5994
|
+
let expr = null;
|
|
5995
|
+
for (let j = child.childCount - 1; j >= 0; j--) {
|
|
5996
|
+
const c = child.child(j);
|
|
5997
|
+
if (c && c.isNamed) {
|
|
5998
|
+
expr = c;
|
|
5999
|
+
break;
|
|
6000
|
+
}
|
|
6001
|
+
}
|
|
6002
|
+
const text = expr ? getNodeText(expr) : getNodeText(child);
|
|
6003
|
+
args2.push({
|
|
6004
|
+
position: position++,
|
|
6005
|
+
expression: text,
|
|
6006
|
+
variable: expr?.type === "identifier" ? text : null,
|
|
6007
|
+
literal: expr?.type === "string_literal" ? text : null,
|
|
6008
|
+
value: null
|
|
6009
|
+
});
|
|
6010
|
+
}
|
|
6011
|
+
return args2;
|
|
6012
|
+
}
|
|
5833
6013
|
function extractJavaScriptCalls(tree, cache) {
|
|
5834
6014
|
const calls = [];
|
|
5835
6015
|
const context = buildJSResolutionContext(tree, cache);
|
|
@@ -8932,8 +9112,72 @@ function buildDFG(tree, cache, language) {
|
|
|
8932
9112
|
if (effectiveLanguage === "go") {
|
|
8933
9113
|
return buildGoDFG(tree);
|
|
8934
9114
|
}
|
|
9115
|
+
if (effectiveLanguage === "csharp") {
|
|
9116
|
+
return buildCSharpDFG(tree, cache);
|
|
9117
|
+
}
|
|
8935
9118
|
return buildJavaDFG(tree, cache);
|
|
8936
9119
|
}
|
|
9120
|
+
function buildCSharpDFG(tree, cache) {
|
|
9121
|
+
const defs = [];
|
|
9122
|
+
const uses = [];
|
|
9123
|
+
let defIdCounter = 1;
|
|
9124
|
+
let useIdCounter = 1;
|
|
9125
|
+
const scopeStack = [/* @__PURE__ */ new Map()];
|
|
9126
|
+
const methods = [
|
|
9127
|
+
...getNodesFromCache(tree.rootNode, "method_declaration", cache),
|
|
9128
|
+
...getNodesFromCache(tree.rootNode, "constructor_declaration", cache),
|
|
9129
|
+
...getNodesFromCache(tree.rootNode, "local_function_statement", cache)
|
|
9130
|
+
];
|
|
9131
|
+
for (const method of methods) {
|
|
9132
|
+
scopeStack.push(/* @__PURE__ */ new Map());
|
|
9133
|
+
const params = method.childForFieldName("parameters");
|
|
9134
|
+
if (params) {
|
|
9135
|
+
for (const def of extractParameterDefs(params, defIdCounter)) {
|
|
9136
|
+
defs.push(def);
|
|
9137
|
+
currentScope(scopeStack).set(def.variable, def.id);
|
|
9138
|
+
defIdCounter++;
|
|
9139
|
+
}
|
|
9140
|
+
}
|
|
9141
|
+
const body2 = method.childForFieldName("body");
|
|
9142
|
+
if (body2) {
|
|
9143
|
+
for (const decl of findNodes(body2, "variable_declarator")) {
|
|
9144
|
+
const nameNode = decl.childForFieldName("name");
|
|
9145
|
+
if (nameNode?.type === "identifier") {
|
|
9146
|
+
const name2 = getNodeText(nameNode);
|
|
9147
|
+
const def = { id: defIdCounter++, variable: name2, line: decl.startPosition.row + 1, kind: "local" };
|
|
9148
|
+
defs.push(def);
|
|
9149
|
+
currentScope(scopeStack).set(name2, def.id);
|
|
9150
|
+
}
|
|
9151
|
+
}
|
|
9152
|
+
for (const asn of findNodes(body2, "assignment_expression")) {
|
|
9153
|
+
const left = asn.childForFieldName("left");
|
|
9154
|
+
if (left?.type === "identifier") {
|
|
9155
|
+
const name2 = getNodeText(left);
|
|
9156
|
+
const def = { id: defIdCounter++, variable: name2, line: asn.startPosition.row + 1, kind: "local" };
|
|
9157
|
+
defs.push(def);
|
|
9158
|
+
currentScope(scopeStack).set(name2, def.id);
|
|
9159
|
+
}
|
|
9160
|
+
}
|
|
9161
|
+
const bodyUses = extractUses(body2, useIdCounter, scopeStack, false);
|
|
9162
|
+
uses.push(...bodyUses.uses);
|
|
9163
|
+
useIdCounter = bodyUses.nextId;
|
|
9164
|
+
}
|
|
9165
|
+
scopeStack.pop();
|
|
9166
|
+
}
|
|
9167
|
+
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
9168
|
+
for (const cls of classes) {
|
|
9169
|
+
const body2 = cls.childForFieldName("body");
|
|
9170
|
+
if (body2) {
|
|
9171
|
+
for (const def of extractFieldDefs(body2, defIdCounter)) {
|
|
9172
|
+
defs.push(def);
|
|
9173
|
+
currentScope(scopeStack).set(def.variable, def.id);
|
|
9174
|
+
defIdCounter++;
|
|
9175
|
+
}
|
|
9176
|
+
}
|
|
9177
|
+
}
|
|
9178
|
+
const chains = computeChains(defs, uses);
|
|
9179
|
+
return { defs, uses, chains };
|
|
9180
|
+
}
|
|
8937
9181
|
function buildJavaDFG(tree, cache) {
|
|
8938
9182
|
const defs = [];
|
|
8939
9183
|
const uses = [];
|
|
@@ -9396,7 +9640,7 @@ function extractParameterDefs(params, startId) {
|
|
|
9396
9640
|
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
9397
9641
|
const param = params.child(i2);
|
|
9398
9642
|
if (!param) continue;
|
|
9399
|
-
if (param.type === "formal_parameter" || param.type === "spread_parameter") {
|
|
9643
|
+
if (param.type === "formal_parameter" || param.type === "spread_parameter" || param.type === "parameter") {
|
|
9400
9644
|
const nameNode = param.childForFieldName("name");
|
|
9401
9645
|
if (nameNode) {
|
|
9402
9646
|
defs.push({
|
|
@@ -13486,6 +13730,67 @@ var DEFAULT_SINKS = [
|
|
|
13486
13730
|
{ method: "put", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
13487
13731
|
{ method: "patch", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
13488
13732
|
{ method: "head", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
13733
|
+
// C# SQLi (Phase-0 spike — ADO.NET). `new SqlCommand(sql, conn)` builds the
|
|
13734
|
+
// command from a raw string; the tainted SQL is arg[0] of the constructor.
|
|
13735
|
+
{ method: "SqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13736
|
+
{ method: "NpgsqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13737
|
+
{ method: "MySqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13738
|
+
{ method: "SqliteCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13739
|
+
{ method: "OracleCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13740
|
+
{ method: "ExecuteSqlRaw", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13741
|
+
{ method: "ExecuteSqlRawAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13742
|
+
{ method: "FromSqlRaw", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13743
|
+
{ method: "FromSqlRawAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13744
|
+
// C# command injection — Process.Start / ProcessStartInfo (CWE-78).
|
|
13745
|
+
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13746
|
+
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13747
|
+
// C# path traversal — System.IO file APIs (CWE-22). Distinctive method names.
|
|
13748
|
+
{ method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13749
|
+
{ method: "ReadAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13750
|
+
{ method: "ReadAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13751
|
+
{ method: "WriteAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13752
|
+
{ method: "WriteAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13753
|
+
{ method: "AppendAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13754
|
+
{ method: "OpenRead", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13755
|
+
{ method: "OpenWrite", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13756
|
+
{ method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13757
|
+
{ method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13758
|
+
{ method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13759
|
+
// C# SSRF — HttpClient / WebClient / WebRequest (CWE-918).
|
|
13760
|
+
{ method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13761
|
+
{ method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13762
|
+
{ method: "PutAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13763
|
+
{ method: "DeleteAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13764
|
+
{ method: "GetStringAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13765
|
+
{ method: "GetByteArrayAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13766
|
+
{ method: "GetStreamAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13767
|
+
{ method: "SendAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13768
|
+
{ method: "DownloadString", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13769
|
+
{ method: "DownloadData", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13770
|
+
{ method: "DownloadFile", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [1], languages: ["csharp"] },
|
|
13771
|
+
{ method: "Create", class: "WebRequest", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13772
|
+
// C# code injection — dynamic script/assembly loading (CWE-94).
|
|
13773
|
+
{ method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13774
|
+
{ method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13775
|
+
// C# insecure deserialization — BinaryFormatter et al. (CWE-502).
|
|
13776
|
+
{ method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13777
|
+
{ method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13778
|
+
// C# XSS — raw HTML output (CWE-79).
|
|
13779
|
+
{ method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13780
|
+
{ method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13781
|
+
{ method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13782
|
+
// C# LDAP injection — System.DirectoryServices (CWE-90). The user-built
|
|
13783
|
+
// filter is the constructor argument.
|
|
13784
|
+
{ method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13785
|
+
{ method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13786
|
+
// C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
|
|
13787
|
+
{ method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13788
|
+
{ method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13789
|
+
{ method: "Compile", class: "XPathExpression", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13790
|
+
// C# XXE — untrusted XML into a parser without DTD hardening (CWE-611).
|
|
13791
|
+
{ method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13792
|
+
{ method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13793
|
+
{ method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13489
13794
|
// Python SQLi — asyncpg Connection.*
|
|
13490
13795
|
{ method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
13491
13796
|
{ method: "fetch", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
@@ -13693,6 +13998,15 @@ var DEFAULT_SANITIZERS = [
|
|
|
13693
13998
|
{ method: "escapeCsv", removes: ["xss"] },
|
|
13694
13999
|
// OWASP HTML sanitizer (`com.googlecode.owasp-java-html-sanitizer`).
|
|
13695
14000
|
{ method: "sanitize", class: "PolicyFactory", removes: ["xss"] },
|
|
14001
|
+
// C# sanitizers (Phase-1). HTML encoders neutralise XSS; Path.GetFileName
|
|
14002
|
+
// strips any directory component (basename) so a traversal payload cannot
|
|
14003
|
+
// escape the target directory. Method names are C#-distinctive.
|
|
14004
|
+
{ method: "HtmlEncode", removes: ["xss"] },
|
|
14005
|
+
// HttpUtility / WebUtility
|
|
14006
|
+
{ method: "JavaScriptStringEncode", removes: ["xss"] },
|
|
14007
|
+
// HttpUtility
|
|
14008
|
+
{ method: "Encode", class: "HtmlEncoder", removes: ["xss"] },
|
|
14009
|
+
{ method: "GetFileName", class: "Path", removes: ["path_traversal"] },
|
|
13696
14010
|
// Java Base64 / Hex / MessageDigest — encoded output is binary-safe
|
|
13697
14011
|
// (cognium-dev #213 seventh slice). None of these carry attacker
|
|
13698
14012
|
// shell/SQL/HTML metacharacters through the encoding — Base64 emits
|
|
@@ -14531,7 +14845,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
14531
14845
|
const skipMethods = ["toString", "hashCode", "equals", "compareTo"];
|
|
14532
14846
|
if (skipMethods.includes(method.name)) continue;
|
|
14533
14847
|
for (const param of method.parameters) {
|
|
14534
|
-
const isTaintable = param.type ? isInterproceduralTaintableType(param.type) : true;
|
|
14848
|
+
const isTaintable = param.type ? isInterproceduralTaintableType(param.type, language) : true;
|
|
14535
14849
|
if (isTaintable) {
|
|
14536
14850
|
const paramLine = param.line ?? method.start_line;
|
|
14537
14851
|
sources.push({
|
|
@@ -14554,7 +14868,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
14554
14868
|
// interprocedural_param at the method decl line instead of the
|
|
14555
14869
|
// real HTTP source, breaking downstream sink-type filters
|
|
14556
14870
|
// (regressed #78, #92.1, #105 FP-31, #215 recall).
|
|
14557
|
-
...language === "java" ? { variable: param.name } : {}
|
|
14871
|
+
...language === "java" || language === "csharp" ? { variable: param.name } : {}
|
|
14558
14872
|
});
|
|
14559
14873
|
}
|
|
14560
14874
|
}
|
|
@@ -14651,7 +14965,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
14651
14965
|
}
|
|
14652
14966
|
return result;
|
|
14653
14967
|
}
|
|
14654
|
-
function isInterproceduralTaintableType(typeName) {
|
|
14968
|
+
function isInterproceduralTaintableType(typeName, language) {
|
|
14655
14969
|
const baseType = typeName.split("<")[0].trim();
|
|
14656
14970
|
const excludedTypes = [
|
|
14657
14971
|
// Servlet framework - taint comes from specific methods, not the parameter itself
|
|
@@ -14724,6 +15038,26 @@ function isInterproceduralTaintableType(typeName) {
|
|
|
14724
15038
|
if (taintableTypes.includes(baseType)) {
|
|
14725
15039
|
return true;
|
|
14726
15040
|
}
|
|
15041
|
+
if (language === "csharp") {
|
|
15042
|
+
const csharpTaintable = [
|
|
15043
|
+
"string",
|
|
15044
|
+
"object",
|
|
15045
|
+
"IEnumerable",
|
|
15046
|
+
"ICollection",
|
|
15047
|
+
"IList",
|
|
15048
|
+
"Dictionary",
|
|
15049
|
+
"IDictionary",
|
|
15050
|
+
"IReadOnlyList",
|
|
15051
|
+
"IReadOnlyCollection",
|
|
15052
|
+
// Byte/stream payloads carry deserialization / upload input.
|
|
15053
|
+
"Stream",
|
|
15054
|
+
"byte[]",
|
|
15055
|
+
"Byte[]",
|
|
15056
|
+
"MemoryStream"
|
|
15057
|
+
];
|
|
15058
|
+
const leaf = baseType.split(".").pop() ?? baseType;
|
|
15059
|
+
if (csharpTaintable.includes(baseType) || csharpTaintable.includes(leaf)) return true;
|
|
15060
|
+
}
|
|
14727
15061
|
if (typeName.endsWith("[]")) {
|
|
14728
15062
|
const elementType = typeName.slice(0, -2);
|
|
14729
15063
|
if (elementType === "String" || elementType === "Object" || elementType === "byte") {
|
|
@@ -25014,6 +25348,73 @@ var GoPlugin = class extends BaseLanguagePlugin {
|
|
|
25014
25348
|
}
|
|
25015
25349
|
};
|
|
25016
25350
|
|
|
25351
|
+
// src/languages/plugins/csharp.ts
|
|
25352
|
+
var CSharpPlugin = class extends BaseLanguagePlugin {
|
|
25353
|
+
id = "csharp";
|
|
25354
|
+
name = "C#";
|
|
25355
|
+
extensions = [".cs"];
|
|
25356
|
+
wasmPath = "tree-sitter-csharp.wasm";
|
|
25357
|
+
nodeTypes = {
|
|
25358
|
+
// Type declarations
|
|
25359
|
+
classDeclaration: ["class_declaration", "record_declaration", "struct_declaration"],
|
|
25360
|
+
interfaceDeclaration: ["interface_declaration"],
|
|
25361
|
+
enumDeclaration: ["enum_declaration"],
|
|
25362
|
+
functionDeclaration: [],
|
|
25363
|
+
methodDeclaration: ["method_declaration", "constructor_declaration", "local_function_statement"],
|
|
25364
|
+
// Expressions — NB these diverge from Java (invocation_expression vs
|
|
25365
|
+
// method_invocation, local_declaration_statement vs local_variable_declaration).
|
|
25366
|
+
methodCall: ["invocation_expression"],
|
|
25367
|
+
functionCall: [],
|
|
25368
|
+
assignment: ["assignment_expression"],
|
|
25369
|
+
variableDeclaration: ["local_declaration_statement", "field_declaration", "variable_declaration"],
|
|
25370
|
+
// Parameters and arguments
|
|
25371
|
+
parameter: ["parameter"],
|
|
25372
|
+
argument: ["argument_list"],
|
|
25373
|
+
// Attributes (C# analogue of annotations/decorators)
|
|
25374
|
+
annotation: ["attribute", "attribute_list"],
|
|
25375
|
+
decorator: [],
|
|
25376
|
+
// Imports
|
|
25377
|
+
importStatement: ["using_directive"],
|
|
25378
|
+
// Control flow
|
|
25379
|
+
ifStatement: ["if_statement"],
|
|
25380
|
+
forStatement: ["for_statement", "for_each_statement"],
|
|
25381
|
+
whileStatement: ["while_statement"],
|
|
25382
|
+
tryStatement: ["try_statement"],
|
|
25383
|
+
returnStatement: ["return_statement"]
|
|
25384
|
+
};
|
|
25385
|
+
detectFramework(context) {
|
|
25386
|
+
for (const imp of context.imports) {
|
|
25387
|
+
const path = imp.from_package || imp.imported_name;
|
|
25388
|
+
if (path.startsWith("Microsoft.AspNetCore") || path.startsWith("Microsoft.Extensions")) {
|
|
25389
|
+
return { name: "aspnetcore", confidence: 0.9, indicators: [`using: ${path}`] };
|
|
25390
|
+
}
|
|
25391
|
+
if (path.startsWith("Microsoft.EntityFrameworkCore")) {
|
|
25392
|
+
return { name: "efcore", confidence: 0.9, indicators: [`using: ${path}`] };
|
|
25393
|
+
}
|
|
25394
|
+
}
|
|
25395
|
+
return void 0;
|
|
25396
|
+
}
|
|
25397
|
+
// --- LanguagePlugin contract (unused by the main analyze() path; Phase-1) ---
|
|
25398
|
+
extractTypes(_context) {
|
|
25399
|
+
return [];
|
|
25400
|
+
}
|
|
25401
|
+
extractCalls(_context) {
|
|
25402
|
+
return [];
|
|
25403
|
+
}
|
|
25404
|
+
extractImports(_context) {
|
|
25405
|
+
return [];
|
|
25406
|
+
}
|
|
25407
|
+
extractPackage(_context) {
|
|
25408
|
+
return void 0;
|
|
25409
|
+
}
|
|
25410
|
+
getBuiltinSources() {
|
|
25411
|
+
return [];
|
|
25412
|
+
}
|
|
25413
|
+
getBuiltinSinks() {
|
|
25414
|
+
return [];
|
|
25415
|
+
}
|
|
25416
|
+
};
|
|
25417
|
+
|
|
25017
25418
|
// src/languages/plugins/index.ts
|
|
25018
25419
|
function registerBuiltinPlugins() {
|
|
25019
25420
|
registerLanguage(new JavaPlugin());
|
|
@@ -25024,6 +25425,7 @@ function registerBuiltinPlugins() {
|
|
|
25024
25425
|
registerLanguage(new HtmlPlugin());
|
|
25025
25426
|
registerLanguage(new VuePlugin());
|
|
25026
25427
|
registerLanguage(new GoPlugin());
|
|
25428
|
+
registerLanguage(new CSharpPlugin());
|
|
25027
25429
|
}
|
|
25028
25430
|
|
|
25029
25431
|
// src/analysis/html/html-extractor.ts
|
|
@@ -25892,6 +26294,7 @@ var LanguageSourcesPass = class {
|
|
|
25892
26294
|
additionalSources.push(...findStaticFieldSources(types, code, language));
|
|
25893
26295
|
additionalSources.push(...findSetterChainSources(types, code, language));
|
|
25894
26296
|
additionalSources.push(...findJavaScriptAssignmentSources(code, language));
|
|
26297
|
+
additionalSources.push(...findCSharpRequestSources(code, language));
|
|
25895
26298
|
const jsDOMSinks = findJavaScriptDOMSinks(code, language);
|
|
25896
26299
|
for (const s of jsDOMSinks) {
|
|
25897
26300
|
const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
|
|
@@ -26546,6 +26949,30 @@ function findSetterChainSources(types, sourceCode, language) {
|
|
|
26546
26949
|
}
|
|
26547
26950
|
return sources;
|
|
26548
26951
|
}
|
|
26952
|
+
function findCSharpRequestSources(sourceCode, language) {
|
|
26953
|
+
if (language !== "csharp") return [];
|
|
26954
|
+
const sources = [];
|
|
26955
|
+
const lines = sourceCode.split("\n");
|
|
26956
|
+
const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
|
|
26957
|
+
const requestReadRe = /\bRequest\s*\.\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\b/;
|
|
26958
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
26959
|
+
const m = assignRe.exec(lines[i2]);
|
|
26960
|
+
if (!m) continue;
|
|
26961
|
+
const [, varName, rhs] = m;
|
|
26962
|
+
if (!requestReadRe.test(rhs)) continue;
|
|
26963
|
+
const lineNumber = i2 + 1;
|
|
26964
|
+
if (sources.some((s) => s.line === lineNumber && s.variable === varName)) continue;
|
|
26965
|
+
sources.push({
|
|
26966
|
+
type: "http_param",
|
|
26967
|
+
location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
|
|
26968
|
+
severity: "high",
|
|
26969
|
+
line: lineNumber,
|
|
26970
|
+
confidence: 1,
|
|
26971
|
+
variable: varName
|
|
26972
|
+
});
|
|
26973
|
+
}
|
|
26974
|
+
return sources;
|
|
26975
|
+
}
|
|
26549
26976
|
function findJavaScriptAssignmentSources(sourceCode, language) {
|
|
26550
26977
|
if (!["javascript", "typescript"].includes(language)) return [];
|
|
26551
26978
|
const sources = [];
|
|
@@ -32531,6 +32958,13 @@ var REPLACE_ALL_TO_PLACEHOLDER_RE = /\.replaceAll\s*\([\s\S]*?,\s*"[\s,?]*\?[\s,
|
|
|
32531
32958
|
var HTML_CONTENT_TYPE_RE = /text\/html|TEXT_HTML/;
|
|
32532
32959
|
var FETCH_GLOBAL_RECEIVER_RE = /(?:^|[^.\w])(?:window|globalThis|self|global)\s*\.\s*fetch\s*\(/;
|
|
32533
32960
|
var FETCH_MEMBER_RECEIVER_RE = /[\w$)\]]\s*\.\s*fetch\s*\(/;
|
|
32961
|
+
var NOSQL_ARRAY_METHODS = /* @__PURE__ */ new Set(["find", "filter", "some", "every", "findIndex", "findLast"]);
|
|
32962
|
+
var FUNCTION_FIRST_ARG_RE = /\.\s*(?:find|filter|some|every|findIndex|findLast)\s*\(\s*(?:async\s+)?(?:function\b|(?:\([^()]*\)|[A-Za-z_$][\w$]*)\s*=>)/;
|
|
32963
|
+
var ORM_BUILDER_KEY_RE = /[{,]\s*(?:where|select|populate|include|orderBy|relations|attributes)\s*:/;
|
|
32964
|
+
var BROWSER_COMPONENT_EXT_RE = /\.(?:jsx|tsx|vue|svelte)$/;
|
|
32965
|
+
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*=/;
|
|
32966
|
+
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*\(/;
|
|
32967
|
+
var FIXED_HOST_TEMPLATE_RE = /`https?:\/\/[^/${}\s`]+[/`]/;
|
|
32534
32968
|
function javaTraversalRejectGuardedLines(code) {
|
|
32535
32969
|
const covered = /* @__PURE__ */ new Set();
|
|
32536
32970
|
const lines = code.split("\n");
|
|
@@ -32622,6 +33056,49 @@ function jsSsrfHostGuardedLines(code) {
|
|
|
32622
33056
|
}
|
|
32623
33057
|
return covered;
|
|
32624
33058
|
}
|
|
33059
|
+
var CSHARP_SANITIZER_RES = [
|
|
33060
|
+
{ re: /\b(?:HtmlEncode|JavaScriptStringEncode)\s*\(/, type: "xss" },
|
|
33061
|
+
{ re: /\bHtmlEncoder\s*\.\s*Encode\s*\(/, type: "xss" },
|
|
33062
|
+
{ re: /\bPath\s*\.\s*GetFileName\s*\(/, type: "path_traversal" }
|
|
33063
|
+
];
|
|
33064
|
+
function csharpSanitizedVarsByType(code) {
|
|
33065
|
+
const lines = code.split("\n");
|
|
33066
|
+
const byType = /* @__PURE__ */ new Map();
|
|
33067
|
+
const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
|
|
33068
|
+
for (const { type } of CSHARP_SANITIZER_RES) {
|
|
33069
|
+
if (!byType.has(type)) byType.set(type, /* @__PURE__ */ new Set());
|
|
33070
|
+
}
|
|
33071
|
+
for (const line of lines) {
|
|
33072
|
+
const m = assignRe.exec(line);
|
|
33073
|
+
if (!m) continue;
|
|
33074
|
+
const [, lhs, rhs] = m;
|
|
33075
|
+
for (const { re, type } of CSHARP_SANITIZER_RES) {
|
|
33076
|
+
if (re.test(rhs)) byType.get(type).add(lhs);
|
|
33077
|
+
}
|
|
33078
|
+
}
|
|
33079
|
+
let changed = true;
|
|
33080
|
+
let guard = 0;
|
|
33081
|
+
while (changed && guard < lines.length + 2) {
|
|
33082
|
+
changed = false;
|
|
33083
|
+
guard++;
|
|
33084
|
+
for (const line of lines) {
|
|
33085
|
+
const m = assignRe.exec(line);
|
|
33086
|
+
if (!m) continue;
|
|
33087
|
+
const [, lhs, rhs] = m;
|
|
33088
|
+
for (const [type, set] of byType) {
|
|
33089
|
+
if (set.has(lhs)) continue;
|
|
33090
|
+
const refsSanitized = [...set].some(
|
|
33091
|
+
(v) => new RegExp(`(?<![\\w])${escapeRegex(v)}(?![\\w])`).test(rhs)
|
|
33092
|
+
);
|
|
33093
|
+
if (refsSanitized) {
|
|
33094
|
+
set.add(lhs);
|
|
33095
|
+
changed = true;
|
|
33096
|
+
}
|
|
33097
|
+
}
|
|
33098
|
+
}
|
|
33099
|
+
}
|
|
33100
|
+
return byType;
|
|
33101
|
+
}
|
|
32625
33102
|
var JAVA_INLINE_MATCHES_RE = /\.\s*matches\s*\(\s*"((?:[^"\\]|\\.)*)"\s*\)/g;
|
|
32626
33103
|
function resolveJavaReceiverType(receiver, sinkLine, sourceLines) {
|
|
32627
33104
|
if (!receiver || !/^[A-Za-z_]\w*$/.test(receiver)) return null;
|
|
@@ -33325,6 +33802,37 @@ var SinkFilterPass = class {
|
|
|
33325
33802
|
return !FETCH_MEMBER_RECEIVER_RE.test(sinkLineText);
|
|
33326
33803
|
});
|
|
33327
33804
|
}
|
|
33805
|
+
if (["javascript", "typescript"].includes(language)) {
|
|
33806
|
+
const sourceLines = ctx.code.split("\n");
|
|
33807
|
+
filtered = filtered.filter((sink) => {
|
|
33808
|
+
if (sink.type !== "nosql_injection") return true;
|
|
33809
|
+
if (!NOSQL_ARRAY_METHODS.has(sink.method ?? "")) return true;
|
|
33810
|
+
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
33811
|
+
return !FUNCTION_FIRST_ARG_RE.test(sinkLineText);
|
|
33812
|
+
});
|
|
33813
|
+
}
|
|
33814
|
+
if (["javascript", "typescript"].includes(language)) {
|
|
33815
|
+
const sourceLines = ctx.code.split("\n");
|
|
33816
|
+
filtered = filtered.filter((sink) => {
|
|
33817
|
+
if (sink.type !== "sql_injection" && sink.type !== "nosql_injection") return true;
|
|
33818
|
+
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
33819
|
+
return !ORM_BUILDER_KEY_RE.test(sinkLineText);
|
|
33820
|
+
});
|
|
33821
|
+
}
|
|
33822
|
+
if (["javascript", "typescript", "tsx"].includes(language)) {
|
|
33823
|
+
const file = ctx.graph?.ir?.meta?.file ?? "";
|
|
33824
|
+
if (BROWSER_COMPONENT_EXT_RE.test(file) && CLIENT_SIGNAL_RE.test(ctx.code) && !SERVER_SIGNAL_RE.test(ctx.code)) {
|
|
33825
|
+
filtered = filtered.filter((sink) => sink.type !== "ssrf");
|
|
33826
|
+
}
|
|
33827
|
+
}
|
|
33828
|
+
if (["javascript", "typescript"].includes(language)) {
|
|
33829
|
+
const sourceLines = ctx.code.split("\n");
|
|
33830
|
+
filtered = filtered.filter((sink) => {
|
|
33831
|
+
if (sink.type !== "ssrf") return true;
|
|
33832
|
+
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
33833
|
+
return !FIXED_HOST_TEMPLATE_RE.test(sinkLineText);
|
|
33834
|
+
});
|
|
33835
|
+
}
|
|
33328
33836
|
if (["javascript", "typescript"].includes(language)) {
|
|
33329
33837
|
const guardedLines = jsSsrfHostGuardedLines(ctx.code);
|
|
33330
33838
|
if (guardedLines.size > 0) {
|
|
@@ -33333,6 +33841,23 @@ var SinkFilterPass = class {
|
|
|
33333
33841
|
);
|
|
33334
33842
|
}
|
|
33335
33843
|
}
|
|
33844
|
+
if (language === "csharp") {
|
|
33845
|
+
const sourceLines = ctx.code.split("\n");
|
|
33846
|
+
const sanitizedByType = csharpSanitizedVarsByType(ctx.code);
|
|
33847
|
+
filtered = filtered.filter((sink) => {
|
|
33848
|
+
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
33849
|
+
for (const { re, type } of CSHARP_SANITIZER_RES) {
|
|
33850
|
+
if (type === sink.type && re.test(sinkLineText)) return false;
|
|
33851
|
+
}
|
|
33852
|
+
const sanitizedVars = sanitizedByType.get(sink.type);
|
|
33853
|
+
if (sanitizedVars && sanitizedVars.size > 0) {
|
|
33854
|
+
for (const v of sanitizedVars) {
|
|
33855
|
+
if (new RegExp(`(?<![\\w])${escapeRegex(v)}(?![\\w])`).test(sinkLineText)) return false;
|
|
33856
|
+
}
|
|
33857
|
+
}
|
|
33858
|
+
return true;
|
|
33859
|
+
});
|
|
33860
|
+
}
|
|
33336
33861
|
if (["javascript", "typescript"].includes(language)) {
|
|
33337
33862
|
const sourceLines = ctx.code.split("\n");
|
|
33338
33863
|
filtered = filtered.filter((sink) => {
|
|
@@ -35781,7 +36306,7 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
35781
36306
|
}
|
|
35782
36307
|
}
|
|
35783
36308
|
}
|
|
35784
|
-
if (language === "java" && typeof code === "string" && sourcesWithVar.length > 0) {
|
|
36309
|
+
if ((language === "java" || language === "csharp") && typeof code === "string" && sourcesWithVar.length > 0) {
|
|
35785
36310
|
const seedVars = new Set(sourcesWithVar.map((s) => s.variable));
|
|
35786
36311
|
const derived = buildJavaTaintedVars(code, seedVars);
|
|
35787
36312
|
if (derived.size > 0) {
|