circle-ir 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 +15 -1
- 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/findings.d.ts.map +1 -1
- package/dist/analysis/findings.js +15 -9
- package/dist/analysis/findings.js.map +1 -1
- package/dist/analysis/note-coalescer.d.ts +0 -4
- package/dist/analysis/note-coalescer.d.ts.map +1 -1
- package/dist/analysis/note-coalescer.js +36 -1
- package/dist/analysis/note-coalescer.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +54 -1
- 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 +573 -17
- 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/docs/SPEC.md +1 -1
- package/package.json +1 -1
|
@@ -4402,8 +4402,84 @@ function extractTypes(tree, cache, language) {
|
|
|
4402
4402
|
if (isJavaScript) {
|
|
4403
4403
|
return extractJavaScriptTypes(tree, cache);
|
|
4404
4404
|
}
|
|
4405
|
+
if (effectiveLanguage === "csharp") {
|
|
4406
|
+
return extractCSharpTypes(tree, cache);
|
|
4407
|
+
}
|
|
4405
4408
|
return extractJavaTypes(tree, cache);
|
|
4406
4409
|
}
|
|
4410
|
+
function extractCSharpTypes(tree, cache) {
|
|
4411
|
+
const types = [];
|
|
4412
|
+
const KINDS = {
|
|
4413
|
+
class_declaration: "class",
|
|
4414
|
+
record_declaration: "class",
|
|
4415
|
+
struct_declaration: "class",
|
|
4416
|
+
interface_declaration: "interface",
|
|
4417
|
+
enum_declaration: "enum"
|
|
4418
|
+
};
|
|
4419
|
+
for (const kindNode of Object.keys(KINDS)) {
|
|
4420
|
+
for (const node of getNodesFromCache(tree.rootNode, kindNode, cache)) {
|
|
4421
|
+
const nameNode = node.childForFieldName("name");
|
|
4422
|
+
const body2 = node.childForFieldName("body");
|
|
4423
|
+
const methods = [];
|
|
4424
|
+
if (body2) {
|
|
4425
|
+
for (let i2 = 0; i2 < body2.childCount; i2++) {
|
|
4426
|
+
const m = body2.child(i2);
|
|
4427
|
+
if (!m || m.type !== "method_declaration") continue;
|
|
4428
|
+
const mName = m.childForFieldName("name");
|
|
4429
|
+
const paramList = m.childForFieldName("parameters");
|
|
4430
|
+
const parameters = [];
|
|
4431
|
+
if (paramList) {
|
|
4432
|
+
for (let j = 0; j < paramList.childCount; j++) {
|
|
4433
|
+
const pnode = paramList.child(j);
|
|
4434
|
+
if (!pnode || pnode.type !== "parameter") continue;
|
|
4435
|
+
const pName = pnode.childForFieldName("name");
|
|
4436
|
+
const pType = pnode.childForFieldName("type");
|
|
4437
|
+
if (pName) {
|
|
4438
|
+
parameters.push({
|
|
4439
|
+
name: getNodeText(pName),
|
|
4440
|
+
type: pType ? getNodeText(pType) : null,
|
|
4441
|
+
annotations: [],
|
|
4442
|
+
line: pnode.startPosition.row + 1
|
|
4443
|
+
});
|
|
4444
|
+
}
|
|
4445
|
+
}
|
|
4446
|
+
}
|
|
4447
|
+
const returns = m.childForFieldName("returns");
|
|
4448
|
+
methods.push({
|
|
4449
|
+
name: mName ? getNodeText(mName) : "unknown",
|
|
4450
|
+
return_type: returns ? getNodeText(returns) : null,
|
|
4451
|
+
parameters,
|
|
4452
|
+
annotations: [],
|
|
4453
|
+
modifiers: extractCSharpModifiers(m),
|
|
4454
|
+
start_line: m.startPosition.row + 1,
|
|
4455
|
+
end_line: m.endPosition.row + 1
|
|
4456
|
+
});
|
|
4457
|
+
}
|
|
4458
|
+
}
|
|
4459
|
+
types.push({
|
|
4460
|
+
name: nameNode ? getNodeText(nameNode) : "anonymous",
|
|
4461
|
+
kind: KINDS[kindNode],
|
|
4462
|
+
package: null,
|
|
4463
|
+
extends: null,
|
|
4464
|
+
implements: [],
|
|
4465
|
+
annotations: [],
|
|
4466
|
+
methods,
|
|
4467
|
+
fields: [],
|
|
4468
|
+
start_line: node.startPosition.row + 1,
|
|
4469
|
+
end_line: node.endPosition.row + 1
|
|
4470
|
+
});
|
|
4471
|
+
}
|
|
4472
|
+
}
|
|
4473
|
+
return types;
|
|
4474
|
+
}
|
|
4475
|
+
function extractCSharpModifiers(node) {
|
|
4476
|
+
const mods = [];
|
|
4477
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
4478
|
+
const c = node.child(i2);
|
|
4479
|
+
if (c && c.type === "modifier") mods.push(getNodeText(c));
|
|
4480
|
+
}
|
|
4481
|
+
return mods;
|
|
4482
|
+
}
|
|
4407
4483
|
function extractJavaTypes(tree, cache) {
|
|
4408
4484
|
const types = [];
|
|
4409
4485
|
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
@@ -5862,6 +5938,9 @@ function extractCalls(tree, cache, language) {
|
|
|
5862
5938
|
if (isJavaScript) {
|
|
5863
5939
|
return extractJavaScriptCalls(tree, cache);
|
|
5864
5940
|
}
|
|
5941
|
+
if (detectedLanguage === "csharp") {
|
|
5942
|
+
return extractCSharpCalls(tree, cache);
|
|
5943
|
+
}
|
|
5865
5944
|
const context = buildResolutionContext(tree, cache);
|
|
5866
5945
|
const invocations = getNodesFromCache(tree.rootNode, "method_invocation", cache);
|
|
5867
5946
|
for (const inv of invocations) {
|
|
@@ -5876,6 +5955,107 @@ function extractCalls(tree, cache, language) {
|
|
|
5876
5955
|
}
|
|
5877
5956
|
return calls;
|
|
5878
5957
|
}
|
|
5958
|
+
function buildCSharpReceiverTypeMap(tree, cache) {
|
|
5959
|
+
const map = /* @__PURE__ */ new Map();
|
|
5960
|
+
const simple = (t) => t.replace(/<[^>]*>/g, "").split(".").pop()?.trim() ?? t;
|
|
5961
|
+
for (const vd of getNodesFromCache(tree.rootNode, "variable_declaration", cache)) {
|
|
5962
|
+
const typeNode = vd.childForFieldName("type");
|
|
5963
|
+
const declaredType = typeNode ? getNodeText(typeNode) : null;
|
|
5964
|
+
for (let i2 = 0; i2 < vd.childCount; i2++) {
|
|
5965
|
+
const decl = vd.child(i2);
|
|
5966
|
+
if (!decl || decl.type !== "variable_declarator") continue;
|
|
5967
|
+
const nameNode = decl.childForFieldName("name");
|
|
5968
|
+
if (nameNode?.type !== "identifier") continue;
|
|
5969
|
+
let t = declaredType && declaredType !== "var" ? declaredType : null;
|
|
5970
|
+
if (!t) {
|
|
5971
|
+
const oce = findFirstDescendant(decl, "object_creation_expression");
|
|
5972
|
+
const oceType = oce?.childForFieldName("type");
|
|
5973
|
+
if (oceType) t = getNodeText(oceType);
|
|
5974
|
+
}
|
|
5975
|
+
if (t && t !== "var") map.set(getNodeText(nameNode), simple(t));
|
|
5976
|
+
}
|
|
5977
|
+
}
|
|
5978
|
+
return map;
|
|
5979
|
+
}
|
|
5980
|
+
function findFirstDescendant(node, type) {
|
|
5981
|
+
for (let i2 = 0; i2 < node.childCount; i2++) {
|
|
5982
|
+
const c = node.child(i2);
|
|
5983
|
+
if (!c) continue;
|
|
5984
|
+
if (c.type === type) return c;
|
|
5985
|
+
const found = findFirstDescendant(c, type);
|
|
5986
|
+
if (found) return found;
|
|
5987
|
+
}
|
|
5988
|
+
return null;
|
|
5989
|
+
}
|
|
5990
|
+
function extractCSharpCalls(tree, cache) {
|
|
5991
|
+
const calls = [];
|
|
5992
|
+
const typeMap = buildCSharpReceiverTypeMap(tree, cache);
|
|
5993
|
+
const invocations = getNodesFromCache(tree.rootNode, "invocation_expression", cache);
|
|
5994
|
+
for (const inv of invocations) {
|
|
5995
|
+
const fn = inv.childForFieldName("function");
|
|
5996
|
+
let methodName = "unknown";
|
|
5997
|
+
let receiver = null;
|
|
5998
|
+
if (fn?.type === "member_access_expression") {
|
|
5999
|
+
const nameNode = fn.childForFieldName("name");
|
|
6000
|
+
const exprNode = fn.childForFieldName("expression");
|
|
6001
|
+
methodName = nameNode ? getNodeText(nameNode) : "unknown";
|
|
6002
|
+
receiver = exprNode ? getNodeText(exprNode) : null;
|
|
6003
|
+
} else if (fn) {
|
|
6004
|
+
methodName = getNodeText(fn);
|
|
6005
|
+
}
|
|
6006
|
+
const argsNode = inv.childForFieldName("arguments");
|
|
6007
|
+
calls.push({
|
|
6008
|
+
method_name: methodName,
|
|
6009
|
+
receiver,
|
|
6010
|
+
receiver_type: receiver ? typeMap.get(receiver) ?? null : null,
|
|
6011
|
+
receiver_type_fqn: null,
|
|
6012
|
+
arguments: argsNode ? extractCSharpArguments(argsNode) : [],
|
|
6013
|
+
location: { line: inv.startPosition.row + 1, column: inv.startPosition.column },
|
|
6014
|
+
in_method: findEnclosingMethod(inv)
|
|
6015
|
+
});
|
|
6016
|
+
}
|
|
6017
|
+
const objectCreations = getNodesFromCache(tree.rootNode, "object_creation_expression", cache);
|
|
6018
|
+
for (const creation of objectCreations) {
|
|
6019
|
+
const typeNode = creation.childForFieldName("type");
|
|
6020
|
+
const argsNode = creation.childForFieldName("arguments");
|
|
6021
|
+
calls.push({
|
|
6022
|
+
method_name: typeNode ? getNodeText(typeNode) : "unknown",
|
|
6023
|
+
receiver: null,
|
|
6024
|
+
receiver_type: null,
|
|
6025
|
+
receiver_type_fqn: null,
|
|
6026
|
+
arguments: argsNode ? extractCSharpArguments(argsNode) : [],
|
|
6027
|
+
location: { line: creation.startPosition.row + 1, column: creation.startPosition.column },
|
|
6028
|
+
in_method: findEnclosingMethod(creation),
|
|
6029
|
+
is_constructor: true
|
|
6030
|
+
});
|
|
6031
|
+
}
|
|
6032
|
+
return calls;
|
|
6033
|
+
}
|
|
6034
|
+
function extractCSharpArguments(argsNode) {
|
|
6035
|
+
const args2 = [];
|
|
6036
|
+
let position = 0;
|
|
6037
|
+
for (let i2 = 0; i2 < argsNode.childCount; i2++) {
|
|
6038
|
+
const child = argsNode.child(i2);
|
|
6039
|
+
if (!child || child.type !== "argument") continue;
|
|
6040
|
+
let expr = null;
|
|
6041
|
+
for (let j = child.childCount - 1; j >= 0; j--) {
|
|
6042
|
+
const c = child.child(j);
|
|
6043
|
+
if (c && c.isNamed) {
|
|
6044
|
+
expr = c;
|
|
6045
|
+
break;
|
|
6046
|
+
}
|
|
6047
|
+
}
|
|
6048
|
+
const text = expr ? getNodeText(expr) : getNodeText(child);
|
|
6049
|
+
args2.push({
|
|
6050
|
+
position: position++,
|
|
6051
|
+
expression: text,
|
|
6052
|
+
variable: expr?.type === "identifier" ? text : null,
|
|
6053
|
+
literal: expr?.type === "string_literal" ? text : null,
|
|
6054
|
+
value: null
|
|
6055
|
+
});
|
|
6056
|
+
}
|
|
6057
|
+
return args2;
|
|
6058
|
+
}
|
|
5879
6059
|
function extractJavaScriptCalls(tree, cache) {
|
|
5880
6060
|
const calls = [];
|
|
5881
6061
|
const context = buildJSResolutionContext(tree, cache);
|
|
@@ -8978,8 +9158,72 @@ function buildDFG(tree, cache, language) {
|
|
|
8978
9158
|
if (effectiveLanguage === "go") {
|
|
8979
9159
|
return buildGoDFG(tree);
|
|
8980
9160
|
}
|
|
9161
|
+
if (effectiveLanguage === "csharp") {
|
|
9162
|
+
return buildCSharpDFG(tree, cache);
|
|
9163
|
+
}
|
|
8981
9164
|
return buildJavaDFG(tree, cache);
|
|
8982
9165
|
}
|
|
9166
|
+
function buildCSharpDFG(tree, cache) {
|
|
9167
|
+
const defs = [];
|
|
9168
|
+
const uses = [];
|
|
9169
|
+
let defIdCounter = 1;
|
|
9170
|
+
let useIdCounter = 1;
|
|
9171
|
+
const scopeStack = [/* @__PURE__ */ new Map()];
|
|
9172
|
+
const methods = [
|
|
9173
|
+
...getNodesFromCache(tree.rootNode, "method_declaration", cache),
|
|
9174
|
+
...getNodesFromCache(tree.rootNode, "constructor_declaration", cache),
|
|
9175
|
+
...getNodesFromCache(tree.rootNode, "local_function_statement", cache)
|
|
9176
|
+
];
|
|
9177
|
+
for (const method of methods) {
|
|
9178
|
+
scopeStack.push(/* @__PURE__ */ new Map());
|
|
9179
|
+
const params = method.childForFieldName("parameters");
|
|
9180
|
+
if (params) {
|
|
9181
|
+
for (const def of extractParameterDefs(params, defIdCounter)) {
|
|
9182
|
+
defs.push(def);
|
|
9183
|
+
currentScope(scopeStack).set(def.variable, def.id);
|
|
9184
|
+
defIdCounter++;
|
|
9185
|
+
}
|
|
9186
|
+
}
|
|
9187
|
+
const body2 = method.childForFieldName("body");
|
|
9188
|
+
if (body2) {
|
|
9189
|
+
for (const decl of findNodes(body2, "variable_declarator")) {
|
|
9190
|
+
const nameNode = decl.childForFieldName("name");
|
|
9191
|
+
if (nameNode?.type === "identifier") {
|
|
9192
|
+
const name2 = getNodeText(nameNode);
|
|
9193
|
+
const def = { id: defIdCounter++, variable: name2, line: decl.startPosition.row + 1, kind: "local" };
|
|
9194
|
+
defs.push(def);
|
|
9195
|
+
currentScope(scopeStack).set(name2, def.id);
|
|
9196
|
+
}
|
|
9197
|
+
}
|
|
9198
|
+
for (const asn of findNodes(body2, "assignment_expression")) {
|
|
9199
|
+
const left = asn.childForFieldName("left");
|
|
9200
|
+
if (left?.type === "identifier") {
|
|
9201
|
+
const name2 = getNodeText(left);
|
|
9202
|
+
const def = { id: defIdCounter++, variable: name2, line: asn.startPosition.row + 1, kind: "local" };
|
|
9203
|
+
defs.push(def);
|
|
9204
|
+
currentScope(scopeStack).set(name2, def.id);
|
|
9205
|
+
}
|
|
9206
|
+
}
|
|
9207
|
+
const bodyUses = extractUses(body2, useIdCounter, scopeStack, false);
|
|
9208
|
+
uses.push(...bodyUses.uses);
|
|
9209
|
+
useIdCounter = bodyUses.nextId;
|
|
9210
|
+
}
|
|
9211
|
+
scopeStack.pop();
|
|
9212
|
+
}
|
|
9213
|
+
const classes = getNodesFromCache(tree.rootNode, "class_declaration", cache);
|
|
9214
|
+
for (const cls of classes) {
|
|
9215
|
+
const body2 = cls.childForFieldName("body");
|
|
9216
|
+
if (body2) {
|
|
9217
|
+
for (const def of extractFieldDefs(body2, defIdCounter)) {
|
|
9218
|
+
defs.push(def);
|
|
9219
|
+
currentScope(scopeStack).set(def.variable, def.id);
|
|
9220
|
+
defIdCounter++;
|
|
9221
|
+
}
|
|
9222
|
+
}
|
|
9223
|
+
}
|
|
9224
|
+
const chains = computeChains(defs, uses);
|
|
9225
|
+
return { defs, uses, chains };
|
|
9226
|
+
}
|
|
8983
9227
|
function buildJavaDFG(tree, cache) {
|
|
8984
9228
|
const defs = [];
|
|
8985
9229
|
const uses = [];
|
|
@@ -9442,7 +9686,7 @@ function extractParameterDefs(params, startId) {
|
|
|
9442
9686
|
for (let i2 = 0; i2 < params.childCount; i2++) {
|
|
9443
9687
|
const param = params.child(i2);
|
|
9444
9688
|
if (!param) continue;
|
|
9445
|
-
if (param.type === "formal_parameter" || param.type === "spread_parameter") {
|
|
9689
|
+
if (param.type === "formal_parameter" || param.type === "spread_parameter" || param.type === "parameter") {
|
|
9446
9690
|
const nameNode = param.childForFieldName("name");
|
|
9447
9691
|
if (nameNode) {
|
|
9448
9692
|
defs.push({
|
|
@@ -12881,6 +13125,67 @@ var DEFAULT_SINKS = [
|
|
|
12881
13125
|
{ method: "put", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
12882
13126
|
{ method: "patch", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
12883
13127
|
{ method: "head", class: "httpx", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0] },
|
|
13128
|
+
// C# SQLi (Phase-0 spike — ADO.NET). `new SqlCommand(sql, conn)` builds the
|
|
13129
|
+
// command from a raw string; the tainted SQL is arg[0] of the constructor.
|
|
13130
|
+
{ method: "SqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13131
|
+
{ method: "NpgsqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13132
|
+
{ method: "MySqlCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13133
|
+
{ method: "SqliteCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13134
|
+
{ method: "OracleCommand", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13135
|
+
{ method: "ExecuteSqlRaw", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13136
|
+
{ method: "ExecuteSqlRawAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13137
|
+
{ method: "FromSqlRaw", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13138
|
+
{ method: "FromSqlRawAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13139
|
+
// C# command injection — Process.Start / ProcessStartInfo (CWE-78).
|
|
13140
|
+
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13141
|
+
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13142
|
+
// C# path traversal — System.IO file APIs (CWE-22). Distinctive method names.
|
|
13143
|
+
{ method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13144
|
+
{ method: "ReadAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13145
|
+
{ method: "ReadAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13146
|
+
{ method: "WriteAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13147
|
+
{ method: "WriteAllBytes", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13148
|
+
{ method: "AppendAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13149
|
+
{ method: "OpenRead", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13150
|
+
{ method: "OpenWrite", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13151
|
+
{ method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13152
|
+
{ method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13153
|
+
{ method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13154
|
+
// C# SSRF — HttpClient / WebClient / WebRequest (CWE-918).
|
|
13155
|
+
{ method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13156
|
+
{ method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13157
|
+
{ method: "PutAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13158
|
+
{ method: "DeleteAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13159
|
+
{ method: "GetStringAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13160
|
+
{ method: "GetByteArrayAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13161
|
+
{ method: "GetStreamAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13162
|
+
{ method: "SendAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13163
|
+
{ method: "DownloadString", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13164
|
+
{ method: "DownloadData", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13165
|
+
{ method: "DownloadFile", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [1], languages: ["csharp"] },
|
|
13166
|
+
{ method: "Create", class: "WebRequest", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13167
|
+
// C# code injection — dynamic script/assembly loading (CWE-94).
|
|
13168
|
+
{ method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13169
|
+
{ method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13170
|
+
// C# insecure deserialization — BinaryFormatter et al. (CWE-502).
|
|
13171
|
+
{ method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13172
|
+
{ method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13173
|
+
// C# XSS — raw HTML output (CWE-79).
|
|
13174
|
+
{ method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13175
|
+
{ method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13176
|
+
{ method: "HtmlString", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13177
|
+
// C# LDAP injection — System.DirectoryServices (CWE-90). The user-built
|
|
13178
|
+
// filter is the constructor argument.
|
|
13179
|
+
{ method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13180
|
+
{ method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13181
|
+
// C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
|
|
13182
|
+
{ method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13183
|
+
{ method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13184
|
+
{ method: "Compile", class: "XPathExpression", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13185
|
+
// C# XXE — untrusted XML into a parser without DTD hardening (CWE-611).
|
|
13186
|
+
{ method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13187
|
+
{ method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13188
|
+
{ method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
12884
13189
|
// Python SQLi — asyncpg Connection.*
|
|
12885
13190
|
{ method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
12886
13191
|
{ method: "fetch", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
@@ -13088,6 +13393,15 @@ var DEFAULT_SANITIZERS = [
|
|
|
13088
13393
|
{ method: "escapeCsv", removes: ["xss"] },
|
|
13089
13394
|
// OWASP HTML sanitizer (`com.googlecode.owasp-java-html-sanitizer`).
|
|
13090
13395
|
{ method: "sanitize", class: "PolicyFactory", removes: ["xss"] },
|
|
13396
|
+
// C# sanitizers (Phase-1). HTML encoders neutralise XSS; Path.GetFileName
|
|
13397
|
+
// strips any directory component (basename) so a traversal payload cannot
|
|
13398
|
+
// escape the target directory. Method names are C#-distinctive.
|
|
13399
|
+
{ method: "HtmlEncode", removes: ["xss"] },
|
|
13400
|
+
// HttpUtility / WebUtility
|
|
13401
|
+
{ method: "JavaScriptStringEncode", removes: ["xss"] },
|
|
13402
|
+
// HttpUtility
|
|
13403
|
+
{ method: "Encode", class: "HtmlEncoder", removes: ["xss"] },
|
|
13404
|
+
{ method: "GetFileName", class: "Path", removes: ["path_traversal"] },
|
|
13091
13405
|
// Java Base64 / Hex / MessageDigest — encoded output is binary-safe
|
|
13092
13406
|
// (cognium-dev #213 seventh slice). None of these carry attacker
|
|
13093
13407
|
// shell/SQL/HTML metacharacters through the encoding — Base64 emits
|
|
@@ -13831,7 +14145,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
13831
14145
|
const skipMethods = ["toString", "hashCode", "equals", "compareTo"];
|
|
13832
14146
|
if (skipMethods.includes(method.name)) continue;
|
|
13833
14147
|
for (const param of method.parameters) {
|
|
13834
|
-
const isTaintable = param.type ? isInterproceduralTaintableType(param.type) : true;
|
|
14148
|
+
const isTaintable = param.type ? isInterproceduralTaintableType(param.type, language) : true;
|
|
13835
14149
|
if (isTaintable) {
|
|
13836
14150
|
const paramLine = param.line ?? method.start_line;
|
|
13837
14151
|
sources.push({
|
|
@@ -13854,7 +14168,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
13854
14168
|
// interprocedural_param at the method decl line instead of the
|
|
13855
14169
|
// real HTTP source, breaking downstream sink-type filters
|
|
13856
14170
|
// (regressed #78, #92.1, #105 FP-31, #215 recall).
|
|
13857
|
-
...language === "java" ? { variable: param.name } : {}
|
|
14171
|
+
...language === "java" || language === "csharp" ? { variable: param.name } : {}
|
|
13858
14172
|
});
|
|
13859
14173
|
}
|
|
13860
14174
|
}
|
|
@@ -13951,7 +14265,7 @@ function findSources(calls, types, patterns, sourceLines, language) {
|
|
|
13951
14265
|
}
|
|
13952
14266
|
return result;
|
|
13953
14267
|
}
|
|
13954
|
-
function isInterproceduralTaintableType(typeName) {
|
|
14268
|
+
function isInterproceduralTaintableType(typeName, language) {
|
|
13955
14269
|
const baseType = typeName.split("<")[0].trim();
|
|
13956
14270
|
const excludedTypes = [
|
|
13957
14271
|
// Servlet framework - taint comes from specific methods, not the parameter itself
|
|
@@ -14024,6 +14338,26 @@ function isInterproceduralTaintableType(typeName) {
|
|
|
14024
14338
|
if (taintableTypes.includes(baseType)) {
|
|
14025
14339
|
return true;
|
|
14026
14340
|
}
|
|
14341
|
+
if (language === "csharp") {
|
|
14342
|
+
const csharpTaintable = [
|
|
14343
|
+
"string",
|
|
14344
|
+
"object",
|
|
14345
|
+
"IEnumerable",
|
|
14346
|
+
"ICollection",
|
|
14347
|
+
"IList",
|
|
14348
|
+
"Dictionary",
|
|
14349
|
+
"IDictionary",
|
|
14350
|
+
"IReadOnlyList",
|
|
14351
|
+
"IReadOnlyCollection",
|
|
14352
|
+
// Byte/stream payloads carry deserialization / upload input.
|
|
14353
|
+
"Stream",
|
|
14354
|
+
"byte[]",
|
|
14355
|
+
"Byte[]",
|
|
14356
|
+
"MemoryStream"
|
|
14357
|
+
];
|
|
14358
|
+
const leaf = baseType.split(".").pop() ?? baseType;
|
|
14359
|
+
if (csharpTaintable.includes(baseType) || csharpTaintable.includes(leaf)) return true;
|
|
14360
|
+
}
|
|
14027
14361
|
if (typeName.endsWith("[]")) {
|
|
14028
14362
|
const elementType = typeName.slice(0, -2);
|
|
14029
14363
|
if (elementType === "String" || elementType === "Object" || elementType === "byte") {
|