blitzstrike 1.0.34 → 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 +55 -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 +812 -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/dist/index.js
CHANGED
|
@@ -14820,7 +14820,7 @@ var require_src = __commonJS(function(exports, module) {
|
|
|
14820
14820
|
});
|
|
14821
14821
|
|
|
14822
14822
|
// src/sync.ts
|
|
14823
|
-
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, readFileSync as
|
|
14823
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync3, readFileSync as readFileSync14, readdirSync as readdirSync5, statSync as statSync3, rmSync as rmSync2 } from "node:fs";
|
|
14824
14824
|
import { join as join10 } from "node:path";
|
|
14825
14825
|
import { homedir as homedir4 } from "node:os";
|
|
14826
14826
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
@@ -14916,7 +14916,7 @@ function manualCopy(src, dst) {
|
|
|
14916
14916
|
if (e.isDirectory())
|
|
14917
14917
|
manualCopy(s, d);
|
|
14918
14918
|
else
|
|
14919
|
-
writeFileSync3(d,
|
|
14919
|
+
writeFileSync3(d, readFileSync14(s));
|
|
14920
14920
|
}
|
|
14921
14921
|
}
|
|
14922
14922
|
var ROOT5, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
|
|
@@ -31067,16 +31067,16 @@ function classifySource(line) {
|
|
|
31067
31067
|
}
|
|
31068
31068
|
var SINK_CLASS_MAP = [
|
|
31069
31069
|
[/->query\(|\$wpdb->query|\$wpdb->get_var|\$wpdb->get_results|->whereRaw|->selectRaw/, { id: "sql_execution", category: "SQL execution", cwe: "CWE-89" }],
|
|
31070
|
-
[
|
|
31071
|
-
[
|
|
31072
|
-
[
|
|
31073
|
-
[
|
|
31074
|
-
[
|
|
31075
|
-
[
|
|
31076
|
-
[
|
|
31077
|
-
[/echo\s|print\s
|
|
31078
|
-
[
|
|
31079
|
-
[
|
|
31070
|
+
[/\beval\(|\bassert\(|\bcreate_function\(|\bcall_user_func\(|\bpreg_replace\(/, { id: "code_execution", category: "Dynamic evaluation / code execution", cwe: "CWE-94" }],
|
|
31071
|
+
[/\bsystem\(|\bexec\(|\bshell_exec\(|\bpassthru\(|\bproc_open\(|\bpopen\(/, { id: "command_execution", category: "Command execution", cwe: "CWE-78" }],
|
|
31072
|
+
[/\bmove_uploaded_file\(|\bfile_put_contents\(|\bfwrite\(|\bfopen\(|\bunlink\(/, { id: "file_operations", category: "File operations", cwe: "CWE-434" }],
|
|
31073
|
+
[/\binclude\(|\brequire\(|\binclude_once\(|\brequire_once\(/, { id: "file_inclusion", category: "File inclusion", cwe: "CWE-98" }],
|
|
31074
|
+
[/\bunserialize\(|\bmaybe_unserialize\(/, { id: "deserialization", category: "Deserialization", cwe: "CWE-502" }],
|
|
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
|
+
[/echo\s|print\s|\bprintf\(/, { id: "html_render", category: "HTML rendering (XSS)", cwe: "CWE-79" }],
|
|
31078
|
+
[/\bsimplexml_load_string\(|new SimpleXMLElement|DOMDocument/, { id: "xml_processing", category: "XML processing (XXE)", cwe: "CWE-611" }],
|
|
31079
|
+
[/\bZipArchive|\bPharData|->extractTo\(/, { id: "archive_extraction", category: "Archive extraction (zip slip)", cwe: "CWE-22" }]
|
|
31080
31080
|
];
|
|
31081
31081
|
function classifySink2(sink) {
|
|
31082
31082
|
for (const [re, cls] of SINK_CLASS_MAP) {
|
|
@@ -31593,6 +31593,739 @@ function taintTree(root, maxFiles = 2000) {
|
|
|
31593
31593
|
return files.map((f) => analyzeTaint(f));
|
|
31594
31594
|
}
|
|
31595
31595
|
|
|
31596
|
+
// src/eagle2.ts
|
|
31597
|
+
var import_php_parser3 = __toESM(require_src(), 1);
|
|
31598
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
31599
|
+
init_scanner();
|
|
31600
|
+
|
|
31601
|
+
// src/callgraph.ts
|
|
31602
|
+
var import_php_parser2 = __toESM(require_src(), 1);
|
|
31603
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
31604
|
+
var parser2 = new import_php_parser2.Engine({
|
|
31605
|
+
parser: { extractDoc: false, php7: true },
|
|
31606
|
+
ast: { withPositions: true, withSource: false }
|
|
31607
|
+
});
|
|
31608
|
+
function isNode2(v) {
|
|
31609
|
+
return !!v && typeof v === "object" && typeof v.kind === "string";
|
|
31610
|
+
}
|
|
31611
|
+
function children2(node) {
|
|
31612
|
+
const out = [];
|
|
31613
|
+
for (const k of Object.keys(node)) {
|
|
31614
|
+
if (k === "loc")
|
|
31615
|
+
continue;
|
|
31616
|
+
const v = node[k];
|
|
31617
|
+
if (Array.isArray(v)) {
|
|
31618
|
+
for (const c of v)
|
|
31619
|
+
if (isNode2(c))
|
|
31620
|
+
out.push(c);
|
|
31621
|
+
} else if (isNode2(v)) {
|
|
31622
|
+
out.push(v);
|
|
31623
|
+
}
|
|
31624
|
+
}
|
|
31625
|
+
return out;
|
|
31626
|
+
}
|
|
31627
|
+
function nameOf(node) {
|
|
31628
|
+
if (!isNode2(node))
|
|
31629
|
+
return null;
|
|
31630
|
+
if (typeof node.name === "string")
|
|
31631
|
+
return node.name;
|
|
31632
|
+
if (isNode2(node.name))
|
|
31633
|
+
return nameOf(node.name);
|
|
31634
|
+
return null;
|
|
31635
|
+
}
|
|
31636
|
+
function nodeId(kind, name, cls) {
|
|
31637
|
+
if (kind === "function")
|
|
31638
|
+
return `func:${name}`;
|
|
31639
|
+
if (kind === "method")
|
|
31640
|
+
return `method:${cls ?? "?"}::${name}`;
|
|
31641
|
+
return "main";
|
|
31642
|
+
}
|
|
31643
|
+
function ensureNode(g, id, name, kind, file, line, cls) {
|
|
31644
|
+
let n = g.nodes.get(id);
|
|
31645
|
+
if (!n) {
|
|
31646
|
+
n = { id, name, kind, class: cls, file, line, callees: new Set, callers: new Set };
|
|
31647
|
+
g.nodes.set(id, n);
|
|
31648
|
+
}
|
|
31649
|
+
return n;
|
|
31650
|
+
}
|
|
31651
|
+
function addEdge(g, caller, callee, name, line, file) {
|
|
31652
|
+
g.edges.push({ caller, callee, name, line, file });
|
|
31653
|
+
const c = g.nodes.get(caller);
|
|
31654
|
+
const t = g.nodes.get(callee);
|
|
31655
|
+
if (c)
|
|
31656
|
+
c.callees.add(callee);
|
|
31657
|
+
if (t)
|
|
31658
|
+
t.callers.add(caller);
|
|
31659
|
+
}
|
|
31660
|
+
function buildCallGraph(path) {
|
|
31661
|
+
const g = { nodes: new Map, edges: [] };
|
|
31662
|
+
let code;
|
|
31663
|
+
try {
|
|
31664
|
+
code = readFileSync11(path, "utf8");
|
|
31665
|
+
} catch {
|
|
31666
|
+
return g;
|
|
31667
|
+
}
|
|
31668
|
+
let ast;
|
|
31669
|
+
try {
|
|
31670
|
+
ast = parser2.parseCode(code, path);
|
|
31671
|
+
} catch {
|
|
31672
|
+
return g;
|
|
31673
|
+
}
|
|
31674
|
+
const mainId = nodeId("main", "main");
|
|
31675
|
+
ensureNode(g, mainId, "<main>", "main", path, 0);
|
|
31676
|
+
const funcs = [];
|
|
31677
|
+
const methods = [];
|
|
31678
|
+
walk(ast, path, (n, ctx) => {
|
|
31679
|
+
if (n.kind === "function") {
|
|
31680
|
+
const name = nameOf(n.name);
|
|
31681
|
+
if (name)
|
|
31682
|
+
funcs.push({ name, node: n });
|
|
31683
|
+
} else if (n.kind === "method" && ctx.className) {
|
|
31684
|
+
const mname = nameOf(n.name);
|
|
31685
|
+
if (mname)
|
|
31686
|
+
methods.push({ cls: ctx.className, name: mname, node: n });
|
|
31687
|
+
}
|
|
31688
|
+
});
|
|
31689
|
+
for (const f of funcs) {
|
|
31690
|
+
const id = nodeId("function", f.name);
|
|
31691
|
+
ensureNode(g, id, f.name, "function", path, f.node.loc?.start?.line ?? 0);
|
|
31692
|
+
}
|
|
31693
|
+
for (const m of methods) {
|
|
31694
|
+
const id = nodeId("method", m.name, m.cls);
|
|
31695
|
+
ensureNode(g, id, m.name, "method", path, m.node.loc?.start?.line ?? 0, m.cls);
|
|
31696
|
+
}
|
|
31697
|
+
walk(ast, path, (n, ctx) => {
|
|
31698
|
+
if (n.kind !== "call")
|
|
31699
|
+
return;
|
|
31700
|
+
const line = n.loc?.start?.line ?? 0;
|
|
31701
|
+
const callerId = ctx.enclosing ?? mainId;
|
|
31702
|
+
resolveCall(g, n, callerId, ctx, path, line);
|
|
31703
|
+
});
|
|
31704
|
+
return g;
|
|
31705
|
+
}
|
|
31706
|
+
function walk(node, file, visit, ctx = {}) {
|
|
31707
|
+
if (!isNode2(node))
|
|
31708
|
+
return;
|
|
31709
|
+
let childCtx = ctx;
|
|
31710
|
+
if (node.kind === "class") {
|
|
31711
|
+
childCtx = { ...ctx, className: nameOf(node.name) ?? undefined };
|
|
31712
|
+
} else if (node.kind === "function") {
|
|
31713
|
+
const name = nameOf(node.name);
|
|
31714
|
+
childCtx = { ...ctx, enclosing: name ? nodeId("function", name) : ctx.enclosing };
|
|
31715
|
+
} else if (node.kind === "method") {
|
|
31716
|
+
const name = nameOf(node.name);
|
|
31717
|
+
childCtx = { ...ctx, enclosing: name ? nodeId("method", name, ctx.className) : ctx.enclosing };
|
|
31718
|
+
}
|
|
31719
|
+
visit(node, ctx);
|
|
31720
|
+
for (const c of children2(node))
|
|
31721
|
+
walk(c, file, visit, childCtx);
|
|
31722
|
+
}
|
|
31723
|
+
function resolveCall(g, call, callerId, ctx, file, line) {
|
|
31724
|
+
const what = call.what;
|
|
31725
|
+
if (!isNode2(what))
|
|
31726
|
+
return;
|
|
31727
|
+
if (what.kind === "name") {
|
|
31728
|
+
const fn = nameOf(what);
|
|
31729
|
+
if (!fn)
|
|
31730
|
+
return;
|
|
31731
|
+
const id = nodeId("function", fn);
|
|
31732
|
+
if (g.nodes.has(id)) {
|
|
31733
|
+
addEdge(g, callerId, id, fn, line, file);
|
|
31734
|
+
} else {
|
|
31735
|
+
addEdge(g, callerId, `unknown:${fn}`, fn, line, file);
|
|
31736
|
+
}
|
|
31737
|
+
return;
|
|
31738
|
+
}
|
|
31739
|
+
if (what.kind === "staticlookup") {
|
|
31740
|
+
const base = what.what;
|
|
31741
|
+
const baseName = nameOf(base) ?? (isNode2(base) && base.kind === "variable" ? base.name : null);
|
|
31742
|
+
const method = nameOf(what.offset);
|
|
31743
|
+
if (!method)
|
|
31744
|
+
return;
|
|
31745
|
+
let cls = baseName;
|
|
31746
|
+
if (cls === "self" || cls === "static")
|
|
31747
|
+
cls = ctx.className ?? "?";
|
|
31748
|
+
if (cls && cls !== "?") {
|
|
31749
|
+
const id = nodeId("method", method, cls);
|
|
31750
|
+
if (g.nodes.has(id))
|
|
31751
|
+
addEdge(g, callerId, id, method, line, file);
|
|
31752
|
+
}
|
|
31753
|
+
return;
|
|
31754
|
+
}
|
|
31755
|
+
if (what.kind === "propertylookup") {
|
|
31756
|
+
const base = what.what;
|
|
31757
|
+
const method = nameOf(what.offset);
|
|
31758
|
+
if (!method)
|
|
31759
|
+
return;
|
|
31760
|
+
if (isNode2(base) && base.kind === "variable" && base.name === "this") {
|
|
31761
|
+
if (ctx.className) {
|
|
31762
|
+
const id = nodeId("method", method, ctx.className);
|
|
31763
|
+
if (g.nodes.has(id))
|
|
31764
|
+
addEdge(g, callerId, id, method, line, file);
|
|
31765
|
+
}
|
|
31766
|
+
return;
|
|
31767
|
+
}
|
|
31768
|
+
let matched = false;
|
|
31769
|
+
for (const [id, n] of g.nodes) {
|
|
31770
|
+
if (n.kind === "method" && n.name === method) {
|
|
31771
|
+
addEdge(g, callerId, id, method, line, file);
|
|
31772
|
+
matched = true;
|
|
31773
|
+
}
|
|
31774
|
+
}
|
|
31775
|
+
if (!matched)
|
|
31776
|
+
addEdge(g, callerId, `unknown:${method}`, method, line, file);
|
|
31777
|
+
return;
|
|
31778
|
+
}
|
|
31779
|
+
}
|
|
31780
|
+
|
|
31781
|
+
// src/eagle2.ts
|
|
31782
|
+
var parser3 = new import_php_parser3.Engine({
|
|
31783
|
+
parser: { extractDoc: false, php7: true },
|
|
31784
|
+
ast: { withPositions: true, withSource: false }
|
|
31785
|
+
});
|
|
31786
|
+
function isNode3(v) {
|
|
31787
|
+
return !!v && typeof v === "object" && typeof v.kind === "string";
|
|
31788
|
+
}
|
|
31789
|
+
function children3(node) {
|
|
31790
|
+
const out = [];
|
|
31791
|
+
for (const k of Object.keys(node)) {
|
|
31792
|
+
if (k === "loc")
|
|
31793
|
+
continue;
|
|
31794
|
+
const v = node[k];
|
|
31795
|
+
if (Array.isArray(v)) {
|
|
31796
|
+
for (const c of v)
|
|
31797
|
+
if (isNode3(c))
|
|
31798
|
+
out.push(c);
|
|
31799
|
+
} else if (isNode3(v)) {
|
|
31800
|
+
out.push(v);
|
|
31801
|
+
}
|
|
31802
|
+
}
|
|
31803
|
+
return out;
|
|
31804
|
+
}
|
|
31805
|
+
function findKind2(node, kind, out = []) {
|
|
31806
|
+
if (!isNode3(node))
|
|
31807
|
+
return out;
|
|
31808
|
+
if (node.kind === kind)
|
|
31809
|
+
out.push(node);
|
|
31810
|
+
for (const c of children3(node))
|
|
31811
|
+
findKind2(c, kind, out);
|
|
31812
|
+
return out;
|
|
31813
|
+
}
|
|
31814
|
+
function nameOf2(node) {
|
|
31815
|
+
if (!isNode3(node))
|
|
31816
|
+
return null;
|
|
31817
|
+
if (typeof node.name === "string")
|
|
31818
|
+
return node.name;
|
|
31819
|
+
if (isNode3(node.name))
|
|
31820
|
+
return nameOf2(node.name);
|
|
31821
|
+
return null;
|
|
31822
|
+
}
|
|
31823
|
+
function rootVarName2(node) {
|
|
31824
|
+
if (!isNode3(node))
|
|
31825
|
+
return null;
|
|
31826
|
+
if (node.kind === "variable")
|
|
31827
|
+
return node.name ?? null;
|
|
31828
|
+
if (node.kind === "name")
|
|
31829
|
+
return typeof node.name === "string" ? node.name : node.name?.name ?? null;
|
|
31830
|
+
if (node.kind === "offsetlookup" || node.kind === "propertylookup")
|
|
31831
|
+
return rootVarName2(node.what);
|
|
31832
|
+
return null;
|
|
31833
|
+
}
|
|
31834
|
+
function sourceOf2(node) {
|
|
31835
|
+
if (!isNode3(node))
|
|
31836
|
+
return null;
|
|
31837
|
+
if (node.kind === "offsetlookup" && node.what?.kind === "variable") {
|
|
31838
|
+
const n = node.what.name;
|
|
31839
|
+
if (["_GET", "_POST", "_REQUEST", "_COOKIE", "_FILES", "_SERVER"].includes(n))
|
|
31840
|
+
return "$" + n;
|
|
31841
|
+
}
|
|
31842
|
+
if (node.kind === "variable") {
|
|
31843
|
+
const n = node.name;
|
|
31844
|
+
if (["_GET", "_POST", "_REQUEST", "_COOKIE", "_FILES", "_SERVER"].includes(n))
|
|
31845
|
+
return "$" + n;
|
|
31846
|
+
}
|
|
31847
|
+
if (node.kind === "call") {
|
|
31848
|
+
const n = rootVarName2(node.what);
|
|
31849
|
+
if (n === "getallheaders")
|
|
31850
|
+
return "getallheaders";
|
|
31851
|
+
if (n === "file_get_contents" && JSON.stringify(node).includes("php://input"))
|
|
31852
|
+
return "php://input";
|
|
31853
|
+
}
|
|
31854
|
+
return null;
|
|
31855
|
+
}
|
|
31856
|
+
function sinkClassOf2(call) {
|
|
31857
|
+
const what = call.what;
|
|
31858
|
+
if (!isNode3(what))
|
|
31859
|
+
return null;
|
|
31860
|
+
let token = "";
|
|
31861
|
+
if (what.kind === "propertylookup") {
|
|
31862
|
+
const baseNode = what.what;
|
|
31863
|
+
const base = rootVarName2(baseNode);
|
|
31864
|
+
const method = what.offset?.name ?? what.offset?.value;
|
|
31865
|
+
const baseStr = isNode3(baseNode) && (baseNode.kind === "variable" || baseNode.kind === "offsetlookup") ? `$${base}` : base;
|
|
31866
|
+
token = `${baseStr}->${method}(`;
|
|
31867
|
+
} else if (what.kind === "name") {
|
|
31868
|
+
token = `${what.name}(`;
|
|
31869
|
+
}
|
|
31870
|
+
if (!token)
|
|
31871
|
+
return null;
|
|
31872
|
+
const cls = classifySink2(token) ?? classifySink2(what.name ?? "");
|
|
31873
|
+
if (!cls)
|
|
31874
|
+
return null;
|
|
31875
|
+
return { token, cls };
|
|
31876
|
+
}
|
|
31877
|
+
var CLEAN = { tainted: false, sources: [], sanitizers: [] };
|
|
31878
|
+
function mergeTaint(...states) {
|
|
31879
|
+
const taintedStates = states.filter((s) => s.tainted);
|
|
31880
|
+
if (taintedStates.length === 0)
|
|
31881
|
+
return CLEAN;
|
|
31882
|
+
const sources = [...new Set(taintedStates.flatMap((s) => s.sources))];
|
|
31883
|
+
let sanitizers = taintedStates[0].sanitizers;
|
|
31884
|
+
for (const s of taintedStates.slice(1)) {
|
|
31885
|
+
sanitizers = sanitizers.filter((san) => s.sanitizers.some((x) => x.id === san.id));
|
|
31886
|
+
}
|
|
31887
|
+
return {
|
|
31888
|
+
tainted: true,
|
|
31889
|
+
sources,
|
|
31890
|
+
sanitizers,
|
|
31891
|
+
sourceLine: taintedStates.find((s) => s.sourceLine !== undefined)?.sourceLine
|
|
31892
|
+
};
|
|
31893
|
+
}
|
|
31894
|
+
function summarizeFunction(fn, id, name, summaries) {
|
|
31895
|
+
const params = (fn.arguments ?? []).map((a) => nameOf2(a) ?? "?");
|
|
31896
|
+
const byrefParams = new Set;
|
|
31897
|
+
(fn.arguments ?? []).forEach((a, i) => {
|
|
31898
|
+
if (a.byref === true)
|
|
31899
|
+
byrefParams.add(params[i]);
|
|
31900
|
+
});
|
|
31901
|
+
const summary = { id, name, params, byrefParams, paramSinks: new Map, paramReturns: new Set, paramCalls: new Map, paramWrites: new Map };
|
|
31902
|
+
const bodyStmts = fn.body?.kind === "block" ? fn.body.children ?? [] : [fn.body];
|
|
31903
|
+
for (const p of params) {
|
|
31904
|
+
const env = new Map;
|
|
31905
|
+
for (const q of params)
|
|
31906
|
+
env.set(q, CLEAN);
|
|
31907
|
+
env.set(p, { tainted: true, sources: [`arg:${p}`], sanitizers: [] });
|
|
31908
|
+
const ctx = { sinks: [], returnsTainted: false, calls: [] };
|
|
31909
|
+
analyzeBody(bodyStmts, env, summary, ctx, p, new Map, [], summaries);
|
|
31910
|
+
summary.paramSinks.set(p, ctx.sinks);
|
|
31911
|
+
if (ctx.returnsTainted)
|
|
31912
|
+
summary.paramReturns.add(p);
|
|
31913
|
+
summary.paramCalls.set(p, ctx.calls);
|
|
31914
|
+
if (byrefParams.has(p)) {
|
|
31915
|
+
const finalTaint = env.get(p);
|
|
31916
|
+
const written = finalTaint?.sources.filter((s) => !s.startsWith("arg:")) ?? [];
|
|
31917
|
+
if (written.length > 0)
|
|
31918
|
+
summary.paramWrites.set(p, written);
|
|
31919
|
+
}
|
|
31920
|
+
}
|
|
31921
|
+
return summary;
|
|
31922
|
+
}
|
|
31923
|
+
function analyzeBody(statements, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating = false) {
|
|
31924
|
+
for (const stmt of statements) {
|
|
31925
|
+
if (!isNode3(stmt))
|
|
31926
|
+
continue;
|
|
31927
|
+
const kind = stmt.kind;
|
|
31928
|
+
if (kind === "expressionstatement") {
|
|
31929
|
+
analyzeExpr(stmt.expression, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31930
|
+
} else if (kind === "if") {
|
|
31931
|
+
const condGates = authGatesOf(stmt.test);
|
|
31932
|
+
const branchAuth = [...activeAuth, ...condGates];
|
|
31933
|
+
const condValidating = validationGatesOf(stmt.test);
|
|
31934
|
+
const thenStmts = stmt.body?.kind === "block" ? stmt.body.children ?? [] : [stmt.body];
|
|
31935
|
+
const thenEnv = cloneEnv(env);
|
|
31936
|
+
const thenCtx = { sinks: [], returnsTainted: false, calls: [] };
|
|
31937
|
+
analyzeBody(thenStmts, thenEnv, summary, thenCtx, taintedParam, cloneProps(props), branchAuth, summaries, validating || condValidating);
|
|
31938
|
+
mergeCtx(ctx, thenCtx);
|
|
31939
|
+
if (stmt.alternate) {
|
|
31940
|
+
const altStmts = stmt.alternate.kind === "block" ? stmt.alternate.children ?? [] : [stmt.alternate];
|
|
31941
|
+
const altEnv = cloneEnv(env);
|
|
31942
|
+
const altCtx = { sinks: [], returnsTainted: false, calls: [] };
|
|
31943
|
+
analyzeBody(altStmts, altEnv, summary, altCtx, taintedParam, cloneProps(props), activeAuth, summaries, validating);
|
|
31944
|
+
mergeCtx(ctx, altCtx);
|
|
31945
|
+
for (const [k, v] of thenEnv) {
|
|
31946
|
+
const alt = altEnv.get(k);
|
|
31947
|
+
env.set(k, alt ? mergeTaint(v, alt) : v);
|
|
31948
|
+
}
|
|
31949
|
+
} else {
|
|
31950
|
+
for (const [k, v] of thenEnv)
|
|
31951
|
+
env.set(k, v);
|
|
31952
|
+
}
|
|
31953
|
+
} else if (kind === "block") {
|
|
31954
|
+
analyzeBody(stmt.children ?? [], env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31955
|
+
} else if (kind === "return") {
|
|
31956
|
+
const t = analyzeExpr(stmt.expr, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31957
|
+
if (t.tainted)
|
|
31958
|
+
ctx.returnsTainted = true;
|
|
31959
|
+
} else if (kind === "echo" || kind === "print") {
|
|
31960
|
+
analyzeExpr(stmt, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31961
|
+
} else {
|
|
31962
|
+
for (const c of children3(stmt)) {
|
|
31963
|
+
if (c.kind === "block" || c.kind === "if") {
|
|
31964
|
+
analyzeBody([c], env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31965
|
+
} else {
|
|
31966
|
+
analyzeExpr(c, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31967
|
+
}
|
|
31968
|
+
}
|
|
31969
|
+
}
|
|
31970
|
+
}
|
|
31971
|
+
}
|
|
31972
|
+
function analyzeExpr(node, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating = false) {
|
|
31973
|
+
if (!isNode3(node))
|
|
31974
|
+
return CLEAN;
|
|
31975
|
+
const src = sourceOf2(node);
|
|
31976
|
+
if (src)
|
|
31977
|
+
return { tainted: true, sources: [src], sanitizers: [], sourceLine: node.loc?.start?.line };
|
|
31978
|
+
if (node.kind === "assign") {
|
|
31979
|
+
const byref = node.operator === "=&";
|
|
31980
|
+
const rhs = analyzeExpr(node.right, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
31981
|
+
const lhs = node.left;
|
|
31982
|
+
if (isNode3(lhs) && lhs.kind === "variable") {
|
|
31983
|
+
const varName = lhs.name;
|
|
31984
|
+
if (byref) {
|
|
31985
|
+
const refVar = rootVarName2(node.right);
|
|
31986
|
+
if (refVar) {
|
|
31987
|
+
const refTaint = env.get(refVar) ?? rhs;
|
|
31988
|
+
env.set(varName, refTaint);
|
|
31989
|
+
env.set(refVar, refTaint);
|
|
31990
|
+
} else {
|
|
31991
|
+
env.set(varName, rhs);
|
|
31992
|
+
}
|
|
31993
|
+
} else {
|
|
31994
|
+
env.set(varName, rhs);
|
|
31995
|
+
}
|
|
31996
|
+
} else if (isNode3(lhs) && lhs.kind === "propertylookup") {
|
|
31997
|
+
const objName = rootVarName2(lhs.what);
|
|
31998
|
+
const propName = lhs.offset?.name ?? "?";
|
|
31999
|
+
if (objName) {
|
|
32000
|
+
let oprops = props.get(objName);
|
|
32001
|
+
if (!oprops) {
|
|
32002
|
+
oprops = new Map;
|
|
32003
|
+
props.set(objName, oprops);
|
|
32004
|
+
}
|
|
32005
|
+
oprops.set(propName, rhs);
|
|
32006
|
+
}
|
|
32007
|
+
}
|
|
32008
|
+
return rhs;
|
|
32009
|
+
}
|
|
32010
|
+
if (node.kind === "call") {
|
|
32011
|
+
const sink = sinkClassOf2(node);
|
|
32012
|
+
const argTaints = node.arguments.map((a) => analyzeExpr(a, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating));
|
|
32013
|
+
const taintedArgs = argTaints.filter((t) => t.tainted);
|
|
32014
|
+
if (sink) {
|
|
32015
|
+
if (taintedArgs.length > 0) {
|
|
32016
|
+
const san = [...taintedArgs.flatMap((t) => t.sanitizers), ...findSanitizers(JSON.stringify(node))];
|
|
32017
|
+
const sanitized = isSanitized(san, sink.cls.id);
|
|
32018
|
+
const gates = activeAuth;
|
|
32019
|
+
ctx.sinks.push({ sink: sink.token, line: node.loc?.start?.line ?? 0, sanitized, authGated: gates.length > 0, authGates: gates });
|
|
32020
|
+
}
|
|
32021
|
+
for (const a of node.arguments)
|
|
32022
|
+
analyzeExpr(a, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32023
|
+
return taintedArgs.length > 0 ? mergeTaint(...taintedArgs) : CLEAN;
|
|
32024
|
+
}
|
|
32025
|
+
const fnName = rootVarName2(node.what);
|
|
32026
|
+
if (fnName) {
|
|
32027
|
+
const san = findSanitizers(fnName + "(");
|
|
32028
|
+
if (san.length > 0) {
|
|
32029
|
+
if (taintedArgs.length > 0) {
|
|
32030
|
+
return mergeTaint(...taintedArgs.map((t) => ({ ...t, sanitizers: [...t.sanitizers, ...san] })));
|
|
32031
|
+
}
|
|
32032
|
+
return CLEAN;
|
|
32033
|
+
}
|
|
32034
|
+
for (let i = 0;i < node.arguments.length; i++) {
|
|
32035
|
+
const argVar = rootVarName2(node.arguments[i]);
|
|
32036
|
+
if (argVar === taintedParam) {
|
|
32037
|
+
ctx.calls.push({ callee: fnName, argIndex: i, line: node.loc?.start?.line ?? 0 });
|
|
32038
|
+
}
|
|
32039
|
+
}
|
|
32040
|
+
const helperId = resolveFuncId(fnName, summaries);
|
|
32041
|
+
const helperSum = helperId ? summaries.get(helperId) : undefined;
|
|
32042
|
+
if (helperSum && taintedArgs.length > 0) {
|
|
32043
|
+
for (let i = 0;i < node.arguments.length; i++) {
|
|
32044
|
+
if (argTaints[i]?.tainted) {
|
|
32045
|
+
const paramName = helperSum.params[i];
|
|
32046
|
+
if (paramName && helperSum.paramReturns.has(paramName)) {
|
|
32047
|
+
return mergeTaint(...taintedArgs);
|
|
32048
|
+
}
|
|
32049
|
+
}
|
|
32050
|
+
}
|
|
32051
|
+
}
|
|
32052
|
+
if (helperSum && helperSum.paramWrites.size > 0) {
|
|
32053
|
+
for (let i = 0;i < node.arguments.length; i++) {
|
|
32054
|
+
const paramName = helperSum.params[i];
|
|
32055
|
+
if (!paramName || !helperSum.paramWrites.has(paramName))
|
|
32056
|
+
continue;
|
|
32057
|
+
const argVar = rootVarName2(node.arguments[i]);
|
|
32058
|
+
const written = helperSum.paramWrites.get(paramName);
|
|
32059
|
+
if (argVar) {
|
|
32060
|
+
env.set(argVar, { tainted: true, sources: written, sanitizers: [] });
|
|
32061
|
+
}
|
|
32062
|
+
}
|
|
32063
|
+
}
|
|
32064
|
+
}
|
|
32065
|
+
if (isNode3(node.what) && node.what.kind === "propertylookup") {
|
|
32066
|
+
analyzeExpr(node.what, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32067
|
+
}
|
|
32068
|
+
return CLEAN;
|
|
32069
|
+
}
|
|
32070
|
+
if (node.kind === "variable") {
|
|
32071
|
+
const t = env.get(node.name);
|
|
32072
|
+
return t ? { ...t } : CLEAN;
|
|
32073
|
+
}
|
|
32074
|
+
if (node.kind === "offsetlookup") {
|
|
32075
|
+
const root = rootVarName2(node);
|
|
32076
|
+
if (root) {
|
|
32077
|
+
const t = env.get(root);
|
|
32078
|
+
if (t)
|
|
32079
|
+
return { ...t };
|
|
32080
|
+
}
|
|
32081
|
+
return analyzeExpr(node.what, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32082
|
+
}
|
|
32083
|
+
if (node.kind === "propertylookup") {
|
|
32084
|
+
const objName = rootVarName2(node.what);
|
|
32085
|
+
const propName = node.offset?.name ?? "?";
|
|
32086
|
+
if (objName) {
|
|
32087
|
+
const pt = props.get(objName)?.get(propName);
|
|
32088
|
+
if (pt)
|
|
32089
|
+
return { ...pt };
|
|
32090
|
+
}
|
|
32091
|
+
return analyzeExpr(node.what, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32092
|
+
}
|
|
32093
|
+
if (node.kind === "retif") {
|
|
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;
|
|
32096
|
+
return mergeTaint(t, f);
|
|
32097
|
+
}
|
|
32098
|
+
if (node.kind === "bin" || node.kind === "concat" || node.kind === "encapsed") {
|
|
32099
|
+
const parts = children3(node);
|
|
32100
|
+
const taints = parts.map((p) => analyzeExpr(p, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating));
|
|
32101
|
+
return mergeTaint(...taints);
|
|
32102
|
+
}
|
|
32103
|
+
if (node.kind === "encapsedpart") {
|
|
32104
|
+
return analyzeExpr(node.expression, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32105
|
+
}
|
|
32106
|
+
if (node.kind === "echo" || node.kind === "print") {
|
|
32107
|
+
const exprs = node.expressions ?? [node.expression];
|
|
32108
|
+
const cls = classifySink2(node.kind === "echo" ? "echo " : "print ");
|
|
32109
|
+
let ret = CLEAN;
|
|
32110
|
+
for (const e of exprs) {
|
|
32111
|
+
const t = analyzeExpr(e, env, summary, ctx, taintedParam, props, activeAuth, summaries, validating);
|
|
32112
|
+
if (t.tainted && cls) {
|
|
32113
|
+
const san = [...t.sanitizers, ...findSanitizers(JSON.stringify(e))];
|
|
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 });
|
|
32115
|
+
}
|
|
32116
|
+
ret = mergeTaint(ret, t);
|
|
32117
|
+
}
|
|
32118
|
+
return ret;
|
|
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
|
+
}
|
|
32140
|
+
return CLEAN;
|
|
32141
|
+
}
|
|
32142
|
+
function cloneEnv(env) {
|
|
32143
|
+
return new Map([...env.entries()].map(([k, v]) => [k, { ...v, sources: [...v.sources], sanitizers: [...v.sanitizers] }]));
|
|
32144
|
+
}
|
|
32145
|
+
function cloneProps(props) {
|
|
32146
|
+
const out = new Map;
|
|
32147
|
+
for (const [k, v] of props)
|
|
32148
|
+
out.set(k, new Map(v));
|
|
32149
|
+
return out;
|
|
32150
|
+
}
|
|
32151
|
+
function mergeCtx(ctx, other) {
|
|
32152
|
+
ctx.sinks.push(...other.sinks);
|
|
32153
|
+
if (other.returnsTainted)
|
|
32154
|
+
ctx.returnsTainted = true;
|
|
32155
|
+
ctx.calls.push(...other.calls);
|
|
32156
|
+
}
|
|
32157
|
+
function authGatesOf(node) {
|
|
32158
|
+
if (!isNode3(node))
|
|
32159
|
+
return [];
|
|
32160
|
+
return AUTH_GATES.filter((g) => JSON.stringify(node).includes(g));
|
|
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
|
+
}
|
|
32169
|
+
function collectDecls(ast, out) {
|
|
32170
|
+
walkDecls(ast, out);
|
|
32171
|
+
}
|
|
32172
|
+
function walkDecls(node, out, className) {
|
|
32173
|
+
if (!isNode3(node))
|
|
32174
|
+
return;
|
|
32175
|
+
if (node.kind === "function") {
|
|
32176
|
+
const name = nameOf2(node.name);
|
|
32177
|
+
if (name)
|
|
32178
|
+
out.push({ id: `func:${name}`, name, node, kind: "function" });
|
|
32179
|
+
} else if (node.kind === "class") {
|
|
32180
|
+
const cn = nameOf2(node.name);
|
|
32181
|
+
for (const c of children3(node)) {
|
|
32182
|
+
if (c.kind === "method") {
|
|
32183
|
+
const mn = nameOf2(c.name);
|
|
32184
|
+
if (mn && cn)
|
|
32185
|
+
out.push({ id: `method:${cn}::${mn}`, name: mn, node: c, kind: "method" });
|
|
32186
|
+
}
|
|
32187
|
+
}
|
|
32188
|
+
return;
|
|
32189
|
+
} else if (node.kind === "method") {
|
|
32190
|
+
const mn = nameOf2(node.name);
|
|
32191
|
+
if (mn && className)
|
|
32192
|
+
out.push({ id: `method:${className}::${mn}`, name: mn, node, kind: "method" });
|
|
32193
|
+
}
|
|
32194
|
+
const childCtx = node.kind === "class" ? nameOf2(node.name) ?? className : className;
|
|
32195
|
+
for (const c of children3(node))
|
|
32196
|
+
walkDecls(c, out, childCtx);
|
|
32197
|
+
}
|
|
32198
|
+
function resolveFuncId(name, summaries) {
|
|
32199
|
+
if (summaries.has(`func:${name}`))
|
|
32200
|
+
return `func:${name}`;
|
|
32201
|
+
for (const [id, s] of summaries) {
|
|
32202
|
+
if (s.name === name && id.startsWith("method:"))
|
|
32203
|
+
return id;
|
|
32204
|
+
}
|
|
32205
|
+
return null;
|
|
32206
|
+
}
|
|
32207
|
+
function analyzeDataFlow2(path) {
|
|
32208
|
+
const cg = buildCallGraph(path);
|
|
32209
|
+
const result = {
|
|
32210
|
+
file: path,
|
|
32211
|
+
findings: [],
|
|
32212
|
+
suppressed: 0,
|
|
32213
|
+
summary: { functions: 0, methods: 0, edges: cg.edges.length, call_graph: cg }
|
|
32214
|
+
};
|
|
32215
|
+
let code;
|
|
32216
|
+
try {
|
|
32217
|
+
code = readFileSync12(path, "utf8");
|
|
32218
|
+
} catch {
|
|
32219
|
+
return result;
|
|
32220
|
+
}
|
|
32221
|
+
let ast;
|
|
32222
|
+
try {
|
|
32223
|
+
ast = parser3.parseCode(code, path);
|
|
32224
|
+
} catch {
|
|
32225
|
+
return result;
|
|
32226
|
+
}
|
|
32227
|
+
const summaries = new Map;
|
|
32228
|
+
const decls = [];
|
|
32229
|
+
collectDecls(ast, decls);
|
|
32230
|
+
for (const d of decls) {
|
|
32231
|
+
summaries.set(d.id, summarizeFunction(d.node, d.id, d.name, summaries));
|
|
32232
|
+
if (d.kind === "function")
|
|
32233
|
+
result.summary.functions += 1;
|
|
32234
|
+
else
|
|
32235
|
+
result.summary.methods += 1;
|
|
32236
|
+
}
|
|
32237
|
+
const topStmts = [];
|
|
32238
|
+
for (const s of ast.children ?? []) {
|
|
32239
|
+
if (isNode3(s) && s.kind !== "function" && s.kind !== "class")
|
|
32240
|
+
topStmts.push(s);
|
|
32241
|
+
}
|
|
32242
|
+
const mainSummary = { id: "main", name: "main", params: [], byrefParams: new Set, paramSinks: new Map, paramReturns: new Set, paramCalls: new Map, paramWrites: new Map };
|
|
32243
|
+
const mainEnv = new Map;
|
|
32244
|
+
const mainCtx = { sinks: [], returnsTainted: false, calls: [] };
|
|
32245
|
+
const mainProps = new Map;
|
|
32246
|
+
analyzeBody(topStmts, mainEnv, mainSummary, mainCtx, "", mainProps, [], summaries);
|
|
32247
|
+
for (const hit of mainCtx.sinks) {
|
|
32248
|
+
const cls = classifySink2(hit.sink);
|
|
32249
|
+
if (!cls)
|
|
32250
|
+
continue;
|
|
32251
|
+
if (hit.sanitized) {
|
|
32252
|
+
result.suppressed += 1;
|
|
32253
|
+
continue;
|
|
32254
|
+
}
|
|
32255
|
+
result.findings.push({
|
|
32256
|
+
sink: hit.sink,
|
|
32257
|
+
sink_line: hit.line,
|
|
32258
|
+
category: cls.category,
|
|
32259
|
+
cwe: cls.cwe,
|
|
32260
|
+
sanitized: false,
|
|
32261
|
+
auth_gated: hit.authGated,
|
|
32262
|
+
auth_gates: hit.authGates,
|
|
32263
|
+
interprocedural: false,
|
|
32264
|
+
via_return: false
|
|
32265
|
+
});
|
|
32266
|
+
}
|
|
32267
|
+
const queue = [];
|
|
32268
|
+
const seen = new Set;
|
|
32269
|
+
const mainCalls = topStmts.flatMap((s) => findKind2(s, "call"));
|
|
32270
|
+
for (const call of mainCalls) {
|
|
32271
|
+
const fnName = rootVarName2(call.what);
|
|
32272
|
+
if (!fnName || sinkClassOf2(call))
|
|
32273
|
+
continue;
|
|
32274
|
+
const target = resolveFuncId(fnName, summaries);
|
|
32275
|
+
if (!target)
|
|
32276
|
+
continue;
|
|
32277
|
+
for (let i = 0;i < call.arguments.length; i++) {
|
|
32278
|
+
const argTaint = analyzeExpr(call.arguments[i], mainEnv, mainSummary, mainCtx, "", mainProps, [], summaries);
|
|
32279
|
+
if (argTaint.tainted) {
|
|
32280
|
+
const param = summaries.get(target).params[i];
|
|
32281
|
+
if (param)
|
|
32282
|
+
queue.push({ funcId: target, param, source: argTaint.sources[0], sourceLine: argTaint.sourceLine, viaReturn: false });
|
|
32283
|
+
}
|
|
32284
|
+
}
|
|
32285
|
+
}
|
|
32286
|
+
while (queue.length) {
|
|
32287
|
+
const item = queue.shift();
|
|
32288
|
+
const key = `${item.funcId}:${item.param}`;
|
|
32289
|
+
if (seen.has(key))
|
|
32290
|
+
continue;
|
|
32291
|
+
seen.add(key);
|
|
32292
|
+
const sum = summaries.get(item.funcId);
|
|
32293
|
+
if (!sum)
|
|
32294
|
+
continue;
|
|
32295
|
+
for (const hit of sum.paramSinks.get(item.param) ?? []) {
|
|
32296
|
+
const cls = classifySink2(hit.sink);
|
|
32297
|
+
if (!cls)
|
|
32298
|
+
continue;
|
|
32299
|
+
if (hit.sanitized) {
|
|
32300
|
+
result.suppressed += 1;
|
|
32301
|
+
continue;
|
|
32302
|
+
}
|
|
32303
|
+
result.findings.push({
|
|
32304
|
+
sink: hit.sink,
|
|
32305
|
+
sink_line: hit.line,
|
|
32306
|
+
category: cls.category,
|
|
32307
|
+
cwe: cls.cwe,
|
|
32308
|
+
source: item.source,
|
|
32309
|
+
source_line: item.sourceLine,
|
|
32310
|
+
sanitized: false,
|
|
32311
|
+
auth_gated: hit.authGated,
|
|
32312
|
+
auth_gates: hit.authGates,
|
|
32313
|
+
interprocedural: true,
|
|
32314
|
+
via_return: item.viaReturn
|
|
32315
|
+
});
|
|
32316
|
+
}
|
|
32317
|
+
for (const c of sum.paramCalls.get(item.param) ?? []) {
|
|
32318
|
+
const calleeId = resolveFuncId(c.callee, summaries);
|
|
32319
|
+
if (!calleeId)
|
|
32320
|
+
continue;
|
|
32321
|
+
const calleeParam = summaries.get(calleeId).params[c.argIndex];
|
|
32322
|
+
if (calleeParam)
|
|
32323
|
+
queue.push({ funcId: calleeId, param: calleeParam, source: item.source, sourceLine: item.sourceLine, viaReturn: false });
|
|
32324
|
+
}
|
|
32325
|
+
}
|
|
32326
|
+
return result;
|
|
32327
|
+
}
|
|
32328
|
+
|
|
31596
32329
|
// src/universal-taint.ts
|
|
31597
32330
|
function runTaintEngine(adapter, code, file) {
|
|
31598
32331
|
const result = { file, language: adapter.language, findings: [], suppressed: 0 };
|
|
@@ -31694,7 +32427,7 @@ function analyzeTaintUniversal(code, file) {
|
|
|
31694
32427
|
}
|
|
31695
32428
|
|
|
31696
32429
|
// src/adapters.ts
|
|
31697
|
-
var
|
|
32430
|
+
var import_php_parser4 = __toESM(require_src(), 1);
|
|
31698
32431
|
|
|
31699
32432
|
// node_modules/@babel/parser/lib/index.js
|
|
31700
32433
|
class Position {
|
|
@@ -49473,7 +50206,7 @@ var pythonHighlighting = styleTags({
|
|
|
49473
50206
|
", ;": tags.separator
|
|
49474
50207
|
});
|
|
49475
50208
|
var spec_identifier = { __proto__: null, await: 44, or: 54, and: 56, in: 60, not: 62, is: 64, if: 70, else: 72, lambda: 76, yield: 94, from: 96, async: 102, for: 104, None: 162, True: 164, False: 164, del: 178, pass: 182, break: 186, continue: 190, return: 194, raise: 202, import: 206, as: 208, global: 212, nonlocal: 214, assert: 218, type: 223, elif: 236, while: 240, try: 246, except: 248, finally: 250, with: 254, def: 258, class: 268, match: 279, case: 285 };
|
|
49476
|
-
var
|
|
50209
|
+
var parser4 = LRParser.deserialize({
|
|
49477
50210
|
version: 14,
|
|
49478
50211
|
states: "##jQ`QeOOP$}OSOOO&WQtO'#HUOOQS'#Co'#CoOOQS'#Cp'#CpO'vQdO'#CnO*UQtO'#HTOOQS'#HU'#HUOOQS'#DU'#DUOOQS'#HT'#HTO*rQdO'#D_O+VQdO'#DfO+gQdO'#DjO+zOWO'#DuO,VOWO'#DvO.[QtO'#GuOOQS'#Gu'#GuO'vQdO'#GtO0ZQtO'#GtOOQS'#Eb'#EbO0rQdO'#EcOOQS'#Gs'#GsO0|QdO'#GrOOQV'#Gr'#GrO1XQdO'#FYOOQS'#G^'#G^O1^QdO'#FXOOQV'#IS'#ISOOQV'#Gq'#GqOOQV'#Fq'#FqQ`QeOOO'vQdO'#CqO1lQdO'#C}O1sQdO'#DRO2RQdO'#HYO2cQtO'#EVO'vQdO'#EWOOQS'#EY'#EYOOQS'#E['#E[OOQS'#E^'#E^O2wQdO'#E`O3_QdO'#EdO3rQdO'#EfO3zQtO'#EfO1XQdO'#EiO0rQdO'#ElO1XQdO'#EnO0rQdO'#EtO0rQdO'#EwO4VQdO'#EyO4^QdO'#FOO4iQdO'#EzO0rQdO'#FOO1XQdO'#FQO1XQdO'#FVO4nQdO'#F[P4uOdO'#GpPOOO)CBd)CBdOOQS'#Ce'#CeOOQS'#Cf'#CfOOQS'#Cg'#CgOOQS'#Ch'#ChOOQS'#Ci'#CiOOQS'#Cj'#CjOOQS'#Cl'#ClO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO'vQdO,59OO5TQdO'#DoOOQS,5:Y,5:YO5hQdO'#HdOOQS,5:],5:]O5uQ!fO,5:]O5zQtO,59YO1lQdO,59bO1lQdO,59bO1lQdO,59bO8jQdO,59bO8oQdO,59bO8vQdO,59jO8}QdO'#HTO:TQdO'#HSOOQS'#HS'#HSOOQS'#D['#D[O:lQdO,59aO'vQdO,59aO:zQdO,59aOOQS,59y,59yO;PQdO,5:RO'vQdO,5:ROOQS,5:Q,5:QO;_QdO,5:QO;dQdO,5:XO'vQdO,5:XO'vQdO,5:VOOQS,5:U,5:UO;uQdO,5:UO;zQdO,5:WOOOW'#Fy'#FyO<POWO,5:aOOQS,5:a,5:aO<[QdO'#HwOOOW'#Dw'#DwOOOW'#Fz'#FzO<lOWO,5:bOOQS,5:b,5:bOOQS'#F}'#F}O<zQtO,5:iO?lQtO,5=`O@VQ#xO,5=`O@vQtO,5=`OOQS,5:},5:}OA_QeO'#GWOBqQdO,5;^OOQV,5=^,5=^OB|QtO'#IPOCkQdO,5;tOOQS-E:[-E:[OOQV,5;s,5;sO4dQdO'#FQOOQV-E9o-E9oOCsQtO,59]OEzQtO,59iOFeQdO'#HVOFpQdO'#HVO1XQdO'#HVOF{QdO'#DTOGTQdO,59mOGYQdO'#HZO'vQdO'#HZO0rQdO,5=tOOQS,5=t,5=tO0rQdO'#EROOQS'#ES'#ESOGwQdO'#GPOHXQdO,58|OHXQdO,58|O*xQdO,5:oOHgQtO'#H]OOQS,5:r,5:rOOQS,5:z,5:zOHzQdO,5;OOI]QdO'#IOO1XQdO'#H}OOQS,5;Q,5;QOOQS'#GT'#GTOIqQtO,5;QOJPQdO,5;QOJUQdO'#IQOOQS,5;T,5;TOJdQdO'#H|OOQS,5;W,5;WOJuQdO,5;YO4iQdO,5;`O4iQdO,5;cOJ}QtO'#ITO'vQdO'#ITOKXQdO,5;eO4VQdO,5;eO0rQdO,5;jO1XQdO,5;lOK^QeO'#EuOLjQgO,5;fO!!kQdO'#IUO4iQdO,5;jO!!vQdO,5;lO!#OQdO,5;qO!#ZQtO,5;vO'vQdO,5;vPOOO,5=[,5=[P!#bOSO,5=[P!#jOdO,5=[O!&bQtO1G.jO!&iQtO1G.jO!)YQtO1G.jO!)dQtO1G.jO!+}QtO1G.jO!,bQtO1G.jO!,uQdO'#HcO!-TQtO'#GuO0rQdO'#HcO!-_QdO'#HbOOQS,5:Z,5:ZO!-gQdO,5:ZO!-lQdO'#HeO!-wQdO'#HeO!.[QdO,5>OOOQS'#Ds'#DsOOQS1G/w1G/wOOQS1G.|1G.|O!/[QtO1G.|O!/cQtO1G.|O1lQdO1G.|O!0OQdO1G/UOOQS'#DZ'#DZO0rQdO,59tOOQS1G.{1G.{O!0VQdO1G/eO!0gQdO1G/eO!0oQdO1G/fO'vQdO'#H[O!0tQdO'#H[O!0yQtO1G.{O!1ZQdO,59iO!2aQdO,5=zO!2qQdO,5=zO!2yQdO1G/mO!3OQtO1G/mOOQS1G/l1G/lO!3`QdO,5=uO!4VQdO,5=uO0rQdO1G/qO!4tQdO1G/sO!4yQtO1G/sO!5ZQtO1G/qOOQS1G/p1G/pOOQS1G/r1G/rOOOW-E9w-E9wOOQS1G/{1G/{O!5kQdO'#HxO0rQdO'#HxO!5|QdO,5>cOOOW-E9x-E9xOOQS1G/|1G/|OOQS-E9{-E9{O!6[Q#xO1G2zO!6{QtO1G2zO'vQdO,5<jOOQS,5<j,5<jOOQS-E9|-E9|OOQS,5<r,5<rOOQS-E:U-E:UOOQV1G0x1G0xO1XQdO'#GRO!7dQtO,5>kOOQS1G1`1G1`O!8RQdO1G1`OOQS'#DV'#DVO0rQdO,5=qOOQS,5=q,5=qO!8WQdO'#FrO!8cQdO,59oO!8kQdO1G/XO!8uQtO,5=uOOQS1G3`1G3`OOQS,5:m,5:mO!9fQdO'#GtOOQS,5<k,5<kOOQS-E9}-E9}O!9wQdO1G.hOOQS1G0Z1G0ZO!:VQdO,5=wO!:gQdO,5=wO0rQdO1G0jO0rQdO1G0jO!:xQdO,5>jO!;ZQdO,5>jO1XQdO,5>jO!;lQdO,5>iOOQS-E:R-E:RO!;qQdO1G0lO!;|QdO1G0lO!<RQdO,5>lO!<aQdO,5>lO!<oQdO,5>hO!=VQdO,5>hO!=hQdO'#EpO0rQdO1G0tO!=sQdO1G0tO!=xQgO1G0zO!AvQgO1G0}O!EqQdO,5>oO!E{QdO,5>oO!FTQtO,5>oO0rQdO1G1PO!F_QdO1G1PO4iQdO1G1UO!!vQdO1G1WOOQV,5;a,5;aO!FdQfO,5;aO!FiQgO1G1QO!JjQdO'#GZO4iQdO1G1QO4iQdO1G1QO!JzQdO,5>pO!KXQdO,5>pO1XQdO,5>pOOQV1G1U1G1UO!KaQdO'#FSO!KrQ!fO1G1WO!KzQdO1G1WOOQV1G1]1G1]O4iQdO1G1]O!LPQdO1G1]O!LXQdO'#F^OOQV1G1b1G1bO!#ZQtO1G1bPOOO1G2v1G2vP!L^OSO1G2vOOQS,5=},5=}OOQS'#Dp'#DpO0rQdO,5=}O!LfQdO,5=|O!LyQdO,5=|OOQS1G/u1G/uO!MRQdO,5>PO!McQdO,5>PO!MkQdO,5>PO!NOQdO,5>PO!N`QdO,5>POOQS1G3j1G3jOOQS7+$h7+$hO!8kQdO7+$pO#!RQdO1G.|O#!YQdO1G.|OOQS1G/`1G/`OOQS,5<`,5<`O'vQdO,5<`OOQS7+%P7+%PO#!aQdO7+%POOQS-E9r-E9rOOQS7+%Q7+%QO#!qQdO,5=vO'vQdO,5=vOOQS7+$g7+$gO#!vQdO7+%PO##OQdO7+%QO##TQdO1G3fOOQS7+%X7+%XO##eQdO1G3fO##mQdO7+%XOOQS,5<_,5<_O'vQdO,5<_O##rQdO1G3aOOQS-E9q-E9qO#$iQdO7+%]OOQS7+%_7+%_O#$wQdO1G3aO#%fQdO7+%_O#%kQdO1G3gO#%{QdO1G3gO#&TQdO7+%]O#&YQdO,5>dO#&sQdO,5>dO#&sQdO,5>dOOQS'#Dx'#DxO#'UO&jO'#DzO#'aO`O'#HyOOOW1G3}1G3}O#'fQdO1G3}O#'nQdO1G3}O#'yQ#xO7+(fO#(jQtO1G2UP#)TQdO'#GOOOQS,5<m,5<mOOQS-E:P-E:POOQS7+&z7+&zOOQS1G3]1G3]OOQS,5<^,5<^OOQS-E9p-E9pOOQS7+$s7+$sO#)bQdO,5=`O#){QdO,5=`O#*^QtO,5<aO#*qQdO1G3cOOQS-E9s-E9sOOQS7+&U7+&UO#+RQdO7+&UO#+aQdO,5<nO#+uQdO1G4UOOQS-E:Q-E:QO#,WQdO1G4UOOQS1G4T1G4TOOQS7+&W7+&WO#,iQdO7+&WOOQS,5<p,5<pO#,tQdO1G4WOOQS-E:S-E:SOOQS,5<l,5<lO#-SQdO1G4SOOQS-E:O-E:OO1XQdO'#EqO#-jQdO'#EqO#-uQdO'#IRO#-}QdO,5;[OOQS7+&`7+&`O0rQdO7+&`O#.SQgO7+&fO!JmQdO'#GXO4iQdO7+&fO4iQdO7+&iO#2QQtO,5<tO'vQdO,5<tO#2[QdO1G4ZOOQS-E:W-E:WO#2fQdO1G4ZO4iQdO7+&kO0rQdO7+&kOOQV7+&p7+&pO!KrQ!fO7+&rO!KzQdO7+&rO`QeO1G0{OOQV-E:X-E:XO4iQdO7+&lO4iQdO7+&lOOQV,5<u,5<uO#2nQdO,5<uO!JmQdO,5<uOOQV7+&l7+&lO#2yQgO7+&lO#6tQdO,5<vO#7PQdO1G4[OOQS-E:Y-E:YO#7^QdO1G4[O#7fQdO'#IWO#7tQdO'#IWO1XQdO'#IWOOQS'#IW'#IWO#8PQdO'#IVOOQS,5;n,5;nO#8XQdO,5;nO0rQdO'#FUOOQV7+&r7+&rO4iQdO7+&rOOQV7+&w7+&wO4iQdO7+&wO#8^QfO,5;xOOQV7+&|7+&|POOO7+(b7+(bO#8cQdO1G3iOOQS,5<c,5<cO#8qQdO1G3hOOQS-E9u-E9uO#9UQdO,5<dO#9aQdO,5<dO#9tQdO1G3kOOQS-E9v-E9vO#:UQdO1G3kO#:^QdO1G3kO#:nQdO1G3kO#:UQdO1G3kOOQS<<H[<<H[O#:yQtO1G1zOOQS<<Hk<<HkP#;WQdO'#FtO8vQdO1G3bO#;eQdO1G3bO#;jQdO<<HkOOQS<<Hl<<HlO#;zQdO7+)QOOQS<<Hs<<HsO#<[QtO1G1yP#<{QdO'#FsO#=YQdO7+)RO#=jQdO7+)RO#=rQdO<<HwO#=wQdO7+({OOQS<<Hy<<HyO#>nQdO,5<bO'vQdO,5<bOOQS-E9t-E9tOOQS<<Hw<<HwOOQS,5<g,5<gO0rQdO,5<gO#>sQdO1G4OOOQS-E9y-E9yO#?^QdO1G4OO<[QdO'#H{OOOO'#D{'#D{OOOO'#F|'#F|O#?oO&jO,5:fOOOW,5>e,5>eOOOW7+)i7+)iO#?zQdO7+)iO#@SQdO1G2zO#@mQdO1G2zP'vQdO'#FuO0rQdO<<IpO1XQdO1G2YP1XQdO'#GSO#AOQdO7+)pO#AaQdO7+)pOOQS<<Ir<<IrP1XQdO'#GUP0rQdO'#GQOOQS,5;],5;]O#ArQdO,5>mO#BQQdO,5>mOOQS1G0v1G0vOOQS<<Iz<<IzOOQV-E:V-E:VO4iQdO<<JQOOQV,5<s,5<sO4iQdO,5<sOOQV<<JQ<<JQOOQV<<JT<<JTO#BYQtO1G2`P#BdQdO'#GYO#BkQdO7+)uO#BuQgO<<JVO4iQdO<<JVOOQV<<J^<<J^O4iQdO<<J^O!KrQ!fO<<J^O#FpQgO7+&gOOQV<<JW<<JWO#FzQgO<<JWOOQV1G2a1G2aO1XQdO1G2aO#JuQdO1G2aO4iQdO<<JWO1XQdO1G2bP0rQdO'#G[O#KQQdO7+)vO#K_QdO7+)vOOQS'#FT'#FTO0rQdO,5>rO#KgQdO,5>rO#KrQdO,5>rO#K}QdO,5>qO#L`QdO,5>qOOQS1G1Y1G1YOOQS,5;p,5;pOOQV<<Jc<<JcO#LhQdO1G1dOOQS7+)T7+)TP#LmQdO'#FwO#L}QdO1G2OO#MbQdO1G2OO#MrQdO1G2OP#M}QdO'#FxO#N[QdO7+)VO#NlQdO7+)VO#NlQdO7+)VO#NtQdO7+)VO$ UQdO7+(|O8vQdO7+(|OOQSAN>VAN>VO$ oQdO<<LmOOQSAN>cAN>cO0rQdO1G1|O$!PQtO1G1|P$!ZQdO'#FvOOQS1G2R1G2RP$!hQdO'#F{O$!uQdO7+)jO$#`QdO,5>gOOOO-E9z-E9zOOOW<<MT<<MTO$#nQdO7+(fOOQSAN?[AN?[OOQS7+'t7+'tO$$XQdO<<M[OOQS,5<q,5<qO$$jQdO1G4XOOQS-E:T-E:TOOQVAN?lAN?lOOQV1G2_1G2_O4iQdOAN?qO$$xQgOAN?qOOQVAN?xAN?xO4iQdOAN?xOOQV<<JR<<JRO4iQdOAN?rO4iQdO7+'{OOQV7+'{7+'{O1XQdO7+'{OOQVAN?rAN?rOOQS7+'|7+'|O$(sQdO<<MbOOQS1G4^1G4^O0rQdO1G4^OOQS,5<w,5<wO$)QQdO1G4]OOQS-E:Z-E:ZOOQU'#G_'#G_O$)cQfO7+'OO$)nQdO'#F_O$*uQdO7+'jO$+VQdO7+'jOOQS7+'j7+'jO$+bQdO<<LqO$+rQdO<<LqO$+rQdO<<LqO$+zQdO'#H^OOQS<<Lh<<LhO$,UQdO<<LhOOQS7+'h7+'hOOQS'#D|'#D|OOOO1G4R1G4RO$,oQdO1G4RO$,wQdO1G4RP!=hQdO'#GVOOQVG25]G25]O4iQdOG25]OOQVG25dG25dOOQVG25^G25^OOQV<<Kg<<KgO4iQdO<<KgOOQS7+)x7+)xP$-SQdO'#G]OOQU-E:]-E:]OOQV<<Jj<<JjO$-vQtO'#FaOOQS'#Fc'#FcO$.WQdO'#FbO$.xQdO'#FbOOQS'#Fb'#FbO$.}QdO'#IYO$)nQdO'#FiO$)nQdO'#FiO$/fQdO'#FjO$)nQdO'#FkO$/mQdO'#IZOOQS'#IZ'#IZO$0[QdO,5;yOOQS<<KU<<KUO$0dQdO<<KUO$0tQdOANB]O$1UQdOANB]O$1^QdO'#H_OOQS'#H_'#H_O1sQdO'#DcO$1wQdO,5=xOOQSANBSANBSOOOO7+)m7+)mO$2`QdO7+)mOOQVLD*wLD*wOOQVANARANARO5uQ!fO'#GaO$2hQtO,5<SO$)nQdO'#FmOOQS,5<W,5<WOOQS'#Fd'#FdO$3YQdO,5;|O$3_QdO,5;|OOQS'#Fg'#FgO$)nQdO'#G`O$4PQdO,5<QO$4kQdO,5>tO$4{QdO,5>tO1XQdO,5<PO$5^QdO,5<TO$5cQdO,5<TO$)nQdO'#I[O$5hQdO'#I[O$5mQdO,5<UOOQS,5<V,5<VO0rQdO'#FpOOQU1G1e1G1eO4iQdO1G1eOOQSAN@pAN@pO$5rQdOG27wO$6SQdO,59}OOQS1G3d1G3dOOOO<<MX<<MXOOQS,5<{,5<{OOQS-E:_-E:_O$6XQtO'#FaO$6`QdO'#I]O$6nQdO'#I]O$6vQdO,5<XOOQS1G1h1G1hO$6{QdO1G1hO$7QQdO,5<zOOQS-E:^-E:^O$7lQdO,5=OO$8TQdO1G4`OOQS-E:b-E:bOOQS1G1k1G1kOOQS1G1o1G1oO$8eQdO,5>vO$)nQdO,5>vOOQS1G1p1G1pOOQS,5<[,5<[OOQU7+'P7+'PO$+zQdO1G/iO$)nQdO,5<YO$8sQdO,5>wO$8zQdO,5>wOOQS1G1s1G1sOOQS7+'S7+'SP$)nQdO'#GdO$9SQdO1G4bO$9^QdO1G4bO$9fQdO1G4bOOQS7+%T7+%TO$9tQdO1G1tO$:SQtO'#FaO$:ZQdO,5<}OOQS,5<},5<}O$:iQdO1G4cOOQS-E:a-E:aO$)nQdO,5<|O$:pQdO,5<|O$:uQdO7+)|OOQS-E:`-E:`O$;PQdO7+)|O$)nQdO,5<ZP$)nQdO'#GcO$;XQdO1G2hO$)nQdO1G2hP$;gQdO'#GbO$;nQdO<<MhO$;xQdO1G1uO$<WQdO7+(SO8vQdO'#C}O8vQdO,59bO8vQdO,59bO8vQdO,59bO$<fQtO,5=`O8vQdO1G.|O0rQdO1G/XO0rQdO7+$pP$<yQdO'#GOO'vQdO'#GtO$=WQdO,59bO$=]QdO,59bO$=dQdO,59mO$=iQdO1G/UO1sQdO'#DRO8vQdO,59j",
|
|
49479
50212
|
stateData: "$>S~O%cOS%^OSSOS%]PQ~OPdOVaOfoOhYOopOs!POvqO!PrO!Q{O!T!SO!U!RO!XZO!][O!h`O!r`O!s`O!t`O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO#l!QO#o!TO#s!UO#u!VO#z!WO#}hO$P!XO%oRO%pRO%tSO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O~O%]!YO~OV!aO_!aOa!bOh!iO!X!kO!f!mO%j![O%k!]O%l!^O%m!_O%n!_O%o!`O%p!`O%q!aO%r!aO%s!aO~Ok%xXl%xXm%xXn%xXo%xXp%xXs%xXz%xX{%xX!x%xX#g%xX%[%xX%_%xX%z%xXg%xX!T%xX!U%xX%{%xX!W%xX![%xX!Q%xX#[%xXt%xX!m%xX~P%SOfoOhYO!XZO!][O!h`O!r`O!s`O!t`O%oRO%pRO%tSO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O~Oz%wX{%wX#g%wX%[%wX%_%wX%z%wX~Ok!pOl!qOm!oOn!oOo!rOp!sOs!tO!x%wX~P)pOV!zOg!|Oo0cOv0qO!PrO~P'vOV#OOo0cOv0qO!W#PO~P'vOV#SOa#TOo0cOv0qO![#UO~P'vOQ#XO%`#XO%a#ZO~OQ#^OR#[O%`#^O%a#`O~OV%iX_%iXa%iXh%iXk%iXl%iXm%iXn%iXo%iXp%iXs%iXz%iX!X%iX!f%iX%j%iX%k%iX%l%iX%m%iX%n%iX%o%iX%p%iX%q%iX%r%iX%s%iXg%iX!T%iX!U%iX~O&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O&c^O&d^O&e^O&f^O&g^O&h^O&i^O&j^O{%iX!x%iX#g%iX%[%iX%_%iX%z%iX%{%iX!W%iX![%iX!Q%iX#[%iXt%iX!m%iX~P,eOz#dO{%hX!x%hX#g%hX%[%hX%_%hX%z%hX~Oo0cOv0qO~P'vO#g#gO%[#iO%_#iO~O%uWO~O!T#nO#u!VO#z!WO#}hO~OopO~P'vOV#sOa#tO%uWO{wP~OV#xOo0cOv0qO!Q#yO~P'vO{#{O!x$QO%z#|O#g!yX%[!yX%_!yX~OV#xOo0cOv0qO#g#SX%[#SX%_#SX~P'vOo0cOv0qO#g#WX%[#WX%_#WX~P'vOh$WO%uWO~O!f$YO!r$YO%uWO~OV$eO~P'vO!U$gO#s$hO#u$iO~O{$jO~OV$qO~P'vOS$sO%[$rO%_$rO%c$tO~OV$}Oa$}Og%POo0cOv0qO~P'vOo0cOv0qO{%SO~P'vO&Y%UO~Oa!bOh!iO!X!kO!f!mOVba_bakbalbambanbaobapbasbazba{ba!xba#gba%[ba%_ba%jba%kba%lba%mba%nba%oba%pba%qba%rba%sba%zbagba!Tba!Uba%{ba!Wba![ba!Qba#[batba!mba~On%ZO~Oo%ZO~P'vOo0cO~P'vOk0eOl0fOm0dOn0dOo0mOp0nOs0rOg%wX!T%wX!U%wX%{%wX!W%wX![%wX!Q%wX#[%wX!m%wX~P)pO%{%]Og%vXz%vX!T%vX!U%vX!W%vX{%vX~Og%_Oz%`O!T%dO!U%cO~Og%_O~Oz%gO!T%dO!U%cO!W&SX~O!W%kO~Oz%lO{%nO!T%dO!U%cO![%}X~O![%rO~O![%sO~OQ#XO%`#XO%a%uO~OV%wOo0cOv0qO!PrO~P'vOQ#^OR#[O%`#^O%a%zO~OV!qa_!qaa!qah!qak!qal!qam!qan!qao!qap!qas!qaz!qa{!qa!X!qa!f!qa!x!qa#g!qa%[!qa%_!qa%j!qa%k!qa%l!qa%m!qa%n!qa%o!qa%p!qa%q!qa%r!qa%s!qa%z!qag!qa!T!qa!U!qa%{!qa!W!qa![!qa!Q!qa#[!qat!qa!m!qa~P#yOz%|O{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~P%SOV&OOopOvqO{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~P'vOz%|O{%ha!x%ha#g%ha%[%ha%_%ha%z%ha~OPdOVaOopOvqO!PrO!Q{O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO#g$zX%[$zX%_$zX~P'vO#g#gO%[&TO%_&TO~O!f&UOh&sX%[&sXz&sX#[&sX#g&sX%_&sX#Z&sXg&sX~Oh!iO%[&WO~Okealeameaneaoeapeaseazea{ea!xea#gea%[ea%_ea%zeagea!Tea!Uea%{ea!Wea![ea!Qea#[eatea!mea~P%SOsqazqa{qa#gqa%[qa%_qa%zqa~Ok!pOl!qOm!oOn!oOo!rOp!sO!xqa~PEcO%z&YOz%yX{%yX~O%uWOz%yX{%yX~Oz&]O{wX~O{&_O~Oz%lO#g%}X%[%}X%_%}Xg%}X{%}X![%}X!m%}X%z%}X~OV0lOo0cOv0qO!PrO~P'vO%z#|O#gUa%[Ua%_Ua~Oz&hO#g&PX%[&PX%_&PXn&PX~P%SOz&kO!Q&jO#g#Wa%[#Wa%_#Wa~Oz&lO#[&nO#g&rX%[&rX%_&rXg&rX~O!f$YO!r$YO#Z&qO%uWO~O#Z&qO~Oz&sO#g&tX%[&tX%_&tX~Oz&uO#g&pX%[&pX%_&pX{&pX~O!X&wO%z&xO~Oz&|On&wX~P%SOn'PO~OPdOVaOopOvqO!PrO!Q{O!{tO!}uO#PvO#RwO#TxO#XyO#ZzO#^|O#_|O#a}O#c!OO%['UO~P'vOt'YO#p'WO#q'XOP#naV#naf#nah#nao#nas#nav#na!P#na!Q#na!T#na!U#na!X#na!]#na!h#na!r#na!s#na!t#na!{#na!}#na#P#na#R#na#T#na#X#na#Z#na#^#na#_#na#a#na#c#na#l#na#o#na#s#na#u#na#z#na#}#na$P#na%X#na%o#na%p#na%t#na%u#na&Z#na&[#na&]#na&^#na&_#na&`#na&a#na&b#na&c#na&d#na&e#na&f#na&g#na&h#na&i#na&j#na%Z#na%_#na~Oz'ZO#[']O{&xX~Oh'_O!X&wO~Oh!iO{$jO!X&wO~O{'eO~P%SO%['hO%_'hO~OS'iO%['hO%_'hO~OV!aO_!aOa!bOh!iO!X!kO!f!mO%l!^O%m!_O%n!_O%o!`O%p!`O%q!aO%r!aO%s!aOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~O%k!]O~P!#uO%kWi~P!#uOV!aO_!aOa!bOh!iO!X!kO!f!mO%o!`O%p!`O%q!aO%r!aO%s!aOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%kWi%lWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~O%m!_O%n!_O~P!&pO%mWi%nWi~P!&pOa!bOh!iO!X!kO!f!mOkWilWimWinWioWipWisWizWi{Wi!xWi#gWi%[Wi%_Wi%jWi%kWi%lWi%mWi%nWi%oWi%pWi%zWigWi!TWi!UWi%{Wi!WWi![Wi!QWi#[WitWi!mWi~OV!aO_!aO%q!aO%r!aO%s!aO~P!)nOVWi_Wi%qWi%rWi%sWi~P!)nO!T%dO!U%cOg&VXz&VX~O%z'kO%{'kO~P,eOz'mOg&UX~Og'oO~Oz'pO{'rO!W&XX~Oo0cOv0qOz'pO{'sO!W&XX~P'vO!W'uO~Om!oOn!oOo!rOp!sOkjisjizji{ji!xji#gji%[ji%_ji%zji~Ol!qO~P!.aOlji~P!.aOk0eOl0fOm0dOn0dOo0mOp0nO~Ot'wO~P!/jOV'|Og'}Oo0cOv0qO~P'vOg'}Oz(OO~Og(QO~O!U(SO~Og(TOz(OO!T%dO!U%cO~P%SOk0eOl0fOm0dOn0dOo0mOp0nOgqa!Tqa!Uqa%{qa!Wqa![qa!Qqa#[qatqa!mqa~PEcOV'|Oo0cOv0qO!W&Sa~P'vOz(WO!W&Sa~O!W(XO~Oz(WO!T%dO!U%cO!W&Sa~P%SOV(]Oo0cOv0qO![%}a#g%}a%[%}a%_%}ag%}a{%}a!m%}a%z%}a~P'vOz(^O![%}a#g%}a%[%}a%_%}ag%}a{%}a!m%}a%z%}a~O![(aO~Oz(^O!T%dO!U%cO![%}a~P%SOz(dO!T%dO!U%cO![&Ta~P%SOz(gO{&lX![&lX!m&lX%z&lX~O{(kO![(mO!m(nO%z(jO~OV&OOopOvqO{%hi!x%hi#g%hi%[%hi%_%hi%z%hi~P'vOz(pO{%hi!x%hi#g%hi%[%hi%_%hi%z%hi~O!f&UOh&sa%[&saz&sa#[&sa#g&sa%_&sa#Z&sag&sa~O%[(uO~OV#sOa#tO%uWO~Oz&]O{wa~OopOvqO~P'vOz(^O#g%}a%[%}a%_%}ag%}a{%}a![%}a!m%}a%z%}a~P%SOz(zO#g%hX%[%hX%_%hX%z%hX~O%z#|O#gUi%[Ui%_Ui~O#g&Pa%[&Pa%_&Pan&Pa~P'vOz(}O#g&Pa%[&Pa%_&Pan&Pa~O%uWO#g&ra%[&ra%_&rag&ra~Oz)SO#g&ra%[&ra%_&rag&ra~Og)VO~OV)WOh$WO%uWO~O#Z)XO~O%uWO#g&ta%[&ta%_&ta~Oz)ZO#g&ta%[&ta%_&ta~Oo0cOv0qO#g&pa%[&pa%_&pa{&pa~P'vOz)^O#g&pa%[&pa%_&pa{&pa~OV)`Oa)`O%uWO~O%z)eO~Ot)hO#j)gOP#hiV#hif#hih#hio#his#hiv#hi!P#hi!Q#hi!T#hi!U#hi!X#hi!]#hi!h#hi!r#hi!s#hi!t#hi!{#hi!}#hi#P#hi#R#hi#T#hi#X#hi#Z#hi#^#hi#_#hi#a#hi#c#hi#l#hi#o#hi#s#hi#u#hi#z#hi#}#hi$P#hi%X#hi%o#hi%p#hi%t#hi%u#hi&Z#hi&[#hi&]#hi&^#hi&_#hi&`#hi&a#hi&b#hi&c#hi&d#hi&e#hi&f#hi&g#hi&h#hi&i#hi&j#hi%Z#hi%_#hi~Ot)iOP#kiV#kif#kih#kio#kis#kiv#ki!P#ki!Q#ki!T#ki!U#ki!X#ki!]#ki!h#ki!r#ki!s#ki!t#ki!{#ki!}#ki#P#ki#R#ki#T#ki#X#ki#Z#ki#^#ki#_#ki#a#ki#c#ki#l#ki#o#ki#s#ki#u#ki#z#ki#}#ki$P#ki%X#ki%o#ki%p#ki%t#ki%u#ki&Z#ki&[#ki&]#ki&^#ki&_#ki&`#ki&a#ki&b#ki&c#ki&d#ki&e#ki&f#ki&g#ki&h#ki&i#ki&j#ki%Z#ki%_#ki~OV)kOn&wa~P'vOz)lOn&wa~Oz)lOn&wa~P%SOn)pO~O%Y)tO~Ot)wO#p'WO#q)vOP#niV#nif#nih#nio#nis#niv#ni!P#ni!Q#ni!T#ni!U#ni!X#ni!]#ni!h#ni!r#ni!s#ni!t#ni!{#ni!}#ni#P#ni#R#ni#T#ni#X#ni#Z#ni#^#ni#_#ni#a#ni#c#ni#l#ni#o#ni#s#ni#u#ni#z#ni#}#ni$P#ni%X#ni%o#ni%p#ni%t#ni%u#ni&Z#ni&[#ni&]#ni&^#ni&_#ni&`#ni&a#ni&b#ni&c#ni&d#ni&e#ni&f#ni&g#ni&h#ni&i#ni&j#ni%Z#ni%_#ni~OV)zOo0cOv0qO{$jO~P'vOo0cOv0qO{&xa~P'vOz*OO{&xa~OV*SOa*TOg*WO%q*UO%uWO~O{$jO&{*YO~Oh'_O~Oh!iO{$jO~O%[*_O~O%[*aO%_*aO~OV$}Oa$}Oo0cOv0qOg&Ua~P'vOz*dOg&Ua~Oo0cOv0qO{*gO!W&Xa~P'vOz*hO!W&Xa~Oo0cOv0qOz*hO{*kO!W&Xa~P'vOo0cOv0qOz*hO!W&Xa~P'vOz*hO{*kO!W&Xa~Om0dOn0dOo0mOp0nOgjikjisjizji!Tji!Uji%{ji!Wji{ji![ji#gji%[ji%_ji!Qji#[jitji!mji%zji~Ol0fO~P!NkOlji~P!NkOV'|Og*pOo0cOv0qO~P'vOn*rO~Og*pOz*tO~Og*uO~OV'|Oo0cOv0qO!W&Si~P'vOz*vO!W&Si~O!W*wO~OV(]Oo0cOv0qO![%}i#g%}i%[%}i%_%}ig%}i{%}i!m%}i%z%}i~P'vOz*zO!T%dO!U%cO![&Ti~Oz*}O![%}i#g%}i%[%}i%_%}ig%}i{%}i!m%}i%z%}i~O![+OO~Oa+QOo0cOv0qO![&Ti~P'vOz*zO![&Ti~O![+SO~OV+UOo0cOv0qO{&la![&la!m&la%z&la~P'vOz+VO{&la![&la!m&la%z&la~O!]+YO&n+[O![!nX~O![+^O~O{(kO![+_O~O{(kO![+_O!m+`O~OV&OOopOvqO{%hq!x%hq#g%hq%[%hq%_%hq%z%hq~P'vOz$ri{$ri!x$ri#g$ri%[$ri%_$ri%z$ri~P%SOV&OOopOvqO~P'vOV&OOo0cOv0qO#g%ha%[%ha%_%ha%z%ha~P'vOz+aO#g%ha%[%ha%_%ha%z%ha~Oz$ia#g$ia%[$ia%_$ian$ia~P%SO#g&Pi%[&Pi%_&Pin&Pi~P'vOz+dO#g#Wq%[#Wq%_#Wq~O#[+eOz$va#g$va%[$va%_$vag$va~O%uWO#g&ri%[&ri%_&rig&ri~Oz+gO#g&ri%[&ri%_&rig&ri~OV+iOh$WO%uWO~O%uWO#g&ti%[&ti%_&ti~Oo0cOv0qO#g&pi%[&pi%_&pi{&pi~P'vO{#{Oz#eX!W#eX~Oz+mO!W&uX~O!W+oO~Ot+rO#j)gOP#hqV#hqf#hqh#hqo#hqs#hqv#hq!P#hq!Q#hq!T#hq!U#hq!X#hq!]#hq!h#hq!r#hq!s#hq!t#hq!{#hq!}#hq#P#hq#R#hq#T#hq#X#hq#Z#hq#^#hq#_#hq#a#hq#c#hq#l#hq#o#hq#s#hq#u#hq#z#hq#}#hq$P#hq%X#hq%o#hq%p#hq%t#hq%u#hq&Z#hq&[#hq&]#hq&^#hq&_#hq&`#hq&a#hq&b#hq&c#hq&d#hq&e#hq&f#hq&g#hq&h#hq&i#hq&j#hq%Z#hq%_#hq~On$|az$|a~P%SOV)kOn&wi~P'vOz+yOn&wi~Oz,TO{$jO#[,TO~O#q,VOP#nqV#nqf#nqh#nqo#nqs#nqv#nq!P#nq!Q#nq!T#nq!U#nq!X#nq!]#nq!h#nq!r#nq!s#nq!t#nq!{#nq!}#nq#P#nq#R#nq#T#nq#X#nq#Z#nq#^#nq#_#nq#a#nq#c#nq#l#nq#o#nq#s#nq#u#nq#z#nq#}#nq$P#nq%X#nq%o#nq%p#nq%t#nq%u#nq&Z#nq&[#nq&]#nq&^#nq&_#nq&`#nq&a#nq&b#nq&c#nq&d#nq&e#nq&f#nq&g#nq&h#nq&i#nq&j#nq%Z#nq%_#nq~O#[,WOz%Oa{%Oa~Oo0cOv0qO{&xi~P'vOz,YO{&xi~O{#{O%z,[Og&zXz&zX~O%uWOg&zXz&zX~Oz,`Og&yX~Og,bO~O%Y,eO~O!T%dO!U%cOg&Viz&Vi~OV$}Oa$}Oo0cOv0qOg&Ui~P'vO{,hOz$la!W$la~Oo0cOv0qO{,iOz$la!W$la~P'vOo0cOv0qO{*gO!W&Xi~P'vOz,lO!W&Xi~Oo0cOv0qOz,lO!W&Xi~P'vOz,lO{,oO!W&Xi~Og$hiz$hi!W$hi~P%SOV'|Oo0cOv0qO~P'vOn,qO~OV'|Og,rOo0cOv0qO~P'vOV'|Oo0cOv0qO!W&Sq~P'vOz$gi![$gi#g$gi%[$gi%_$gig$gi{$gi!m$gi%z$gi~P%SOV(]Oo0cOv0qO~P'vOa+QOo0cOv0qO![&Tq~P'vOz,sO![&Tq~O![,tO~OV(]Oo0cOv0qO![%}q#g%}q%[%}q%_%}qg%}q{%}q!m%}q%z%}q~P'vO{,uO~OV+UOo0cOv0qO{&li![&li!m&li%z&li~P'vOz,zO{&li![&li!m&li%z&li~O!]+YO&n+[O![!na~O{(kO![,}O~OV&OOo0cOv0qO#g%hi%[%hi%_%hi%z%hi~P'vOz-OO#g%hi%[%hi%_%hi%z%hi~O%uWO#g&rq%[&rq%_&rqg&rq~Oz-RO#g&rq%[&rq%_&rqg&rq~OV)`Oa)`O%uWO!W&ua~Oz-TO!W&ua~On$|iz$|i~P%SOV)kO~P'vOV)kOn&wq~P'vOt-XOP#myV#myf#myh#myo#mys#myv#my!P#my!Q#my!T#my!U#my!X#my!]#my!h#my!r#my!s#my!t#my!{#my!}#my#P#my#R#my#T#my#X#my#Z#my#^#my#_#my#a#my#c#my#l#my#o#my#s#my#u#my#z#my#}#my$P#my%X#my%o#my%p#my%t#my%u#my&Z#my&[#my&]#my&^#my&_#my&`#my&a#my&b#my&c#my&d#my&e#my&f#my&g#my&h#my&i#my&j#my%Z#my%_#my~O%Z-]O%_-]O~P`O#q-^OP#nyV#nyf#nyh#nyo#nys#nyv#ny!P#ny!Q#ny!T#ny!U#ny!X#ny!]#ny!h#ny!r#ny!s#ny!t#ny!{#ny!}#ny#P#ny#R#ny#T#ny#X#ny#Z#ny#^#ny#_#ny#a#ny#c#ny#l#ny#o#ny#s#ny#u#ny#z#ny#}#ny$P#ny%X#ny%o#ny%p#ny%t#ny%u#ny&Z#ny&[#ny&]#ny&^#ny&_#ny&`#ny&a#ny&b#ny&c#ny&d#ny&e#ny&f#ny&g#ny&h#ny&i#ny&j#ny%Z#ny%_#ny~Oz-aO{$jO#[-aO~Oo0cOv0qO{&xq~P'vOz-dO{&xq~O%z,[Og&zaz&za~O{#{Og&zaz&za~OV*SOa*TO%q*UO%uWOg&ya~Oz-hOg&ya~O$S-lO~OV$}Oa$}Oo0cOv0qO~P'vOo0cOv0qO{-mOz$li!W$li~P'vOo0cOv0qOz$li!W$li~P'vO{-mOz$li!W$li~Oo0cOv0qO{*gO~P'vOo0cOv0qO{*gO!W&Xq~P'vOz-pO!W&Xq~Oo0cOv0qOz-pO!W&Xq~P'vOs-sO!T%dO!U%cOg&Oq!W&Oq![&Oqz&Oq~P!/jOa+QOo0cOv0qO![&Ty~P'vOz$ji![$ji~P%SOa+QOo0cOv0qO~P'vOV+UOo0cOv0qO~P'vOV+UOo0cOv0qO{&lq![&lq!m&lq%z&lq~P'vO{(kO![-xO!m-yO%z-wO~OV&OOo0cOv0qO#g%hq%[%hq%_%hq%z%hq~P'vO%uWO#g&ry%[&ry%_&ryg&ry~OV)`Oa)`O%uWO!W&ui~Ot-}OP#m!RV#m!Rf#m!Rh#m!Ro#m!Rs#m!Rv#m!R!P#m!R!Q#m!R!T#m!R!U#m!R!X#m!R!]#m!R!h#m!R!r#m!R!s#m!R!t#m!R!{#m!R!}#m!R#P#m!R#R#m!R#T#m!R#X#m!R#Z#m!R#^#m!R#_#m!R#a#m!R#c#m!R#l#m!R#o#m!R#s#m!R#u#m!R#z#m!R#}#m!R$P#m!R%X#m!R%o#m!R%p#m!R%t#m!R%u#m!R&Z#m!R&[#m!R&]#m!R&^#m!R&_#m!R&`#m!R&a#m!R&b#m!R&c#m!R&d#m!R&e#m!R&f#m!R&g#m!R&h#m!R&i#m!R&j#m!R%Z#m!R%_#m!R~Oo0cOv0qO{&xy~P'vOV*SOa*TO%q*UO%uWOg&yi~O$S-lO%Z.VO%_.VO~OV.aOh._O!X.^O!].`O!h.YO!s.[O!t.[O%p.XO%uWO&Z]O&[]O&]]O&^]O&_]O&`]O&a]O&b]O~Oo0cOv0qOz$lq!W$lq~P'vO{.fOz$lq!W$lq~Oo0cOv0qO{*gO!W&Xy~P'vOz.gO!W&Xy~Oo0cOv.kO~P'vOs-sO!T%dO!U%cOg&Oy!W&Oy![&Oyz&Oy~P!/jO{(kO![.nO~O{(kO![.nO!m.oO~OV*SOa*TO%q*UO%uWO~Oh.tO!f.rOz$TX#[$TX%j$TXg$TX~Os$TX{$TX!W$TX![$TX~P$-bO%o.vO%p.vOs$UXz$UX{$UX#[$UX%j$UX!W$UXg$UX![$UX~O!h.xO~Oz.|O#[/OO%j.yOs&|X{&|X!W&|Xg&|X~Oa/RO~P$)zOh.tOs&}Xz&}X{&}X#[&}X%j&}X!W&}Xg&}X![&}X~Os/VO{$jO~Oo0cOv0qOz$ly!W$ly~P'vOo0cOv0qO{*gO!W&X!R~P'vOz/ZO!W&X!R~Og&RXs&RX!T&RX!U&RX!W&RX![&RXz&RX~P!/jOs-sO!T%dO!U%cOg&Qa!W&Qa![&Qaz&Qa~O{(kO![/^O~O!f.rOh$[as$[az$[a{$[a#[$[a%j$[a!W$[ag$[a![$[a~O!h/eO~O%o.vO%p.vOs$Uaz$Ua{$Ua#[$Ua%j$Ua!W$Uag$Ua![$Ua~O%j.yOs$Yaz$Ya{$Ya#[$Ya!W$Yag$Ya![$Ya~Os&|a{&|a!W&|ag&|a~P$)nOz/jOs&|a{&|a!W&|ag&|a~O!W/mO~Og/mO~O{/oO~O![/pO~Oo0cOv0qO{*gO!W&X!Z~P'vO{/sO~O%z/tO~P$-bOz/uO#[/OO%j.yOg'PX~Oz/uOg'PX~Og/wO~O!h/xO~O#[/OOs%Saz%Sa{%Sa%j%Sa!W%Sag%Sa![%Sa~O#[/OO%j.yOs%Waz%Wa{%Wa!W%Wag%Wa~Os&|i{&|i!W&|ig&|i~P$)nOz/zO#[/OO%j.yO!['Oa~Og'Pa~P$)nOz0SOg'Pa~Oa0UO!['Oi~P$)zOz0WO!['Oi~Oz0WO#[/OO%j.yO!['Oi~O#[/OO%j.yOg$biz$bi~O%z0ZO~P$-bO#[/OO%j.yOg%Vaz%Va~Og'Pi~P$)nO{0^O~Oa0UO!['Oq~P$)zOz0`O!['Oq~O#[/OO%j.yOz%Ui![%Ui~Oa0UO~P$)zOa0UO!['Oy~P$)zO#[/OO%j.yOg$ciz$ci~O#[/OO%j.yOz%Uq![%Uq~Oz+aO#g%ha%[%ha%_%ha%z%ha~P%SOV&OOo0cOv0qO~P'vOn0hO~Oo0hO~P'vO{0iO~Ot0jO~P!/jO&]&Z&j&h&i&g&f&d&e&c&b&`&a&_&^&[%u~",
|
|
@@ -49498,7 +50231,7 @@ var parser2 = LRParser.deserialize({
|
|
|
49498
50231
|
});
|
|
49499
50232
|
|
|
49500
50233
|
// src/adapters.ts
|
|
49501
|
-
var phpEngine = new
|
|
50234
|
+
var phpEngine = new import_php_parser4.Engine({ parser: { extractDoc: false, php7: true }, ast: { withPositions: true, withSource: false } });
|
|
49502
50235
|
var PHP_SOURCES = [
|
|
49503
50236
|
["_GET", "http_parameter"],
|
|
49504
50237
|
["_POST", "http_body"],
|
|
@@ -49646,8 +50379,13 @@ var JS_SINKS = [
|
|
|
49646
50379
|
[/document\.write/i, "html_render", "CWE-79"],
|
|
49647
50380
|
[/fs\.writeFile/i, "file_operations", "CWE-434"],
|
|
49648
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"],
|
|
49649
50385
|
[/require\s*\(/i, "file_inclusion", "CWE-98"],
|
|
49650
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"],
|
|
49651
50389
|
[/redirect\s*\(/i, "redirect", "CWE-601"],
|
|
49652
50390
|
[/res\.redirect/i, "redirect", "CWE-601"]
|
|
49653
50391
|
];
|
|
@@ -49734,6 +50472,8 @@ var jsAdapter = {
|
|
|
49734
50472
|
argVars.push(c.name);
|
|
49735
50473
|
for (const [re, kind, cwe] of JS_SINKS) {
|
|
49736
50474
|
if (re.test(fullCallText) || re.test(calleeText + "(")) {
|
|
50475
|
+
if (kind === "sql_execution" && /\?/.test(fullCallText) && /,\s*\[/.test(fullCallText))
|
|
50476
|
+
continue;
|
|
49737
50477
|
for (const v of argVars)
|
|
49738
50478
|
sinks.push({ variable: v, kind, line: n.loc?.start?.line ?? 0, cwe });
|
|
49739
50479
|
}
|
|
@@ -49772,6 +50512,8 @@ var PY_SINKS = [
|
|
|
49772
50512
|
[/\.save\s*\(/i, "file_operations", "CWE-434"],
|
|
49773
50513
|
[/render_template/i, "html_render", "CWE-79"],
|
|
49774
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"],
|
|
49775
50517
|
[/pickle\.loads\s*\(/i, "deserialization", "CWE-502"],
|
|
49776
50518
|
[/yaml\.load\s*\(/i, "deserialization", "CWE-502"],
|
|
49777
50519
|
[/os\.popen\s*\(/i, "command_execution", "CWE-78"],
|
|
@@ -49791,7 +50533,7 @@ var pythonAdapter = {
|
|
|
49791
50533
|
language: "python",
|
|
49792
50534
|
extensions: [".py", ".pyw"],
|
|
49793
50535
|
parse(code, _file) {
|
|
49794
|
-
const tree =
|
|
50536
|
+
const tree = parser4.parse(code);
|
|
49795
50537
|
const sources = [];
|
|
49796
50538
|
const sinks = [];
|
|
49797
50539
|
const sanitizers = [];
|
|
@@ -49856,9 +50598,14 @@ var pythonAdapter = {
|
|
|
49856
50598
|
if (m[1] !== callee.trim().split(".").pop())
|
|
49857
50599
|
argVars.push(m[1]);
|
|
49858
50600
|
for (const [re, kind, cwe] of PY_SINKS) {
|
|
49859
|
-
if (re.test(full))
|
|
49860
|
-
|
|
49861
|
-
|
|
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 });
|
|
49862
50609
|
}
|
|
49863
50610
|
for (const [re, kinds] of PY_SANITIZERS) {
|
|
49864
50611
|
if (re.test(full))
|
|
@@ -49894,6 +50641,10 @@ var JAVA_SINKS = [
|
|
|
49894
50641
|
[/readObject\s*\(/i, "deserialization", "CWE-502"],
|
|
49895
50642
|
[/new File\s*\(/i, "file_operations", "CWE-434"],
|
|
49896
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"],
|
|
49897
50648
|
[/sendRedirect\s*\(/i, "redirect", "CWE-601"],
|
|
49898
50649
|
[/\.forward\s*\(/i, "redirect", "CWE-601"]
|
|
49899
50650
|
];
|
|
@@ -50774,7 +51525,7 @@ function reportJson(findings, opts = {}) {
|
|
|
50774
51525
|
}
|
|
50775
51526
|
|
|
50776
51527
|
// src/benchmark.ts
|
|
50777
|
-
import { readFileSync as
|
|
51528
|
+
import { readFileSync as readFileSync13, readdirSync as readdirSync4, writeFileSync as writeFileSync2, rmSync } from "node:fs";
|
|
50778
51529
|
import { join as join9 } from "node:path";
|
|
50779
51530
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
50780
51531
|
import { tmpdir } from "node:os";
|
|
@@ -50783,7 +51534,7 @@ var CORPUS = join9(ROOT4, "bench", "corpus");
|
|
|
50783
51534
|
function parseCorpusFile(path) {
|
|
50784
51535
|
let text;
|
|
50785
51536
|
try {
|
|
50786
|
-
text =
|
|
51537
|
+
text = readFileSync13(path, "utf8");
|
|
50787
51538
|
} catch {
|
|
50788
51539
|
return null;
|
|
50789
51540
|
}
|
|
@@ -50847,7 +51598,7 @@ function detect(entry) {
|
|
|
50847
51598
|
if (lang === "php") {
|
|
50848
51599
|
const tmp = join9(tmpdir(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
|
|
50849
51600
|
writeFileSync2(tmp, entry.code);
|
|
50850
|
-
const r =
|
|
51601
|
+
const r = analyzeDataFlow2(tmp);
|
|
50851
51602
|
rmSync(tmp, { force: true });
|
|
50852
51603
|
if (r.findings.length > 0)
|
|
50853
51604
|
return { detected: true, sinkKind: r.findings[0].sink };
|
|
@@ -51631,6 +52382,38 @@ function createServer() {
|
|
|
51631
52382
|
});
|
|
51632
52383
|
return { content: [{ type: "text", text: JSON.stringify({ ...r, next_steps: guidance }) }] };
|
|
51633
52384
|
});
|
|
52385
|
+
server.registerTool("eagle_eye2", {
|
|
52386
|
+
title: "Whole-program interprocedural taint (EAGLE-EYE 2.0)",
|
|
52387
|
+
description: "EAGLE-EYE 2.0: whole-program data-flow on a PHP AST — call-graph-aware interprocedural taint with return propagation, by-reference alias tracking, ternary/conditional merge, context-sensitive sanitizers, and authorization-gate detection. Prefer this over taint_scan for accurate cross-function reachability.",
|
|
52388
|
+
inputSchema: { path: string2().describe("Source file path (PHP)") }
|
|
52389
|
+
}, async ({ path }) => {
|
|
52390
|
+
const r = analyzeDataFlow2(path);
|
|
52391
|
+
const taintedSinks = [...new Set(r.findings.map((f) => f.sink))];
|
|
52392
|
+
const authGated = r.findings.filter((f) => f.auth_gated).length;
|
|
52393
|
+
const guidance = nextSteps({
|
|
52394
|
+
tier: "eagle-eye",
|
|
52395
|
+
hasFindings: r.findings.length > 0,
|
|
52396
|
+
sanitizedCount: r.suppressed,
|
|
52397
|
+
taintedSinks
|
|
52398
|
+
});
|
|
52399
|
+
return {
|
|
52400
|
+
content: [{
|
|
52401
|
+
type: "text",
|
|
52402
|
+
text: JSON.stringify({
|
|
52403
|
+
file: r.file,
|
|
52404
|
+
findings: r.findings.length,
|
|
52405
|
+
suppressed: r.suppressed,
|
|
52406
|
+
auth_gated: authGated,
|
|
52407
|
+
interprocedural: r.findings.filter((f) => f.interprocedural).length,
|
|
52408
|
+
functions: r.summary.functions,
|
|
52409
|
+
methods: r.summary.methods,
|
|
52410
|
+
call_edges: r.summary.edges,
|
|
52411
|
+
results: r.findings,
|
|
52412
|
+
next_steps: guidance
|
|
52413
|
+
})
|
|
52414
|
+
}]
|
|
52415
|
+
};
|
|
52416
|
+
});
|
|
51634
52417
|
server.registerTool("taint_tree", {
|
|
51635
52418
|
title: "Whole-tree taint scan",
|
|
51636
52419
|
description: "EAGLE-EYE: run inter-procedural taint analysis across an entire source tree.",
|
|
@@ -51784,7 +52567,7 @@ async function serve() {
|
|
|
51784
52567
|
}
|
|
51785
52568
|
|
|
51786
52569
|
// src/cli.ts
|
|
51787
|
-
import { readFileSync as
|
|
52570
|
+
import { readFileSync as readFileSync15, existsSync as existsSync6, writeFileSync as writeFileSync4, mkdirSync as mkdirSync4 } from "node:fs";
|
|
51788
52571
|
import { join as join11, dirname as dirname2 } from "node:path";
|
|
51789
52572
|
import { homedir as homedir5 } from "node:os";
|
|
51790
52573
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
@@ -51866,7 +52649,7 @@ function resolveCommand() {
|
|
|
51866
52649
|
}
|
|
51867
52650
|
function readJson(p) {
|
|
51868
52651
|
try {
|
|
51869
|
-
return JSON.parse(
|
|
52652
|
+
return JSON.parse(readFileSync15(p, "utf8"));
|
|
51870
52653
|
} catch {
|
|
51871
52654
|
return null;
|
|
51872
52655
|
}
|
|
@@ -51909,7 +52692,7 @@ function copyOpenCodeAgents() {
|
|
|
51909
52692
|
const src = join11(srcDir, f);
|
|
51910
52693
|
const dst = join11(dstDir, f);
|
|
51911
52694
|
if (existsSync6(src)) {
|
|
51912
|
-
writeFileSync4(dst,
|
|
52695
|
+
writeFileSync4(dst, readFileSync15(src, "utf8"));
|
|
51913
52696
|
}
|
|
51914
52697
|
}
|
|
51915
52698
|
}
|
|
@@ -51918,7 +52701,7 @@ function codexWrite(p) {
|
|
|
51918
52701
|
const dir = dirname2(p);
|
|
51919
52702
|
if (!existsSync6(dir))
|
|
51920
52703
|
mkdirSync4(dir, { recursive: true });
|
|
51921
|
-
let existing = existsSync6(p) ?
|
|
52704
|
+
let existing = existsSync6(p) ? readFileSync15(p, "utf8") : "";
|
|
51922
52705
|
if (!existing.trimEnd().endsWith(`
|
|
51923
52706
|
`))
|
|
51924
52707
|
existing += `
|
|
@@ -51937,7 +52720,7 @@ function hermesWrite(p) {
|
|
|
51937
52720
|
const dir = dirname2(p);
|
|
51938
52721
|
if (!existsSync6(dir))
|
|
51939
52722
|
mkdirSync4(dir, { recursive: true });
|
|
51940
|
-
let existing = existsSync6(p) ?
|
|
52723
|
+
let existing = existsSync6(p) ? readFileSync15(p, "utf8") : "";
|
|
51941
52724
|
existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
|
|
51942
52725
|
if (!existing.trimEnd().endsWith(`
|
|
51943
52726
|
`))
|
|
@@ -52075,7 +52858,7 @@ Registered with ${ok}/${installed.length} agent(s).`);
|
|
|
52075
52858
|
}
|
|
52076
52859
|
|
|
52077
52860
|
// src/index.ts
|
|
52078
|
-
import { readFileSync as
|
|
52861
|
+
import { readFileSync as readFileSync16, existsSync as existsSync7 } from "node:fs";
|
|
52079
52862
|
import { join as join12 } from "node:path";
|
|
52080
52863
|
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
52081
52864
|
var ROOT7 = join12(fileURLToPath9(new URL(".", import.meta.url)), "..");
|
|
@@ -52083,7 +52866,7 @@ function readVersion() {
|
|
|
52083
52866
|
try {
|
|
52084
52867
|
const p = join12(ROOT7, "package.json");
|
|
52085
52868
|
if (existsSync7(p))
|
|
52086
|
-
return JSON.parse(
|
|
52869
|
+
return JSON.parse(readFileSync16(p, "utf8")).version ?? "1.0.0";
|
|
52087
52870
|
} catch {}
|
|
52088
52871
|
return "1.0.0";
|
|
52089
52872
|
}
|