circle-ir 4.5.0 → 4.7.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 +20 -1
- 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 +8 -1
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/missing-public-doc-pass.d.ts.map +1 -1
- package/dist/analysis/passes/missing-public-doc-pass.js +6 -1
- package/dist/analysis/passes/missing-public-doc-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 +10 -0
- package/dist/analysis/passes/sink-filter-pass.js.map +1 -1
- package/dist/analysis/passes/weak-crypto-pass.d.ts.map +1 -1
- package/dist/analysis/passes/weak-crypto-pass.js +66 -0
- package/dist/analysis/passes/weak-crypto-pass.js.map +1 -1
- package/dist/analysis/passes/weak-hash-pass.d.ts.map +1 -1
- package/dist/analysis/passes/weak-hash-pass.js +46 -0
- package/dist/analysis/passes/weak-hash-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 +151 -3
- package/dist/core/circle-ir-core.cjs +20 -1
- package/dist/core/circle-ir-core.js +20 -1
- package/package.json +1 -1
|
@@ -13793,7 +13793,10 @@ var DEFAULT_SINKS = [
|
|
|
13793
13793
|
{ method: "ExecuteReaderAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13794
13794
|
{ method: "ExecuteNonQueryAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13795
13795
|
// C# command injection — Process.Start / ProcessStartInfo (CWE-78).
|
|
13796
|
-
|
|
13796
|
+
// Both the single-string overload `Process.Start("sh -c " + x)` and the
|
|
13797
|
+
// `(fileName, arguments)` overload `Process.Start("/bin/sh", "-c " + x)` are
|
|
13798
|
+
// injectable — the second is the argv path where taint rides arg[1] (#276).
|
|
13799
|
+
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13797
13800
|
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13798
13801
|
// C# path traversal — System.IO file APIs (CWE-22). Distinctive method names.
|
|
13799
13802
|
{ method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13834,6 +13837,22 @@ var DEFAULT_SINKS = [
|
|
|
13834
13837
|
// filter is the constructor argument.
|
|
13835
13838
|
{ method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13836
13839
|
{ method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13840
|
+
// C# open redirect — ASP.NET MVC / Minimal API redirect helpers (CWE-601,
|
|
13841
|
+
// cognium-dev#273/#275). Classless: `return Redirect(url)` on a controller is
|
|
13842
|
+
// an implicit-`this` call (no receiver), and `Results.Redirect(url)` /
|
|
13843
|
+
// `TypedResults.Redirect(url)` also route through the same method name. Taint-
|
|
13844
|
+
// gated, so a constant URL never fires. `LocalRedirect` is deliberately NOT
|
|
13845
|
+
// registered — it rejects non-local URLs and is the documented mitigation.
|
|
13846
|
+
{ method: "Redirect", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
13847
|
+
{ method: "RedirectPermanent", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
13848
|
+
// C# CRLF / HTTP response-header injection — classic ASP.NET header writers
|
|
13849
|
+
// (CWE-113, cognium-dev#273/#275). The injectable value is the second arg.
|
|
13850
|
+
{ method: "AddHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["csharp"] },
|
|
13851
|
+
{ method: "AppendHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["csharp"] },
|
|
13852
|
+
// C# NoSQL injection — MongoDB filter built from raw JSON (CWE-943,
|
|
13853
|
+
// cognium-dev#273/#275). `BsonDocument.Parse(userJson)` deserializes an
|
|
13854
|
+
// attacker-controlled query document. Class-scoped (static receiver resolves).
|
|
13855
|
+
{ method: "Parse", class: "BsonDocument", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13837
13856
|
// C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
|
|
13838
13857
|
{ method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13839
13858
|
{ method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -27036,11 +27055,12 @@ function findCSharpRequestSources(sourceCode, language) {
|
|
|
27036
27055
|
const assignRe = /^\s*(?:var\s+|[A-Za-z_][\w.<>\[\]]*\s+)?([A-Za-z_]\w*)\s*=\s*(.+?);?\s*$/;
|
|
27037
27056
|
const requestReadRe = /\bRequest\s*\.\s*(?:Query|Form|Headers|Cookies|QueryString|RouteValues|Body|Files|Params)\b/;
|
|
27038
27057
|
const consoleReadRe = /\bConsole\s*\.\s*ReadLine\s*\(/;
|
|
27058
|
+
const envReadRe = /\bEnvironment\s*\.\s*GetEnvironmentVariables?\s*(?:\(|\[)/;
|
|
27039
27059
|
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
27040
27060
|
const m = assignRe.exec(lines[i2]);
|
|
27041
27061
|
if (!m) continue;
|
|
27042
27062
|
const [, varName, rhs] = m;
|
|
27043
|
-
const type = requestReadRe.test(rhs) ? "http_param" : consoleReadRe.test(rhs) ? "io_input" : null;
|
|
27063
|
+
const type = requestReadRe.test(rhs) ? "http_param" : consoleReadRe.test(rhs) ? "io_input" : envReadRe.test(rhs) ? "env_input" : null;
|
|
27044
27064
|
if (!type) continue;
|
|
27045
27065
|
const lineNumber = i2 + 1;
|
|
27046
27066
|
if (sources.some((s) => s.line === lineNumber && s.variable === varName)) continue;
|
|
@@ -33928,7 +33948,9 @@ var SinkFilterPass = class {
|
|
|
33928
33948
|
if (language === "csharp") {
|
|
33929
33949
|
const sourceLines = ctx.code.split("\n");
|
|
33930
33950
|
const sanitizedByType = csharpSanitizedVarsByType(ctx.code);
|
|
33951
|
+
const xxeHardened = /\bXmlResolver\s*=\s*null\b/.test(ctx.code) || /\bDtdProcessing\s*=\s*(?:DtdProcessing\s*\.\s*)?(?:Prohibit|Ignore)\b/.test(ctx.code);
|
|
33931
33952
|
filtered = filtered.filter((sink) => {
|
|
33953
|
+
if (sink.type === "xxe" && xxeHardened) return false;
|
|
33932
33954
|
const sinkLineText = sourceLines[sink.line - 1] ?? "";
|
|
33933
33955
|
for (const { re, type } of CSHARP_SANITIZER_RES) {
|
|
33934
33956
|
if (type === sink.type && re.test(sinkLineText)) return false;
|
|
@@ -37084,6 +37106,8 @@ function isPublicMethod(method, language) {
|
|
|
37084
37106
|
switch (language) {
|
|
37085
37107
|
case "java":
|
|
37086
37108
|
return method.modifiers.includes("public");
|
|
37109
|
+
case "csharp":
|
|
37110
|
+
return method.modifiers.includes("public");
|
|
37087
37111
|
case "javascript":
|
|
37088
37112
|
case "typescript":
|
|
37089
37113
|
case "tsx":
|
|
@@ -37105,7 +37129,7 @@ var MissingPublicDocPass = class {
|
|
|
37105
37129
|
if (UTIL_DIR_RE.test(graph.ir.meta.file)) {
|
|
37106
37130
|
return { missingDocMethods: [], missingDocTypes: [] };
|
|
37107
37131
|
}
|
|
37108
|
-
if (!["java", "javascript", "typescript", "tsx", "python"].includes(language)) {
|
|
37132
|
+
if (!["java", "javascript", "typescript", "tsx", "python", "csharp"].includes(language)) {
|
|
37109
37133
|
return { missingDocMethods: [], missingDocTypes: [] };
|
|
37110
37134
|
}
|
|
37111
37135
|
const lines = code.split("\n");
|
|
@@ -41538,6 +41562,28 @@ var WEAK_HASH_NAMES = /* @__PURE__ */ new Set([
|
|
|
41538
41562
|
"sha-1",
|
|
41539
41563
|
"sha1"
|
|
41540
41564
|
]);
|
|
41565
|
+
var CSHARP_WEAK_HASH_CLASSES = /* @__PURE__ */ new Set(["MD5", "SHA1"]);
|
|
41566
|
+
var CSHARP_HASH_FACTORY_RECEIVERS = /* @__PURE__ */ new Set([
|
|
41567
|
+
"HashAlgorithm",
|
|
41568
|
+
"KeyedHashAlgorithm",
|
|
41569
|
+
"HMAC",
|
|
41570
|
+
"CryptoConfig"
|
|
41571
|
+
]);
|
|
41572
|
+
var CSHARP_WEAK_HASH_CTORS = {
|
|
41573
|
+
MD5CryptoServiceProvider: "md5",
|
|
41574
|
+
MD5Cng: "md5",
|
|
41575
|
+
SHA1CryptoServiceProvider: "sha1",
|
|
41576
|
+
SHA1Managed: "sha1",
|
|
41577
|
+
SHA1Cng: "sha1",
|
|
41578
|
+
HMACMD5: "md5",
|
|
41579
|
+
HMACSHA1: "sha1"
|
|
41580
|
+
};
|
|
41581
|
+
function csharpWeakHashAlgo(cleaned) {
|
|
41582
|
+
if (!cleaned) return null;
|
|
41583
|
+
const base = (cleaned.split(".").pop() ?? cleaned).replace(/^hmac/, "");
|
|
41584
|
+
if (base === "sha") return "sha1";
|
|
41585
|
+
return WEAK_HASH_NAMES.has(base) ? base : null;
|
|
41586
|
+
}
|
|
41541
41587
|
var COMMONS_DIGEST_METHODS = /* @__PURE__ */ new Set([
|
|
41542
41588
|
"md2",
|
|
41543
41589
|
"md2Hex",
|
|
@@ -41697,6 +41743,19 @@ var WeakHashPass = class {
|
|
|
41697
41743
|
}
|
|
41698
41744
|
return null;
|
|
41699
41745
|
}
|
|
41746
|
+
if (language === "csharp") {
|
|
41747
|
+
if ((method === "Create" || method === "HashData") && CSHARP_WEAK_HASH_CLASSES.has(receiver)) {
|
|
41748
|
+
return { algorithm: receiver.toLowerCase(), api: `${receiver}.${method}` };
|
|
41749
|
+
}
|
|
41750
|
+
if ((method === "Create" || method === "CreateFromName") && CSHARP_HASH_FACTORY_RECEIVERS.has(receiver)) {
|
|
41751
|
+
const algo = csharpWeakHashAlgo(literalAlgo(call, 0));
|
|
41752
|
+
if (algo) return { algorithm: algo, api: `${receiver}.${method}` };
|
|
41753
|
+
}
|
|
41754
|
+
if (call.is_constructor && CSHARP_WEAK_HASH_CTORS[method]) {
|
|
41755
|
+
return { algorithm: CSHARP_WEAK_HASH_CTORS[method], api: `new ${method}()` };
|
|
41756
|
+
}
|
|
41757
|
+
return null;
|
|
41758
|
+
}
|
|
41700
41759
|
return null;
|
|
41701
41760
|
}
|
|
41702
41761
|
};
|
|
@@ -41758,6 +41817,27 @@ var WEAK_CIPHER_BASES = /* @__PURE__ */ new Set([
|
|
|
41758
41817
|
"seed",
|
|
41759
41818
|
"cast5"
|
|
41760
41819
|
]);
|
|
41820
|
+
var CSHARP_WEAK_CIPHER_CLASSES = /* @__PURE__ */ new Set(["DES", "TripleDES", "RC2", "RC4"]);
|
|
41821
|
+
var CSHARP_CIPHER_FACTORY_RECEIVERS = /* @__PURE__ */ new Set(["SymmetricAlgorithm", "CryptoConfig"]);
|
|
41822
|
+
var CSHARP_WEAK_CIPHER_CTORS = {
|
|
41823
|
+
DESCryptoServiceProvider: "des",
|
|
41824
|
+
TripleDESCryptoServiceProvider: "3des",
|
|
41825
|
+
RC2CryptoServiceProvider: "rc2"
|
|
41826
|
+
};
|
|
41827
|
+
var CSHARP_RSA_FACTORY_CLASSES = /* @__PURE__ */ new Set(["RSA", "DSA"]);
|
|
41828
|
+
var CSHARP_RSA_CTORS = /* @__PURE__ */ new Set([
|
|
41829
|
+
"RSACryptoServiceProvider",
|
|
41830
|
+
"DSACryptoServiceProvider",
|
|
41831
|
+
"RSACng",
|
|
41832
|
+
"DSACng"
|
|
41833
|
+
]);
|
|
41834
|
+
function csharpWeakCipher(cleaned) {
|
|
41835
|
+
if (!cleaned) return null;
|
|
41836
|
+
const base = (cleaned.split(".").pop() ?? cleaned).toLowerCase();
|
|
41837
|
+
if (base === "tripledes" || base === "desede") return "3des";
|
|
41838
|
+
if (base === "arc4") return "rc4";
|
|
41839
|
+
return WEAK_CIPHER_BASES.has(base) ? base : null;
|
|
41840
|
+
}
|
|
41761
41841
|
function classifyJavaCipherSpec(spec) {
|
|
41762
41842
|
const parts2 = spec.split("/").map((p) => p.trim().toLowerCase());
|
|
41763
41843
|
const base = parts2[0] ?? "";
|
|
@@ -42149,6 +42229,34 @@ var WeakCryptoPass = class {
|
|
|
42149
42229
|
}
|
|
42150
42230
|
return out2;
|
|
42151
42231
|
}
|
|
42232
|
+
if (language === "csharp") {
|
|
42233
|
+
if (method === "Create" && CSHARP_WEAK_CIPHER_CLASSES.has(receiver)) {
|
|
42234
|
+
const base = receiver === "TripleDES" ? "3des" : receiver.toLowerCase();
|
|
42235
|
+
out2.push({ issue: "weak-cipher", detail: base, api: `${receiver}.Create` });
|
|
42236
|
+
}
|
|
42237
|
+
if ((method === "Create" || method === "CreateFromName") && CSHARP_CIPHER_FACTORY_RECEIVERS.has(receiver)) {
|
|
42238
|
+
const base = csharpWeakCipher(literalAlgo2(call, 0));
|
|
42239
|
+
if (base) out2.push({ issue: "weak-cipher", detail: base, api: `${receiver}.${method}` });
|
|
42240
|
+
}
|
|
42241
|
+
if (call.is_constructor && CSHARP_WEAK_CIPHER_CTORS[method]) {
|
|
42242
|
+
out2.push({ issue: "weak-cipher", detail: CSHARP_WEAK_CIPHER_CTORS[method], api: `new ${method}()` });
|
|
42243
|
+
}
|
|
42244
|
+
const rsaFactory = method === "Create" && CSHARP_RSA_FACTORY_CLASSES.has(receiver);
|
|
42245
|
+
const rsaCtor = call.is_constructor && CSHARP_RSA_CTORS.has(method);
|
|
42246
|
+
if (rsaFactory || rsaCtor) {
|
|
42247
|
+
const sizeArg = call.arguments.find((a) => a.position === 0);
|
|
42248
|
+
const expr = (sizeArg?.literal ?? sizeArg?.expression ?? "").trim();
|
|
42249
|
+
const n = parseInt(expr, 10);
|
|
42250
|
+
if (Number.isFinite(n) && n > 0 && n < 2048) {
|
|
42251
|
+
out2.push({
|
|
42252
|
+
issue: "weak-rsa-key",
|
|
42253
|
+
detail: String(n),
|
|
42254
|
+
api: rsaCtor ? `new ${method}()` : `${receiver}.Create`
|
|
42255
|
+
});
|
|
42256
|
+
}
|
|
42257
|
+
}
|
|
42258
|
+
return out2;
|
|
42259
|
+
}
|
|
42152
42260
|
return out2;
|
|
42153
42261
|
}
|
|
42154
42262
|
};
|
|
@@ -43162,6 +43270,45 @@ function methodAcceptsHtmlShapedParam(method) {
|
|
|
43162
43270
|
return false;
|
|
43163
43271
|
}
|
|
43164
43272
|
|
|
43273
|
+
// src/analysis/passes/insecure-deserialization-config-pass.ts
|
|
43274
|
+
var ANY_TYPE_PERMISSION_RE = /\bAnyTypePermission\b/;
|
|
43275
|
+
var InsecureDeserializationConfigPass = class {
|
|
43276
|
+
name = "insecure-deserialization-config";
|
|
43277
|
+
category = "security";
|
|
43278
|
+
run(ctx) {
|
|
43279
|
+
const { graph, language } = ctx;
|
|
43280
|
+
if (language !== "java") return { findings: [] };
|
|
43281
|
+
const file = graph.ir.meta.file;
|
|
43282
|
+
const findings = [];
|
|
43283
|
+
for (const call of graph.ir.calls) {
|
|
43284
|
+
if (!this.isPermissiveXStreamConfig(call)) continue;
|
|
43285
|
+
const line = call.location.line;
|
|
43286
|
+
const api = `addPermission(${call.arguments[0]?.expression ?? "AnyTypePermission"})`;
|
|
43287
|
+
findings.push({ line, api });
|
|
43288
|
+
ctx.addFinding({
|
|
43289
|
+
id: `${this.name}-${file}-${line}`,
|
|
43290
|
+
pass: this.name,
|
|
43291
|
+
category: this.category,
|
|
43292
|
+
rule_id: this.name,
|
|
43293
|
+
cwe: "CWE-502",
|
|
43294
|
+
severity: "high",
|
|
43295
|
+
level: "error",
|
|
43296
|
+
message: "XStream configured with AnyTypePermission (grant-all): untrusted XML passed to fromXML/unmarshal can instantiate arbitrary types (deserialization RCE)",
|
|
43297
|
+
file,
|
|
43298
|
+
line,
|
|
43299
|
+
fix: "Replace AnyTypePermission with an explicit type allow-list: xstream.addPermission(NoTypePermission.NONE) then allowTypes/allowTypesByWildcard for the classes you deserialize.",
|
|
43300
|
+
evidence: { api, language }
|
|
43301
|
+
});
|
|
43302
|
+
}
|
|
43303
|
+
return { findings };
|
|
43304
|
+
}
|
|
43305
|
+
isPermissiveXStreamConfig(call) {
|
|
43306
|
+
if (call.method_name !== "addPermission") return false;
|
|
43307
|
+
const arg0 = call.arguments[0]?.expression;
|
|
43308
|
+
return typeof arg0 === "string" && ANY_TYPE_PERMISSION_RE.test(arg0);
|
|
43309
|
+
}
|
|
43310
|
+
};
|
|
43311
|
+
|
|
43165
43312
|
// src/analysis/passes/plaintext-password-storage-pass.ts
|
|
43166
43313
|
function isWriteStorageCall(call, language) {
|
|
43167
43314
|
const method = call.method_name ?? "";
|
|
@@ -45518,6 +45665,7 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
45518
45665
|
if (!disabledPasses.has("info-disclosure-stacktrace")) pipeline.add(new InfoDisclosureStacktracePass());
|
|
45519
45666
|
if (!disabledPasses.has("unrestricted-file-upload")) pipeline.add(new UnrestrictedFileUploadPass());
|
|
45520
45667
|
if (!disabledPasses.has("missing-sanitizer-gate")) pipeline.add(new MissingSanitizerGatePass());
|
|
45668
|
+
if (!disabledPasses.has("insecure-deserialization-config")) pipeline.add(new InsecureDeserializationConfigPass());
|
|
45521
45669
|
const { results, findings } = pipeline.run(graph, code, language, config);
|
|
45522
45670
|
const sinkFilter = results.get("sink-filter");
|
|
45523
45671
|
const interProc = results.get("interprocedural");
|
|
@@ -13188,7 +13188,10 @@ var DEFAULT_SINKS = [
|
|
|
13188
13188
|
{ method: "ExecuteReaderAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13189
13189
|
{ method: "ExecuteNonQueryAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13190
13190
|
// C# command injection — Process.Start / ProcessStartInfo (CWE-78).
|
|
13191
|
-
|
|
13191
|
+
// Both the single-string overload `Process.Start("sh -c " + x)` and the
|
|
13192
|
+
// `(fileName, arguments)` overload `Process.Start("/bin/sh", "-c " + x)` are
|
|
13193
|
+
// injectable — the second is the argv path where taint rides arg[1] (#276).
|
|
13194
|
+
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13192
13195
|
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13193
13196
|
// C# path traversal — System.IO file APIs (CWE-22). Distinctive method names.
|
|
13194
13197
|
{ method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13229,6 +13232,22 @@ var DEFAULT_SINKS = [
|
|
|
13229
13232
|
// filter is the constructor argument.
|
|
13230
13233
|
{ method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13231
13234
|
{ method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13235
|
+
// C# open redirect — ASP.NET MVC / Minimal API redirect helpers (CWE-601,
|
|
13236
|
+
// cognium-dev#273/#275). Classless: `return Redirect(url)` on a controller is
|
|
13237
|
+
// an implicit-`this` call (no receiver), and `Results.Redirect(url)` /
|
|
13238
|
+
// `TypedResults.Redirect(url)` also route through the same method name. Taint-
|
|
13239
|
+
// gated, so a constant URL never fires. `LocalRedirect` is deliberately NOT
|
|
13240
|
+
// registered — it rejects non-local URLs and is the documented mitigation.
|
|
13241
|
+
{ method: "Redirect", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
13242
|
+
{ method: "RedirectPermanent", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
13243
|
+
// C# CRLF / HTTP response-header injection — classic ASP.NET header writers
|
|
13244
|
+
// (CWE-113, cognium-dev#273/#275). The injectable value is the second arg.
|
|
13245
|
+
{ method: "AddHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["csharp"] },
|
|
13246
|
+
{ method: "AppendHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["csharp"] },
|
|
13247
|
+
// C# NoSQL injection — MongoDB filter built from raw JSON (CWE-943,
|
|
13248
|
+
// cognium-dev#273/#275). `BsonDocument.Parse(userJson)` deserializes an
|
|
13249
|
+
// attacker-controlled query document. Class-scoped (static receiver resolves).
|
|
13250
|
+
{ method: "Parse", class: "BsonDocument", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13232
13251
|
// C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
|
|
13233
13252
|
{ method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13234
13253
|
{ method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13122,7 +13122,10 @@ var DEFAULT_SINKS = [
|
|
|
13122
13122
|
{ method: "ExecuteReaderAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13123
13123
|
{ method: "ExecuteNonQueryAsync", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13124
13124
|
// C# command injection — Process.Start / ProcessStartInfo (CWE-78).
|
|
13125
|
-
|
|
13125
|
+
// Both the single-string overload `Process.Start("sh -c " + x)` and the
|
|
13126
|
+
// `(fileName, arguments)` overload `Process.Start("/bin/sh", "-c " + x)` are
|
|
13127
|
+
// injectable — the second is the argv path where taint rides arg[1] (#276).
|
|
13128
|
+
{ method: "Start", class: "Process", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13126
13129
|
{ method: "ProcessStartInfo", type: "command_injection", cwe: "CWE-78", severity: "critical", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13127
13130
|
// C# path traversal — System.IO file APIs (CWE-22). Distinctive method names.
|
|
13128
13131
|
{ method: "ReadAllText", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13163,6 +13166,22 @@ var DEFAULT_SINKS = [
|
|
|
13163
13166
|
// filter is the constructor argument.
|
|
13164
13167
|
{ method: "DirectorySearcher", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13165
13168
|
{ method: "DirectoryEntry", type: "ldap_injection", cwe: "CWE-90", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13169
|
+
// C# open redirect — ASP.NET MVC / Minimal API redirect helpers (CWE-601,
|
|
13170
|
+
// cognium-dev#273/#275). Classless: `return Redirect(url)` on a controller is
|
|
13171
|
+
// an implicit-`this` call (no receiver), and `Results.Redirect(url)` /
|
|
13172
|
+
// `TypedResults.Redirect(url)` also route through the same method name. Taint-
|
|
13173
|
+
// gated, so a constant URL never fires. `LocalRedirect` is deliberately NOT
|
|
13174
|
+
// registered — it rejects non-local URLs and is the documented mitigation.
|
|
13175
|
+
{ method: "Redirect", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
13176
|
+
{ method: "RedirectPermanent", type: "open_redirect", cwe: "CWE-601", severity: "medium", arg_positions: [0], languages: ["csharp"] },
|
|
13177
|
+
// C# CRLF / HTTP response-header injection — classic ASP.NET header writers
|
|
13178
|
+
// (CWE-113, cognium-dev#273/#275). The injectable value is the second arg.
|
|
13179
|
+
{ method: "AddHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["csharp"] },
|
|
13180
|
+
{ method: "AppendHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["csharp"] },
|
|
13181
|
+
// C# NoSQL injection — MongoDB filter built from raw JSON (CWE-943,
|
|
13182
|
+
// cognium-dev#273/#275). `BsonDocument.Parse(userJson)` deserializes an
|
|
13183
|
+
// attacker-controlled query document. Class-scoped (static receiver resolves).
|
|
13184
|
+
{ method: "Parse", class: "BsonDocument", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13166
13185
|
// C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
|
|
13167
13186
|
{ method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13168
13187
|
{ method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "circle-ir",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.7.0",
|
|
4
4
|
"description": "High-performance Static Application Security Testing (SAST) library for detecting security vulnerabilities through taint analysis",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.js",
|