cognium-dev 3.54.0 → 3.55.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/cli.js +380 -11
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -10476,8 +10476,8 @@ var DEFAULT_SINKS = [
|
|
|
10476
10476
|
{ method: "print", class: "ServletOutputStream", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
10477
10477
|
{ method: "println", class: "ServletOutputStream", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
10478
10478
|
{ method: "sendError", class: "HttpServletResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
10479
|
-
{ method: "setHeader", class: "HttpServletResponse", type: "
|
|
10480
|
-
{ method: "addHeader", class: "HttpServletResponse", type: "
|
|
10479
|
+
{ method: "setHeader", class: "HttpServletResponse", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1] },
|
|
10480
|
+
{ method: "addHeader", class: "HttpServletResponse", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1] },
|
|
10481
10481
|
{ method: "setContentType", class: "HttpServletResponse", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
10482
10482
|
{ method: "setAttribute", class: "PageContext", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
10483
10483
|
{ method: "addAttribute", class: "Model", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
@@ -11163,7 +11163,18 @@ var DEFAULT_SINKS = [
|
|
|
11163
11163
|
{ method: "Sprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
11164
11164
|
{ method: "Printf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
11165
11165
|
{ method: "Errorf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
11166
|
-
{ method: "Fprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [1], languages: ["go"] }
|
|
11166
|
+
{ method: "Fprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11167
|
+
{ method: "setHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["javascript", "typescript"] },
|
|
11168
|
+
{ method: "writeHead", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [2], languages: ["javascript", "typescript"] },
|
|
11169
|
+
{ method: "cookie", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["javascript", "typescript"] },
|
|
11170
|
+
{ method: "location", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11171
|
+
{ method: "redirect", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11172
|
+
{ method: "Set", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11173
|
+
{ method: "Add", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11174
|
+
{ method: "assign", class: "Object", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11175
|
+
{ method: "merge", class: "_", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11176
|
+
{ method: "extend", class: "_", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11177
|
+
{ method: "extend", class: "$", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] }
|
|
11167
11178
|
];
|
|
11168
11179
|
var DEFAULT_SANITIZERS = [
|
|
11169
11180
|
{ method: "setString", class: "PreparedStatement", removes: ["sql_injection"] },
|
|
@@ -21503,7 +21514,9 @@ var KNOWN_SINK_TYPES = new Set([
|
|
|
21503
21514
|
"code_injection",
|
|
21504
21515
|
"mybatis_mapper_call",
|
|
21505
21516
|
"redos",
|
|
21506
|
-
"format_string"
|
|
21517
|
+
"format_string",
|
|
21518
|
+
"crlf",
|
|
21519
|
+
"mass_assignment"
|
|
21507
21520
|
]);
|
|
21508
21521
|
function checkSanitized(_fromLine, toLine, sinkType, sanitizersByLine) {
|
|
21509
21522
|
const sanitizersAtTarget = sanitizersByLine.get(toLine);
|
|
@@ -21558,19 +21571,19 @@ function buildTaintFlow(source, sink, taintInfo) {
|
|
|
21558
21571
|
// ../circle-ir/dist/analysis/findings.js
|
|
21559
21572
|
function canSourceReachSink(sourceType, sinkType) {
|
|
21560
21573
|
const sourceToSinkMapping = {
|
|
21561
|
-
http_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "mybatis_mapper_call", "code_injection"],
|
|
21562
|
-
http_body: ["sql_injection", "command_injection", "deserialization", "xxe", "xss", "code_injection", "mybatis_mapper_call"],
|
|
21563
|
-
http_header: ["sql_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection"],
|
|
21564
|
-
http_cookie: ["sql_injection", "xss", "mybatis_mapper_call", "code_injection"],
|
|
21574
|
+
http_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment"],
|
|
21575
|
+
http_body: ["sql_injection", "command_injection", "deserialization", "xxe", "xss", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment"],
|
|
21576
|
+
http_header: ["sql_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf"],
|
|
21577
|
+
http_cookie: ["sql_injection", "xss", "mybatis_mapper_call", "code_injection", "crlf"],
|
|
21565
21578
|
http_path: ["path_traversal", "sql_injection", "ssrf", "mybatis_mapper_call"],
|
|
21566
|
-
http_query: ["sql_injection", "command_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection"],
|
|
21579
|
+
http_query: ["sql_injection", "command_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment"],
|
|
21567
21580
|
io_input: ["command_injection", "path_traversal", "deserialization", "xxe", "code_injection", "xss"],
|
|
21568
21581
|
env_input: ["command_injection", "path_traversal"],
|
|
21569
21582
|
db_input: ["xss", "sql_injection"],
|
|
21570
21583
|
file_input: ["deserialization", "xxe", "path_traversal", "command_injection", "code_injection"],
|
|
21571
21584
|
network_input: ["sql_injection", "command_injection", "xss", "ssrf"],
|
|
21572
21585
|
config_param: ["sql_injection", "command_injection", "path_traversal", "xss", "ssrf"],
|
|
21573
|
-
interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call"],
|
|
21586
|
+
interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment"],
|
|
21574
21587
|
plugin_param: ["sql_injection", "command_injection", "path_traversal", "xss", "code_injection"]
|
|
21575
21588
|
};
|
|
21576
21589
|
const validSinks = sourceToSinkMapping[sourceType];
|
|
@@ -28154,6 +28167,356 @@ class JwtVerifyDisabledPass {
|
|
|
28154
28167
|
}
|
|
28155
28168
|
}
|
|
28156
28169
|
|
|
28170
|
+
// ../circle-ir/dist/analysis/passes/csrf-protection-disabled-pass.js
|
|
28171
|
+
var JAVA_CSRF_DISABLE_RE = /\.csrf\s*\([^)]*\)\s*\.\s*disable\b/;
|
|
28172
|
+
var JAVA_CSRF_LAMBDA_DISABLE_RE = /\bcsrf\s*\(\s*\w+\s*->\s*\w+\s*\.\s*disable\s*\(/;
|
|
28173
|
+
var JAVA_CSRF_METHODREF_RE = /\bcsrf\s*\(\s*[\w.]+::disable\s*\)/;
|
|
28174
|
+
var JAVA_CSRF_NULL_REPO_RE = /\.csrfTokenRepository\s*\(\s*null\s*\)/;
|
|
28175
|
+
|
|
28176
|
+
class CsrfProtectionDisabledPass {
|
|
28177
|
+
name = "csrf-protection-disabled";
|
|
28178
|
+
category = "security";
|
|
28179
|
+
run(ctx) {
|
|
28180
|
+
const { graph, language } = ctx;
|
|
28181
|
+
const file = graph.ir.meta.file;
|
|
28182
|
+
const findings = [];
|
|
28183
|
+
for (const call of graph.ir.calls) {
|
|
28184
|
+
const detections = this.detectCall(call, language);
|
|
28185
|
+
for (const det of detections) {
|
|
28186
|
+
const line = call.location.line;
|
|
28187
|
+
findings.push({ line, language, ...det });
|
|
28188
|
+
ctx.addFinding({
|
|
28189
|
+
id: `${this.name}-${file}-${line}-${det.pattern}`,
|
|
28190
|
+
pass: this.name,
|
|
28191
|
+
category: this.category,
|
|
28192
|
+
rule_id: this.name,
|
|
28193
|
+
cwe: "CWE-352",
|
|
28194
|
+
severity: "critical",
|
|
28195
|
+
level: "error",
|
|
28196
|
+
message: `CSRF protection explicitly disabled via \`${det.pattern}\` ` + `(${det.api}). Any browser session can be silently used to ` + "perform state-changing requests from a malicious origin.",
|
|
28197
|
+
file,
|
|
28198
|
+
line,
|
|
28199
|
+
fix: this.fixFor(language),
|
|
28200
|
+
evidence: { ...det, language }
|
|
28201
|
+
});
|
|
28202
|
+
}
|
|
28203
|
+
}
|
|
28204
|
+
if (language === "java") {
|
|
28205
|
+
const src = ctx.code ?? "";
|
|
28206
|
+
if (src) {
|
|
28207
|
+
const lines = src.split(`
|
|
28208
|
+
`);
|
|
28209
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
28210
|
+
const line = i2 + 1;
|
|
28211
|
+
const text = lines[i2] ?? "";
|
|
28212
|
+
let det = null;
|
|
28213
|
+
if (JAVA_CSRF_LAMBDA_DISABLE_RE.test(text)) {
|
|
28214
|
+
det = { pattern: "csrf(c -> c.disable())", api: "HttpSecurity.csrf" };
|
|
28215
|
+
} else if (JAVA_CSRF_METHODREF_RE.test(text)) {
|
|
28216
|
+
det = { pattern: "csrf(::disable)", api: "HttpSecurity.csrf" };
|
|
28217
|
+
} else if (JAVA_CSRF_NULL_REPO_RE.test(text)) {
|
|
28218
|
+
det = { pattern: "csrfTokenRepository(null)", api: "HttpSecurity.csrfTokenRepository" };
|
|
28219
|
+
} else if (JAVA_CSRF_DISABLE_RE.test(text)) {
|
|
28220
|
+
det = { pattern: "csrf().disable()", api: "HttpSecurity.csrf" };
|
|
28221
|
+
}
|
|
28222
|
+
if (det && !findings.some((f) => f.line === line && f.pattern === det.pattern)) {
|
|
28223
|
+
findings.push({ line, language, ...det });
|
|
28224
|
+
ctx.addFinding({
|
|
28225
|
+
id: `${this.name}-${file}-${line}-${det.pattern}`,
|
|
28226
|
+
pass: this.name,
|
|
28227
|
+
category: this.category,
|
|
28228
|
+
rule_id: this.name,
|
|
28229
|
+
cwe: "CWE-352",
|
|
28230
|
+
severity: "critical",
|
|
28231
|
+
level: "error",
|
|
28232
|
+
message: `CSRF protection explicitly disabled via \`${det.pattern}\` ` + `(${det.api}). Any browser session can be silently used to ` + "perform state-changing requests from a malicious origin.",
|
|
28233
|
+
file,
|
|
28234
|
+
line,
|
|
28235
|
+
fix: this.fixFor(language),
|
|
28236
|
+
evidence: { ...det, language }
|
|
28237
|
+
});
|
|
28238
|
+
}
|
|
28239
|
+
}
|
|
28240
|
+
}
|
|
28241
|
+
}
|
|
28242
|
+
if (language === "python") {
|
|
28243
|
+
const src = ctx.code ?? "";
|
|
28244
|
+
if (src) {
|
|
28245
|
+
const lines = src.split(`
|
|
28246
|
+
`);
|
|
28247
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
28248
|
+
const text = lines[i2] ?? "";
|
|
28249
|
+
if (/^\s*@csrf_exempt\b/.test(text)) {
|
|
28250
|
+
const line = i2 + 1;
|
|
28251
|
+
const det = { pattern: "@csrf_exempt", api: "django.views.decorators.csrf" };
|
|
28252
|
+
findings.push({ line, language, ...det });
|
|
28253
|
+
ctx.addFinding({
|
|
28254
|
+
id: `${this.name}-${file}-${line}-${det.pattern}`,
|
|
28255
|
+
pass: this.name,
|
|
28256
|
+
category: this.category,
|
|
28257
|
+
rule_id: this.name,
|
|
28258
|
+
cwe: "CWE-352",
|
|
28259
|
+
severity: "critical",
|
|
28260
|
+
level: "error",
|
|
28261
|
+
message: "Django view is decorated with `@csrf_exempt`, bypassing the " + "framework CSRF middleware for this endpoint. Any browser " + "session can be silently used to invoke this handler from " + "a malicious origin.",
|
|
28262
|
+
file,
|
|
28263
|
+
line,
|
|
28264
|
+
fix: this.fixFor(language),
|
|
28265
|
+
evidence: { ...det, language }
|
|
28266
|
+
});
|
|
28267
|
+
}
|
|
28268
|
+
}
|
|
28269
|
+
}
|
|
28270
|
+
}
|
|
28271
|
+
return { findings };
|
|
28272
|
+
}
|
|
28273
|
+
detectCall(call, language) {
|
|
28274
|
+
const out2 = [];
|
|
28275
|
+
if (language !== "java")
|
|
28276
|
+
return out2;
|
|
28277
|
+
if (call.method_name === "disable") {
|
|
28278
|
+
const recv = call.receiver ?? "";
|
|
28279
|
+
if (/\bcsrf\s*\(\s*\)\s*$/.test(recv) || recv.endsWith(".csrf()")) {
|
|
28280
|
+
out2.push({ pattern: "csrf().disable()", api: "HttpSecurity.csrf" });
|
|
28281
|
+
}
|
|
28282
|
+
}
|
|
28283
|
+
if (call.method_name === "csrfTokenRepository") {
|
|
28284
|
+
const arg = call.arguments.find((a) => a.position === 0);
|
|
28285
|
+
const expr = (arg?.expression ?? arg?.literal ?? "").trim();
|
|
28286
|
+
if (expr === "null") {
|
|
28287
|
+
out2.push({
|
|
28288
|
+
pattern: "csrfTokenRepository(null)",
|
|
28289
|
+
api: "HttpSecurity.csrfTokenRepository"
|
|
28290
|
+
});
|
|
28291
|
+
}
|
|
28292
|
+
}
|
|
28293
|
+
return out2;
|
|
28294
|
+
}
|
|
28295
|
+
fixFor(language) {
|
|
28296
|
+
if (language === "java") {
|
|
28297
|
+
return "Leave Spring Security CSRF protection enabled. If you need to " + "exempt a specific endpoint (e.g. webhook), use " + '`.csrf(c -> c.ignoringRequestMatchers("/webhook"))` rather than ' + "`.disable()`. For stateless APIs, prefer a per-request token over " + "disabling CSRF entirely.";
|
|
28298
|
+
}
|
|
28299
|
+
if (language === "python") {
|
|
28300
|
+
return "Remove `@csrf_exempt`. For stateless API endpoints, use Django REST " + "Framework with a token / session auth backend that does not rely on " + "cookies. For webhook receivers, verify a shared-secret signature " + "instead of disabling CSRF.";
|
|
28301
|
+
}
|
|
28302
|
+
return "Re-enable framework CSRF protection or replace with origin / token validation.";
|
|
28303
|
+
}
|
|
28304
|
+
}
|
|
28305
|
+
|
|
28306
|
+
// ../circle-ir/dist/analysis/passes/xml-entity-expansion-pass.js
|
|
28307
|
+
var JAVA_FACTORIES = new Set([
|
|
28308
|
+
"SAXParserFactory",
|
|
28309
|
+
"DocumentBuilderFactory",
|
|
28310
|
+
"XMLInputFactory",
|
|
28311
|
+
"SchemaFactory",
|
|
28312
|
+
"TransformerFactory"
|
|
28313
|
+
]);
|
|
28314
|
+
var JAVA_SAFE_EVIDENCE_RE = /(disallow-doctype-decl|external-general-entities|external-parameter-entities|SUPPORT_DTD|ACCESS_EXTERNAL_DTD|ACCESS_EXTERNAL_SCHEMA|setXIncludeAware\s*\(\s*false\s*\)|setExpandEntityReferences\s*\(\s*false\s*\))/;
|
|
28315
|
+
var PY_LXML_PARSER_INSECURE_DEFAULT_RE = /\bresolve_entities\s*=\s*False\b/;
|
|
28316
|
+
|
|
28317
|
+
class XmlEntityExpansionPass {
|
|
28318
|
+
name = "xml-entity-expansion";
|
|
28319
|
+
category = "security";
|
|
28320
|
+
run(ctx) {
|
|
28321
|
+
const { graph, language } = ctx;
|
|
28322
|
+
const file = graph.ir.meta.file;
|
|
28323
|
+
const findings = [];
|
|
28324
|
+
const code = ctx.code ?? "";
|
|
28325
|
+
if (language === "java") {
|
|
28326
|
+
const safeInFile = JAVA_SAFE_EVIDENCE_RE.test(code);
|
|
28327
|
+
if (safeInFile)
|
|
28328
|
+
return { findings };
|
|
28329
|
+
for (const call of graph.ir.calls) {
|
|
28330
|
+
const det = this.detectJavaCall(call);
|
|
28331
|
+
if (!det)
|
|
28332
|
+
continue;
|
|
28333
|
+
const line = call.location.line;
|
|
28334
|
+
findings.push({ line, language, ...det });
|
|
28335
|
+
ctx.addFinding({
|
|
28336
|
+
id: `${this.name}-${file}-${line}-${det.api}`,
|
|
28337
|
+
pass: this.name,
|
|
28338
|
+
category: this.category,
|
|
28339
|
+
rule_id: this.name,
|
|
28340
|
+
cwe: det.cwe,
|
|
28341
|
+
severity: "high",
|
|
28342
|
+
level: "error",
|
|
28343
|
+
message: `${det.api} created without disabling DTD / external-entity ` + "processing. Vulnerable to billion-laughs / quadratic " + "blow-up DoS (CWE-776) and external-entity disclosure " + '(CWE-611). Add `setFeature("http://apache.org/xml/features/' + 'disallow-doctype-decl", true)` (or the equivalent) before ' + "parsing.",
|
|
28344
|
+
file,
|
|
28345
|
+
line,
|
|
28346
|
+
fix: this.fixForJava(det.api),
|
|
28347
|
+
evidence: { ...det, language, safeFeatureInFile: false }
|
|
28348
|
+
});
|
|
28349
|
+
}
|
|
28350
|
+
return { findings };
|
|
28351
|
+
}
|
|
28352
|
+
if (language === "python") {
|
|
28353
|
+
const safeInFile = PY_LXML_PARSER_INSECURE_DEFAULT_RE.test(code) || /\bdefusedxml\b/.test(code);
|
|
28354
|
+
if (safeInFile)
|
|
28355
|
+
return { findings };
|
|
28356
|
+
for (const call of graph.ir.calls) {
|
|
28357
|
+
const det = this.detectPythonCall(call);
|
|
28358
|
+
if (!det)
|
|
28359
|
+
continue;
|
|
28360
|
+
const line = call.location.line;
|
|
28361
|
+
findings.push({ line, language, ...det });
|
|
28362
|
+
ctx.addFinding({
|
|
28363
|
+
id: `${this.name}-${file}-${line}-${det.api}`,
|
|
28364
|
+
pass: this.name,
|
|
28365
|
+
category: this.category,
|
|
28366
|
+
rule_id: this.name,
|
|
28367
|
+
cwe: det.cwe,
|
|
28368
|
+
severity: "high",
|
|
28369
|
+
level: "error",
|
|
28370
|
+
message: `${det.api} called without an entity-safe parser. Vulnerable ` + "to billion-laughs / quadratic blow-up DoS (CWE-776) and " + "external-entity disclosure (CWE-611). Use `defusedxml` or pass " + "an `XMLParser(resolve_entities=False)` to lxml.",
|
|
28371
|
+
file,
|
|
28372
|
+
line,
|
|
28373
|
+
fix: this.fixForPython(det.api),
|
|
28374
|
+
evidence: { ...det, language, safeFeatureInFile: false }
|
|
28375
|
+
});
|
|
28376
|
+
}
|
|
28377
|
+
return { findings };
|
|
28378
|
+
}
|
|
28379
|
+
return { findings };
|
|
28380
|
+
}
|
|
28381
|
+
detectJavaCall(call) {
|
|
28382
|
+
if (call.method_name !== "newInstance")
|
|
28383
|
+
return null;
|
|
28384
|
+
const recv = call.receiver ?? "";
|
|
28385
|
+
const recvType = call.receiver_type ?? "";
|
|
28386
|
+
for (const factory of JAVA_FACTORIES) {
|
|
28387
|
+
if (recv === factory || recvType === factory || recv.endsWith("." + factory) || recvType.endsWith("." + factory)) {
|
|
28388
|
+
return {
|
|
28389
|
+
pattern: `${factory}.newInstance()`,
|
|
28390
|
+
api: factory,
|
|
28391
|
+
cwe: "CWE-776"
|
|
28392
|
+
};
|
|
28393
|
+
}
|
|
28394
|
+
}
|
|
28395
|
+
return null;
|
|
28396
|
+
}
|
|
28397
|
+
detectPythonCall(call) {
|
|
28398
|
+
const recv = call.receiver ?? "";
|
|
28399
|
+
const method = call.method_name;
|
|
28400
|
+
if ((method === "parse" || method === "fromstring" || method === "XML") && (recv === "etree" || recv.endsWith(".etree"))) {
|
|
28401
|
+
return {
|
|
28402
|
+
pattern: `etree.${method}`,
|
|
28403
|
+
api: `lxml.etree.${method}`,
|
|
28404
|
+
cwe: "CWE-776"
|
|
28405
|
+
};
|
|
28406
|
+
}
|
|
28407
|
+
if ((method === "parse" || method === "fromstring") && (recv === "ET" || recv === "ElementTree" || recv.endsWith(".ElementTree"))) {
|
|
28408
|
+
return {
|
|
28409
|
+
pattern: `ElementTree.${method}`,
|
|
28410
|
+
api: `xml.etree.ElementTree.${method}`,
|
|
28411
|
+
cwe: "CWE-776"
|
|
28412
|
+
};
|
|
28413
|
+
}
|
|
28414
|
+
return null;
|
|
28415
|
+
}
|
|
28416
|
+
fixForJava(api) {
|
|
28417
|
+
if (api === "SAXParserFactory") {
|
|
28418
|
+
return 'Call `factory.setFeature("http://apache.org/xml/features/' + 'disallow-doctype-decl", true)` and ' + "`factory.setXIncludeAware(false)` before `newSAXParser()`.";
|
|
28419
|
+
}
|
|
28420
|
+
if (api === "DocumentBuilderFactory") {
|
|
28421
|
+
return 'Call `factory.setFeature("http://apache.org/xml/features/' + 'disallow-doctype-decl", true)` and ' + "`factory.setExpandEntityReferences(false)` before " + "`newDocumentBuilder()`.";
|
|
28422
|
+
}
|
|
28423
|
+
if (api === "XMLInputFactory") {
|
|
28424
|
+
return "Call `factory.setProperty(XMLInputFactory.SUPPORT_DTD, false)` " + "and `factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_" + "ENTITIES, false)` before `createXMLStreamReader`.";
|
|
28425
|
+
}
|
|
28426
|
+
return "Use `XMLConstants.FEATURE_SECURE_PROCESSING` and explicitly disable " + "DTD / external-entity loading on the factory before parsing.";
|
|
28427
|
+
}
|
|
28428
|
+
fixForPython(api) {
|
|
28429
|
+
if (api.startsWith("lxml.etree")) {
|
|
28430
|
+
return "Pass an explicit parser: " + "`etree.parse(src, parser=etree.XMLParser(resolve_entities=False, " + "no_network=True))`. Even better, use the `defusedxml.lxml` wrapper.";
|
|
28431
|
+
}
|
|
28432
|
+
return "Replace `xml.etree.ElementTree` with `defusedxml.ElementTree`, which " + "disables DTD / entity processing by default.";
|
|
28433
|
+
}
|
|
28434
|
+
}
|
|
28435
|
+
|
|
28436
|
+
// ../circle-ir/dist/analysis/passes/mass-assignment-pass.js
|
|
28437
|
+
var PY_KWARGS_SPLAT_RE = /\*\*\s*(?:request|self\.request|flask\.request|ctx|self)\s*\.\s*(?:form|args|values|json|get_json\s*\(\s*\)|files|data)/;
|
|
28438
|
+
var JS_OBJECT_SPREAD_RE = /\{\s*\.\.\.\s*(?:req|request|ctx|context)(?:\.request)?\s*\.\s*(?:body|query|params|form)\b/;
|
|
28439
|
+
|
|
28440
|
+
class MassAssignmentPass {
|
|
28441
|
+
name = "mass-assignment";
|
|
28442
|
+
category = "security";
|
|
28443
|
+
run(ctx) {
|
|
28444
|
+
const { graph, language } = ctx;
|
|
28445
|
+
const file = graph.ir.meta.file;
|
|
28446
|
+
const findings = [];
|
|
28447
|
+
const code = ctx.code ?? "";
|
|
28448
|
+
if (!code)
|
|
28449
|
+
return { findings };
|
|
28450
|
+
const lines = code.split(`
|
|
28451
|
+
`);
|
|
28452
|
+
if (language === "python") {
|
|
28453
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
28454
|
+
const text = lines[i2] ?? "";
|
|
28455
|
+
const m = PY_KWARGS_SPLAT_RE.exec(text);
|
|
28456
|
+
if (!m)
|
|
28457
|
+
continue;
|
|
28458
|
+
const line = i2 + 1;
|
|
28459
|
+
const det = {
|
|
28460
|
+
pattern: "**request.<bag>",
|
|
28461
|
+
match: m[0]
|
|
28462
|
+
};
|
|
28463
|
+
findings.push({
|
|
28464
|
+
line,
|
|
28465
|
+
language,
|
|
28466
|
+
pattern: det.pattern,
|
|
28467
|
+
snippet: text.trim().slice(0, 200)
|
|
28468
|
+
});
|
|
28469
|
+
ctx.addFinding({
|
|
28470
|
+
id: `${this.name}-${file}-${line}`,
|
|
28471
|
+
pass: this.name,
|
|
28472
|
+
category: this.category,
|
|
28473
|
+
rule_id: this.name,
|
|
28474
|
+
cwe: "CWE-915",
|
|
28475
|
+
severity: "high",
|
|
28476
|
+
level: "error",
|
|
28477
|
+
message: `HTTP request bag splatted into constructor / ORM helper via ` + `\`${det.match}\`. Every form field becomes a settable attribute ` + "on the domain object, including ones the endpoint did not " + "intend to expose (e.g. `is_admin`, `role`, `owner_id`).",
|
|
28478
|
+
file,
|
|
28479
|
+
line,
|
|
28480
|
+
fix: "Replace the `**` splat with an explicit allow-list: " + "`Model(name=request.form['name'], email=request.form['email'])`. " + "For Django, use a `ModelForm` / serializer with `fields = [...]`.",
|
|
28481
|
+
evidence: { pattern: det.pattern, match: det.match, language }
|
|
28482
|
+
});
|
|
28483
|
+
}
|
|
28484
|
+
return { findings };
|
|
28485
|
+
}
|
|
28486
|
+
if (language === "javascript" || language === "typescript") {
|
|
28487
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
28488
|
+
const text = lines[i2] ?? "";
|
|
28489
|
+
const m = JS_OBJECT_SPREAD_RE.exec(text);
|
|
28490
|
+
if (!m)
|
|
28491
|
+
continue;
|
|
28492
|
+
const line = i2 + 1;
|
|
28493
|
+
findings.push({
|
|
28494
|
+
line,
|
|
28495
|
+
language,
|
|
28496
|
+
pattern: "{...req.<bag>}",
|
|
28497
|
+
snippet: text.trim().slice(0, 200)
|
|
28498
|
+
});
|
|
28499
|
+
ctx.addFinding({
|
|
28500
|
+
id: `${this.name}-${file}-${line}`,
|
|
28501
|
+
pass: this.name,
|
|
28502
|
+
category: this.category,
|
|
28503
|
+
rule_id: this.name,
|
|
28504
|
+
cwe: "CWE-915",
|
|
28505
|
+
severity: "high",
|
|
28506
|
+
level: "error",
|
|
28507
|
+
message: `HTTP request bag spread into object literal via \`${m[0]}\`. ` + "Every body field becomes a settable property on the resulting " + "object, including ones the endpoint did not intend to expose " + "(e.g. `isAdmin`, `role`, `ownerId`).",
|
|
28508
|
+
file,
|
|
28509
|
+
line,
|
|
28510
|
+
fix: "Replace the spread with an explicit pick: " + "`const { name, email } = req.body; const user = { name, email };`. " + "For ORMs, use a DTO / Zod schema with `.pick(...)` or " + "allow-list serializers.",
|
|
28511
|
+
evidence: { pattern: "{...req.<bag>}", match: m[0], language }
|
|
28512
|
+
});
|
|
28513
|
+
}
|
|
28514
|
+
return { findings };
|
|
28515
|
+
}
|
|
28516
|
+
return { findings };
|
|
28517
|
+
}
|
|
28518
|
+
}
|
|
28519
|
+
|
|
28157
28520
|
// ../circle-ir/dist/graph/import-graph.js
|
|
28158
28521
|
function dirname(filePath) {
|
|
28159
28522
|
const idx = filePath.lastIndexOf("/");
|
|
@@ -29276,6 +29639,12 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
29276
29639
|
pipeline.add(new TlsVerifyDisabledPass);
|
|
29277
29640
|
if (!disabledPasses.has("jwt-verify-disabled"))
|
|
29278
29641
|
pipeline.add(new JwtVerifyDisabledPass);
|
|
29642
|
+
if (!disabledPasses.has("csrf-protection-disabled"))
|
|
29643
|
+
pipeline.add(new CsrfProtectionDisabledPass);
|
|
29644
|
+
if (!disabledPasses.has("xml-entity-expansion"))
|
|
29645
|
+
pipeline.add(new XmlEntityExpansionPass);
|
|
29646
|
+
if (!disabledPasses.has("mass-assignment"))
|
|
29647
|
+
pipeline.add(new MassAssignmentPass);
|
|
29279
29648
|
const { results, findings } = pipeline.run(graph, code, language, config);
|
|
29280
29649
|
const sinkFilter = results.get("sink-filter");
|
|
29281
29650
|
const interProc = results.get("interprocedural");
|
|
@@ -29469,7 +29838,7 @@ var colors = {
|
|
|
29469
29838
|
};
|
|
29470
29839
|
|
|
29471
29840
|
// src/version.ts
|
|
29472
|
-
var version = "3.
|
|
29841
|
+
var version = "3.55.0";
|
|
29473
29842
|
|
|
29474
29843
|
// src/formatters.ts
|
|
29475
29844
|
var SINK_SEVERITY = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cognium-dev",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.55.0",
|
|
4
4
|
"description": "Static Application Security Testing CLI for detecting security vulnerabilities via taint tracking",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"registry": "https://registry.npmjs.org/"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"circle-ir": "^3.
|
|
68
|
+
"circle-ir": "^3.55.0"
|
|
69
69
|
},
|
|
70
70
|
"devDependencies": {
|
|
71
71
|
"@types/node": "^25.5.0",
|