circle-ir 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/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/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 +389 -11
- 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);
|
|
@@ -28087,6 +28127,341 @@ var JwtVerifyDisabledPass = class {
|
|
|
28087
28127
|
}
|
|
28088
28128
|
};
|
|
28089
28129
|
|
|
28130
|
+
// src/analysis/passes/csrf-protection-disabled-pass.ts
|
|
28131
|
+
var JAVA_CSRF_DISABLE_RE = /\.csrf\s*\([^)]*\)\s*\.\s*disable\b/;
|
|
28132
|
+
var JAVA_CSRF_LAMBDA_DISABLE_RE = /\bcsrf\s*\(\s*\w+\s*->\s*\w+\s*\.\s*disable\s*\(/;
|
|
28133
|
+
var JAVA_CSRF_METHODREF_RE = /\bcsrf\s*\(\s*[\w.]+::disable\s*\)/;
|
|
28134
|
+
var JAVA_CSRF_NULL_REPO_RE = /\.csrfTokenRepository\s*\(\s*null\s*\)/;
|
|
28135
|
+
var CsrfProtectionDisabledPass = class {
|
|
28136
|
+
name = "csrf-protection-disabled";
|
|
28137
|
+
category = "security";
|
|
28138
|
+
run(ctx) {
|
|
28139
|
+
const { graph, language } = ctx;
|
|
28140
|
+
const file = graph.ir.meta.file;
|
|
28141
|
+
const findings = [];
|
|
28142
|
+
for (const call of graph.ir.calls) {
|
|
28143
|
+
const detections = this.detectCall(call, language);
|
|
28144
|
+
for (const det of detections) {
|
|
28145
|
+
const line = call.location.line;
|
|
28146
|
+
findings.push({ line, language, ...det });
|
|
28147
|
+
ctx.addFinding({
|
|
28148
|
+
id: `${this.name}-${file}-${line}-${det.pattern}`,
|
|
28149
|
+
pass: this.name,
|
|
28150
|
+
category: this.category,
|
|
28151
|
+
rule_id: this.name,
|
|
28152
|
+
cwe: "CWE-352",
|
|
28153
|
+
severity: "critical",
|
|
28154
|
+
level: "error",
|
|
28155
|
+
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.`,
|
|
28156
|
+
file,
|
|
28157
|
+
line,
|
|
28158
|
+
fix: this.fixFor(language),
|
|
28159
|
+
evidence: { ...det, language }
|
|
28160
|
+
});
|
|
28161
|
+
}
|
|
28162
|
+
}
|
|
28163
|
+
if (language === "java") {
|
|
28164
|
+
const src = ctx.code ?? "";
|
|
28165
|
+
if (src) {
|
|
28166
|
+
const lines = src.split("\n");
|
|
28167
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
28168
|
+
const line = i2 + 1;
|
|
28169
|
+
const text = lines[i2] ?? "";
|
|
28170
|
+
let det = null;
|
|
28171
|
+
if (JAVA_CSRF_LAMBDA_DISABLE_RE.test(text)) {
|
|
28172
|
+
det = { pattern: "csrf(c -> c.disable())", api: "HttpSecurity.csrf" };
|
|
28173
|
+
} else if (JAVA_CSRF_METHODREF_RE.test(text)) {
|
|
28174
|
+
det = { pattern: "csrf(::disable)", api: "HttpSecurity.csrf" };
|
|
28175
|
+
} else if (JAVA_CSRF_NULL_REPO_RE.test(text)) {
|
|
28176
|
+
det = { pattern: "csrfTokenRepository(null)", api: "HttpSecurity.csrfTokenRepository" };
|
|
28177
|
+
} else if (JAVA_CSRF_DISABLE_RE.test(text)) {
|
|
28178
|
+
det = { pattern: "csrf().disable()", api: "HttpSecurity.csrf" };
|
|
28179
|
+
}
|
|
28180
|
+
if (det && !findings.some((f) => f.line === line && f.pattern === det.pattern)) {
|
|
28181
|
+
findings.push({ line, language, ...det });
|
|
28182
|
+
ctx.addFinding({
|
|
28183
|
+
id: `${this.name}-${file}-${line}-${det.pattern}`,
|
|
28184
|
+
pass: this.name,
|
|
28185
|
+
category: this.category,
|
|
28186
|
+
rule_id: this.name,
|
|
28187
|
+
cwe: "CWE-352",
|
|
28188
|
+
severity: "critical",
|
|
28189
|
+
level: "error",
|
|
28190
|
+
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.`,
|
|
28191
|
+
file,
|
|
28192
|
+
line,
|
|
28193
|
+
fix: this.fixFor(language),
|
|
28194
|
+
evidence: { ...det, language }
|
|
28195
|
+
});
|
|
28196
|
+
}
|
|
28197
|
+
}
|
|
28198
|
+
}
|
|
28199
|
+
}
|
|
28200
|
+
if (language === "python") {
|
|
28201
|
+
const src = ctx.code ?? "";
|
|
28202
|
+
if (src) {
|
|
28203
|
+
const lines = src.split("\n");
|
|
28204
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
28205
|
+
const text = lines[i2] ?? "";
|
|
28206
|
+
if (/^\s*@csrf_exempt\b/.test(text)) {
|
|
28207
|
+
const line = i2 + 1;
|
|
28208
|
+
const det = { pattern: "@csrf_exempt", api: "django.views.decorators.csrf" };
|
|
28209
|
+
findings.push({ line, language, ...det });
|
|
28210
|
+
ctx.addFinding({
|
|
28211
|
+
id: `${this.name}-${file}-${line}-${det.pattern}`,
|
|
28212
|
+
pass: this.name,
|
|
28213
|
+
category: this.category,
|
|
28214
|
+
rule_id: this.name,
|
|
28215
|
+
cwe: "CWE-352",
|
|
28216
|
+
severity: "critical",
|
|
28217
|
+
level: "error",
|
|
28218
|
+
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.",
|
|
28219
|
+
file,
|
|
28220
|
+
line,
|
|
28221
|
+
fix: this.fixFor(language),
|
|
28222
|
+
evidence: { ...det, language }
|
|
28223
|
+
});
|
|
28224
|
+
}
|
|
28225
|
+
}
|
|
28226
|
+
}
|
|
28227
|
+
}
|
|
28228
|
+
return { findings };
|
|
28229
|
+
}
|
|
28230
|
+
detectCall(call, language) {
|
|
28231
|
+
const out2 = [];
|
|
28232
|
+
if (language !== "java") return out2;
|
|
28233
|
+
if (call.method_name === "disable") {
|
|
28234
|
+
const recv = call.receiver ?? "";
|
|
28235
|
+
if (/\bcsrf\s*\(\s*\)\s*$/.test(recv) || recv.endsWith(".csrf()")) {
|
|
28236
|
+
out2.push({ pattern: "csrf().disable()", api: "HttpSecurity.csrf" });
|
|
28237
|
+
}
|
|
28238
|
+
}
|
|
28239
|
+
if (call.method_name === "csrfTokenRepository") {
|
|
28240
|
+
const arg = call.arguments.find((a) => a.position === 0);
|
|
28241
|
+
const expr = (arg?.expression ?? arg?.literal ?? "").trim();
|
|
28242
|
+
if (expr === "null") {
|
|
28243
|
+
out2.push({
|
|
28244
|
+
pattern: "csrfTokenRepository(null)",
|
|
28245
|
+
api: "HttpSecurity.csrfTokenRepository"
|
|
28246
|
+
});
|
|
28247
|
+
}
|
|
28248
|
+
}
|
|
28249
|
+
return out2;
|
|
28250
|
+
}
|
|
28251
|
+
fixFor(language) {
|
|
28252
|
+
if (language === "java") {
|
|
28253
|
+
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.';
|
|
28254
|
+
}
|
|
28255
|
+
if (language === "python") {
|
|
28256
|
+
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.";
|
|
28257
|
+
}
|
|
28258
|
+
return "Re-enable framework CSRF protection or replace with origin / token validation.";
|
|
28259
|
+
}
|
|
28260
|
+
};
|
|
28261
|
+
|
|
28262
|
+
// src/analysis/passes/xml-entity-expansion-pass.ts
|
|
28263
|
+
var JAVA_FACTORIES = /* @__PURE__ */ new Set([
|
|
28264
|
+
"SAXParserFactory",
|
|
28265
|
+
"DocumentBuilderFactory",
|
|
28266
|
+
"XMLInputFactory",
|
|
28267
|
+
"SchemaFactory",
|
|
28268
|
+
"TransformerFactory"
|
|
28269
|
+
]);
|
|
28270
|
+
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*\))/;
|
|
28271
|
+
var PY_LXML_PARSER_INSECURE_DEFAULT_RE = /\bresolve_entities\s*=\s*False\b/;
|
|
28272
|
+
var XmlEntityExpansionPass = class {
|
|
28273
|
+
name = "xml-entity-expansion";
|
|
28274
|
+
category = "security";
|
|
28275
|
+
run(ctx) {
|
|
28276
|
+
const { graph, language } = ctx;
|
|
28277
|
+
const file = graph.ir.meta.file;
|
|
28278
|
+
const findings = [];
|
|
28279
|
+
const code = ctx.code ?? "";
|
|
28280
|
+
if (language === "java") {
|
|
28281
|
+
const safeInFile = JAVA_SAFE_EVIDENCE_RE.test(code);
|
|
28282
|
+
if (safeInFile) return { findings };
|
|
28283
|
+
for (const call of graph.ir.calls) {
|
|
28284
|
+
const det = this.detectJavaCall(call);
|
|
28285
|
+
if (!det) continue;
|
|
28286
|
+
const line = call.location.line;
|
|
28287
|
+
findings.push({ line, language, ...det });
|
|
28288
|
+
ctx.addFinding({
|
|
28289
|
+
id: `${this.name}-${file}-${line}-${det.api}`,
|
|
28290
|
+
pass: this.name,
|
|
28291
|
+
category: this.category,
|
|
28292
|
+
rule_id: this.name,
|
|
28293
|
+
cwe: det.cwe,
|
|
28294
|
+
severity: "high",
|
|
28295
|
+
level: "error",
|
|
28296
|
+
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.`,
|
|
28297
|
+
file,
|
|
28298
|
+
line,
|
|
28299
|
+
fix: this.fixForJava(det.api),
|
|
28300
|
+
evidence: { ...det, language, safeFeatureInFile: false }
|
|
28301
|
+
});
|
|
28302
|
+
}
|
|
28303
|
+
return { findings };
|
|
28304
|
+
}
|
|
28305
|
+
if (language === "python") {
|
|
28306
|
+
const safeInFile = PY_LXML_PARSER_INSECURE_DEFAULT_RE.test(code) || /\bdefusedxml\b/.test(code);
|
|
28307
|
+
if (safeInFile) return { findings };
|
|
28308
|
+
for (const call of graph.ir.calls) {
|
|
28309
|
+
const det = this.detectPythonCall(call);
|
|
28310
|
+
if (!det) continue;
|
|
28311
|
+
const line = call.location.line;
|
|
28312
|
+
findings.push({ line, language, ...det });
|
|
28313
|
+
ctx.addFinding({
|
|
28314
|
+
id: `${this.name}-${file}-${line}-${det.api}`,
|
|
28315
|
+
pass: this.name,
|
|
28316
|
+
category: this.category,
|
|
28317
|
+
rule_id: this.name,
|
|
28318
|
+
cwe: det.cwe,
|
|
28319
|
+
severity: "high",
|
|
28320
|
+
level: "error",
|
|
28321
|
+
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.`,
|
|
28322
|
+
file,
|
|
28323
|
+
line,
|
|
28324
|
+
fix: this.fixForPython(det.api),
|
|
28325
|
+
evidence: { ...det, language, safeFeatureInFile: false }
|
|
28326
|
+
});
|
|
28327
|
+
}
|
|
28328
|
+
return { findings };
|
|
28329
|
+
}
|
|
28330
|
+
return { findings };
|
|
28331
|
+
}
|
|
28332
|
+
detectJavaCall(call) {
|
|
28333
|
+
if (call.method_name !== "newInstance") return null;
|
|
28334
|
+
const recv = call.receiver ?? "";
|
|
28335
|
+
const recvType = call.receiver_type ?? "";
|
|
28336
|
+
for (const factory of JAVA_FACTORIES) {
|
|
28337
|
+
if (recv === factory || recvType === factory || recv.endsWith("." + factory) || recvType.endsWith("." + factory)) {
|
|
28338
|
+
return {
|
|
28339
|
+
pattern: `${factory}.newInstance()`,
|
|
28340
|
+
api: factory,
|
|
28341
|
+
cwe: "CWE-776"
|
|
28342
|
+
};
|
|
28343
|
+
}
|
|
28344
|
+
}
|
|
28345
|
+
return null;
|
|
28346
|
+
}
|
|
28347
|
+
detectPythonCall(call) {
|
|
28348
|
+
const recv = call.receiver ?? "";
|
|
28349
|
+
const method = call.method_name;
|
|
28350
|
+
if ((method === "parse" || method === "fromstring" || method === "XML") && (recv === "etree" || recv.endsWith(".etree"))) {
|
|
28351
|
+
return {
|
|
28352
|
+
pattern: `etree.${method}`,
|
|
28353
|
+
api: `lxml.etree.${method}`,
|
|
28354
|
+
cwe: "CWE-776"
|
|
28355
|
+
};
|
|
28356
|
+
}
|
|
28357
|
+
if ((method === "parse" || method === "fromstring") && (recv === "ET" || recv === "ElementTree" || recv.endsWith(".ElementTree"))) {
|
|
28358
|
+
return {
|
|
28359
|
+
pattern: `ElementTree.${method}`,
|
|
28360
|
+
api: `xml.etree.ElementTree.${method}`,
|
|
28361
|
+
cwe: "CWE-776"
|
|
28362
|
+
};
|
|
28363
|
+
}
|
|
28364
|
+
return null;
|
|
28365
|
+
}
|
|
28366
|
+
fixForJava(api) {
|
|
28367
|
+
if (api === "SAXParserFactory") {
|
|
28368
|
+
return 'Call `factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)` and `factory.setXIncludeAware(false)` before `newSAXParser()`.';
|
|
28369
|
+
}
|
|
28370
|
+
if (api === "DocumentBuilderFactory") {
|
|
28371
|
+
return 'Call `factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true)` and `factory.setExpandEntityReferences(false)` before `newDocumentBuilder()`.';
|
|
28372
|
+
}
|
|
28373
|
+
if (api === "XMLInputFactory") {
|
|
28374
|
+
return "Call `factory.setProperty(XMLInputFactory.SUPPORT_DTD, false)` and `factory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, false)` before `createXMLStreamReader`.";
|
|
28375
|
+
}
|
|
28376
|
+
return "Use `XMLConstants.FEATURE_SECURE_PROCESSING` and explicitly disable DTD / external-entity loading on the factory before parsing.";
|
|
28377
|
+
}
|
|
28378
|
+
fixForPython(api) {
|
|
28379
|
+
if (api.startsWith("lxml.etree")) {
|
|
28380
|
+
return "Pass an explicit parser: `etree.parse(src, parser=etree.XMLParser(resolve_entities=False, no_network=True))`. Even better, use the `defusedxml.lxml` wrapper.";
|
|
28381
|
+
}
|
|
28382
|
+
return "Replace `xml.etree.ElementTree` with `defusedxml.ElementTree`, which disables DTD / entity processing by default.";
|
|
28383
|
+
}
|
|
28384
|
+
};
|
|
28385
|
+
|
|
28386
|
+
// src/analysis/passes/mass-assignment-pass.ts
|
|
28387
|
+
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)/;
|
|
28388
|
+
var JS_OBJECT_SPREAD_RE = /\{\s*\.\.\.\s*(?:req|request|ctx|context)(?:\.request)?\s*\.\s*(?:body|query|params|form)\b/;
|
|
28389
|
+
var MassAssignmentPass = class {
|
|
28390
|
+
name = "mass-assignment";
|
|
28391
|
+
category = "security";
|
|
28392
|
+
run(ctx) {
|
|
28393
|
+
const { graph, language } = ctx;
|
|
28394
|
+
const file = graph.ir.meta.file;
|
|
28395
|
+
const findings = [];
|
|
28396
|
+
const code = ctx.code ?? "";
|
|
28397
|
+
if (!code) return { findings };
|
|
28398
|
+
const lines = code.split("\n");
|
|
28399
|
+
if (language === "python") {
|
|
28400
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
28401
|
+
const text = lines[i2] ?? "";
|
|
28402
|
+
const m = PY_KWARGS_SPLAT_RE.exec(text);
|
|
28403
|
+
if (!m) continue;
|
|
28404
|
+
const line = i2 + 1;
|
|
28405
|
+
const det = {
|
|
28406
|
+
pattern: "**request.<bag>",
|
|
28407
|
+
match: m[0]
|
|
28408
|
+
};
|
|
28409
|
+
findings.push({
|
|
28410
|
+
line,
|
|
28411
|
+
language,
|
|
28412
|
+
pattern: det.pattern,
|
|
28413
|
+
snippet: text.trim().slice(0, 200)
|
|
28414
|
+
});
|
|
28415
|
+
ctx.addFinding({
|
|
28416
|
+
id: `${this.name}-${file}-${line}`,
|
|
28417
|
+
pass: this.name,
|
|
28418
|
+
category: this.category,
|
|
28419
|
+
rule_id: this.name,
|
|
28420
|
+
cwe: "CWE-915",
|
|
28421
|
+
severity: "high",
|
|
28422
|
+
level: "error",
|
|
28423
|
+
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\`).`,
|
|
28424
|
+
file,
|
|
28425
|
+
line,
|
|
28426
|
+
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 = [...]`.",
|
|
28427
|
+
evidence: { pattern: det.pattern, match: det.match, language }
|
|
28428
|
+
});
|
|
28429
|
+
}
|
|
28430
|
+
return { findings };
|
|
28431
|
+
}
|
|
28432
|
+
if (language === "javascript" || language === "typescript") {
|
|
28433
|
+
for (let i2 = 0; i2 < lines.length; i2++) {
|
|
28434
|
+
const text = lines[i2] ?? "";
|
|
28435
|
+
const m = JS_OBJECT_SPREAD_RE.exec(text);
|
|
28436
|
+
if (!m) continue;
|
|
28437
|
+
const line = i2 + 1;
|
|
28438
|
+
findings.push({
|
|
28439
|
+
line,
|
|
28440
|
+
language,
|
|
28441
|
+
pattern: "{...req.<bag>}",
|
|
28442
|
+
snippet: text.trim().slice(0, 200)
|
|
28443
|
+
});
|
|
28444
|
+
ctx.addFinding({
|
|
28445
|
+
id: `${this.name}-${file}-${line}`,
|
|
28446
|
+
pass: this.name,
|
|
28447
|
+
category: this.category,
|
|
28448
|
+
rule_id: this.name,
|
|
28449
|
+
cwe: "CWE-915",
|
|
28450
|
+
severity: "high",
|
|
28451
|
+
level: "error",
|
|
28452
|
+
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\`).`,
|
|
28453
|
+
file,
|
|
28454
|
+
line,
|
|
28455
|
+
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.",
|
|
28456
|
+
evidence: { pattern: "{...req.<bag>}", match: m[0], language }
|
|
28457
|
+
});
|
|
28458
|
+
}
|
|
28459
|
+
return { findings };
|
|
28460
|
+
}
|
|
28461
|
+
return { findings };
|
|
28462
|
+
}
|
|
28463
|
+
};
|
|
28464
|
+
|
|
28090
28465
|
// src/analysis/metrics/passes/size-metrics-pass.ts
|
|
28091
28466
|
var SizeMetricsPass = class {
|
|
28092
28467
|
name = "size-metrics";
|
|
@@ -28989,6 +29364,9 @@ async function analyze(code, filePath, language, options = {}) {
|
|
|
28989
29364
|
if (!disabledPasses.has("weak-random")) pipeline.add(new WeakRandomPass());
|
|
28990
29365
|
if (!disabledPasses.has("tls-verify-disabled")) pipeline.add(new TlsVerifyDisabledPass());
|
|
28991
29366
|
if (!disabledPasses.has("jwt-verify-disabled")) pipeline.add(new JwtVerifyDisabledPass());
|
|
29367
|
+
if (!disabledPasses.has("csrf-protection-disabled")) pipeline.add(new CsrfProtectionDisabledPass());
|
|
29368
|
+
if (!disabledPasses.has("xml-entity-expansion")) pipeline.add(new XmlEntityExpansionPass());
|
|
29369
|
+
if (!disabledPasses.has("mass-assignment")) pipeline.add(new MassAssignmentPass());
|
|
28992
29370
|
const { results, findings } = pipeline.run(graph, code, language, config);
|
|
28993
29371
|
const sinkFilter = results.get("sink-filter");
|
|
28994
29372
|
const interProc = results.get("interprocedural");
|
|
@@ -10559,9 +10559,16 @@ var DEFAULT_SINKS = [
|
|
|
10559
10559
|
{ method: "println", class: "ServletOutputStream", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
10560
10560
|
// XSS in error messages (CWE-81)
|
|
10561
10561
|
{ method: "sendError", class: "HttpServletResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
10562
|
-
// Response header injection
|
|
10563
|
-
|
|
10564
|
-
|
|
10562
|
+
// Response header injection — re-categorised from `xss` to `crlf`
|
|
10563
|
+
// (CWE-113) in Sprint 6 of #86. Header injection is HTTP response
|
|
10564
|
+
// splitting / cache-poisoning / cookie forging; reflected XSS via header
|
|
10565
|
+
// reflection remains a downstream concern of body-writing sinks.
|
|
10566
|
+
{ method: "setHeader", class: "HttpServletResponse", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1] },
|
|
10567
|
+
{ method: "addHeader", class: "HttpServletResponse", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1] },
|
|
10568
|
+
// Note: `sendRedirect` is primarily classified as `ssrf` / open-redirect
|
|
10569
|
+
// (CWE-601) further down — see entry near line 1195. CRLF via Location
|
|
10570
|
+
// header is a secondary concern; keeping the canonical SSRF entry avoids
|
|
10571
|
+
// double-emission that would mask the open-redirect chain.
|
|
10565
10572
|
{ method: "setContentType", class: "HttpServletResponse", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
10566
10573
|
// JSP output
|
|
10567
10574
|
{ method: "setAttribute", class: "PageContext", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
@@ -11530,7 +11537,33 @@ var DEFAULT_SINKS = [
|
|
|
11530
11537
|
{ method: "Sprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
11531
11538
|
{ method: "Printf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
11532
11539
|
{ method: "Errorf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
11533
|
-
{ method: "Fprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [1], languages: ["go"] }
|
|
11540
|
+
{ method: "Fprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11541
|
+
// CRLF / HTTP response splitting (CWE-113) — Sprint 6, #86.
|
|
11542
|
+
// Node.js / Express response header / cookie sinks. The header *name* (arg 0)
|
|
11543
|
+
// is also CRLF-sensitive but is almost always a string literal; we model
|
|
11544
|
+
// arg 1 (the value) as the primary sink.
|
|
11545
|
+
{ method: "setHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["javascript", "typescript"] },
|
|
11546
|
+
{ method: "writeHead", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [2], languages: ["javascript", "typescript"] },
|
|
11547
|
+
// Express: res.cookie(name, value, options) — value is CRLF-sensitive.
|
|
11548
|
+
{ method: "cookie", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["javascript", "typescript"] },
|
|
11549
|
+
// Express: res.location(url) and res.redirect(url) — Location header.
|
|
11550
|
+
{ method: "location", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11551
|
+
{ method: "redirect", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11552
|
+
// Go net/http: w.Header().Set(k, v) / Add(k, v) — first arg is the value
|
|
11553
|
+
// (Header is a map; the actual `value` is arg 1 of the call). We flag the
|
|
11554
|
+
// value position so a tainted variable is detected.
|
|
11555
|
+
{ method: "Set", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11556
|
+
{ method: "Add", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11557
|
+
// Mass-assignment (CWE-915) — Sprint 6, #86.
|
|
11558
|
+
// JS Object.assign(target, ...sources) — sources are arg 1..N, and if any
|
|
11559
|
+
// source is request-tainted, every key gets written onto the target. We
|
|
11560
|
+
// flag the source positions; the analyzer only needs one tainted to fire.
|
|
11561
|
+
{ method: "assign", class: "Object", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11562
|
+
// Lodash bulk-merge helpers behave identically.
|
|
11563
|
+
{ method: "merge", class: "_", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11564
|
+
{ method: "extend", class: "_", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11565
|
+
// jQuery $.extend(target, source) (legacy).
|
|
11566
|
+
{ method: "extend", class: "$", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] }
|
|
11534
11567
|
];
|
|
11535
11568
|
var DEFAULT_SANITIZERS = [
|
|
11536
11569
|
// SQL Injection - proper parameter binding sanitizes input
|
|
@@ -13186,7 +13219,9 @@ var KNOWN_SINK_TYPES = /* @__PURE__ */ new Set([
|
|
|
13186
13219
|
"code_injection",
|
|
13187
13220
|
"mybatis_mapper_call",
|
|
13188
13221
|
"redos",
|
|
13189
|
-
"format_string"
|
|
13222
|
+
"format_string",
|
|
13223
|
+
"crlf",
|
|
13224
|
+
"mass_assignment"
|
|
13190
13225
|
]);
|
|
13191
13226
|
function checkSanitized(_fromLine, toLine, sinkType, sanitizersByLine) {
|
|
13192
13227
|
const sanitizersAtTarget = sanitizersByLine.get(toLine);
|
|
@@ -10493,9 +10493,16 @@ var DEFAULT_SINKS = [
|
|
|
10493
10493
|
{ method: "println", class: "ServletOutputStream", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [0] },
|
|
10494
10494
|
// XSS in error messages (CWE-81)
|
|
10495
10495
|
{ method: "sendError", class: "HttpServletResponse", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
10496
|
-
// Response header injection
|
|
10497
|
-
|
|
10498
|
-
|
|
10496
|
+
// Response header injection — re-categorised from `xss` to `crlf`
|
|
10497
|
+
// (CWE-113) in Sprint 6 of #86. Header injection is HTTP response
|
|
10498
|
+
// splitting / cache-poisoning / cookie forging; reflected XSS via header
|
|
10499
|
+
// reflection remains a downstream concern of body-writing sinks.
|
|
10500
|
+
{ method: "setHeader", class: "HttpServletResponse", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1] },
|
|
10501
|
+
{ method: "addHeader", class: "HttpServletResponse", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1] },
|
|
10502
|
+
// Note: `sendRedirect` is primarily classified as `ssrf` / open-redirect
|
|
10503
|
+
// (CWE-601) further down — see entry near line 1195. CRLF via Location
|
|
10504
|
+
// header is a secondary concern; keeping the canonical SSRF entry avoids
|
|
10505
|
+
// double-emission that would mask the open-redirect chain.
|
|
10499
10506
|
{ method: "setContentType", class: "HttpServletResponse", type: "xss", cwe: "CWE-79", severity: "medium", arg_positions: [0] },
|
|
10500
10507
|
// JSP output
|
|
10501
10508
|
{ method: "setAttribute", class: "PageContext", type: "xss", cwe: "CWE-79", severity: "high", arg_positions: [1] },
|
|
@@ -11464,7 +11471,33 @@ var DEFAULT_SINKS = [
|
|
|
11464
11471
|
{ method: "Sprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
11465
11472
|
{ method: "Printf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
11466
11473
|
{ method: "Errorf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [0], languages: ["go"] },
|
|
11467
|
-
{ method: "Fprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [1], languages: ["go"] }
|
|
11474
|
+
{ method: "Fprintf", class: "fmt", type: "format_string", cwe: "CWE-134", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11475
|
+
// CRLF / HTTP response splitting (CWE-113) — Sprint 6, #86.
|
|
11476
|
+
// Node.js / Express response header / cookie sinks. The header *name* (arg 0)
|
|
11477
|
+
// is also CRLF-sensitive but is almost always a string literal; we model
|
|
11478
|
+
// arg 1 (the value) as the primary sink.
|
|
11479
|
+
{ method: "setHeader", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["javascript", "typescript"] },
|
|
11480
|
+
{ method: "writeHead", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [2], languages: ["javascript", "typescript"] },
|
|
11481
|
+
// Express: res.cookie(name, value, options) — value is CRLF-sensitive.
|
|
11482
|
+
{ method: "cookie", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["javascript", "typescript"] },
|
|
11483
|
+
// Express: res.location(url) and res.redirect(url) — Location header.
|
|
11484
|
+
{ method: "location", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11485
|
+
{ method: "redirect", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [0], languages: ["javascript", "typescript"] },
|
|
11486
|
+
// Go net/http: w.Header().Set(k, v) / Add(k, v) — first arg is the value
|
|
11487
|
+
// (Header is a map; the actual `value` is arg 1 of the call). We flag the
|
|
11488
|
+
// value position so a tainted variable is detected.
|
|
11489
|
+
{ method: "Set", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11490
|
+
{ method: "Add", class: "Header", type: "crlf", cwe: "CWE-113", severity: "medium", arg_positions: [1], languages: ["go"] },
|
|
11491
|
+
// Mass-assignment (CWE-915) — Sprint 6, #86.
|
|
11492
|
+
// JS Object.assign(target, ...sources) — sources are arg 1..N, and if any
|
|
11493
|
+
// source is request-tainted, every key gets written onto the target. We
|
|
11494
|
+
// flag the source positions; the analyzer only needs one tainted to fire.
|
|
11495
|
+
{ method: "assign", class: "Object", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11496
|
+
// Lodash bulk-merge helpers behave identically.
|
|
11497
|
+
{ method: "merge", class: "_", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11498
|
+
{ method: "extend", class: "_", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] },
|
|
11499
|
+
// jQuery $.extend(target, source) (legacy).
|
|
11500
|
+
{ method: "extend", class: "$", type: "mass_assignment", cwe: "CWE-915", severity: "high", arg_positions: [1, 2, 3], languages: ["javascript", "typescript"] }
|
|
11468
11501
|
];
|
|
11469
11502
|
var DEFAULT_SANITIZERS = [
|
|
11470
11503
|
// SQL Injection - proper parameter binding sanitizes input
|
|
@@ -13120,7 +13153,9 @@ var KNOWN_SINK_TYPES = /* @__PURE__ */ new Set([
|
|
|
13120
13153
|
"code_injection",
|
|
13121
13154
|
"mybatis_mapper_call",
|
|
13122
13155
|
"redos",
|
|
13123
|
-
"format_string"
|
|
13156
|
+
"format_string",
|
|
13157
|
+
"crlf",
|
|
13158
|
+
"mass_assignment"
|
|
13124
13159
|
]);
|
|
13125
13160
|
function checkSanitized(_fromLine, toLine, sinkType, sanitizersByLine) {
|
|
13126
13161
|
const sanitizersAtTarget = sanitizersByLine.get(toLine);
|
package/dist/types/index.d.ts
CHANGED
|
@@ -158,7 +158,7 @@ export interface TaintFlowStep {
|
|
|
158
158
|
type: 'source' | 'assignment' | 'use' | 'return' | 'field' | 'sink';
|
|
159
159
|
}
|
|
160
160
|
export type SourceType = "http_param" | "http_body" | "http_header" | "http_cookie" | "http_path" | "http_query" | "io_input" | "env_input" | "db_input" | "network_input" | "file_input" | "dom_input" | "config_param" | "interprocedural_param" | "plugin_param" | "constructor_field";
|
|
161
|
-
export type SinkType = "sql_injection" | "nosql_injection" | "command_injection" | "path_traversal" | "xss" | "xxe" | "deserialization" | "ldap_injection" | "xpath_injection" | "ssrf" | "open_redirect" | "code_injection" | "log_injection" | "redos" | "format_string" | "mybatis_mapper_call" | "weak_random" | "weak_hash" | "weak_crypto" | "insecure_cookie" | "trust_boundary" | "external_taint_escape";
|
|
161
|
+
export type SinkType = "sql_injection" | "nosql_injection" | "command_injection" | "path_traversal" | "xss" | "xxe" | "deserialization" | "ldap_injection" | "xpath_injection" | "ssrf" | "open_redirect" | "code_injection" | "log_injection" | "redos" | "format_string" | "crlf" | "mass_assignment" | "mybatis_mapper_call" | "weak_random" | "weak_hash" | "weak_crypto" | "insecure_cookie" | "trust_boundary" | "external_taint_escape";
|
|
162
162
|
export type Severity = "critical" | "high" | "medium" | "low";
|
|
163
163
|
export interface TaintSource {
|
|
164
164
|
type: SourceType;
|