circle-ir 4.4.0 → 4.6.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 +10 -0
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/passes/insecure-deserialization-config-pass.d.ts +38 -0
- package/dist/analysis/passes/insecure-deserialization-config-pass.d.ts.map +1 -0
- package/dist/analysis/passes/insecure-deserialization-config-pass.js +69 -0
- package/dist/analysis/passes/insecure-deserialization-config-pass.js.map +1 -0
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +11 -2
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/taint-propagation-pass.js +39 -0
- package/dist/analysis/passes/taint-propagation-pass.js.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +3 -0
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +92 -3
- package/dist/core/circle-ir-core.cjs +23 -1
- package/dist/core/circle-ir-core.js +23 -1
- package/dist/core/extractors/calls.js +15 -1
- package/dist/core/extractors/calls.js.map +1 -1
- package/package.json +1 -1
|
@@ -5941,6 +5941,14 @@ function findFirstDescendant(node, type) {
|
|
|
5941
5941
|
}
|
|
5942
5942
|
return null;
|
|
5943
5943
|
}
|
|
5944
|
+
var CSHARP_COMMAND_EXECUTE_METHODS = /* @__PURE__ */ new Set([
|
|
5945
|
+
"ExecuteScalar",
|
|
5946
|
+
"ExecuteReader",
|
|
5947
|
+
"ExecuteNonQuery",
|
|
5948
|
+
"ExecuteScalarAsync",
|
|
5949
|
+
"ExecuteReaderAsync",
|
|
5950
|
+
"ExecuteNonQueryAsync"
|
|
5951
|
+
]);
|
|
5944
5952
|
function extractCSharpCalls(tree, cache) {
|
|
5945
5953
|
const calls = [];
|
|
5946
5954
|
const typeMap = buildCSharpReceiverTypeMap(tree, cache);
|
|
@@ -5958,12 +5966,16 @@ function extractCSharpCalls(tree, cache) {
|
|
|
5958
5966
|
methodName = getNodeText(fn);
|
|
5959
5967
|
}
|
|
5960
5968
|
const argsNode = inv.childForFieldName("arguments");
|
|
5969
|
+
let args2 = argsNode ? extractCSharpArguments(argsNode) : [];
|
|
5970
|
+
if (receiver && CSHARP_COMMAND_EXECUTE_METHODS.has(methodName)) {
|
|
5971
|
+
args2 = [{ position: 0, expression: receiver, variable: receiver, literal: null, value: null }, ...args2];
|
|
5972
|
+
}
|
|
5961
5973
|
calls.push({
|
|
5962
5974
|
method_name: methodName,
|
|
5963
5975
|
receiver,
|
|
5964
5976
|
receiver_type: receiver ? typeMap.get(receiver) ?? null : null,
|
|
5965
5977
|
receiver_type_fqn: null,
|
|
5966
|
-
arguments:
|
|
5978
|
+
arguments: args2,
|
|
5967
5979
|
location: { line: inv.startPosition.row + 1, column: inv.startPosition.column },
|
|
5968
5980
|
in_method: findEnclosingMethod(inv)
|
|
5969
5981
|
});
|
|
@@ -13770,6 +13782,16 @@ var DEFAULT_SINKS = [
|
|
|
13770
13782
|
// ADO.NET `cmd.CommandText = "…" + x` — emitted as a synthetic call by
|
|
13771
13783
|
// extractCSharpCalls (property-assignment sink; the ctor-arg sink misses it).
|
|
13772
13784
|
{ method: "CommandText", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13785
|
+
// ADO.NET `cmd.Execute*()` — object-carried sink (cognium-dev#271). The taint
|
|
13786
|
+
// rides the SqlCommand receiver (CommandText set from tainted data); the
|
|
13787
|
+
// receiver is surfaced as arg[0] by extractCSharpCalls, and the command object
|
|
13788
|
+
// is tainted in taint-propagation when its CommandText takes a tainted value.
|
|
13789
|
+
{ method: "ExecuteScalar", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13790
|
+
{ method: "ExecuteReader", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13791
|
+
{ method: "ExecuteNonQuery", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13792
|
+
{ method: "ExecuteScalarAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13793
|
+
{ method: "ExecuteReaderAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13794
|
+
{ method: "ExecuteNonQueryAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13773
13795
|
// C# command injection — Process.Start / ProcessStartInfo (CWE-78).
|
|
13774
13796
|
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13775
13797
|
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
@@ -27013,15 +27035,17 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
27013
27035
|
const lines = sourceCode.split("\n");
|
|
27014
27036
|
const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
|
|
27015
27037
|
const requestReadRe = /\bRequest\s*\.\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\b/;
|
|
27038
|
+
const consoleReadRe = /\bConsole\s*\.\s*ReadLine\s*\(/;
|
|
27016
27039
|
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
27017
27040
|
const m = assignRe.exec(lines[i2]);
|
|
27018
27041
|
if (!m) continue;
|
|
27019
27042
|
const [, varName, rhs] = m;
|
|
27020
|
-
|
|
27043
|
+
const type = requestReadRe.test(rhs) ? "http_param" : consoleReadRe.test(rhs) ? "io_input" : null;
|
|
27044
|
+
if (!type) continue;
|
|
27021
27045
|
const lineNumber = i2 + 1;
|
|
27022
27046
|
if (sources.some((s) => s.line === lineNumber && s.variable === varName)) continue;
|
|
27023
27047
|
sources.push({
|
|
27024
|
-
type
|
|
27048
|
+
type,
|
|
27025
27049
|
location: `${varName} = ${rhs.trim().substring(0, 50)}${rhs.length > 50 ? "..." : ""}`,
|
|
27026
27050
|
severity: "high",
|
|
27027
27051
|
line: lineNumber,
|
|
@@ -36385,6 +36409,31 @@ function detectExpressionScanFlows(calls, sources, sinks, sanitizers, unreachabl
|
|
|
36385
36409
|
}
|
|
36386
36410
|
}
|
|
36387
36411
|
}
|
|
36412
|
+
if (language === "csharp" && typeof code === "string" && sourcesWithVar.length > 0) {
|
|
36413
|
+
const lines = code.split("\n");
|
|
36414
|
+
const ctRe = /(\w+)\s*\.\s*CommandText\s*\+?=\s*(.+)/;
|
|
36415
|
+
const anchor = sourcesWithVar.reduce((a, s) => s.line < a.line ? s : a, sourcesWithVar[0]);
|
|
36416
|
+
let added = true;
|
|
36417
|
+
let guard = 0;
|
|
36418
|
+
while (added && guard < lines.length + 2) {
|
|
36419
|
+
added = false;
|
|
36420
|
+
guard++;
|
|
36421
|
+
const known = new Set(sourcesWithVar.map((s) => s.variable));
|
|
36422
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
36423
|
+
const m = ctRe.exec(lines[i2]);
|
|
36424
|
+
if (!m) continue;
|
|
36425
|
+
const [, obj, rhs] = m;
|
|
36426
|
+
if (known.has(obj)) continue;
|
|
36427
|
+
const codePart = rhs.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ");
|
|
36428
|
+
const rhsTainted = sourcesWithVar.some(
|
|
36429
|
+
(s) => new RegExp(`(?<![\\p{L}\\p{N}_])${s.variable.replace(/[.*+?^${}()|[\\]\\\\]/g, "\\$&")}(?![\\p{L}\\p{N}_])`, "u").test(codePart)
|
|
36430
|
+
);
|
|
36431
|
+
if (!rhsTainted) continue;
|
|
36432
|
+
sourcesWithVar.push({ ...anchor, variable: obj, line: i2 + 1 });
|
|
36433
|
+
added = true;
|
|
36434
|
+
}
|
|
36435
|
+
}
|
|
36436
|
+
}
|
|
36388
36437
|
const reCache = /* @__PURE__ */ new Map();
|
|
36389
36438
|
for (const s of sourcesWithVar) {
|
|
36390
36439
|
if (reCache.has(s.variable)) continue;
|
|
@@ -43113,6 +43162,45 @@ function methodAcceptsHtmlShapedParam(method) {
|
|
|
43113
43162
|
return false;
|
|
43114
43163
|
}
|
|
43115
43164
|
|
|
43165
|
+
// src/analysis/passes/insecure-deserialization-config-pass.ts
|
|
43166
|
+
var ANY_TYPE_PERMISSION_RE = /\bAnyTypePermission\b/;
|
|
43167
|
+
var InsecureDeserializationConfigPass = class {
|
|
43168
|
+
name = "insecure-deserialization-config";
|
|
43169
|
+
category = "security";
|
|
43170
|
+
run(ctx) {
|
|
43171
|
+
const { graph, language } = ctx;
|
|
43172
|
+
if (language !== "java") return { findings: [] };
|
|
43173
|
+
const file = graph.ir.meta.file;
|
|
43174
|
+
const findings = [];
|
|
43175
|
+
for (const call of graph.ir.calls) {
|
|
43176
|
+
if (!this.isPermissiveXStreamConfig(call)) continue;
|
|
43177
|
+
const line = call.location.line;
|
|
43178
|
+
const api = `addPermission(${call.arguments[0]?.expression ?? "AnyTypePermission"})`;
|
|
43179
|
+
findings.push({ line, api });
|
|
43180
|
+
ctx.addFinding({
|
|
43181
|
+
id: `${this.name}-${file}-${line}`,
|
|
43182
|
+
pass: this.name,
|
|
43183
|
+
category: this.category,
|
|
43184
|
+
rule_id: this.name,
|
|
43185
|
+
cwe: "CWE-502",
|
|
43186
|
+
severity: "high",
|
|
43187
|
+
level: "error",
|
|
43188
|
+
message: "XStream configured with AnyTypePermission (grant-all): untrusted XML passed to fromXML/unmarshal can instantiate arbitrary types (deserialization RCE)",
|
|
43189
|
+
file,
|
|
43190
|
+
line,
|
|
43191
|
+
fix: "Replace AnyTypePermission with an explicit type allow-list: xstream.addPermission(NoTypePermission.NONE) then allowTypes/allowTypesByWildcard for the classes you deserialize.",
|
|
43192
|
+
evidence: { api, language }
|
|
43193
|
+
});
|
|
43194
|
+
}
|
|
43195
|
+
return { findings };
|
|
43196
|
+
}
|
|
43197
|
+
isPermissiveXStreamConfig(call) {
|
|
43198
|
+
if (call.method_name !== "addPermission") return false;
|
|
43199
|
+
const arg0 = call.arguments[0]?.expression;
|
|
43200
|
+
return typeof arg0 === "string" && ANY_TYPE_PERMISSION_RE.test(arg0);
|
|
43201
|
+
}
|
|
43202
|
+
};
|
|
43203
|
+
|
|
43116
43204
|
// src/analysis/passes/plaintext-password-storage-pass.ts
|
|
43117
43205
|
function isWriteStorageCall(call, language) {
|
|
43118
43206
|
const method = call.method_name ?? "";
|
|
@@ -45469,6 +45557,7 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
45469
45557
|
if (!disabledPasses.has("info-disclosure-stacktrace")) pipeline.add(new InfoDisclosureStacktracePass());
|
|
45470
45558
|
if (!disabledPasses.has("unrestricted-file-upload")) pipeline.add(new UnrestrictedFileUploadPass());
|
|
45471
45559
|
if (!disabledPasses.has("missing-sanitizer-gate")) pipeline.add(new MissingSanitizerGatePass());
|
|
45560
|
+
if (!disabledPasses.has("insecure-deserialization-config")) pipeline.add(new InsecureDeserializationConfigPass());
|
|
45472
45561
|
const { results, findings } = pipeline.run(graph, code, language, config);
|
|
45473
45562
|
const sinkFilter = results.get("sink-filter");
|
|
45474
45563
|
const interProc = results.get("interprocedural");
|
|
@@ -5987,6 +5987,14 @@ function findFirstDescendant(node, type) {
|
|
|
5987
5987
|
}
|
|
5988
5988
|
return null;
|
|
5989
5989
|
}
|
|
5990
|
+
var CSHARP_COMMAND_EXECUTE_METHODS = /* @__PURE__ */ new Set([
|
|
5991
|
+
"ExecuteScalar",
|
|
5992
|
+
"ExecuteReader",
|
|
5993
|
+
"ExecuteNonQuery",
|
|
5994
|
+
"ExecuteScalarAsync",
|
|
5995
|
+
"ExecuteReaderAsync",
|
|
5996
|
+
"ExecuteNonQueryAsync"
|
|
5997
|
+
]);
|
|
5990
5998
|
function extractCSharpCalls(tree, cache) {
|
|
5991
5999
|
const calls = [];
|
|
5992
6000
|
const typeMap = buildCSharpReceiverTypeMap(tree, cache);
|
|
@@ -6004,12 +6012,16 @@ function extractCSharpCalls(tree, cache) {
|
|
|
6004
6012
|
methodName = getNodeText(fn);
|
|
6005
6013
|
}
|
|
6006
6014
|
const argsNode = inv.childForFieldName("arguments");
|
|
6015
|
+
let args2 = argsNode ? extractCSharpArguments(argsNode) : [];
|
|
6016
|
+
if (receiver && CSHARP_COMMAND_EXECUTE_METHODS.has(methodName)) {
|
|
6017
|
+
args2 = [{ position: 0, expression: receiver, variable: receiver, literal: null, value: null }, ...args2];
|
|
6018
|
+
}
|
|
6007
6019
|
calls.push({
|
|
6008
6020
|
method_name: methodName,
|
|
6009
6021
|
receiver,
|
|
6010
6022
|
receiver_type: receiver ? typeMap.get(receiver) ?? null : null,
|
|
6011
6023
|
receiver_type_fqn: null,
|
|
6012
|
-
arguments:
|
|
6024
|
+
arguments: args2,
|
|
6013
6025
|
location: { line: inv.startPosition.row + 1, column: inv.startPosition.column },
|
|
6014
6026
|
in_method: findEnclosingMethod(inv)
|
|
6015
6027
|
});
|
|
@@ -13165,6 +13177,16 @@ var DEFAULT_SINKS = [
|
|
|
13165
13177
|
// ADO.NET `cmd.CommandText = "…" + x` — emitted as a synthetic call by
|
|
13166
13178
|
// extractCSharpCalls (property-assignment sink; the ctor-arg sink misses it).
|
|
13167
13179
|
{ method: "CommandText", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13180
|
+
// ADO.NET `cmd.Execute*()` — object-carried sink (cognium-dev#271). The taint
|
|
13181
|
+
// rides the SqlCommand receiver (CommandText set from tainted data); the
|
|
13182
|
+
// receiver is surfaced as arg[0] by extractCSharpCalls, and the command object
|
|
13183
|
+
// is tainted in taint-propagation when its CommandText takes a tainted value.
|
|
13184
|
+
{ method: "ExecuteScalar", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13185
|
+
{ method: "ExecuteReader", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13186
|
+
{ method: "ExecuteNonQuery", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13187
|
+
{ method: "ExecuteScalarAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13188
|
+
{ method: "ExecuteReaderAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13189
|
+
{ method: "ExecuteNonQueryAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13168
13190
|
// C# command injection — Process.Start / ProcessStartInfo (CWE-78).
|
|
13169
13191
|
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13170
13192
|
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
@@ -5921,6 +5921,14 @@ function findFirstDescendant(node, type) {
|
|
|
5921
5921
|
}
|
|
5922
5922
|
return null;
|
|
5923
5923
|
}
|
|
5924
|
+
var CSHARP_COMMAND_EXECUTE_METHODS = /* @__PURE__ */ new Set([
|
|
5925
|
+
"ExecuteScalar",
|
|
5926
|
+
"ExecuteReader",
|
|
5927
|
+
"ExecuteNonQuery",
|
|
5928
|
+
"ExecuteScalarAsync",
|
|
5929
|
+
"ExecuteReaderAsync",
|
|
5930
|
+
"ExecuteNonQueryAsync"
|
|
5931
|
+
]);
|
|
5924
5932
|
function extractCSharpCalls(tree, cache) {
|
|
5925
5933
|
const calls = [];
|
|
5926
5934
|
const typeMap = buildCSharpReceiverTypeMap(tree, cache);
|
|
@@ -5938,12 +5946,16 @@ function extractCSharpCalls(tree, cache) {
|
|
|
5938
5946
|
methodName = getNodeText(fn);
|
|
5939
5947
|
}
|
|
5940
5948
|
const argsNode = inv.childForFieldName("arguments");
|
|
5949
|
+
let args2 = argsNode ? extractCSharpArguments(argsNode) : [];
|
|
5950
|
+
if (receiver && CSHARP_COMMAND_EXECUTE_METHODS.has(methodName)) {
|
|
5951
|
+
args2 = [{ position: 0, expression: receiver, variable: receiver, literal: null, value: null }, ...args2];
|
|
5952
|
+
}
|
|
5941
5953
|
calls.push({
|
|
5942
5954
|
method_name: methodName,
|
|
5943
5955
|
receiver,
|
|
5944
5956
|
receiver_type: receiver ? typeMap.get(receiver) ?? null : null,
|
|
5945
5957
|
receiver_type_fqn: null,
|
|
5946
|
-
arguments:
|
|
5958
|
+
arguments: args2,
|
|
5947
5959
|
location: { line: inv.startPosition.row + 1, column: inv.startPosition.column },
|
|
5948
5960
|
in_method: findEnclosingMethod(inv)
|
|
5949
5961
|
});
|
|
@@ -13099,6 +13111,16 @@ var DEFAULT_SINKS = [
|
|
|
13099
13111
|
// ADO.NET `cmd.CommandText = "…" + x` — emitted as a synthetic call by
|
|
13100
13112
|
// extractCSharpCalls (property-assignment sink; the ctor-arg sink misses it).
|
|
13101
13113
|
{ method: "CommandText", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13114
|
+
// ADO.NET `cmd.Execute*()` — object-carried sink (cognium-dev#271). The taint
|
|
13115
|
+
// rides the SqlCommand receiver (CommandText set from tainted data); the
|
|
13116
|
+
// receiver is surfaced as arg[0] by extractCSharpCalls, and the command object
|
|
13117
|
+
// is tainted in taint-propagation when its CommandText takes a tainted value.
|
|
13118
|
+
{ method: "ExecuteScalar", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13119
|
+
{ method: "ExecuteReader", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13120
|
+
{ method: "ExecuteNonQuery", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13121
|
+
{ method: "ExecuteScalarAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13122
|
+
{ method: "ExecuteReaderAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13123
|
+
{ method: "ExecuteNonQueryAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13102
13124
|
// C# command injection — Process.Start / ProcessStartInfo (CWE-78).
|
|
13103
13125
|
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13104
13126
|
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
@@ -155,6 +155,11 @@ function findFirstDescendant(node, type) {
|
|
|
155
155
|
}
|
|
156
156
|
return null;
|
|
157
157
|
}
|
|
158
|
+
/** ADO.NET command-execution methods whose taint rides the receiver object. */
|
|
159
|
+
const CSHARP_COMMAND_EXECUTE_METHODS = new Set([
|
|
160
|
+
'ExecuteScalar', 'ExecuteReader', 'ExecuteNonQuery',
|
|
161
|
+
'ExecuteScalarAsync', 'ExecuteReaderAsync', 'ExecuteNonQueryAsync',
|
|
162
|
+
]);
|
|
158
163
|
function extractCSharpCalls(tree, cache) {
|
|
159
164
|
const calls = [];
|
|
160
165
|
const typeMap = buildCSharpReceiverTypeMap(tree, cache);
|
|
@@ -173,12 +178,21 @@ function extractCSharpCalls(tree, cache) {
|
|
|
173
178
|
methodName = getNodeText(fn);
|
|
174
179
|
}
|
|
175
180
|
const argsNode = inv.childForFieldName('arguments');
|
|
181
|
+
let args = argsNode ? extractCSharpArguments(argsNode) : [];
|
|
182
|
+
// ADO.NET `Execute*()` carries taint via the RECEIVER (a SqlCommand whose
|
|
183
|
+
// CommandText was set from tainted data), not an argument. Surface the
|
|
184
|
+
// receiver as arg[0] so the arg-based flow scan can match a tainted command
|
|
185
|
+
// object (cognium-dev#271 — the canonical `cmd.CommandText=…; cmd.Execute*()`
|
|
186
|
+
// Juliet shape). The command-object taint is seeded in taint-propagation.
|
|
187
|
+
if (receiver && CSHARP_COMMAND_EXECUTE_METHODS.has(methodName)) {
|
|
188
|
+
args = [{ position: 0, expression: receiver, variable: receiver, literal: null, value: null }, ...args];
|
|
189
|
+
}
|
|
176
190
|
calls.push({
|
|
177
191
|
method_name: methodName,
|
|
178
192
|
receiver,
|
|
179
193
|
receiver_type: receiver ? (typeMap.get(receiver) ?? null) : null,
|
|
180
194
|
receiver_type_fqn: null,
|
|
181
|
-
arguments:
|
|
195
|
+
arguments: args,
|
|
182
196
|
location: { line: inv.startPosition.row + 1, column: inv.startPosition.column },
|
|
183
197
|
in_method: findEnclosingMethod(inv),
|
|
184
198
|
});
|