circle-ir 3.54.0 → 3.56.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 +36 -3
- package/dist/analysis/config-loader.js.map +1 -1
- package/dist/analysis/findings.d.ts.map +1 -1
- package/dist/analysis/findings.js +11 -6
- package/dist/analysis/findings.js.map +1 -1
- package/dist/analysis/passes/csrf-protection-disabled-pass.d.ts +42 -0
- package/dist/analysis/passes/csrf-protection-disabled-pass.d.ts.map +1 -0
- package/dist/analysis/passes/csrf-protection-disabled-pass.js +185 -0
- package/dist/analysis/passes/csrf-protection-disabled-pass.js.map +1 -0
- package/dist/analysis/passes/mass-assignment-pass.d.ts +41 -0
- package/dist/analysis/passes/mass-assignment-pass.d.ts.map +1 -0
- package/dist/analysis/passes/mass-assignment-pass.js +124 -0
- package/dist/analysis/passes/mass-assignment-pass.js.map +1 -0
- package/dist/analysis/passes/weak-crypto-pass.d.ts +10 -0
- package/dist/analysis/passes/weak-crypto-pass.d.ts.map +1 -1
- package/dist/analysis/passes/weak-crypto-pass.js +263 -3
- package/dist/analysis/passes/weak-crypto-pass.js.map +1 -1
- package/dist/analysis/passes/xml-entity-expansion-pass.d.ts +58 -0
- package/dist/analysis/passes/xml-entity-expansion-pass.d.ts.map +1 -0
- package/dist/analysis/passes/xml-entity-expansion-pass.js +196 -0
- package/dist/analysis/passes/xml-entity-expansion-pass.js.map +1 -0
- package/dist/analysis/rules.d.ts.map +1 -1
- package/dist/analysis/rules.js +18 -0
- package/dist/analysis/rules.js.map +1 -1
- package/dist/analysis/taint-propagation.js +1 -1
- package/dist/analysis/taint-propagation.js.map +1 -1
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +9 -0
- package/dist/analyzer.js.map +1 -1
- package/dist/browser/circle-ir.js +533 -14
- package/dist/core/circle-ir-core.cjs +40 -5
- package/dist/core/circle-ir-core.js +40 -5
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -11177,9 +11177,16 @@ var DEFAULT_SINKS = [
|
|
|
11177
11177
|
{ method: "println", class: "ServletOutputStream", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
11178
11178
|
// XSS in error messages (CWE-81)
|
|
11179
11179
|
{ method: "sendError", class: "HttpServletResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
11180
|
-
// Response header injection
|
|
11181
|
-
|
|
11182
|
-
|
|
11180
|
+
// Response header injection — re-categorised from `xss` to `crlf`
|
|
11181
|
+
// (CWE-113) in Sprint 6 of #86. Header injection is HTTP response
|
|
11182
|
+
// splitting / cache-poisoning / cookie forging; reflected XSS via header
|
|
11183
|
+
// reflection remains a downstream concern of body-writing sinks.
|
|
11184
|
+
{ method: "setHeader", class: "HttpServletResponse", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1] },
|
|
11185
|
+
{ method: "addHeader", class: "HttpServletResponse", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1] },
|
|
11186
|
+
// Note: `sendRedirect` is primarily classified as `ssrf` / open-redirect
|
|
11187
|
+
// (CWE-601) further down — see entry near line 1195. CRLF via Location
|
|
11188
|
+
// header is a secondary concern; keeping the canonical SSRF entry avoids
|
|
11189
|
+
// double-emission that would mask the open-redirect chain.
|
|
11183
11190
|
{ method: "setContentType", class: "HttpServletResponse", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
11184
11191
|
// JSP output
|
|
11185
11192
|
{ method: "setAttribute", class: "PageContext", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
@@ -12148,7 +12155,33 @@ var DEFAULT_SINKS = [
|
|
|
12148
12155
|
{ method: "Sprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
12149
12156
|
{ method: "Printf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
12150
12157
|
{ method: "Errorf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
12151
|
-
{ method: "Fprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [1], languages: ["go"] }
|
|
12158
|
+
{ method: "Fprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
12159
|
+
// CRLF / HTTP response splitting (CWE-113) — Sprint 6, #86.
|
|
12160
|
+
// Node.js / Express response header / cookie sinks. The header *name* (arg 0)
|
|
12161
|
+
// is also CRLF-sensitive but is almost always a string literal; we model
|
|
12162
|
+
// arg 1 (the value) as the primary sink.
|
|
12163
|
+
{ method: "setHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["javascript", "typescript"] },
|
|
12164
|
+
{ method: "writeHead", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [2], languages: ["javascript", "typescript"] },
|
|
12165
|
+
// Express: res.cookie(name, value, options) — value is CRLF-sensitive.
|
|
12166
|
+
{ method: "cookie", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["javascript", "typescript"] },
|
|
12167
|
+
// Express: res.location(url) and res.redirect(url) — Location header.
|
|
12168
|
+
{ method: "location", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
12169
|
+
{ method: "redirect", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
12170
|
+
// Go net/http: w.Header().Set(k, v) / Add(k, v) — first arg is the value
|
|
12171
|
+
// (Header is a map; the actual `value` is arg 1 of the call). We flag the
|
|
12172
|
+
// value position so a tainted variable is detected.
|
|
12173
|
+
{ method: "Set", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
12174
|
+
{ method: "Add", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
12175
|
+
// Mass-assignment (CWE-915) — Sprint 6, #86.
|
|
12176
|
+
// JS Object.assign(target, ...sources) — sources are arg 1..N, and if any
|
|
12177
|
+
// source is request-tainted, every key gets written onto the target. We
|
|
12178
|
+
// flag the source positions; the analyzer only needs one tainted to fire.
|
|
12179
|
+
{ method: "assign", class: "Object", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
12180
|
+
// Lodash bulk-merge helpers behave identically.
|
|
12181
|
+
{ method: "merge", class: "_", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
12182
|
+
{ method: "extend", class: "_", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
12183
|
+
// jQuery $.extend(target, source) (legacy).
|
|
12184
|
+
{ method: "extend", class: "$", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] }
|
|
12152
12185
|
];
|
|
12153
12186
|
var DEFAULT_SANITIZERS = [
|
|
12154
12187
|
// SQL Injection - proper parameter binding sanitizes input
|
|
@@ -13618,12 +13651,17 @@ function canSourceReachSink(sourceType, sinkType) {
|
|
|
13618
13651
|
// code_injection added to http_param/http_query/http_header/http_cookie:
|
|
13619
13652
|
// `eval(req.query.x)`, `Function(req.header('x'))`, `vm.runInThisContext(req.cookies.c)`
|
|
13620
13653
|
// are all real RCE patterns in JS web apps (cognium-dev #83).
|
|
13621
|
-
|
|
13622
|
-
|
|
13623
|
-
|
|
13624
|
-
|
|
13654
|
+
// crlf added to http_param/http_query/http_header/http_cookie/http_body:
|
|
13655
|
+
// setHeader/setCookie/redirect of any user-controlled string is CRLF / response
|
|
13656
|
+
// splitting (CWE-113) — Sprint 6, issue #86.
|
|
13657
|
+
// mass_assignment added to http_body / http_param: Object.assign(user, req.body),
|
|
13658
|
+
// User(**request.form) — CWE-915.
|
|
13659
|
+
http_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment"],
|
|
13660
|
+
http_body: ["sql_injection", "command_injection", "deserialization", "xxe", "xss", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment"],
|
|
13661
|
+
http_header: ["sql_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf"],
|
|
13662
|
+
http_cookie: ["sql_injection", "xss", "mybatis_mapper_call", "code_injection", "crlf"],
|
|
13625
13663
|
http_path: ["path_traversal", "sql_injection", "ssrf", "mybatis_mapper_call"],
|
|
13626
|
-
http_query: ["sql_injection", "command_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection"],
|
|
13664
|
+
http_query: ["sql_injection", "command_injection", "xss", "ssrf", "mybatis_mapper_call", "code_injection", "crlf", "mass_assignment"],
|
|
13627
13665
|
io_input: ["command_injection", "path_traversal", "deserialization", "xxe", "code_injection", "xss"],
|
|
13628
13666
|
env_input: ["command_injection", "path_traversal"],
|
|
13629
13667
|
db_input: ["xss", "sql_injection"],
|
|
@@ -13632,7 +13670,7 @@ function canSourceReachSink(sourceType, sinkType) {
|
|
|
13632
13670
|
network_input: ["sql_injection", "command_injection", "xss", "ssrf"],
|
|
13633
13671
|
config_param: ["sql_injection", "command_injection", "path_traversal", "xss", "ssrf"],
|
|
13634
13672
|
// Servlet init params
|
|
13635
|
-
interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call"],
|
|
13673
|
+
interprocedural_param: ["sql_injection", "command_injection", "path_traversal", "xss", "xpath_injection", "ldap_injection", "ssrf", "code_injection", "mybatis_mapper_call", "crlf", "mass_assignment"],
|
|
13636
13674
|
// Cross-method taint
|
|
13637
13675
|
plugin_param: ["sql_injection", "command_injection", "path_traversal", "xss", "code_injection"]
|
|
13638
13676
|
// Plugin/config parameters
|
|
@@ -14833,7 +14871,9 @@ var KNOWN_SINK_TYPES = /* @__PURE__ */ new Set([
|
|
|
14833
14871
|
"code_injection",
|
|
14834
14872
|
"mybatis_mapper_call",
|
|
14835
14873
|
"redos",
|
|
14836
|
-
"format_string"
|
|
14874
|
+
"format_string",
|
|
14875
|
+
"crlf",
|
|
14876
|
+
"mass_assignment"
|
|
14837
14877
|
]);
|
|
14838
14878
|
function checkSanitized(_fromLine, toLine, sinkType, sanitizersByLine) {
|
|
14839
14879
|
const sanitizersAtTarget = sanitizersByLine.get(toLine);
|
|
@@ -27445,6 +27485,102 @@ function detectHardcodedKeyJava(call) {
|
|
|
27445
27485
|
if (/^"[^"]*"$/.test(expr)) return `literal string`;
|
|
27446
27486
|
return null;
|
|
27447
27487
|
}
|
|
27488
|
+
function detectHardcodedKeyPython(call, constProp, literalBindings) {
|
|
27489
|
+
const arg = call.arguments.find((a) => a.position === 0);
|
|
27490
|
+
if (!arg) return null;
|
|
27491
|
+
const expr = (arg.expression ?? arg.literal ?? "").trim();
|
|
27492
|
+
if (!expr) return null;
|
|
27493
|
+
if (/^[bB][rR]?["'][^"']*["']$/.test(expr) || /^[rR][bB]["'][^"']*["']$/.test(expr)) {
|
|
27494
|
+
return `literal bytes ${expr.slice(0, 24)}${expr.length > 24 ? "\u2026" : ""}`;
|
|
27495
|
+
}
|
|
27496
|
+
if (/^["'][^"']*["']$/.test(expr)) {
|
|
27497
|
+
return `literal string ${expr.slice(0, 24)}${expr.length > 24 ? "\u2026" : ""}`;
|
|
27498
|
+
}
|
|
27499
|
+
if (arg.variable && constProp) {
|
|
27500
|
+
const sym = constProp.symbols.get(arg.variable);
|
|
27501
|
+
if (sym && sym.type === "string" && typeof sym.value === "string") {
|
|
27502
|
+
return `constant-propagated bytes from \`${arg.variable}\``;
|
|
27503
|
+
}
|
|
27504
|
+
}
|
|
27505
|
+
if (arg.variable) {
|
|
27506
|
+
const lit = literalBindings.get(arg.variable);
|
|
27507
|
+
if (lit) {
|
|
27508
|
+
return `literal-bound ${arg.variable} = ${lit.slice(0, 24)}${lit.length > 24 ? "\u2026" : ""}`;
|
|
27509
|
+
}
|
|
27510
|
+
}
|
|
27511
|
+
return null;
|
|
27512
|
+
}
|
|
27513
|
+
function detectHardcodedKeyGo(call, constProp, literalBindings) {
|
|
27514
|
+
const arg = call.arguments.find((a) => a.position === 0);
|
|
27515
|
+
if (!arg) return null;
|
|
27516
|
+
const expr = (arg.literal ?? arg.expression ?? "").trim();
|
|
27517
|
+
if (!expr) return null;
|
|
27518
|
+
if (/^\[\s*\]\s*byte\s*\(\s*["'`][^"'`]*["'`]\s*\)$/.test(expr)) {
|
|
27519
|
+
return `literal []byte("\u2026")`;
|
|
27520
|
+
}
|
|
27521
|
+
if (/^\[\s*\]\s*byte\s*\{[^}]*\}$/.test(expr)) {
|
|
27522
|
+
return `literal []byte{\u2026} composite`;
|
|
27523
|
+
}
|
|
27524
|
+
if (arg.variable && constProp) {
|
|
27525
|
+
const sym = constProp.symbols.get(arg.variable);
|
|
27526
|
+
if (sym && sym.type === "string" && typeof sym.value === "string") {
|
|
27527
|
+
return `constant-propagated key from \`${arg.variable}\``;
|
|
27528
|
+
}
|
|
27529
|
+
}
|
|
27530
|
+
if (arg.variable) {
|
|
27531
|
+
const lit = literalBindings.get(arg.variable);
|
|
27532
|
+
if (lit) {
|
|
27533
|
+
return `literal-bound ${arg.variable} = ${lit.slice(0, 24)}${lit.length > 24 ? "\u2026" : ""}`;
|
|
27534
|
+
}
|
|
27535
|
+
}
|
|
27536
|
+
return null;
|
|
27537
|
+
}
|
|
27538
|
+
function parseWeakRsaKeySizePython(call) {
|
|
27539
|
+
for (const arg of call.arguments) {
|
|
27540
|
+
const expr = (arg.expression ?? "").trim();
|
|
27541
|
+
const lit = (arg.literal ?? "").trim();
|
|
27542
|
+
const m = expr.match(/^key_size\s*=\s*(-?\d+)\s*$/);
|
|
27543
|
+
if (m && m[1]) {
|
|
27544
|
+
const n = parseInt(m[1], 10);
|
|
27545
|
+
if (Number.isFinite(n) && n > 0 && n < 2048) return n;
|
|
27546
|
+
return null;
|
|
27547
|
+
}
|
|
27548
|
+
if (/^key_size\s*=/.test(expr) && lit) {
|
|
27549
|
+
const n = parseInt(lit, 10);
|
|
27550
|
+
if (Number.isFinite(n) && n > 0 && n < 2048) return n;
|
|
27551
|
+
}
|
|
27552
|
+
}
|
|
27553
|
+
return null;
|
|
27554
|
+
}
|
|
27555
|
+
function scanLiteralBindings(code, language) {
|
|
27556
|
+
const out2 = /* @__PURE__ */ new Map();
|
|
27557
|
+
if (!code) return out2;
|
|
27558
|
+
if (language === "python") {
|
|
27559
|
+
const re = /^[ \t]*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(b[rR]?["'][^"']*["']|[rR]?b["'][^"']*["']|["'][^"']*["'])\s*(?:$|#)/gm;
|
|
27560
|
+
let m;
|
|
27561
|
+
while ((m = re.exec(code)) !== null) {
|
|
27562
|
+
if (m[1] && m[2]) out2.set(m[1], m[2]);
|
|
27563
|
+
}
|
|
27564
|
+
return out2;
|
|
27565
|
+
}
|
|
27566
|
+
if (language === "go") {
|
|
27567
|
+
const reByte = /^[ \t]*(?:var\s+)?([A-Za-z_][A-Za-z0-9_]*)\s*(?::=|=)\s*(\[\s*\]\s*byte\s*\(\s*["'`][^"'`]*["'`]\s*\))/gm;
|
|
27568
|
+
let m;
|
|
27569
|
+
while ((m = reByte.exec(code)) !== null) {
|
|
27570
|
+
if (m[1] && m[2]) out2.set(m[1], m[2]);
|
|
27571
|
+
}
|
|
27572
|
+
const reStr = /^[ \t]*(?:var|const)\s+([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(["'`][^"'`]*["'`])/gm;
|
|
27573
|
+
while ((m = reStr.exec(code)) !== null) {
|
|
27574
|
+
if (m[1] && m[2]) out2.set(m[1], m[2]);
|
|
27575
|
+
}
|
|
27576
|
+
const reShort = /^[ \t]*([A-Za-z_][A-Za-z0-9_]*)\s*:=\s*(["'`][^"'`]*["'`])/gm;
|
|
27577
|
+
while ((m = reShort.exec(code)) !== null) {
|
|
27578
|
+
if (m[1] && m[2]) out2.set(m[1], m[2]);
|
|
27579
|
+
}
|
|
27580
|
+
return out2;
|
|
27581
|
+
}
|
|
27582
|
+
return out2;
|
|
27583
|
+
}
|
|
27448
27584
|
var ISSUE_CWE = {
|
|
27449
27585
|
"weak-cipher": "CWE-327",
|
|
27450
27586
|
"ecb-mode": "CWE-327",
|
|
@@ -27457,11 +27593,13 @@ var WeakCryptoPass = class {
|
|
|
27457
27593
|
name = "weak-crypto";
|
|
27458
27594
|
category = "security";
|
|
27459
27595
|
run(ctx) {
|
|
27460
|
-
const { graph, language } = ctx;
|
|
27596
|
+
const { graph, language, code } = ctx;
|
|
27461
27597
|
const file = graph.ir.meta.file;
|
|
27462
27598
|
const findings = [];
|
|
27599
|
+
const constProp = ctx.hasResult("constant-propagation") ? ctx.getResult("constant-propagation") : null;
|
|
27600
|
+
const literalBindings = scanLiteralBindings(code, language);
|
|
27463
27601
|
for (const call of graph.ir.calls) {
|
|
27464
|
-
const detections = this.detect(call, language);
|
|
27602
|
+
const detections = this.detect(call, language, constProp, literalBindings);
|
|
27465
27603
|
for (const det of detections) {
|
|
27466
27604
|
const line = call.location.line;
|
|
27467
27605
|
findings.push({ line, language, ...det });
|
|
@@ -27514,7 +27652,7 @@ var WeakCryptoPass = class {
|
|
|
27514
27652
|
return "Use AES-GCM (authenticated) or ChaCha20-Poly1305. Avoid DES, 3DES, RC2, RC4, Blowfish, and ECB mode. For asymmetric encryption use RSA-OAEP with \u22652048-bit keys or modern curve-based schemes.";
|
|
27515
27653
|
}
|
|
27516
27654
|
}
|
|
27517
|
-
detect(call, language) {
|
|
27655
|
+
detect(call, language, constProp, literalBindings) {
|
|
27518
27656
|
const method = call.method_name;
|
|
27519
27657
|
const receiver = call.receiver ?? "";
|
|
27520
27658
|
const out2 = [];
|
|
@@ -27572,6 +27710,12 @@ var WeakCryptoPass = class {
|
|
|
27572
27710
|
out2.push({ issue: "ecb-mode", detail: "AES.MODE_ECB", api: `${receiver}.new` });
|
|
27573
27711
|
}
|
|
27574
27712
|
}
|
|
27713
|
+
if (lastSeg === "aes" || lastSeg.endsWith(".aes") || WEAK_CIPHER_BASES.has(lastSeg)) {
|
|
27714
|
+
const keyDetail = detectHardcodedKeyPython(call, constProp, literalBindings);
|
|
27715
|
+
if (keyDetail) {
|
|
27716
|
+
out2.push({ issue: "hardcoded-key", detail: keyDetail, api: `${receiver}.new` });
|
|
27717
|
+
}
|
|
27718
|
+
}
|
|
27575
27719
|
}
|
|
27576
27720
|
const isHazmatAlgos = receiver === "algorithms" || receiver.endsWith(".algorithms");
|
|
27577
27721
|
if (isHazmatAlgos) {
|
|
@@ -27580,6 +27724,25 @@ var WeakCryptoPass = class {
|
|
|
27580
27724
|
if (WEAK_CIPHER_BASES.has(normalized)) {
|
|
27581
27725
|
out2.push({ issue: "weak-cipher", detail: normalized, api: `algorithms.${method}` });
|
|
27582
27726
|
}
|
|
27727
|
+
if (m === "aes") {
|
|
27728
|
+
const keyDetail = detectHardcodedKeyPython(call, constProp, literalBindings);
|
|
27729
|
+
if (keyDetail) {
|
|
27730
|
+
out2.push({ issue: "hardcoded-key", detail: keyDetail, api: `algorithms.${method}` });
|
|
27731
|
+
}
|
|
27732
|
+
}
|
|
27733
|
+
}
|
|
27734
|
+
if (method === "ECB" && (receiver === "modes" || receiver.endsWith(".modes"))) {
|
|
27735
|
+
out2.push({ issue: "ecb-mode", detail: "modes.ECB()", api: `${receiver}.ECB` });
|
|
27736
|
+
}
|
|
27737
|
+
if (method === "generate_private_key" && (receiver === "rsa" || receiver === "dsa" || receiver.endsWith(".rsa") || receiver.endsWith(".dsa"))) {
|
|
27738
|
+
const n = parseWeakRsaKeySizePython(call);
|
|
27739
|
+
if (n !== null) {
|
|
27740
|
+
out2.push({
|
|
27741
|
+
issue: "weak-rsa-key",
|
|
27742
|
+
detail: String(n),
|
|
27743
|
+
api: `${receiver}.generate_private_key`
|
|
27744
|
+
});
|
|
27745
|
+
}
|
|
27583
27746
|
}
|
|
27584
27747
|
return out2;
|
|
27585
27748
|
}
|
|
@@ -27619,6 +27782,24 @@ var WeakCryptoPass = class {
|
|
|
27619
27782
|
if ((method === "NewECBEncrypter" || method === "NewECBDecrypter") && receiver === "cipher") {
|
|
27620
27783
|
out2.push({ issue: "ecb-mode", detail: method, api: `cipher.${method}` });
|
|
27621
27784
|
}
|
|
27785
|
+
if (receiver === "aes" && method === "NewCipher" || receiver === "des" && (method === "NewCipher" || method === "NewTripleDESCipher") || receiver === "rc4" && method === "NewCipher") {
|
|
27786
|
+
const keyDetail = detectHardcodedKeyGo(call, constProp, literalBindings);
|
|
27787
|
+
if (keyDetail) {
|
|
27788
|
+
out2.push({ issue: "hardcoded-key", detail: keyDetail, api: `${receiver}.${method}` });
|
|
27789
|
+
}
|
|
27790
|
+
}
|
|
27791
|
+
if (receiver === "rsa" && method === "GenerateKey") {
|
|
27792
|
+
const bitsArg = call.arguments.find((a) => a.position === 1);
|
|
27793
|
+
const expr = (bitsArg?.literal ?? bitsArg?.expression ?? "").trim();
|
|
27794
|
+
const n = parseInt(expr, 10);
|
|
27795
|
+
if (Number.isFinite(n) && n > 0 && n < 2048) {
|
|
27796
|
+
out2.push({
|
|
27797
|
+
issue: "weak-rsa-key",
|
|
27798
|
+
detail: String(n),
|
|
27799
|
+
api: "rsa.GenerateKey"
|
|
27800
|
+
});
|
|
27801
|
+
}
|
|
27802
|
+
}
|
|
27622
27803
|
return out2;
|
|
27623
27804
|
}
|
|
27624
27805
|
return out2;
|
|
@@ -28087,6 +28268,341 @@ var JwtVerifyDisabledPass = class {
|
|
|
28087
28268
|
}
|
|
28088
28269
|
};
|
|
28089
28270
|
|
|
28271
|
+
// src/analysis/passes/csrf-protection-disabled-pass.ts
|
|
28272
|
+
var JAVA_CSRF_DISABLE_RE = /\.csrf\s*\([^)]*\)\s*\.\s*disable\b/;
|
|
28273
|
+
var JAVA_CSRF_LAMBDA_DISABLE_RE = /\bcsrf\s*\(\s*\w+\s*->\s*\w+\s*\.\s*disable\s*\(/;
|
|
28274
|
+
var JAVA_CSRF_METHODREF_RE = /\bcsrf\s*\(\s*[\w.]+::disable\s*\)/;
|
|
28275
|
+
var JAVA_CSRF_NULL_REPO_RE = /\.csrfTokenRepository\s*\(\s*null\s*\)/;
|
|
28276
|
+
var CsrfProtectionDisabledPass = class {
|
|
28277
|
+
name = "csrf-protection-disabled";
|
|
28278
|
+
category = "security";
|
|
28279
|
+
run(ctx) {
|
|
28280
|
+
const { graph, language } = ctx;
|
|
28281
|
+
const file = graph.ir.meta.file;
|
|
28282
|
+
const findings = [];
|
|
28283
|
+
for (const call of graph.ir.calls) {
|
|
28284
|
+
const detections = this.detectCall(call, language);
|
|
28285
|
+
for (const det of detections) {
|
|
28286
|
+
const line = call.location.line;
|
|
28287
|
+
findings.push({ line, language, ...det });
|
|
28288
|
+
ctx.addFinding({
|
|
28289
|
+
id: `${this.name}-${file}-${line}-${det.pattern}`,
|
|
28290
|
+
pass: this.name,
|
|
28291
|
+
category: this.category,
|
|
28292
|
+
rule_id: this.name,
|
|
28293
|
+
cwe: "CWE-352",
|
|
28294
|
+
severity: "critical",
|
|
28295
|
+
level: "error",
|
|
28296
|
+
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.`,
|
|
28297
|
+
file,
|
|
28298
|
+
line,
|
|
28299
|
+
fix: this.fixFor(language),
|
|
28300
|
+
evidence: { ...det, language }
|
|
28301
|
+
});
|
|
28302
|
+
}
|
|
28303
|
+
}
|
|
28304
|
+
if (language === "java") {
|
|
28305
|
+
const src = ctx.code ?? "";
|
|
28306
|
+
if (src) {
|
|
28307
|
+
const lines = src.split("\n");
|
|
28308
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
28309
|
+
const line = i2 + 1;
|
|
28310
|
+
const text = lines[i2] ?? "";
|
|
28311
|
+
let det = null;
|
|
28312
|
+
if (JAVA_CSRF_LAMBDA_DISABLE_RE.test(text)) {
|
|
28313
|
+
det = { pattern: "csrf(c -> c.disable())", api: "HttpSecurity.csrf" };
|
|
28314
|
+
} else if (JAVA_CSRF_METHODREF_RE.test(text)) {
|
|
28315
|
+
det = { pattern: "csrf(::disable)", api: "HttpSecurity.csrf" };
|
|
28316
|
+
} else if (JAVA_CSRF_NULL_REPO_RE.test(text)) {
|
|
28317
|
+
det = { pattern: "csrfTokenRepository(null)", api: "HttpSecurity.csrfTokenRepository" };
|
|
28318
|
+
} else if (JAVA_CSRF_DISABLE_RE.test(text)) {
|
|
28319
|
+
det = { pattern: "csrf().disable()", api: "HttpSecurity.csrf" };
|
|
28320
|
+
}
|
|
28321
|
+
if (det && !findings.some((f) => f.line === line && f.pattern === det.pattern)) {
|
|
28322
|
+
findings.push({ line, language, ...det });
|
|
28323
|
+
ctx.addFinding({
|
|
28324
|
+
id: `${this.name}-${file}-${line}-${det.pattern}`,
|
|
28325
|
+
pass: this.name,
|
|
28326
|
+
category: this.category,
|
|
28327
|
+
rule_id: this.name,
|
|
28328
|
+
cwe: "CWE-352",
|
|
28329
|
+
severity: "critical",
|
|
28330
|
+
level: "error",
|
|
28331
|
+
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.`,
|
|
28332
|
+
file,
|
|
28333
|
+
line,
|
|
28334
|
+
fix: this.fixFor(language),
|
|
28335
|
+
evidence: { ...det, language }
|
|
28336
|
+
});
|
|
28337
|
+
}
|
|
28338
|
+
}
|
|
28339
|
+
}
|
|
28340
|
+
}
|
|
28341
|
+
if (language === "python") {
|
|
28342
|
+
const src = ctx.code ?? "";
|
|
28343
|
+
if (src) {
|
|
28344
|
+
const lines = src.split("\n");
|
|
28345
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
28346
|
+
const text = lines[i2] ?? "";
|
|
28347
|
+
if (/^\s*@csrf_exempt\b/.test(text)) {
|
|
28348
|
+
const line = i2 + 1;
|
|
28349
|
+
const det = { pattern: "@csrf_exempt", api: "django.views.decorators.csrf" };
|
|
28350
|
+
findings.push({ line, language, ...det });
|
|
28351
|
+
ctx.addFinding({
|
|
28352
|
+
id: `${this.name}-${file}-${line}-${det.pattern}`,
|
|
28353
|
+
pass: this.name,
|
|
28354
|
+
category: this.category,
|
|
28355
|
+
rule_id: this.name,
|
|
28356
|
+
cwe: "CWE-352",
|
|
28357
|
+
severity: "critical",
|
|
28358
|
+
level: "error",
|
|
28359
|
+
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.",
|
|
28360
|
+
file,
|
|
28361
|
+
line,
|
|
28362
|
+
fix: this.fixFor(language),
|
|
28363
|
+
evidence: { ...det, language }
|
|
28364
|
+
});
|
|
28365
|
+
}
|
|
28366
|
+
}
|
|
28367
|
+
}
|
|
28368
|
+
}
|
|
28369
|
+
return { findings };
|
|
28370
|
+
}
|
|
28371
|
+
detectCall(call, language) {
|
|
28372
|
+
const out2 = [];
|
|
28373
|
+
if (language !== "java") return out2;
|
|
28374
|
+
if (call.method_name === "disable") {
|
|
28375
|
+
const recv = call.receiver ?? "";
|
|
28376
|
+
if (/\bcsrf\s*\(\s*\)\s*$/.test(recv) || recv.endsWith(".csrf()")) {
|
|
28377
|
+
out2.push({ pattern: "csrf().disable()", api: "HttpSecurity.csrf" });
|
|
28378
|
+
}
|
|
28379
|
+
}
|
|
28380
|
+
if (call.method_name === "csrfTokenRepository") {
|
|
28381
|
+
const arg = call.arguments.find((a) => a.position === 0);
|
|
28382
|
+
const expr = (arg?.expression ?? arg?.literal ?? "").trim();
|
|
28383
|
+
if (expr === "null") {
|
|
28384
|
+
out2.push({
|
|
28385
|
+
pattern: "csrfTokenRepository(null)",
|
|
28386
|
+
api: "HttpSecurity.csrfTokenRepository"
|
|
28387
|
+
});
|
|
28388
|
+
}
|
|
28389
|
+
}
|
|
28390
|
+
return out2;
|
|
28391
|
+
}
|
|
28392
|
+
fixFor(language) {
|
|
28393
|
+
if (language === "java") {
|
|
28394
|
+
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.';
|
|
28395
|
+
}
|
|
28396
|
+
if (language === "python") {
|
|
28397
|
+
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.";
|
|
28398
|
+
}
|
|
28399
|
+
return "Re-enable framework CSRF protection or replace with origin / token validation.";
|
|
28400
|
+
}
|
|
28401
|
+
};
|
|
28402
|
+
|
|
28403
|
+
// src/analysis/passes/xml-entity-expansion-pass.ts
|
|
28404
|
+
var JAVA_FACTORIES = /* @__PURE__ */ new Set([
|
|
28405
|
+
"SAXParserFactory",
|
|
28406
|
+
"DocumentBuilderFactory",
|
|
28407
|
+
"XMLInputFactory",
|
|
28408
|
+
"SchemaFactory",
|
|
28409
|
+
"TransformerFactory"
|
|
28410
|
+
]);
|
|
28411
|
+
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*\))/;
|
|
28412
|
+
var PY_LXML_PARSER_INSECURE_DEFAULT_RE = /\bresolve_entities\s*=\s*False\b/;
|
|
28413
|
+
var XmlEntityExpansionPass = class {
|
|
28414
|
+
name = "xml-entity-expansion";
|
|
28415
|
+
category = "security";
|
|
28416
|
+
run(ctx) {
|
|
28417
|
+
const { graph, language } = ctx;
|
|
28418
|
+
const file = graph.ir.meta.file;
|
|
28419
|
+
const findings = [];
|
|
28420
|
+
const code = ctx.code ?? "";
|
|
28421
|
+
if (language === "java") {
|
|
28422
|
+
const safeInFile = JAVA_SAFE_EVIDENCE_RE.test(code);
|
|
28423
|
+
if (safeInFile) return { findings };
|
|
28424
|
+
for (const call of graph.ir.calls) {
|
|
28425
|
+
const det = this.detectJavaCall(call);
|
|
28426
|
+
if (!det) continue;
|
|
28427
|
+
const line = call.location.line;
|
|
28428
|
+
findings.push({ line, language, ...det });
|
|
28429
|
+
ctx.addFinding({
|
|
28430
|
+
id: `${this.name}-${file}-${line}-${det.api}`,
|
|
28431
|
+
pass: this.name,
|
|
28432
|
+
category: this.category,
|
|
28433
|
+
rule_id: this.name,
|
|
28434
|
+
cwe: det.cwe,
|
|
28435
|
+
severity: "high",
|
|
28436
|
+
level: "error",
|
|
28437
|
+
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.`,
|
|
28438
|
+
file,
|
|
28439
|
+
line,
|
|
28440
|
+
fix: this.fixForJava(det.api),
|
|
28441
|
+
evidence: { ...det, language, safeFeatureInFile: false }
|
|
28442
|
+
});
|
|
28443
|
+
}
|
|
28444
|
+
return { findings };
|
|
28445
|
+
}
|
|
28446
|
+
if (language === "python") {
|
|
28447
|
+
const safeInFile = PY_LXML_PARSER_INSECURE_DEFAULT_RE.test(code) || /\bdefusedxml\b/.test(code);
|
|
28448
|
+
if (safeInFile) return { findings };
|
|
28449
|
+
for (const call of graph.ir.calls) {
|
|
28450
|
+
const det = this.detectPythonCall(call);
|
|
28451
|
+
if (!det) continue;
|
|
28452
|
+
const line = call.location.line;
|
|
28453
|
+
findings.push({ line, language, ...det });
|
|
28454
|
+
ctx.addFinding({
|
|
28455
|
+
id: `${this.name}-${file}-${line}-${det.api}`,
|
|
28456
|
+
pass: this.name,
|
|
28457
|
+
category: this.category,
|
|
28458
|
+
rule_id: this.name,
|
|
28459
|
+
cwe: det.cwe,
|
|
28460
|
+
severity: "high",
|
|
28461
|
+
level: "error",
|
|
28462
|
+
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.`,
|
|
28463
|
+
file,
|
|
28464
|
+
line,
|
|
28465
|
+
fix: this.fixForPython(det.api),
|
|
28466
|
+
evidence: { ...det, language, safeFeatureInFile: false }
|
|
28467
|
+
});
|
|
28468
|
+
}
|
|
28469
|
+
return { findings };
|
|
28470
|
+
}
|
|
28471
|
+
return { findings };
|
|
28472
|
+
}
|
|
28473
|
+
detectJavaCall(call) {
|
|
28474
|
+
if (call.method_name !== "newInstance") return null;
|
|
28475
|
+
const recv = call.receiver ?? "";
|
|
28476
|
+
const recvType = call.receiver_type ?? "";
|
|
28477
|
+
for (const factory of JAVA_FACTORIES) {
|
|
28478
|
+
if (recv === factory || recvType === factory || recv.endsWith("." + factory) || recvType.endsWith("." + factory)) {
|
|
28479
|
+
return {
|
|
28480
|
+
pattern: `${factory}.newInstance()`,
|
|
28481
|
+
api: factory,
|
|
28482
|
+
cwe: "CWE-776"
|
|
28483
|
+
};
|
|
28484
|
+
}
|
|
28485
|
+
}
|
|
28486
|
+
return null;
|
|
28487
|
+
}
|
|
28488
|
+
detectPythonCall(call) {
|
|
28489
|
+
const recv = call.receiver ?? "";
|
|
28490
|
+
const method = call.method_name;
|
|
28491
|
+
if ((method === "parse" || method === "fromstring" || method === "XML") && (recv === "etree" || recv.endsWith(".etree"))) {
|
|
28492
|
+
return {
|
|
28493
|
+
pattern: `etree.${method}`,
|
|
28494
|
+
api: `lxml.etree.${method}`,
|
|
28495
|
+
cwe: "CWE-776"
|
|
28496
|
+
};
|
|
28497
|
+
}
|
|
28498
|
+
if ((method === "parse" || method === "fromstring") && (recv === "ET" || recv === "ElementTree" || recv.endsWith(".ElementTree"))) {
|
|
28499
|
+
return {
|
|
28500
|
+
pattern: `ElementTree.${method}`,
|
|
28501
|
+
api: `xml.etree.ElementTree.${method}`,
|
|
28502
|
+
cwe: "CWE-776"
|
|
28503
|
+
};
|
|
28504
|
+
}
|
|
28505
|
+
return null;
|
|
28506
|
+
}
|
|
28507
|
+
fixForJava(api) {
|
|
28508
|
+
if (api === "SAXParserFactory") {
|
|
28509
|
+
return 'Call `factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)` and `factory.setXIncludeAware(false)` before `newSAXParser()`.';
|
|
28510
|
+
}
|
|
28511
|
+
if (api === "DocumentBuilderFactory") {
|
|
28512
|
+
return 'Call `factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)` and `factory.setExpandEntityReferences(false)` before `newDocumentBuilder()`.';
|
|
28513
|
+
}
|
|
28514
|
+
if (api === "XMLInputFactory") {
|
|
28515
|
+
return "Call `factory.setProperty(XMLInputFactory.SUPPORT_DTD, false)` and `factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false)` before `createXMLStreamReader`.";
|
|
28516
|
+
}
|
|
28517
|
+
return "Use `XMLConstants.FEATURE_SECURE_PROCESSING` and explicitly disable DTD / external-entity loading on the factory before parsing.";
|
|
28518
|
+
}
|
|
28519
|
+
fixForPython(api) {
|
|
28520
|
+
if (api.startsWith("lxml.etree")) {
|
|
28521
|
+
return "Pass an explicit parser: `etree.parse(src, parser=etree.XMLParser(resolve_entities=False, no_network=True))`. Even better, use the `defusedxml.lxml` wrapper.";
|
|
28522
|
+
}
|
|
28523
|
+
return "Replace `xml.etree.ElementTree` with `defusedxml.ElementTree`, which disables DTD / entity processing by default.";
|
|
28524
|
+
}
|
|
28525
|
+
};
|
|
28526
|
+
|
|
28527
|
+
// src/analysis/passes/mass-assignment-pass.ts
|
|
28528
|
+
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)/;
|
|
28529
|
+
var JS_OBJECT_SPREAD_RE = /\{\s*\.\.\.\s*(?:req|request|ctx|context)(?:\.request)?\s*\.\s*(?:body|query|params|form)\b/;
|
|
28530
|
+
var MassAssignmentPass = class {
|
|
28531
|
+
name = "mass-assignment";
|
|
28532
|
+
category = "security";
|
|
28533
|
+
run(ctx) {
|
|
28534
|
+
const { graph, language } = ctx;
|
|
28535
|
+
const file = graph.ir.meta.file;
|
|
28536
|
+
const findings = [];
|
|
28537
|
+
const code = ctx.code ?? "";
|
|
28538
|
+
if (!code) return { findings };
|
|
28539
|
+
const lines = code.split("\n");
|
|
28540
|
+
if (language === "python") {
|
|
28541
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
28542
|
+
const text = lines[i2] ?? "";
|
|
28543
|
+
const m = PY_KWARGS_SPLAT_RE.exec(text);
|
|
28544
|
+
if (!m) continue;
|
|
28545
|
+
const line = i2 + 1;
|
|
28546
|
+
const det = {
|
|
28547
|
+
pattern: "**request.<bag>",
|
|
28548
|
+
match: m[0]
|
|
28549
|
+
};
|
|
28550
|
+
findings.push({
|
|
28551
|
+
line,
|
|
28552
|
+
language,
|
|
28553
|
+
pattern: det.pattern,
|
|
28554
|
+
snippet: text.trim().slice(0, 200)
|
|
28555
|
+
});
|
|
28556
|
+
ctx.addFinding({
|
|
28557
|
+
id: `${this.name}-${file}-${line}`,
|
|
28558
|
+
pass: this.name,
|
|
28559
|
+
category: this.category,
|
|
28560
|
+
rule_id: this.name,
|
|
28561
|
+
cwe: "CWE-915",
|
|
28562
|
+
severity: "high",
|
|
28563
|
+
level: "error",
|
|
28564
|
+
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\`).`,
|
|
28565
|
+
file,
|
|
28566
|
+
line,
|
|
28567
|
+
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 = [...]`.",
|
|
28568
|
+
evidence: { pattern: det.pattern, match: det.match, language }
|
|
28569
|
+
});
|
|
28570
|
+
}
|
|
28571
|
+
return { findings };
|
|
28572
|
+
}
|
|
28573
|
+
if (language === "javascript" || language === "typescript") {
|
|
28574
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
28575
|
+
const text = lines[i2] ?? "";
|
|
28576
|
+
const m = JS_OBJECT_SPREAD_RE.exec(text);
|
|
28577
|
+
if (!m) continue;
|
|
28578
|
+
const line = i2 + 1;
|
|
28579
|
+
findings.push({
|
|
28580
|
+
line,
|
|
28581
|
+
language,
|
|
28582
|
+
pattern: "{...req.<bag>}",
|
|
28583
|
+
snippet: text.trim().slice(0, 200)
|
|
28584
|
+
});
|
|
28585
|
+
ctx.addFinding({
|
|
28586
|
+
id: `${this.name}-${file}-${line}`,
|
|
28587
|
+
pass: this.name,
|
|
28588
|
+
category: this.category,
|
|
28589
|
+
rule_id: this.name,
|
|
28590
|
+
cwe: "CWE-915",
|
|
28591
|
+
severity: "high",
|
|
28592
|
+
level: "error",
|
|
28593
|
+
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\`).`,
|
|
28594
|
+
file,
|
|
28595
|
+
line,
|
|
28596
|
+
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.",
|
|
28597
|
+
evidence: { pattern: "{...req.<bag>}", match: m[0], language }
|
|
28598
|
+
});
|
|
28599
|
+
}
|
|
28600
|
+
return { findings };
|
|
28601
|
+
}
|
|
28602
|
+
return { findings };
|
|
28603
|
+
}
|
|
28604
|
+
};
|
|
28605
|
+
|
|
28090
28606
|
// src/analysis/metrics/passes/size-metrics-pass.ts
|
|
28091
28607
|
var SizeMetricsPass = class {
|
|
28092
28608
|
name = "size-metrics";
|
|
@@ -28989,6 +29505,9 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
28989
29505
|
if (!disabledPasses.has("weak-random")) pipeline.add(new WeakRandomPass());
|
|
28990
29506
|
if (!disabledPasses.has("tls-verify-disabled")) pipeline.add(new TlsVerifyDisabledPass());
|
|
28991
29507
|
if (!disabledPasses.has("jwt-verify-disabled")) pipeline.add(new JwtVerifyDisabledPass());
|
|
29508
|
+
if (!disabledPasses.has("csrf-protection-disabled")) pipeline.add(new CsrfProtectionDisabledPass());
|
|
29509
|
+
if (!disabledPasses.has("xml-entity-expansion")) pipeline.add(new XmlEntityExpansionPass());
|
|
29510
|
+
if (!disabledPasses.has("mass-assignment")) pipeline.add(new MassAssignmentPass());
|
|
28992
29511
|
const { results, findings } = pipeline.run(graph, code, language, config);
|
|
28993
29512
|
const sinkFilter = results.get("sink-filter");
|
|
28994
29513
|
const interProc = results.get("interprocedural");
|