circle-ir 4.7.0 → 4.7.2
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 +61 -4
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/findings.js +1 -1
- package/dist/analysis/findings.js.map +1 -1
- package/dist/analysis/interprocedural.d.ts.map +1 -1
- package/dist/analysis/interprocedural.js +16 -1
- package/dist/analysis/interprocedural.js.map +1 -1
- package/dist/analysis/passes/language-sources-pass.d.ts.map +1 -1
- package/dist/analysis/passes/language-sources-pass.js +53 -3
- package/dist/analysis/passes/language-sources-pass.js.map +1 -1
- package/dist/analysis/passes/scan-secrets-pass.d.ts.map +1 -1
- package/dist/analysis/passes/scan-secrets-pass.js +10 -0
- package/dist/analysis/passes/scan-secrets-pass.js.map +1 -1
- package/dist/analysis/passes/unrestricted-file-upload-pass.d.ts.map +1 -1
- package/dist/analysis/passes/unrestricted-file-upload-pass.js +6 -4
- package/dist/analysis/passes/unrestricted-file-upload-pass.js.map +1 -1
- package/dist/analysis/passes/unused-variable-pass.d.ts.map +1 -1
- package/dist/analysis/passes/unused-variable-pass.js +69 -20
- package/dist/analysis/passes/unused-variable-pass.js.map +1 -1
- package/dist/analysis/taint-matcher.d.ts.map +1 -1
- package/dist/analysis/taint-matcher.js +46 -0
- package/dist/analysis/taint-matcher.js.map +1 -1
- package/dist/browser/circle-ir.js +185 -27
- package/dist/core/circle-ir-core.cjs +96 -8
- package/dist/core/circle-ir-core.js +96 -8
- package/dist/core/extractors/dfg.js +6 -3
- package/dist/core/extractors/dfg.js.map +1 -1
- package/package.json +1 -1
|
@@ -9918,15 +9918,15 @@ function computeChains(defs, uses) {
|
|
|
9918
9918
|
existing.push(use);
|
|
9919
9919
|
usesByLine.set(use.line, existing);
|
|
9920
9920
|
}
|
|
9921
|
+
const seenChains = /* @__PURE__ */ new Set();
|
|
9921
9922
|
for (const def of defs) {
|
|
9922
9923
|
if (def.kind === "local") {
|
|
9923
9924
|
const usesOnLine = usesByLine.get(def.line) ?? [];
|
|
9924
9925
|
for (const use of usesOnLine) {
|
|
9925
9926
|
if (use.def_id !== null && use.def_id !== def.id) {
|
|
9926
|
-
const
|
|
9927
|
-
|
|
9928
|
-
|
|
9929
|
-
if (!exists) {
|
|
9927
|
+
const key = `${use.def_id}\0${def.id}\0${use.variable}`;
|
|
9928
|
+
if (!seenChains.has(key)) {
|
|
9929
|
+
seenChains.add(key);
|
|
9930
9930
|
chains.push({
|
|
9931
9931
|
from_def: use.def_id,
|
|
9932
9932
|
to_def: def.id,
|
|
@@ -12258,7 +12258,11 @@ var DEFAULT_SINKS = [
|
|
|
12258
12258
|
// File: covers both File(String pathname) and File(parent, child). The 2-arg
|
|
12259
12259
|
// overload's child argument carries CVE-2018-8041 (Camel mail Content-Disposition
|
|
12260
12260
|
// filename written to disk).
|
|
12261
|
-
|
|
12261
|
+
// Java `new File(path)` idiom. Excluded for C#: there is no `new File()` in
|
|
12262
|
+
// .NET (it's `System.IO.File` statics + `FileStream`, covered separately), and
|
|
12263
|
+
// this over-matched ASP.NET `ControllerBase.File(stream|bytes, contentType)` —
|
|
12264
|
+
// a result helper that returns a file, not a path sink (cognium-ai#326 B).
|
|
12265
|
+
{ method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], exclude_languages: ["csharp"] },
|
|
12262
12266
|
{ method: "FileInputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
12263
12267
|
{ method: "FileOutputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
12264
12268
|
{ method: "FileReader", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
@@ -12501,8 +12505,11 @@ var DEFAULT_SINKS = [
|
|
|
12501
12505
|
// SAX handler sinks (can lead to XSS in parsed content)
|
|
12502
12506
|
{ method: "startElement", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1, 2] },
|
|
12503
12507
|
{ method: "characters", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
12504
|
-
// Template output sinks
|
|
12505
|
-
|
|
12508
|
+
// Template output sinks. `output` is classless (Java template engines /
|
|
12509
|
+
// Velocity), but Rust `std::process::Command::…output()` is process
|
|
12510
|
+
// execution, not HTML — it already fires the CWE-78 command_injection sink,
|
|
12511
|
+
// so exclude Rust here to drop the spurious xss (cognium-dev#146).
|
|
12512
|
+
{ method: "output", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], exclude_languages: ["rust"] },
|
|
12506
12513
|
{ method: "setOutput", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
12507
12514
|
{ method: "writeAttribute", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1] },
|
|
12508
12515
|
// AntiSamy specific (SAX filters)
|
|
@@ -13810,6 +13817,22 @@ var DEFAULT_SINKS = [
|
|
|
13810
13817
|
{ method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13811
13818
|
{ method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13812
13819
|
{ method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13820
|
+
// Remaining System.IO.File path APIs — class-scoped so they don't collide
|
|
13821
|
+
// with unrelated methods. Copy/Move take a source AND destination path (both
|
|
13822
|
+
// attacker-controllable → read-anywhere / write-anywhere), hence [0, 1].
|
|
13823
|
+
{ method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13824
|
+
{ method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13825
|
+
{ method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13826
|
+
{ method: "Open", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13827
|
+
{ method: "OpenText", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13828
|
+
{ method: "Create", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13829
|
+
{ method: "WriteAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13830
|
+
{ method: "AppendAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13831
|
+
// System.IO.Directory path APIs.
|
|
13832
|
+
{ method: "CreateDirectory", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13833
|
+
{ method: "Delete", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13834
|
+
{ method: "GetFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13835
|
+
{ method: "EnumerateFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13813
13836
|
// C# SSRF — HttpClient / WebClient / WebRequest (CWE-918).
|
|
13814
13837
|
{ method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13815
13838
|
{ method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13826,9 +13849,24 @@ var DEFAULT_SINKS = [
|
|
|
13826
13849
|
// C# code injection — dynamic script/assembly loading (CWE-94).
|
|
13827
13850
|
{ method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13828
13851
|
{ method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13829
|
-
//
|
|
13852
|
+
// Loading an assembly from an attacker-controlled name/path is arbitrary code
|
|
13853
|
+
// execution. Class-scoped to `Assembly` (low FP — a tainted arg here is rare
|
|
13854
|
+
// in benign code). `Type.GetType`/`Activator.CreateInstance` are intentionally
|
|
13855
|
+
// NOT added — reflection with config-driven type names is common in DI, an FP
|
|
13856
|
+
// trap without the reflection-suppress machinery.
|
|
13857
|
+
{ method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13858
|
+
{ method: "LoadFrom", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13859
|
+
{ method: "LoadFile", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13860
|
+
// C# insecure deserialization — the polymorphic BCL formatters that can
|
|
13861
|
+
// instantiate arbitrary types named in the payload (CWE-502, cognium-ai#318).
|
|
13862
|
+
// Each of these classes exists only to deserialize and is unsafe on untrusted
|
|
13863
|
+
// input; class-scoped, so no collision with safe JSON APIs (System.Text.Json /
|
|
13864
|
+
// Newtonsoft-default, which are intentionally NOT sinks).
|
|
13830
13865
|
{ method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13831
13866
|
{ method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13867
|
+
{ method: "Deserialize", class: "SoapFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13868
|
+
{ method: "Deserialize", class: "LosFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13869
|
+
{ method: "Deserialize", class: "ObjectStateFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13832
13870
|
// C# XSS — raw HTML output (CWE-79).
|
|
13833
13871
|
{ method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13834
13872
|
{ method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13853,6 +13891,20 @@ var DEFAULT_SINKS = [
|
|
|
13853
13891
|
// cognium-dev#273/#275). `BsonDocument.Parse(userJson)` deserializes an
|
|
13854
13892
|
// attacker-controlled query document. Class-scoped (static receiver resolves).
|
|
13855
13893
|
{ method: "Parse", class: "BsonDocument", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13894
|
+
// C# log injection — Microsoft.Extensions.Logging `ILogger` (CWE-117,
|
|
13895
|
+
// cognium-ai#318). Only the message TEMPLATE (arg[0]) is the injectable
|
|
13896
|
+
// position; the structured form `LogInformation("… {Id}", id)` keeps taint in
|
|
13897
|
+
// later args (the logger renders them as data), so arg_positions:[0] does not
|
|
13898
|
+
// flag it. Mirrors the Java `Logger` sinks; severity low.
|
|
13899
|
+
{ method: "LogInformation", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13900
|
+
{ method: "LogWarning", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13901
|
+
{ method: "LogError", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13902
|
+
{ method: "LogDebug", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13903
|
+
{ method: "LogCritical", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13904
|
+
{ method: "LogTrace", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13905
|
+
// Generic `ILogger.Log(logLevel, message, …)` — class-scoped (`Log` alone is
|
|
13906
|
+
// too generic); the message is arg[1].
|
|
13907
|
+
{ method: "Log", class: "ILogger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1], languages: ["csharp"] },
|
|
13856
13908
|
// C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
|
|
13857
13909
|
{ method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13858
13910
|
{ method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13861,6 +13913,11 @@ var DEFAULT_SINKS = [
|
|
|
13861
13913
|
{ method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13862
13914
|
{ method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13863
13915
|
{ method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13916
|
+
// `new XmlTextReader(input)` — its DtdProcessing defaults to Parse (resolves
|
|
13917
|
+
// external entities), unlike `XmlReader.Create` (Prohibit). CWE-611,
|
|
13918
|
+
// cognium-ai#318. The 4.7.0 XmlResolver=null / DtdProcessing=Prohibit
|
|
13919
|
+
// hardening still suppresses the safe shape.
|
|
13920
|
+
{ method: "XmlTextReader", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13864
13921
|
// Python SQLi — asyncpg Connection.*
|
|
13865
13922
|
{ method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
13866
13923
|
{ method: "fetch", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
@@ -15273,6 +15330,34 @@ function isSafeJSChildProcessCall(call, pattern, language) {
|
|
|
15273
15330
|
if (SHELL_PROGRAMS.has(program)) return false;
|
|
15274
15331
|
return true;
|
|
15275
15332
|
}
|
|
15333
|
+
function isSafeCSharpProcessStartCall(call, pattern, language) {
|
|
15334
|
+
if (language !== "csharp") return false;
|
|
15335
|
+
if (pattern.type !== "command_injection") return false;
|
|
15336
|
+
if (call.method_name !== "Start") return false;
|
|
15337
|
+
if (call.arguments.length < 2) return false;
|
|
15338
|
+
const fileArg = call.arguments.find((a) => a.position === 0);
|
|
15339
|
+
if (!fileArg) return false;
|
|
15340
|
+
let raw;
|
|
15341
|
+
if (fileArg.literal !== null && fileArg.literal !== void 0) {
|
|
15342
|
+
raw = String(fileArg.literal).trim();
|
|
15343
|
+
} else {
|
|
15344
|
+
raw = (fileArg.expression ?? "").trim();
|
|
15345
|
+
}
|
|
15346
|
+
if (!/^@?"[^"]*"$/.test(raw)) return false;
|
|
15347
|
+
const program = (raw.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
|
|
15348
|
+
const SHELL_PROGRAMS = /* @__PURE__ */ new Set([
|
|
15349
|
+
"sh",
|
|
15350
|
+
"bash",
|
|
15351
|
+
"zsh",
|
|
15352
|
+
"dash",
|
|
15353
|
+
"ash",
|
|
15354
|
+
"ksh",
|
|
15355
|
+
"cmd",
|
|
15356
|
+
"powershell",
|
|
15357
|
+
"pwsh"
|
|
15358
|
+
]);
|
|
15359
|
+
return !SHELL_PROGRAMS.has(program);
|
|
15360
|
+
}
|
|
15276
15361
|
function isSafeRustCommandCall(call, pattern, language) {
|
|
15277
15362
|
if (language !== "rust") return false;
|
|
15278
15363
|
if (pattern.type !== "command_injection") return false;
|
|
@@ -15528,6 +15613,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
15528
15613
|
if (isSafeJSChildProcessCall(call, pattern, language)) {
|
|
15529
15614
|
continue;
|
|
15530
15615
|
}
|
|
15616
|
+
if (isSafeCSharpProcessStartCall(call, pattern, language)) {
|
|
15617
|
+
continue;
|
|
15618
|
+
}
|
|
15531
15619
|
if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at, types)) {
|
|
15532
15620
|
continue;
|
|
15533
15621
|
}
|
|
@@ -16758,8 +16846,8 @@ function canSourceReachSink(sourceType, sinkType) {
|
|
|
16758
16846
|
network_input: ["sql_injection", "command_injection", "xss", "ssrf", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
|
|
16759
16847
|
config_param: ["sql_injection", "command_injection", "path_traversal", "xss", "ssrf", "log_injection", "format_string"],
|
|
16760
16848
|
// Servlet init params
|
|
16761
|
-
interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection"],
|
|
16762
|
-
// Cross-method taint; Sprint 82 (#189) — open_redirect added; Sprint 91 (#117) — trust_boundary added; cognium-ai#129 — log_injection/format_string/nosql_injection added; cognium-ai#281 — prompt_injection added
|
|
16849
|
+
interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment", "open_redirect", "trust_boundary", "log_injection", "format_string", "nosql_injection", "prompt_injection", "xxe", "deserialization"],
|
|
16850
|
+
// Cross-method taint; Sprint 82 (#189) — open_redirect added; Sprint 91 (#117) — trust_boundary added; cognium-ai#129 — log_injection/format_string/nosql_injection added; cognium-ai#281 — prompt_injection added; cognium-ai#317 — xxe/deserialization added (C# xxe/deser sinks are reached via param-seeded sources and were silently dropped, 0% finding conversion)
|
|
16763
16851
|
plugin_param: ["sql_injection", "command_injection", "path_traversal", "xss", "code_injection", "log_injection", "format_string"]
|
|
16764
16852
|
// Plugin/config parameters
|
|
16765
16853
|
};
|
|
@@ -18581,6 +18669,26 @@ function analyzeInterprocedural(graphOrTypes, callsOrSources, dfgOrSinks, source
|
|
|
18581
18669
|
"Command",
|
|
18582
18670
|
"CommandContext"
|
|
18583
18671
|
]);
|
|
18672
|
+
const ormQueryBuilderMethods = /* @__PURE__ */ new Set([
|
|
18673
|
+
"findMany",
|
|
18674
|
+
"findFirst",
|
|
18675
|
+
"findUnique",
|
|
18676
|
+
"findUniqueOrThrow",
|
|
18677
|
+
"findFirstOrThrow",
|
|
18678
|
+
"findOne",
|
|
18679
|
+
"findAll",
|
|
18680
|
+
"findByPk",
|
|
18681
|
+
"findAndCountAll",
|
|
18682
|
+
"findBy",
|
|
18683
|
+
"findOneBy",
|
|
18684
|
+
"where",
|
|
18685
|
+
"andWhere",
|
|
18686
|
+
"orWhere",
|
|
18687
|
+
"whereIn",
|
|
18688
|
+
"whereNot",
|
|
18689
|
+
"first",
|
|
18690
|
+
"bind"
|
|
18691
|
+
]);
|
|
18584
18692
|
const sanitizerMethods = /* @__PURE__ */ new Set();
|
|
18585
18693
|
for (const san of sanitizers) {
|
|
18586
18694
|
sanitizerMethods.add(san.method);
|
|
@@ -18605,7 +18713,7 @@ function analyzeInterprocedural(graphOrTypes, callsOrSources, dfgOrSinks, source
|
|
|
18605
18713
|
}
|
|
18606
18714
|
const targetMethod = getMethodNode(methodNodes, call.method_name);
|
|
18607
18715
|
if (!targetMethod) {
|
|
18608
|
-
if (taintedArgPositions.length > 0 && !collectionMethods.has(call.method_name) && !sanitizerMethods.has(call.method_name) && !safeUtilityMethods.has(call.method_name)) {
|
|
18716
|
+
if (taintedArgPositions.length > 0 && !collectionMethods.has(call.method_name) && !sanitizerMethods.has(call.method_name) && !safeUtilityMethods.has(call.method_name) && !ormQueryBuilderMethods.has(call.method_name)) {
|
|
18609
18717
|
const isBash = graph.ir.meta.language === "bash";
|
|
18610
18718
|
const bashSafeBuiltins = /* @__PURE__ */ new Set([
|
|
18611
18719
|
"echo",
|
|
@@ -26394,6 +26502,7 @@ var LanguageSourcesPass = class {
|
|
|
26394
26502
|
additionalSources.push(...findSetterChainSources(types, code, language));
|
|
26395
26503
|
additionalSources.push(...findJavaScriptAssignmentSources(code, language));
|
|
26396
26504
|
additionalSources.push(...findCSharpRequestSources(code, language));
|
|
26505
|
+
additionalSources.push(...findGoArgvSources(code, language));
|
|
26397
26506
|
const jsDOMSinks = findJavaScriptDOMSinks(code, language);
|
|
26398
26507
|
for (const s of jsDOMSinks) {
|
|
26399
26508
|
const alreadyExists = additionalSinks.some((x) => x.line === s.line && x.cwe === s.cwe);
|
|
@@ -27048,6 +27157,31 @@ function findSetterChainSources(types, sourceCode, language) {
|
|
|
27048
27157
|
}
|
|
27049
27158
|
return sources;
|
|
27050
27159
|
}
|
|
27160
|
+
function findGoArgvSources(sourceCode, language) {
|
|
27161
|
+
if (language !== "go") return [];
|
|
27162
|
+
const sources = [];
|
|
27163
|
+
const lines = sourceCode.split("\n");
|
|
27164
|
+
const rangeRe = /\bfor\b[^;{]*?,\s*([A-Za-z_]\w*)\s*:?=\s*range\s+os\.Args\b/;
|
|
27165
|
+
const assignRe = /\b([A-Za-z_]\w*)\s*:?=\s*os\.Args\b/;
|
|
27166
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
27167
|
+
const line = lines[i2];
|
|
27168
|
+
const rm = rangeRe.exec(line);
|
|
27169
|
+
const m = rm ?? assignRe.exec(line);
|
|
27170
|
+
if (!m) continue;
|
|
27171
|
+
const varName = m[1];
|
|
27172
|
+
const lineNumber = i2 + 1;
|
|
27173
|
+
if (sources.some((s) => s.line === lineNumber && s.variable === varName)) continue;
|
|
27174
|
+
sources.push({
|
|
27175
|
+
type: "io_input",
|
|
27176
|
+
location: `${varName} = os.Args`,
|
|
27177
|
+
severity: "high",
|
|
27178
|
+
line: lineNumber,
|
|
27179
|
+
confidence: 1,
|
|
27180
|
+
variable: varName
|
|
27181
|
+
});
|
|
27182
|
+
}
|
|
27183
|
+
return sources;
|
|
27184
|
+
}
|
|
27051
27185
|
function findCSharpRequestSources(sourceCode, language) {
|
|
27052
27186
|
if (language !== "csharp") return [];
|
|
27053
27187
|
const sources = [];
|
|
@@ -27715,8 +27849,9 @@ function buildJavaTaintedVars(sourceCode, seedVars) {
|
|
|
27715
27849
|
if (JAVA_KEYWORDS.has(lhs)) continue;
|
|
27716
27850
|
if (knownTainted.has(lhs)) continue;
|
|
27717
27851
|
const escaped = (v) => v.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
27852
|
+
const rhsCode = rhs.replace(/\$@?"(?:[^"\\]|\\.)*"/g, (lit) => " " + [...lit.matchAll(/\{([^}]*)\}/g)].map((x) => x[1]).join(" ") + " ").replace(/@?"(?:[^"\\]|\\.)*"/g, " ").replace(/'(?:[^'\\]|\\.)'/g, " ");
|
|
27718
27853
|
const ref = [...knownTainted].some(
|
|
27719
|
-
(v) => new RegExp(`(?<![\\p{L}\\p{N}_])${escaped(v)}(?![\\p{L}\\p{N}_])`, "u").test(
|
|
27854
|
+
(v) => new RegExp(`(?<![\\p{L}\\p{N}_])${escaped(v)}(?![\\p{L}\\p{N}_])`, "u").test(rhsCode)
|
|
27720
27855
|
);
|
|
27721
27856
|
if (ref) {
|
|
27722
27857
|
derived.set(lhs, i2 + 1);
|
|
@@ -27892,7 +28027,7 @@ function findBashTaintSources(sourceCode, dfg) {
|
|
|
27892
28027
|
});
|
|
27893
28028
|
}
|
|
27894
28029
|
}
|
|
27895
|
-
const envRe = /\$([A-Z][A-Z0-9_]{2,})|\$\{([A-Z][A-Z0-9_]{2,})
|
|
28030
|
+
const envRe = /\$([A-Z][A-Z0-9_]{2,})|\$\{([A-Z][A-Z0-9_]{2,})(?:[:#%/^,@!*+?=-][^}]*)?\}/g;
|
|
27896
28031
|
let em;
|
|
27897
28032
|
while ((em = envRe.exec(line)) !== null) {
|
|
27898
28033
|
const envVar = em[1] ?? em[2];
|
|
@@ -38125,6 +38260,7 @@ var SKIP_NAMES3 = /* @__PURE__ */ new Set([
|
|
|
38125
38260
|
"unknown",
|
|
38126
38261
|
"bigint"
|
|
38127
38262
|
]);
|
|
38263
|
+
var EMPTY_LINES = [];
|
|
38128
38264
|
var UnusedVariablePass = class {
|
|
38129
38265
|
name = "unused-variable";
|
|
38130
38266
|
category = "reliability";
|
|
@@ -38137,6 +38273,29 @@ var UnusedVariablePass = class {
|
|
|
38137
38273
|
const codeLines = code.split("\n");
|
|
38138
38274
|
const unusedVars = [];
|
|
38139
38275
|
const reported = /* @__PURE__ */ new Set();
|
|
38276
|
+
const defsByVar = /* @__PURE__ */ new Map();
|
|
38277
|
+
for (const d of graph.ir.dfg.defs) {
|
|
38278
|
+
const arr = defsByVar.get(d.variable);
|
|
38279
|
+
if (arr) arr.push(d);
|
|
38280
|
+
else defsByVar.set(d.variable, [d]);
|
|
38281
|
+
}
|
|
38282
|
+
const nameLines = /* @__PURE__ */ new Map();
|
|
38283
|
+
const idRe = /[A-Za-z_][A-Za-z0-9_]*/g;
|
|
38284
|
+
for (let i2 = 0; i2 < codeLines.length; i2++) {
|
|
38285
|
+
const line = codeLines[i2];
|
|
38286
|
+
idRe.lastIndex = 0;
|
|
38287
|
+
let seen = null;
|
|
38288
|
+
let m;
|
|
38289
|
+
while ((m = idRe.exec(line)) !== null) {
|
|
38290
|
+
const w = m[0];
|
|
38291
|
+
if (seen === null) seen = /* @__PURE__ */ new Set();
|
|
38292
|
+
if (seen.has(w)) continue;
|
|
38293
|
+
seen.add(w);
|
|
38294
|
+
const arr = nameLines.get(w);
|
|
38295
|
+
if (arr) arr.push(i2 + 1);
|
|
38296
|
+
else nameLines.set(w, [i2 + 1]);
|
|
38297
|
+
}
|
|
38298
|
+
}
|
|
38140
38299
|
for (const def of graph.ir.dfg.defs) {
|
|
38141
38300
|
if (def.kind !== "local") continue;
|
|
38142
38301
|
const variable = def.variable;
|
|
@@ -38147,22 +38306,19 @@ var UnusedVariablePass = class {
|
|
|
38147
38306
|
if (/\bexport\b/.test(lineText)) continue;
|
|
38148
38307
|
const uses = graph.usesOfDef(def.id);
|
|
38149
38308
|
if (uses.length > 0) continue;
|
|
38150
|
-
const otherDefs =
|
|
38151
|
-
(d) => d.variable === variable && d.id !== def.id
|
|
38152
|
-
);
|
|
38309
|
+
const otherDefs = (defsByVar.get(variable) ?? []).filter((d) => d.id !== def.id);
|
|
38153
38310
|
const otherDefLines = new Set(otherDefs.map((d) => d.line));
|
|
38154
|
-
const
|
|
38155
|
-
const
|
|
38311
|
+
const plainName = /^[A-Za-z_][A-Za-z0-9_]*$/.test(variable);
|
|
38312
|
+
const idxLines = plainName ? nameLines.get(variable) ?? EMPTY_LINES : null;
|
|
38313
|
+
const namePattern = plainName ? null : new RegExp(`\\b${variable.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`);
|
|
38156
38314
|
if (otherDefLines.size === 0) {
|
|
38157
|
-
const usedElsewhere = codeLines.some(
|
|
38158
|
-
(line, idx) => idx !== def.line - 1 && namePattern.test(line)
|
|
38159
|
-
);
|
|
38315
|
+
const usedElsewhere = idxLines !== null ? idxLines.some((ln) => ln !== def.line) : codeLines.some((line, idx) => idx !== def.line - 1 && namePattern.test(line));
|
|
38160
38316
|
if (usedElsewhere) continue;
|
|
38161
38317
|
} else {
|
|
38162
38318
|
const lineText2 = codeLines[def.line - 1] ?? "";
|
|
38163
38319
|
const isTrueDeclaration = /\b(?:let|const|var)\s+[\w{[]/.test(lineText2) || /\b(?:int|long|float|double|boolean|byte|char|short|var|final)\b/.test(lineText2) || /\b[A-Z]\w*(?:<[^>]*>)?\s+\w/.test(lineText2) || /\blet\s+(?:mut\s+)?\w/.test(lineText2);
|
|
38164
38320
|
if (isTrueDeclaration) {
|
|
38165
|
-
const usedBeforeNextDef = codeLines.some((line, idx) => {
|
|
38321
|
+
const usedBeforeNextDef = idxLines !== null ? idxLines.some((ln) => ln !== def.line && !otherDefLines.has(ln) && !otherDefs.some((d) => d.line > def.line && d.line < ln)) : codeLines.some((line, idx) => {
|
|
38166
38322
|
const lineNum = idx + 1;
|
|
38167
38323
|
if (lineNum === def.line || otherDefLines.has(lineNum)) return false;
|
|
38168
38324
|
if (!namePattern.test(line)) return false;
|
|
@@ -38170,7 +38326,7 @@ var UnusedVariablePass = class {
|
|
|
38170
38326
|
});
|
|
38171
38327
|
if (usedBeforeNextDef) continue;
|
|
38172
38328
|
} else {
|
|
38173
|
-
const usedOnNonDefLine = codeLines.some((line, idx) => {
|
|
38329
|
+
const usedOnNonDefLine = idxLines !== null ? idxLines.some((ln) => ln !== def.line && !otherDefLines.has(ln)) : codeLines.some((line, idx) => {
|
|
38174
38330
|
const lineNum = idx + 1;
|
|
38175
38331
|
return lineNum !== def.line && !otherDefLines.has(lineNum) && namePattern.test(line);
|
|
38176
38332
|
});
|
|
@@ -40720,6 +40876,8 @@ function isLikelyCredentialAssignment(line) {
|
|
|
40720
40876
|
if (CRED_DYNAMIC_VALUE_RE.test(value)) return null;
|
|
40721
40877
|
if (value.length < 3) return null;
|
|
40722
40878
|
if (isAllSameChar(value)) return null;
|
|
40879
|
+
if (value.toLowerCase() === name2.toLowerCase()) return null;
|
|
40880
|
+
if (/^(?:https?:\/\/|urn:|xmlns)/i.test(value)) return null;
|
|
40723
40881
|
if (value.length < 12) return null;
|
|
40724
40882
|
if (shannonEntropy(value) < 3.5) return null;
|
|
40725
40883
|
if (charClassDiversity(value) < 2) return null;
|
|
@@ -42940,8 +43098,7 @@ var InfoDisclosureStacktracePass = class {
|
|
|
42940
43098
|
// src/analysis/passes/unrestricted-file-upload-pass.ts
|
|
42941
43099
|
var UPLOAD_NAME_RE = /(?:getOriginalFilename|getSubmittedFileName|originalname|originalName|\.filename|\.Filename|FileHeader\.Filename|UploadFile)/;
|
|
42942
43100
|
var FILE_SAFE_CALL_RE = /(?:secure_filename|FilenameUtils\.getExtension|\.lastIndexOf\(['"]\.['"]\)|ALLOWED_EXT|ALLOWED_EXTENSIONS|allowedExtensions|\bfileFilter\b|filepath\.Ext|path\.extname)/;
|
|
42943
|
-
function lineWindow(
|
|
42944
|
-
const lines = code.split("\n");
|
|
43101
|
+
function lineWindow(lines, startLine, endLine) {
|
|
42945
43102
|
const s = Math.max(0, startLine - 1);
|
|
42946
43103
|
const e = Math.min(lines.length, endLine);
|
|
42947
43104
|
return lines.slice(s, e).join("\n");
|
|
@@ -42961,10 +43118,11 @@ var UnrestrictedFileUploadPass = class {
|
|
|
42961
43118
|
const { graph, language, code } = ctx;
|
|
42962
43119
|
const file = graph.ir.meta.file;
|
|
42963
43120
|
const findings = [];
|
|
43121
|
+
const codeLines = code.split("\n");
|
|
42964
43122
|
const safeFunctionRanges = [];
|
|
42965
43123
|
for (const t of graph.ir.types) {
|
|
42966
43124
|
for (const m of t.methods) {
|
|
42967
|
-
const body2 = lineWindow(
|
|
43125
|
+
const body2 = lineWindow(codeLines, m.start_line, m.end_line);
|
|
42968
43126
|
if (FILE_SAFE_CALL_RE.test(body2)) {
|
|
42969
43127
|
safeFunctionRanges.push({ start: m.start_line, end: m.end_line });
|
|
42970
43128
|
}
|
|
@@ -42974,7 +43132,7 @@ var UnrestrictedFileUploadPass = class {
|
|
|
42974
43132
|
for (const r of safeFunctionRanges) {
|
|
42975
43133
|
if (line >= r.start && line <= r.end) return true;
|
|
42976
43134
|
}
|
|
42977
|
-
const win = lineWindow(
|
|
43135
|
+
const win = lineWindow(codeLines, Math.max(1, line - 20), line + 5);
|
|
42978
43136
|
return FILE_SAFE_CALL_RE.test(win);
|
|
42979
43137
|
};
|
|
42980
43138
|
if (language === "java") {
|
|
@@ -9964,15 +9964,15 @@ function computeChains(defs, uses) {
|
|
|
9964
9964
|
existing.push(use);
|
|
9965
9965
|
usesByLine.set(use.line, existing);
|
|
9966
9966
|
}
|
|
9967
|
+
const seenChains = /* @__PURE__ */ new Set();
|
|
9967
9968
|
for (const def of defs) {
|
|
9968
9969
|
if (def.kind === "local") {
|
|
9969
9970
|
const usesOnLine = usesByLine.get(def.line) ?? [];
|
|
9970
9971
|
for (const use of usesOnLine) {
|
|
9971
9972
|
if (use.def_id !== null && use.def_id !== def.id) {
|
|
9972
|
-
const
|
|
9973
|
-
|
|
9974
|
-
|
|
9975
|
-
if (!exists) {
|
|
9973
|
+
const key = `${use.def_id}\0${def.id}\0${use.variable}`;
|
|
9974
|
+
if (!seenChains.has(key)) {
|
|
9975
|
+
seenChains.add(key);
|
|
9976
9976
|
chains.push({
|
|
9977
9977
|
from_def: use.def_id,
|
|
9978
9978
|
to_def: def.id,
|
|
@@ -11653,7 +11653,11 @@ var DEFAULT_SINKS = [
|
|
|
11653
11653
|
// File: covers both File(String pathname) and File(parent, child). The 2-arg
|
|
11654
11654
|
// overload's child argument carries CVE-2018-8041 (Camel mail Content-Disposition
|
|
11655
11655
|
// filename written to disk).
|
|
11656
|
-
|
|
11656
|
+
// Java `new File(path)` idiom. Excluded for C#: there is no `new File()` in
|
|
11657
|
+
// .NET (it's `System.IO.File` statics + `FileStream`, covered separately), and
|
|
11658
|
+
// this over-matched ASP.NET `ControllerBase.File(stream|bytes, contentType)` —
|
|
11659
|
+
// a result helper that returns a file, not a path sink (cognium-ai#326 B).
|
|
11660
|
+
{ method: "File", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], exclude_languages: ["csharp"] },
|
|
11657
11661
|
{ method: "FileInputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
11658
11662
|
{ method: "FileOutputStream", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
11659
11663
|
{ method: "FileReader", class: "constructor", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0] },
|
|
@@ -11896,8 +11900,11 @@ var DEFAULT_SINKS = [
|
|
|
11896
11900
|
// SAX handler sinks (can lead to XSS in parsed content)
|
|
11897
11901
|
{ method: "startElement", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1, 2] },
|
|
11898
11902
|
{ method: "characters", class: "ContentHandler", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
11899
|
-
// Template output sinks
|
|
11900
|
-
|
|
11903
|
+
// Template output sinks. `output` is classless (Java template engines /
|
|
11904
|
+
// Velocity), but Rust `std::process::Command::…output()` is process
|
|
11905
|
+
// execution, not HTML — it already fires the CWE-78 command_injection sink,
|
|
11906
|
+
// so exclude Rust here to drop the spurious xss (cognium-dev#146).
|
|
11907
|
+
{ method: "output", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], exclude_languages: ["rust"] },
|
|
11901
11908
|
{ method: "setOutput", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
11902
11909
|
{ method: "writeAttribute", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0, 1] },
|
|
11903
11910
|
// AntiSamy specific (SAX filters)
|
|
@@ -13205,6 +13212,22 @@ var DEFAULT_SINKS = [
|
|
|
13205
13212
|
{ method: "FileStream", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13206
13213
|
{ method: "StreamReader", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13207
13214
|
{ method: "StreamWriter", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13215
|
+
// Remaining System.IO.File path APIs — class-scoped so they don't collide
|
|
13216
|
+
// with unrelated methods. Copy/Move take a source AND destination path (both
|
|
13217
|
+
// attacker-controllable → read-anywhere / write-anywhere), hence [0, 1].
|
|
13218
|
+
{ method: "Copy", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13219
|
+
{ method: "Move", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0, 1], languages: ["csharp"] },
|
|
13220
|
+
{ method: "Delete", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13221
|
+
{ method: "Open", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13222
|
+
{ method: "OpenText", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13223
|
+
{ method: "Create", class: "File", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13224
|
+
{ method: "WriteAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13225
|
+
{ method: "AppendAllLines", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13226
|
+
// System.IO.Directory path APIs.
|
|
13227
|
+
{ method: "CreateDirectory", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13228
|
+
{ method: "Delete", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13229
|
+
{ method: "GetFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13230
|
+
{ method: "EnumerateFiles", class: "Directory", type: "path_traversal", cwe: "CWE-22", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13208
13231
|
// C# SSRF — HttpClient / WebClient / WebRequest (CWE-918).
|
|
13209
13232
|
{ method: "GetAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13210
13233
|
{ method: "PostAsync", type: "ssrf", cwe: "CWE-918", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13221,9 +13244,24 @@ var DEFAULT_SINKS = [
|
|
|
13221
13244
|
// C# code injection — dynamic script/assembly loading (CWE-94).
|
|
13222
13245
|
{ method: "EvaluateAsync", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13223
13246
|
{ method: "RunAsync", class: "CSharpScript", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13224
|
-
//
|
|
13247
|
+
// Loading an assembly from an attacker-controlled name/path is arbitrary code
|
|
13248
|
+
// execution. Class-scoped to `Assembly` (low FP — a tainted arg here is rare
|
|
13249
|
+
// in benign code). `Type.GetType`/`Activator.CreateInstance` are intentionally
|
|
13250
|
+
// NOT added — reflection with config-driven type names is common in DI, an FP
|
|
13251
|
+
// trap without the reflection-suppress machinery.
|
|
13252
|
+
{ method: "Load", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13253
|
+
{ method: "LoadFrom", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13254
|
+
{ method: "LoadFile", class: "Assembly", type: "code_injection", cwe: "CWE-94", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13255
|
+
// C# insecure deserialization — the polymorphic BCL formatters that can
|
|
13256
|
+
// instantiate arbitrary types named in the payload (CWE-502, cognium-ai#318).
|
|
13257
|
+
// Each of these classes exists only to deserialize and is unsafe on untrusted
|
|
13258
|
+
// input; class-scoped, so no collision with safe JSON APIs (System.Text.Json /
|
|
13259
|
+
// Newtonsoft-default, which are intentionally NOT sinks).
|
|
13225
13260
|
{ method: "Deserialize", class: "BinaryFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13226
13261
|
{ method: "Deserialize", class: "NetDataContractSerializer", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13262
|
+
{ method: "Deserialize", class: "SoapFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13263
|
+
{ method: "Deserialize", class: "LosFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13264
|
+
{ method: "Deserialize", class: "ObjectStateFormatter", type: "deserialization", cwe: "CWE-502", severity: "critical", arg_positions: [0], languages: ["csharp"] },
|
|
13227
13265
|
// C# XSS — raw HTML output (CWE-79).
|
|
13228
13266
|
{ method: "Raw", class: "Html", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13229
13267
|
{ method: "Write", class: "Response", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13248,6 +13286,20 @@ var DEFAULT_SINKS = [
|
|
|
13248
13286
|
// cognium-dev#273/#275). `BsonDocument.Parse(userJson)` deserializes an
|
|
13249
13287
|
// attacker-controlled query document. Class-scoped (static receiver resolves).
|
|
13250
13288
|
{ method: "Parse", class: "BsonDocument", type: "nosql_injection", cwe: "CWE-943", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13289
|
+
// C# log injection — Microsoft.Extensions.Logging `ILogger` (CWE-117,
|
|
13290
|
+
// cognium-ai#318). Only the message TEMPLATE (arg[0]) is the injectable
|
|
13291
|
+
// position; the structured form `LogInformation("… {Id}", id)` keeps taint in
|
|
13292
|
+
// later args (the logger renders them as data), so arg_positions:[0] does not
|
|
13293
|
+
// flag it. Mirrors the Java `Logger` sinks; severity low.
|
|
13294
|
+
{ method: "LogInformation", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13295
|
+
{ method: "LogWarning", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13296
|
+
{ method: "LogError", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13297
|
+
{ method: "LogDebug", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13298
|
+
{ method: "LogCritical", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13299
|
+
{ method: "LogTrace", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [0], languages: ["csharp"] },
|
|
13300
|
+
// Generic `ILogger.Log(logLevel, message, …)` — class-scoped (`Log` alone is
|
|
13301
|
+
// too generic); the message is arg[1].
|
|
13302
|
+
{ method: "Log", class: "ILogger", type: "log_injection", cwe: "CWE-117", severity: "low", arg_positions: [1], languages: ["csharp"] },
|
|
13251
13303
|
// C# XPath injection — System.Xml.XPath (CWE-643). Distinctive selector methods.
|
|
13252
13304
|
{ method: "SelectSingleNode", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13253
13305
|
{ method: "SelectNodes", type: "xpath_injection", cwe: "CWE-643", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
@@ -13256,6 +13308,11 @@ var DEFAULT_SINKS = [
|
|
|
13256
13308
|
{ method: "LoadXml", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13257
13309
|
{ method: "Load", class: "XmlDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13258
13310
|
{ method: "Load", class: "XDocument", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13311
|
+
// `new XmlTextReader(input)` — its DtdProcessing defaults to Parse (resolves
|
|
13312
|
+
// external entities), unlike `XmlReader.Create` (Prohibit). CWE-611,
|
|
13313
|
+
// cognium-ai#318. The 4.7.0 XmlResolver=null / DtdProcessing=Prohibit
|
|
13314
|
+
// hardening still suppresses the safe shape.
|
|
13315
|
+
{ method: "XmlTextReader", type: "xxe", cwe: "CWE-611", severity: "high", arg_positions: [0], languages: ["csharp"] },
|
|
13259
13316
|
// Python SQLi — asyncpg Connection.*
|
|
13260
13317
|
{ method: "execute", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
13261
13318
|
{ method: "fetch", class: "Connection", type: "sql_injection", cwe: "CWE-89", severity: "critical", arg_positions: [0] },
|
|
@@ -14573,6 +14630,34 @@ function isSafeJSChildProcessCall(call, pattern, language) {
|
|
|
14573
14630
|
if (SHELL_PROGRAMS.has(program)) return false;
|
|
14574
14631
|
return true;
|
|
14575
14632
|
}
|
|
14633
|
+
function isSafeCSharpProcessStartCall(call, pattern, language) {
|
|
14634
|
+
if (language !== "csharp") return false;
|
|
14635
|
+
if (pattern.type !== "command_injection") return false;
|
|
14636
|
+
if (call.method_name !== "Start") return false;
|
|
14637
|
+
if (call.arguments.length < 2) return false;
|
|
14638
|
+
const fileArg = call.arguments.find((a) => a.position === 0);
|
|
14639
|
+
if (!fileArg) return false;
|
|
14640
|
+
let raw;
|
|
14641
|
+
if (fileArg.literal !== null && fileArg.literal !== void 0) {
|
|
14642
|
+
raw = String(fileArg.literal).trim();
|
|
14643
|
+
} else {
|
|
14644
|
+
raw = (fileArg.expression ?? "").trim();
|
|
14645
|
+
}
|
|
14646
|
+
if (!/^@?"[^"]*"$/.test(raw)) return false;
|
|
14647
|
+
const program = (raw.replace(/^@?"|"$/g, "").split(/[\\/]/).pop() ?? "").toLowerCase().replace(/\.exe$/, "");
|
|
14648
|
+
const SHELL_PROGRAMS = /* @__PURE__ */ new Set([
|
|
14649
|
+
"sh",
|
|
14650
|
+
"bash",
|
|
14651
|
+
"zsh",
|
|
14652
|
+
"dash",
|
|
14653
|
+
"ash",
|
|
14654
|
+
"ksh",
|
|
14655
|
+
"cmd",
|
|
14656
|
+
"powershell",
|
|
14657
|
+
"pwsh"
|
|
14658
|
+
]);
|
|
14659
|
+
return !SHELL_PROGRAMS.has(program);
|
|
14660
|
+
}
|
|
14576
14661
|
function isSafeRustCommandCall(call, pattern, language) {
|
|
14577
14662
|
if (language !== "rust") return false;
|
|
14578
14663
|
if (pattern.type !== "command_injection") return false;
|
|
@@ -14828,6 +14913,9 @@ function findSinks(calls, patterns, typeHierarchy, language, sourceLines, types)
|
|
|
14828
14913
|
if (isSafeJSChildProcessCall(call, pattern, language)) {
|
|
14829
14914
|
continue;
|
|
14830
14915
|
}
|
|
14916
|
+
if (isSafeCSharpProcessStartCall(call, pattern, language)) {
|
|
14917
|
+
continue;
|
|
14918
|
+
}
|
|
14831
14919
|
if (pattern.safe_if_class_literal_at !== void 0 && argIsClassLiteral(call, pattern.safe_if_class_literal_at, types)) {
|
|
14832
14920
|
continue;
|
|
14833
14921
|
}
|