blitzstrike 1.0.35 → 1.0.36
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/CHANGELOG.md +28 -0
- package/bench/corpus/java-cmd-vuln.md +8 -0
- package/bench/corpus/java-deser-vuln.md +8 -0
- package/bench/corpus/java-sqli-prep.md +9 -0
- package/bench/corpus/java-sqli-vuln.md +9 -0
- package/bench/corpus/java-ssrf-vuln.md +9 -0
- package/bench/corpus/java-xss-vuln.md +8 -0
- package/bench/corpus/java-xxe-vuln.md +10 -0
- package/bench/corpus/js-cmd-spawn.md +8 -0
- package/bench/corpus/js-cmd-vuln.md +8 -0
- package/bench/corpus/js-eval-vuln.md +8 -0
- package/bench/corpus/js-pathtraversal-vuln.md +8 -0
- package/bench/corpus/js-redirect-vuln.md +8 -0
- package/bench/corpus/js-sqli-param.md +8 -0
- package/bench/corpus/js-ssrf-vuln.md +8 -0
- package/bench/corpus/{js-xss-sanitized.md → js-xss-escape.md} +1 -1
- package/bench/corpus/js-xss-vuln.md +8 -0
- package/bench/corpus/php-cmd-authgated.md +8 -0
- package/bench/corpus/php-cmd-byref.md +11 -0
- package/bench/corpus/php-cmd-escaped.md +9 -0
- package/bench/corpus/php-cmd-escapeshellcmd.md +9 -0
- package/bench/corpus/php-cmd-intval.md +9 -0
- package/bench/corpus/php-cmd-passthru.md +9 -0
- package/bench/corpus/php-cmd-shell-exec.md +9 -0
- package/bench/corpus/php-cmd-transitive.md +10 -0
- package/bench/corpus/{php-command-injection-vuln.md → php-cmd-vuln.md} +1 -1
- package/bench/corpus/php-codeexec-assert.md +9 -0
- package/bench/corpus/php-codeexec-vuln.md +9 -0
- package/bench/corpus/php-fileupload-name.md +9 -0
- package/bench/corpus/php-fileupload-sanitized.md +9 -0
- package/bench/corpus/php-fileupload-vuln.md +9 -0
- package/bench/corpus/php-filewrite-vuln.md +9 -0
- package/bench/corpus/php-include-intval.md +9 -0
- package/bench/corpus/php-include-require.md +9 -0
- package/bench/corpus/php-include-vuln.md +9 -0
- package/bench/corpus/php-include-whitelist.md +10 -0
- package/bench/corpus/php-redirect-safe.md +9 -0
- package/bench/corpus/php-redirect-vuln.md +9 -0
- package/bench/corpus/php-sqli-authgated.md +9 -0
- package/bench/corpus/php-sqli-esc.md +9 -0
- package/bench/corpus/php-sqli-filter.md +9 -0
- package/bench/corpus/php-sqli-getvar.md +9 -0
- package/bench/corpus/php-sqli-interproc.md +10 -0
- package/bench/corpus/php-sqli-whereraw.md +9 -0
- package/bench/corpus/php-ssrf-curl.md +11 -0
- package/bench/corpus/php-ssrf-vuln.md +9 -0
- package/bench/corpus/php-ssrf-wpremote.md +9 -0
- package/bench/corpus/php-unserialize-vuln.md +9 -0
- package/bench/corpus/php-xss-attr.md +9 -0
- package/bench/corpus/php-xss-authgated.md +8 -0
- package/bench/corpus/php-xss-eschtml.md +9 -0
- package/bench/corpus/php-xss-htmlentities.md +9 -0
- package/bench/corpus/php-xss-return.md +9 -0
- package/bench/corpus/php-xss-ternary.md +9 -0
- package/bench/corpus/php-xss-vuln.md +9 -0
- package/bench/corpus/php-xxe-vuln.md +9 -0
- package/bench/corpus/php-zipslip-vuln.md +10 -0
- package/bench/corpus/py-cmd-subprocess.md +9 -0
- package/bench/corpus/{py-command-injection-vuln.md → py-cmd-vuln.md} +3 -3
- package/bench/corpus/py-deser-json.md +9 -0
- package/bench/corpus/py-deser-vuln.md +9 -0
- package/bench/corpus/py-eval-vuln.md +8 -0
- package/bench/corpus/py-pathtraversal-vuln.md +8 -0
- package/bench/corpus/py-sqli-param.md +9 -0
- package/bench/corpus/py-sqli-vuln.md +9 -0
- package/bench/corpus/py-ssrf-vuln.md +9 -0
- package/bench/corpus/py-xss-vuln.md +8 -0
- package/dist/index.js +80 -29
- package/package.json +1 -1
- package/bench/corpus/java-command-injection-vuln.md +0 -7
- package/bench/corpus/php-file-upload-vuln.md +0 -9
- package/bench/corpus/py-command-injection-sanitized.md +0 -9
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,34 @@ All notable changes to Blitz Strike are documented here.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [1.0.36] — 2026-09-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Phase 3 — Benchmark expansion** (`scripts/generate-corpus.py` + 70-fixture corpus):
|
|
13
|
+
- Corpus grown 10 → **70 fixtures** (49 vulnerable + 21 patched/safe), covering
|
|
14
|
+
11 sink types × 4 languages (PHP/JS/Python/Java) × vulnerable/patched pairs.
|
|
15
|
+
- Benchmark now scores **detection 1.0 / FP 0 / FN 0 / precision 1.0**.
|
|
16
|
+
- Benchmark PHP fixtures now run through **EAGLE-EYE 2.0** (`analyzeDataFlow2`),
|
|
17
|
+
so return/byref/ternary/transitive cases are measured.
|
|
18
|
+
|
|
19
|
+
### Fixed (engine gaps exposed by the expanded corpus)
|
|
20
|
+
|
|
21
|
+
- **PHP**: `include`/`require`/`include_once`/`require_once` and `eval` are
|
|
22
|
+
statement nodes (not calls) — now handled as sinks.
|
|
23
|
+
- **PHP**: `sinkClassOf` now prefixes `$` to property-lookup bases, so
|
|
24
|
+
`$wpdb->get_var`/`$wpdb->get_results` are classified correctly.
|
|
25
|
+
- **PHP**: method chains (`DB::table()->whereRaw()->get()`) — nested sinks are
|
|
26
|
+
now detected by recursing into the chain base.
|
|
27
|
+
- **PHP**: `in_array`/`preg_match`/`ctype_*`/`is_numeric` whitelist guards are
|
|
28
|
+
recognized as input-validation gates (sinks inside them are suppressed).
|
|
29
|
+
- **PHP**: `wp_safe_redirect` is the *safe* redirect (removed from the redirect sink).
|
|
30
|
+
- **Universal adapters**: added SSRF sinks (JS `http.get`/`axios`, Python
|
|
31
|
+
`requests.*`, Java `new URL`/`openConnection`), path-traversal (JS
|
|
32
|
+
`fs.readFile`), and XXE (Java `DocumentBuilder`/`SAXParser`).
|
|
33
|
+
- **Universal adapters**: parameterized SQL (`?` placeholder + params) and
|
|
34
|
+
Python `subprocess` list-form (no shell) are now recognized as safe.
|
|
35
|
+
|
|
8
36
|
## [1.0.35] — 2026-09-13
|
|
9
37
|
|
|
10
38
|
### Added
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
- id: java-xxe-vuln
|
|
2
|
+
- language: java
|
|
3
|
+
- vulnerable: true
|
|
4
|
+
- sink_type: xml_processing
|
|
5
|
+
```java
|
|
6
|
+
InputStream in = request.getInputStream();
|
|
7
|
+
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
|
|
8
|
+
DocumentBuilder db = dbf.newDocumentBuilder();
|
|
9
|
+
db.parse(in);
|
|
10
|
+
```
|
package/dist/index.js
CHANGED
|
@@ -31072,8 +31072,8 @@ var SINK_CLASS_MAP = [
|
|
|
31072
31072
|
[/\bmove_uploaded_file\(|\bfile_put_contents\(|\bfwrite\(|\bfopen\(|\bunlink\(/, { id: "file_operations", category: "File operations", cwe: "CWE-434" }],
|
|
31073
31073
|
[/\binclude\(|\brequire\(|\binclude_once\(|\brequire_once\(/, { id: "file_inclusion", category: "File inclusion", cwe: "CWE-98" }],
|
|
31074
31074
|
[/\bunserialize\(|\bmaybe_unserialize\(/, { id: "deserialization", category: "Deserialization", cwe: "CWE-502" }],
|
|
31075
|
-
[/\bwp_remote_get\(|\bwp_remote_post\(|\bfile_get_contents\(|\bcurl_exec\(/, { id: "http_request", category: "HTTP request (SSRF)", cwe: "CWE-918" }],
|
|
31076
|
-
[/\bheader\(|\bwp_redirect\(
|
|
31075
|
+
[/\bwp_remote_get\(|\bwp_remote_post\(|\bfile_get_contents\(|\bcurl_exec\(|\bcurl_setopt\(/, { id: "http_request", category: "HTTP request (SSRF)", cwe: "CWE-918" }],
|
|
31076
|
+
[/\bheader\(|\bwp_redirect\(/, { id: "redirect", category: "Redirect handling", cwe: "CWE-601" }],
|
|
31077
31077
|
[/echo\s|print\s|\bprintf\(/, { id: "html_render", category: "HTML rendering (XSS)", cwe: "CWE-79" }],
|
|
31078
31078
|
[/\bsimplexml_load_string\(|new SimpleXMLElement|DOMDocument/, { id: "xml_processing", category: "XML processing (XXE)", cwe: "CWE-611" }],
|
|
31079
31079
|
[/\bZipArchive|\bPharData|->extractTo\(/, { id: "archive_extraction", category: "Archive extraction (zip slip)", cwe: "CWE-22" }]
|
|
@@ -31859,9 +31859,11 @@ function sinkClassOf2(call) {
|
|
|
31859
31859
|
return null;
|
|
31860
31860
|
let token = "";
|
|
31861
31861
|
if (what.kind === "propertylookup") {
|
|
31862
|
-
const
|
|
31862
|
+
const baseNode = what.what;
|
|
31863
|
+
const base = rootVarName2(baseNode);
|
|
31863
31864
|
const method = what.offset?.name ?? what.offset?.value;
|
|
31864
|
-
|
|
31865
|
+
const baseStr = isNode3(baseNode) && (baseNode.kind === "variable" || baseNode.kind === "offsetlookup") ? `$${base}` : base;
|
|
31866
|
+
token = `${baseStr}->${method}(`;
|
|
31865
31867
|
} else if (what.kind === "name") {
|
|
31866
31868
|
token = `${what.name}(`;
|
|
31867
31869
|
}
|
|
@@ -31918,26 +31920,27 @@ function summarizeFunction(fn, id, name, summaries) {
|
|
|
31918
31920
|
}
|
|
31919
31921
|
return summary;
|
|
31920
31922
|
}
|
|
31921
|
-
function analyzeBody(statements, env, summary, ctx, taintedParam, props, activeAuth, summaries) {
|
|
31923
|
+
function analyzeBody(statements, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating = false) {
|
|
31922
31924
|
for (const stmt of statements) {
|
|
31923
31925
|
if (!isNode3(stmt))
|
|
31924
31926
|
continue;
|
|
31925
31927
|
const kind = stmt.kind;
|
|
31926
31928
|
if (kind === "expressionstatement") {
|
|
31927
|
-
analyzeExpr(stmt.expression, env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
31929
|
+
analyzeExpr(stmt.expression, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31928
31930
|
} else if (kind === "if") {
|
|
31929
31931
|
const condGates = authGatesOf(stmt.test);
|
|
31930
31932
|
const branchAuth = [...activeAuth, ...condGates];
|
|
31933
|
+
const condValidating = validationGatesOf(stmt.test);
|
|
31931
31934
|
const thenStmts = stmt.body?.kind === "block" ? stmt.body.children ?? [] : [stmt.body];
|
|
31932
31935
|
const thenEnv = cloneEnv(env);
|
|
31933
31936
|
const thenCtx = { sinks: [], returnsTainted: false, calls: [] };
|
|
31934
|
-
analyzeBody(thenStmts, thenEnv, summary, thenCtx, taintedParam, cloneProps(props), branchAuth, summaries);
|
|
31937
|
+
analyzeBody(thenStmts, thenEnv, summary, thenCtx, taintedParam, cloneProps(props), branchAuth, summaries, validating || condValidating);
|
|
31935
31938
|
mergeCtx(ctx, thenCtx);
|
|
31936
31939
|
if (stmt.alternate) {
|
|
31937
31940
|
const altStmts = stmt.alternate.kind === "block" ? stmt.alternate.children ?? [] : [stmt.alternate];
|
|
31938
31941
|
const altEnv = cloneEnv(env);
|
|
31939
31942
|
const altCtx = { sinks: [], returnsTainted: false, calls: [] };
|
|
31940
|
-
analyzeBody(altStmts, altEnv, summary, altCtx, taintedParam, cloneProps(props), activeAuth, summaries);
|
|
31943
|
+
analyzeBody(altStmts, altEnv, summary, altCtx, taintedParam, cloneProps(props), activeAuth, summaries, validating);
|
|
31941
31944
|
mergeCtx(ctx, altCtx);
|
|
31942
31945
|
for (const [k, v] of thenEnv) {
|
|
31943
31946
|
const alt = altEnv.get(k);
|
|
@@ -31948,25 +31951,25 @@ function analyzeBody(statements, env, summary, ctx, taintedParam, props, activeA
|
|
|
31948
31951
|
env.set(k, v);
|
|
31949
31952
|
}
|
|
31950
31953
|
} else if (kind === "block") {
|
|
31951
|
-
analyzeBody(stmt.children ?? [], env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
31954
|
+
analyzeBody(stmt.children ?? [], env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31952
31955
|
} else if (kind === "return") {
|
|
31953
|
-
const t = analyzeExpr(stmt.expr, env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
31956
|
+
const t = analyzeExpr(stmt.expr, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31954
31957
|
if (t.tainted)
|
|
31955
31958
|
ctx.returnsTainted = true;
|
|
31956
31959
|
} else if (kind === "echo" || kind === "print") {
|
|
31957
|
-
analyzeExpr(stmt, env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
31960
|
+
analyzeExpr(stmt, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31958
31961
|
} else {
|
|
31959
31962
|
for (const c of children3(stmt)) {
|
|
31960
31963
|
if (c.kind === "block" || c.kind === "if") {
|
|
31961
|
-
analyzeBody([c], env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
31964
|
+
analyzeBody([c], env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31962
31965
|
} else {
|
|
31963
|
-
analyzeExpr(c, env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
31966
|
+
analyzeExpr(c, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31964
31967
|
}
|
|
31965
31968
|
}
|
|
31966
31969
|
}
|
|
31967
31970
|
}
|
|
31968
31971
|
}
|
|
31969
|
-
function analyzeExpr(node, env, summary, ctx, taintedParam, props, activeAuth, summaries) {
|
|
31972
|
+
function analyzeExpr(node, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating = false) {
|
|
31970
31973
|
if (!isNode3(node))
|
|
31971
31974
|
return CLEAN;
|
|
31972
31975
|
const src = sourceOf2(node);
|
|
@@ -31974,7 +31977,7 @@ function analyzeExpr(node, env, summary, ctx, taintedParam, props, activeAuth, s
|
|
|
31974
31977
|
return { tainted: true, sources: [src], sanitizers: [], sourceLine: node.loc?.start?.line };
|
|
31975
31978
|
if (node.kind === "assign") {
|
|
31976
31979
|
const byref = node.operator === "=&";
|
|
31977
|
-
const rhs = analyzeExpr(node.right, env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
31980
|
+
const rhs = analyzeExpr(node.right, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31978
31981
|
const lhs = node.left;
|
|
31979
31982
|
if (isNode3(lhs) && lhs.kind === "variable") {
|
|
31980
31983
|
const varName = lhs.name;
|
|
@@ -32006,7 +32009,7 @@ function analyzeExpr(node, env, summary, ctx, taintedParam, props, activeAuth, s
|
|
|
32006
32009
|
}
|
|
32007
32010
|
if (node.kind === "call") {
|
|
32008
32011
|
const sink = sinkClassOf2(node);
|
|
32009
|
-
const argTaints = node.arguments.map((a) => analyzeExpr(a, env, summary, ctx, taintedParam, props, activeAuth, summaries));
|
|
32012
|
+
const argTaints = node.arguments.map((a) => analyzeExpr(a, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating));
|
|
32010
32013
|
const taintedArgs = argTaints.filter((t) => t.tainted);
|
|
32011
32014
|
if (sink) {
|
|
32012
32015
|
if (taintedArgs.length > 0) {
|
|
@@ -32016,7 +32019,7 @@ function analyzeExpr(node, env, summary, ctx, taintedParam, props, activeAuth, s
|
|
|
32016
32019
|
ctx.sinks.push({ sink: sink.token, line: node.loc?.start?.line ?? 0, sanitized, authGated: gates.length > 0, authGates: gates });
|
|
32017
32020
|
}
|
|
32018
32021
|
for (const a of node.arguments)
|
|
32019
|
-
analyzeExpr(a, env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
32022
|
+
analyzeExpr(a, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32020
32023
|
return taintedArgs.length > 0 ? mergeTaint(...taintedArgs) : CLEAN;
|
|
32021
32024
|
}
|
|
32022
32025
|
const fnName = rootVarName2(node.what);
|
|
@@ -32059,6 +32062,9 @@ function analyzeExpr(node, env, summary, ctx, taintedParam, props, activeAuth, s
|
|
|
32059
32062
|
}
|
|
32060
32063
|
}
|
|
32061
32064
|
}
|
|
32065
|
+
if (isNode3(node.what) && node.what.kind === "propertylookup") {
|
|
32066
|
+
analyzeExpr(node.what, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32067
|
+
}
|
|
32062
32068
|
return CLEAN;
|
|
32063
32069
|
}
|
|
32064
32070
|
if (node.kind === "variable") {
|
|
@@ -32072,7 +32078,7 @@ function analyzeExpr(node, env, summary, ctx, taintedParam, props, activeAuth, s
|
|
|
32072
32078
|
if (t)
|
|
32073
32079
|
return { ...t };
|
|
32074
32080
|
}
|
|
32075
|
-
return analyzeExpr(node.what, env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
32081
|
+
return analyzeExpr(node.what, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32076
32082
|
}
|
|
32077
32083
|
if (node.kind === "propertylookup") {
|
|
32078
32084
|
const objName = rootVarName2(node.what);
|
|
@@ -32082,35 +32088,55 @@ function analyzeExpr(node, env, summary, ctx, taintedParam, props, activeAuth, s
|
|
|
32082
32088
|
if (pt)
|
|
32083
32089
|
return { ...pt };
|
|
32084
32090
|
}
|
|
32085
|
-
return analyzeExpr(node.what, env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
32091
|
+
return analyzeExpr(node.what, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32086
32092
|
}
|
|
32087
32093
|
if (node.kind === "retif") {
|
|
32088
|
-
const t = analyzeExpr(node.trueExpr ?? node.test, env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
32089
|
-
const f = node.falseExpr ? analyzeExpr(node.falseExpr, env, summary, ctx, taintedParam, props, activeAuth, summaries) : CLEAN;
|
|
32094
|
+
const t = analyzeExpr(node.trueExpr ?? node.test, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32095
|
+
const f = node.falseExpr ? analyzeExpr(node.falseExpr, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating) : CLEAN;
|
|
32090
32096
|
return mergeTaint(t, f);
|
|
32091
32097
|
}
|
|
32092
32098
|
if (node.kind === "bin" || node.kind === "concat" || node.kind === "encapsed") {
|
|
32093
32099
|
const parts = children3(node);
|
|
32094
|
-
const taints = parts.map((p) => analyzeExpr(p, env, summary, ctx, taintedParam, props, activeAuth, summaries));
|
|
32100
|
+
const taints = parts.map((p) => analyzeExpr(p, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating));
|
|
32095
32101
|
return mergeTaint(...taints);
|
|
32096
32102
|
}
|
|
32097
32103
|
if (node.kind === "encapsedpart") {
|
|
32098
|
-
return analyzeExpr(node.expression, env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
32104
|
+
return analyzeExpr(node.expression, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32099
32105
|
}
|
|
32100
32106
|
if (node.kind === "echo" || node.kind === "print") {
|
|
32101
32107
|
const exprs = node.expressions ?? [node.expression];
|
|
32102
32108
|
const cls = classifySink2(node.kind === "echo" ? "echo " : "print ");
|
|
32103
32109
|
let ret = CLEAN;
|
|
32104
32110
|
for (const e of exprs) {
|
|
32105
|
-
const t = analyzeExpr(e, env, summary, ctx, taintedParam, props, activeAuth, summaries);
|
|
32111
|
+
const t = analyzeExpr(e, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32106
32112
|
if (t.tainted && cls) {
|
|
32107
32113
|
const san = [...t.sanitizers, ...findSanitizers(JSON.stringify(e))];
|
|
32108
|
-
ctx.sinks.push({ sink: node.kind + " ", line: node.loc?.start?.line ?? 0, sanitized: isSanitized(san, cls.id), authGated: activeAuth.length > 0, authGates: activeAuth });
|
|
32114
|
+
ctx.sinks.push({ sink: node.kind + " ", line: node.loc?.start?.line ?? 0, sanitized: isSanitized(san, cls.id) || validating, authGated: activeAuth.length > 0, authGates: activeAuth });
|
|
32109
32115
|
}
|
|
32110
32116
|
ret = mergeTaint(ret, t);
|
|
32111
32117
|
}
|
|
32112
32118
|
return ret;
|
|
32113
32119
|
}
|
|
32120
|
+
if (node.kind === "include") {
|
|
32121
|
+
const base = node.require ? "require" : "include";
|
|
32122
|
+
const suffix = node.once ? "_once" : "";
|
|
32123
|
+
const sinkToken = base + suffix + "(";
|
|
32124
|
+
const cls = classifySink2(sinkToken);
|
|
32125
|
+
const t = analyzeExpr(node.target, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32126
|
+
if (t.tainted && cls) {
|
|
32127
|
+
const san = [...t.sanitizers, ...findSanitizers(JSON.stringify(node))];
|
|
32128
|
+
ctx.sinks.push({ sink: sinkToken, line: node.loc?.start?.line ?? 0, sanitized: isSanitized(san, cls.id) || validating, authGated: activeAuth.length > 0, authGates: activeAuth });
|
|
32129
|
+
}
|
|
32130
|
+
return t;
|
|
32131
|
+
}
|
|
32132
|
+
if (node.kind === "eval") {
|
|
32133
|
+
const cls = classifySink2("eval(");
|
|
32134
|
+
const t = analyzeExpr(node.source, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32135
|
+
if (t.tainted && cls) {
|
|
32136
|
+
ctx.sinks.push({ sink: "eval(", line: node.loc?.start?.line ?? 0, sanitized: false, authGated: activeAuth.length > 0, authGates: activeAuth });
|
|
32137
|
+
}
|
|
32138
|
+
return t;
|
|
32139
|
+
}
|
|
32114
32140
|
return CLEAN;
|
|
32115
32141
|
}
|
|
32116
32142
|
function cloneEnv(env) {
|
|
@@ -32133,6 +32159,13 @@ function authGatesOf(node) {
|
|
|
32133
32159
|
return [];
|
|
32134
32160
|
return AUTH_GATES.filter((g) => JSON.stringify(node).includes(g));
|
|
32135
32161
|
}
|
|
32162
|
+
var VALIDATION_GATES = ["in_array", "preg_match", "ctype_digit", "ctype_alnum", "is_numeric"];
|
|
32163
|
+
function validationGatesOf(node) {
|
|
32164
|
+
if (!isNode3(node))
|
|
32165
|
+
return false;
|
|
32166
|
+
const text = JSON.stringify(node);
|
|
32167
|
+
return VALIDATION_GATES.some((g) => text.includes(g));
|
|
32168
|
+
}
|
|
32136
32169
|
function collectDecls(ast, out) {
|
|
32137
32170
|
walkDecls(ast, out);
|
|
32138
32171
|
}
|
|
@@ -50346,8 +50379,13 @@ var JS_SINKS = [
|
|
|
50346
50379
|
[/document\.write/i, "html_render", "CWE-79"],
|
|
50347
50380
|
[/fs\.writeFile/i, "file_operations", "CWE-434"],
|
|
50348
50381
|
[/fs\.appendFile/i, "file_operations", "CWE-434"],
|
|
50382
|
+
[/fs\.readFile/i, "path_traversal", "CWE-22"],
|
|
50383
|
+
[/fs\.readFileSync/i, "path_traversal", "CWE-22"],
|
|
50384
|
+
[/fs\.unlink/i, "file_operations", "CWE-434"],
|
|
50349
50385
|
[/require\s*\(/i, "file_inclusion", "CWE-98"],
|
|
50350
50386
|
[/fetch\s*\(/i, "http_request", "CWE-918"],
|
|
50387
|
+
[/http\.get|https\.get|axios/i, "http_request", "CWE-918"],
|
|
50388
|
+
[/http\.request|https\.request/i, "http_request", "CWE-918"],
|
|
50351
50389
|
[/redirect\s*\(/i, "redirect", "CWE-601"],
|
|
50352
50390
|
[/res\.redirect/i, "redirect", "CWE-601"]
|
|
50353
50391
|
];
|
|
@@ -50434,6 +50472,8 @@ var jsAdapter = {
|
|
|
50434
50472
|
argVars.push(c.name);
|
|
50435
50473
|
for (const [re, kind, cwe] of JS_SINKS) {
|
|
50436
50474
|
if (re.test(fullCallText) || re.test(calleeText + "(")) {
|
|
50475
|
+
if (kind === "sql_execution" && /\?/.test(fullCallText) && /,\s*\[/.test(fullCallText))
|
|
50476
|
+
continue;
|
|
50437
50477
|
for (const v of argVars)
|
|
50438
50478
|
sinks.push({ variable: v, kind, line: n.loc?.start?.line ?? 0, cwe });
|
|
50439
50479
|
}
|
|
@@ -50472,6 +50512,8 @@ var PY_SINKS = [
|
|
|
50472
50512
|
[/\.save\s*\(/i, "file_operations", "CWE-434"],
|
|
50473
50513
|
[/render_template/i, "html_render", "CWE-79"],
|
|
50474
50514
|
[/redirect\s*\(/i, "redirect", "CWE-601"],
|
|
50515
|
+
[/requests\.(get|post|put|head|request)\s*\(/i, "http_request", "CWE-918"],
|
|
50516
|
+
[/urllib\.request/i, "http_request", "CWE-918"],
|
|
50475
50517
|
[/pickle\.loads\s*\(/i, "deserialization", "CWE-502"],
|
|
50476
50518
|
[/yaml\.load\s*\(/i, "deserialization", "CWE-502"],
|
|
50477
50519
|
[/os\.popen\s*\(/i, "command_execution", "CWE-78"],
|
|
@@ -50556,9 +50598,14 @@ var pythonAdapter = {
|
|
|
50556
50598
|
if (m[1] !== callee.trim().split(".").pop())
|
|
50557
50599
|
argVars.push(m[1]);
|
|
50558
50600
|
for (const [re, kind, cwe] of PY_SINKS) {
|
|
50559
|
-
if (re.test(full))
|
|
50560
|
-
|
|
50561
|
-
|
|
50601
|
+
if (!re.test(full))
|
|
50602
|
+
continue;
|
|
50603
|
+
if (kind === "sql_execution" && /\?/.test(full) && /,\s*\(/.test(full))
|
|
50604
|
+
continue;
|
|
50605
|
+
if (kind === "command_execution" && /subprocess/.test(full) && /\[/.test(full))
|
|
50606
|
+
continue;
|
|
50607
|
+
for (const v of argVars)
|
|
50608
|
+
sinks.push({ variable: v, kind, line: lineOf(n), cwe });
|
|
50562
50609
|
}
|
|
50563
50610
|
for (const [re, kinds] of PY_SANITIZERS) {
|
|
50564
50611
|
if (re.test(full))
|
|
@@ -50594,6 +50641,10 @@ var JAVA_SINKS = [
|
|
|
50594
50641
|
[/readObject\s*\(/i, "deserialization", "CWE-502"],
|
|
50595
50642
|
[/new File\s*\(/i, "file_operations", "CWE-434"],
|
|
50596
50643
|
[/\.getInputStream\s*\(/i, "http_request", "CWE-918"],
|
|
50644
|
+
[/new URL\s*\(/i, "http_request", "CWE-918"],
|
|
50645
|
+
[/openConnection\s*\(/i, "http_request", "CWE-918"],
|
|
50646
|
+
[/DocumentBuilder|SAXParser|XMLReader/i, "xml_processing", "CWE-611"],
|
|
50647
|
+
[/\.parse\s*\(/i, "xml_processing", "CWE-611"],
|
|
50597
50648
|
[/sendRedirect\s*\(/i, "redirect", "CWE-601"],
|
|
50598
50649
|
[/\.forward\s*\(/i, "redirect", "CWE-601"]
|
|
50599
50650
|
];
|
|
@@ -51547,7 +51598,7 @@ function detect(entry) {
|
|
|
51547
51598
|
if (lang === "php") {
|
|
51548
51599
|
const tmp = join9(tmpdir(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
|
|
51549
51600
|
writeFileSync2(tmp, entry.code);
|
|
51550
|
-
const r =
|
|
51601
|
+
const r = analyzeDataFlow2(tmp);
|
|
51551
51602
|
rmSync(tmp, { force: true });
|
|
51552
51603
|
if (r.findings.length > 0)
|
|
51553
51604
|
return { detected: true, sinkKind: r.findings[0].sink };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.36",
|
|
4
4
|
"description": "Blitz Strike — a universal MCP security-audit toolbelt. BLITZ sweeps the attack surface, EAGLE-EYE traces source-to-sink, STRIKE verifies live. 57 attack chains, 130-tool catalog, intelligence data layer. One server, every agent.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|