blitzstrike 2.4.97 → 2.4.99
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 +22 -1
- package/dist/index.js +230 -203
- package/intelligence/watchlist.json +36 -18
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,9 +5,30 @@ 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
|
+
## [2.4.99] — 2026-09-17
|
|
9
|
+
|
|
10
|
+
### Fixed — grounding completeness for the CVE watchlist class tags
|
|
11
|
+
|
|
12
|
+
- The CVE watchlist's `class` tags (`auth_bypass`, `infoleak`) did not resolve
|
|
13
|
+
to a detector pattern. Added synonyms (`auth bypass`/`authentication bypass`
|
|
14
|
+
→ `type_juggling`; `infoleak`/`info|information disclosure|exposure|leak` →
|
|
15
|
+
`hardcoded_secret`). Every one of the 18 CVE class tags now resolves to a
|
|
16
|
+
grounded pattern (locked by a regression check).
|
|
17
|
+
|
|
18
|
+
## [2.4.98] — 2026-09-17
|
|
19
|
+
|
|
20
|
+
### Added — fingerprint-first CVE matching wired into live_recon
|
|
21
|
+
|
|
22
|
+
- `live_recon()` now cross-references the detected stack (tech + headers +
|
|
23
|
+
body) against the CVE watchlist and returns `suggested_cves` + a
|
|
24
|
+
"FINGERPRINT MATCH" next-step: when the target matches a high-value
|
|
25
|
+
watchlist product (Next.js, Kibana, Langflow, Confluence/Jira, PAN-OS,
|
|
26
|
+
Grafana, Keycloak, …) it names the specific CVEs to test first (with EPSS)
|
|
27
|
+
instead of relying on generic scanning.
|
|
28
|
+
|
|
8
29
|
## [2.4.97] — 2026-09-17
|
|
9
30
|
|
|
10
|
-
### Added —
|
|
31
|
+
### Added — hunting-intel upgrade (Hacktivity-ranked CVE watchlist + dedup doctrine)
|
|
11
32
|
|
|
12
33
|
- Upgraded `watchlist()` CVE data from a generic 9-CVE list to the 18
|
|
13
34
|
Hacktivity-ranked CVEs with EPSS, report counts, and per-CVE fingerprint
|
package/dist/index.js
CHANGED
|
@@ -17285,12 +17285,12 @@ document.getElementById('out').innerHTML = u;
|
|
|
17285
17285
|
});
|
|
17286
17286
|
|
|
17287
17287
|
// src/variants.ts
|
|
17288
|
-
import { readFileSync as
|
|
17289
|
-
import { join as
|
|
17288
|
+
import { readFileSync as readFileSync21 } from "node:fs";
|
|
17289
|
+
import { join as join21, dirname as dirname5 } from "node:path";
|
|
17290
17290
|
import { fileURLToPath as fileURLToPath11 } from "node:url";
|
|
17291
17291
|
function loadVariants() {
|
|
17292
17292
|
try {
|
|
17293
|
-
const doc = JSON.parse(
|
|
17293
|
+
const doc = JSON.parse(readFileSync21(join21(__dirname5, "..", "intelligence", "variants.json"), "utf8"));
|
|
17294
17294
|
return doc.variants ?? [];
|
|
17295
17295
|
} catch {
|
|
17296
17296
|
return [];
|
|
@@ -32693,8 +32693,8 @@ async function nvdLookup(cveId) {
|
|
|
32693
32693
|
|
|
32694
32694
|
// src/orchestrator.ts
|
|
32695
32695
|
init_scanner();
|
|
32696
|
-
import { readFileSync as
|
|
32697
|
-
import { join as
|
|
32696
|
+
import { readFileSync as readFileSync11, existsSync as existsSync8, statSync as statSync2 } from "node:fs";
|
|
32697
|
+
import { join as join13 } from "node:path";
|
|
32698
32698
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
32699
32699
|
|
|
32700
32700
|
// src/memory.ts
|
|
@@ -34489,6 +34489,14 @@ var ALIASES = {
|
|
|
34489
34489
|
"hardcoded credential": "hardcoded_secret",
|
|
34490
34490
|
"hardcoded secret": "hardcoded_secret",
|
|
34491
34491
|
"credential leak": "hardcoded_secret",
|
|
34492
|
+
"auth bypass": "type_juggling",
|
|
34493
|
+
"authentication bypass": "type_juggling",
|
|
34494
|
+
auth_bypass: "type_juggling",
|
|
34495
|
+
infoleak: "hardcoded_secret",
|
|
34496
|
+
"info leak": "hardcoded_secret",
|
|
34497
|
+
"information leak": "hardcoded_secret",
|
|
34498
|
+
"information disclosure": "hardcoded_secret",
|
|
34499
|
+
"information exposure": "hardcoded_secret",
|
|
34492
34500
|
"hard coded credentials": "hardcoded_secret",
|
|
34493
34501
|
"priv esc": "priv_esc",
|
|
34494
34502
|
privesc: "priv_esc",
|
|
@@ -36067,8 +36075,135 @@ function toSarif(findings, opts = {}) {
|
|
|
36067
36075
|
// src/orchestrator.ts
|
|
36068
36076
|
init_version();
|
|
36069
36077
|
|
|
36078
|
+
// src/hunting-intel.ts
|
|
36079
|
+
import { execFileSync } from "node:child_process";
|
|
36080
|
+
import { resolve4, Resolver } from "node:dns/promises";
|
|
36081
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
36082
|
+
import { join as join12 } from "node:path";
|
|
36083
|
+
var BLOCKING_KEYWORDS = /internet positif|kominfo|\.go\.id\b|pemblokiran|ditutup|dilarang|akses dibatasi|blocked|restricted|illegal|advertencia|coljuegos|regulat|forbidden|this (site|website) has been (blocked|banned)/i;
|
|
36084
|
+
var HOSTNAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i;
|
|
36085
|
+
function isValidHostname(host) {
|
|
36086
|
+
return host.length <= 253 && HOSTNAME_RE.test(host);
|
|
36087
|
+
}
|
|
36088
|
+
async function localResolve(host) {
|
|
36089
|
+
try {
|
|
36090
|
+
return await resolve4(host);
|
|
36091
|
+
} catch {
|
|
36092
|
+
return [];
|
|
36093
|
+
}
|
|
36094
|
+
}
|
|
36095
|
+
async function publicResolve(host) {
|
|
36096
|
+
const r = new Resolver;
|
|
36097
|
+
r.setServers(["8.8.8.8", "1.1.1.1"]);
|
|
36098
|
+
try {
|
|
36099
|
+
return await r.resolve4(host);
|
|
36100
|
+
} catch {
|
|
36101
|
+
return [];
|
|
36102
|
+
}
|
|
36103
|
+
}
|
|
36104
|
+
function probeBlockingPage(host, ip, timeoutMs = 8000) {
|
|
36105
|
+
for (const port of ["443", "80"]) {
|
|
36106
|
+
const scheme = port === "443" ? "https" : "http";
|
|
36107
|
+
try {
|
|
36108
|
+
const out = execFileSync("curl", ["-sk", "-m", "6", "--resolve", `${host}:${port}:${ip}`, `${scheme}://${host}/`], { encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"] });
|
|
36109
|
+
const preview = out.replace(/\s+/g, " ").trim().slice(0, 300);
|
|
36110
|
+
if (!preview)
|
|
36111
|
+
continue;
|
|
36112
|
+
return { probed: true, sinkhole: BLOCKING_KEYWORDS.test(preview), preview };
|
|
36113
|
+
} catch {}
|
|
36114
|
+
}
|
|
36115
|
+
return { probed: false, sinkhole: false, preview: "" };
|
|
36116
|
+
}
|
|
36117
|
+
async function resolveCheck(host) {
|
|
36118
|
+
const clean = host.replace(/^https?:\/\//, "").split("/")[0];
|
|
36119
|
+
if (!isValidHostname(clean)) {
|
|
36120
|
+
return { host: clean, local_ips: [], public_ips: [], sinkhole: false, verdict: "invalid", evidence: "not a valid hostname — rejected before DNS/subprocess" };
|
|
36121
|
+
}
|
|
36122
|
+
const local = await localResolve(clean);
|
|
36123
|
+
const pub = await publicResolve(clean);
|
|
36124
|
+
if (local.length === 0 && pub.length === 0) {
|
|
36125
|
+
return { host: clean, local_ips: [], public_ips: [], sinkhole: false, verdict: "unresolvable", evidence: "no A record from local or public DNS" };
|
|
36126
|
+
}
|
|
36127
|
+
if (local.length === 0) {
|
|
36128
|
+
return { host: clean, local_ips: [], public_ips: pub, sinkhole: false, verdict: "local-unresolvable", evidence: "local resolver returned no A record; public DNS resolves. Use the public IP to reach the real target." };
|
|
36129
|
+
}
|
|
36130
|
+
const firstLocal = local[0];
|
|
36131
|
+
const probe = probeBlockingPage(clean, firstLocal);
|
|
36132
|
+
if (probe.sinkhole) {
|
|
36133
|
+
return {
|
|
36134
|
+
host: clean,
|
|
36135
|
+
local_ips: local,
|
|
36136
|
+
public_ips: pub,
|
|
36137
|
+
sinkhole: true,
|
|
36138
|
+
verdict: "sinkhole",
|
|
36139
|
+
evidence: `local IP ${firstLocal} serves a blocking/sinkhole page ("${probe.preview.slice(0, 120)}"). Audit the PUBLIC IP (${pub[0] ?? "n/a"}) instead — e.g. curl --resolve ${clean}:443:${pub[0]} https://${clean}/`
|
|
36140
|
+
};
|
|
36141
|
+
}
|
|
36142
|
+
if (!probe.probed) {
|
|
36143
|
+
return { host: clean, local_ips: local, public_ips: pub, sinkhole: false, verdict: "inconclusive", evidence: "local IP did not respond on 443/80 — inconclusive; for sensitive niches prefer the public IP anyway." };
|
|
36144
|
+
}
|
|
36145
|
+
const ipDiff = new Set(local).size !== new Set(pub).size || !pub.every((ip) => local.includes(ip));
|
|
36146
|
+
return {
|
|
36147
|
+
host: clean,
|
|
36148
|
+
local_ips: local,
|
|
36149
|
+
public_ips: pub,
|
|
36150
|
+
sinkhole: false,
|
|
36151
|
+
verdict: ipDiff ? "clean-cdn" : "clean",
|
|
36152
|
+
evidence: ipDiff ? "local and public IPs differ but the local page shows no blocking content (normal for CDN/anycast/round-robin)." : "local resolution matches public DNS and shows no blocking content."
|
|
36153
|
+
};
|
|
36154
|
+
}
|
|
36155
|
+
function loadWatchlist() {
|
|
36156
|
+
const raw = readFileSync10(join12(process.cwd(), "intelligence", "watchlist.json"), "utf8");
|
|
36157
|
+
const data = JSON.parse(raw);
|
|
36158
|
+
return { cwe_frequency: data.cwe_frequency ?? [], cve_watchlist: data.cve_watchlist ?? [] };
|
|
36159
|
+
}
|
|
36160
|
+
function fmtPayout(min, max) {
|
|
36161
|
+
if (min == null || max == null)
|
|
36162
|
+
return "n/a";
|
|
36163
|
+
const f = (n) => n >= 1000 ? `$${(n / 1000).toFixed(n % 1000 === 0 ? 0 : 1)}K` : `$${n}`;
|
|
36164
|
+
return `${f(min)}–${f(max)}`;
|
|
36165
|
+
}
|
|
36166
|
+
function watchlist() {
|
|
36167
|
+
const { cwe_frequency, cve_watchlist } = loadWatchlist();
|
|
36168
|
+
const entries = cwe_frequency.map((e) => {
|
|
36169
|
+
const pat = e.pattern ? patternLookup(e.pattern) : null;
|
|
36170
|
+
const payoutMin = e.payout_min ?? pat?.payout_min ?? null;
|
|
36171
|
+
const payoutMax = e.payout_max ?? pat?.payout_max ?? null;
|
|
36172
|
+
return {
|
|
36173
|
+
cwe: e.cwe,
|
|
36174
|
+
name: e.name,
|
|
36175
|
+
rank: e.rank,
|
|
36176
|
+
saturated: e.saturated,
|
|
36177
|
+
pattern: e.pattern ?? null,
|
|
36178
|
+
payout_min: payoutMin,
|
|
36179
|
+
payout_max: payoutMax,
|
|
36180
|
+
typical_payout: fmtPayout(payoutMin, payoutMax),
|
|
36181
|
+
notes: e.notes,
|
|
36182
|
+
grounded: pat != null
|
|
36183
|
+
};
|
|
36184
|
+
}).sort((a, b) => a.rank - b.rank);
|
|
36185
|
+
const unsaturated = entries.filter((e) => !e.saturated);
|
|
36186
|
+
const topCve = cve_watchlist.filter((c) => (c.epss ?? 0) >= 0.8 || (c.reports ?? 0) >= 100).slice(0, 4).map((c) => c.cve).join(", ");
|
|
36187
|
+
const recommendation = `Prioritize the ${unsaturated.length} non-saturated classes (top: ${unsaturated.slice(0, 3).map((e) => e.cwe).join(", ")}) — high frequency + high payout + not yet saturated. Skip saturated/low-value classes (XSS/CSRF/open-redirect standalone) unless they chain into account takeover. FINGERPRINT FIRST: if the target stack matches a watchlist product (Next.js, Kibana, Langflow, Confluence/Jira, PAN-OS, Grafana), test that specific CVE — top EPSS/report CVEs: ${topCve}.`;
|
|
36188
|
+
return { cwe_frequency: entries, cve_watchlist, recommendation };
|
|
36189
|
+
}
|
|
36190
|
+
function watchlistCveMatches(techs, headers = {}, body = "") {
|
|
36191
|
+
const { cve_watchlist } = loadWatchlist();
|
|
36192
|
+
const haystack = `${techs.join(" ")} ${Object.keys(headers).join(" ")} ${Object.values(headers).join(" ")} ${body}`.toLowerCase();
|
|
36193
|
+
return cve_watchlist.filter((c) => {
|
|
36194
|
+
const m = c.match;
|
|
36195
|
+
if (!m)
|
|
36196
|
+
return false;
|
|
36197
|
+
try {
|
|
36198
|
+
return new RegExp(m, "i").test(haystack);
|
|
36199
|
+
} catch {
|
|
36200
|
+
return false;
|
|
36201
|
+
}
|
|
36202
|
+
});
|
|
36203
|
+
}
|
|
36204
|
+
|
|
36070
36205
|
// src/live-recon.ts
|
|
36071
|
-
import { resolve4 } from "node:dns/promises";
|
|
36206
|
+
import { resolve4 as resolve42 } from "node:dns/promises";
|
|
36072
36207
|
import { connect } from "node:net";
|
|
36073
36208
|
var HTTP_TIMEOUT_MS = 20000;
|
|
36074
36209
|
function isLikelyCdn(ip) {
|
|
@@ -36284,7 +36419,7 @@ async function dnsBrute(domain) {
|
|
|
36284
36419
|
const hosts = COMMON_SUBDOMAINS.map((p) => `${p}.${base}`).filter((h) => !seen.has(h));
|
|
36285
36420
|
const settled = await Promise.allSettled(hosts.map(async (host) => {
|
|
36286
36421
|
try {
|
|
36287
|
-
const ips = await
|
|
36422
|
+
const ips = await resolve42(host);
|
|
36288
36423
|
return ips.length > 0 ? { host, ips } : null;
|
|
36289
36424
|
} catch {
|
|
36290
36425
|
return null;
|
|
@@ -36501,6 +36636,7 @@ async function liveRecon(target, includeActive = false) {
|
|
|
36501
36636
|
const waf = detectWaf(root.headers, root.body);
|
|
36502
36637
|
const techs = detectTech(root.headers, root.body);
|
|
36503
36638
|
const versions = extractVersion(root.headers, root.body);
|
|
36639
|
+
const suggestedCves = watchlistCveMatches(techs, root.headers, root.body);
|
|
36504
36640
|
const { links, scripts } = crawlLinks(base, root.body);
|
|
36505
36641
|
const { robots, sitemap } = await fetchRobotsAndSitemap(base);
|
|
36506
36642
|
const allUrls = [...links, ...scripts, ...robots, ...sitemap];
|
|
@@ -36534,6 +36670,9 @@ async function liveRecon(target, includeActive = false) {
|
|
|
36534
36670
|
for (const v of versions) {
|
|
36535
36671
|
nextSteps.push(`Version ${v.product}/${v.version} detected — check cve_correlation + nvd_lookup for known CVEs affecting ${v.product} ${v.version}.`);
|
|
36536
36672
|
}
|
|
36673
|
+
if (suggestedCves.length > 0) {
|
|
36674
|
+
nextSteps.push(`FINGERPRINT MATCH: the stack matches a high-value watchlist product — test these specific CVEs first instead of generic scanning: ${suggestedCves.map((c) => `${c.cve} (${c.name}, EPSS ${((c.epss ?? 0) * 100).toFixed(0)}%)`).join("; ")}.`);
|
|
36675
|
+
}
|
|
36537
36676
|
if (crawl.endpoints.length > 0)
|
|
36538
36677
|
nextSteps.push(`Found ${crawl.endpoints.length} endpoints — feed them to taint_file/blitz_scan for source analysis, or strike_verify for live validation.`);
|
|
36539
36678
|
if (params.length > 0)
|
|
@@ -36560,6 +36699,7 @@ async function liveRecon(target, includeActive = false) {
|
|
|
36560
36699
|
waf,
|
|
36561
36700
|
tech: { detected: techs, server: fingerprint.server },
|
|
36562
36701
|
versions,
|
|
36702
|
+
suggested_cves: suggestedCves.map((c) => ({ cve: c.cve, name: c.name, product: c.product, epss: c.epss, reports: c.reports })),
|
|
36563
36703
|
crawl,
|
|
36564
36704
|
params,
|
|
36565
36705
|
subdomains,
|
|
@@ -36636,9 +36776,9 @@ function loadChains() {
|
|
|
36636
36776
|
if (_chainsCache)
|
|
36637
36777
|
return _chainsCache;
|
|
36638
36778
|
try {
|
|
36639
|
-
const dir =
|
|
36640
|
-
const path =
|
|
36641
|
-
const raw = JSON.parse(
|
|
36779
|
+
const dir = join13(fileURLToPath6(new URL(".", import.meta.url)), "..");
|
|
36780
|
+
const path = join13(dir, "chains.json");
|
|
36781
|
+
const raw = JSON.parse(readFileSync11(path, "utf8"));
|
|
36642
36782
|
_chainsCache = raw.chains ?? [];
|
|
36643
36783
|
return _chainsCache;
|
|
36644
36784
|
} catch {
|
|
@@ -37419,8 +37559,8 @@ var CHAIN_ID_TO_ESCALATION = {
|
|
|
37419
37559
|
};
|
|
37420
37560
|
function loadChainLinks() {
|
|
37421
37561
|
try {
|
|
37422
|
-
const dir =
|
|
37423
|
-
const data = JSON.parse(
|
|
37562
|
+
const dir = join13(fileURLToPath6(new URL(".", import.meta.url)), "..");
|
|
37563
|
+
const data = JSON.parse(readFileSync11(join13(dir, "intelligence", "chain_links.json"), "utf8"));
|
|
37424
37564
|
return data.links ?? [];
|
|
37425
37565
|
} catch {
|
|
37426
37566
|
return [];
|
|
@@ -37491,7 +37631,7 @@ async function runAutonomous(target, scope = "", mode = "bug-bounty", maxPasses
|
|
|
37491
37631
|
}
|
|
37492
37632
|
|
|
37493
37633
|
// src/ops.ts
|
|
37494
|
-
import { execSync, execFileSync } from "node:child_process";
|
|
37634
|
+
import { execSync, execFileSync as execFileSync2 } from "node:child_process";
|
|
37495
37635
|
function decomposeBatches(tasks) {
|
|
37496
37636
|
const remaining = [...tasks];
|
|
37497
37637
|
const done = new Set;
|
|
@@ -37536,13 +37676,13 @@ function worktree(action, path, branch) {
|
|
|
37536
37676
|
if (!path)
|
|
37537
37677
|
return { action, ok: false, error: "path is required for worktree add" };
|
|
37538
37678
|
const b = branch ?? `blitz-${Date.now().toString(36)}`;
|
|
37539
|
-
const out =
|
|
37679
|
+
const out = execFileSync2("git", ["worktree", "add", "-b", b, path], { encoding: "utf8" }).trim();
|
|
37540
37680
|
return { action, ok: true, branch: b, path, out };
|
|
37541
37681
|
}
|
|
37542
37682
|
if (action === "remove") {
|
|
37543
37683
|
if (!path)
|
|
37544
37684
|
return { action, ok: false, error: "path is required for worktree remove" };
|
|
37545
|
-
|
|
37685
|
+
execFileSync2("git", ["worktree", "remove", path, "--force"], { encoding: "utf8" });
|
|
37546
37686
|
return { action, ok: true, path };
|
|
37547
37687
|
}
|
|
37548
37688
|
return { action, ok: false, error: "unknown action (use add | list | remove)" };
|
|
@@ -37576,18 +37716,18 @@ function compactState(phase, findings) {
|
|
|
37576
37716
|
}
|
|
37577
37717
|
|
|
37578
37718
|
// src/tasks.ts
|
|
37579
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
37580
|
-
import { join as
|
|
37719
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "node:fs";
|
|
37720
|
+
import { join as join14 } from "node:path";
|
|
37581
37721
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
37582
37722
|
function tasksDir() {
|
|
37583
|
-
return
|
|
37723
|
+
return join14(fileURLToPath7(new URL(".", import.meta.url)), "..", ".blitzstrike", "tasks");
|
|
37584
37724
|
}
|
|
37585
37725
|
function taskPath(id) {
|
|
37586
|
-
return
|
|
37726
|
+
return join14(tasksDir(), `${id}.json`);
|
|
37587
37727
|
}
|
|
37588
37728
|
function load2(id) {
|
|
37589
37729
|
try {
|
|
37590
|
-
return JSON.parse(
|
|
37730
|
+
return JSON.parse(readFileSync12(taskPath(id), "utf8"));
|
|
37591
37731
|
} catch {
|
|
37592
37732
|
return null;
|
|
37593
37733
|
}
|
|
@@ -37925,18 +38065,18 @@ function scaffoldEngagement(target, scope = [], out_of_scope = []) {
|
|
|
37925
38065
|
}
|
|
37926
38066
|
|
|
37927
38067
|
// src/incremental.ts
|
|
37928
|
-
import { execFileSync as
|
|
38068
|
+
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
37929
38069
|
import { writeFileSync as writeFileSync4, mkdtempSync, rmSync } from "node:fs";
|
|
37930
|
-
import { join as
|
|
38070
|
+
import { join as join15 } from "node:path";
|
|
37931
38071
|
import { tmpdir } from "node:os";
|
|
37932
38072
|
|
|
37933
38073
|
// src/eagle2.ts
|
|
37934
38074
|
var import_php_parser2 = __toESM(require_src(), 1);
|
|
37935
|
-
import { readFileSync as
|
|
38075
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
37936
38076
|
|
|
37937
38077
|
// src/dataflow.ts
|
|
37938
38078
|
init_scanner();
|
|
37939
|
-
import { readFileSync as
|
|
38079
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
37940
38080
|
var SOURCES = {
|
|
37941
38081
|
$_GET: { id: "http_get", label: "HTTP GET parameter", attacker_controlled: true },
|
|
37942
38082
|
$_POST: { id: "http_post", label: "HTTP POST body", attacker_controlled: true },
|
|
@@ -38021,7 +38161,7 @@ function traceDataFlow(path) {
|
|
|
38021
38161
|
return result;
|
|
38022
38162
|
let text;
|
|
38023
38163
|
try {
|
|
38024
|
-
text =
|
|
38164
|
+
text = readFileSync13(path, "utf8");
|
|
38025
38165
|
} catch {
|
|
38026
38166
|
return result;
|
|
38027
38167
|
}
|
|
@@ -38102,7 +38242,7 @@ init_scanner();
|
|
|
38102
38242
|
|
|
38103
38243
|
// src/callgraph.ts
|
|
38104
38244
|
var import_php_parser = __toESM(require_src(), 1);
|
|
38105
|
-
import { readFileSync as
|
|
38245
|
+
import { readFileSync as readFileSync14 } from "node:fs";
|
|
38106
38246
|
var parser = new import_php_parser.Engine({
|
|
38107
38247
|
parser: { extractDoc: false, php7: true },
|
|
38108
38248
|
ast: { withPositions: true, withSource: false }
|
|
@@ -38163,7 +38303,7 @@ function buildCallGraph(path) {
|
|
|
38163
38303
|
const g = { nodes: new Map, edges: [] };
|
|
38164
38304
|
let code;
|
|
38165
38305
|
try {
|
|
38166
|
-
code =
|
|
38306
|
+
code = readFileSync14(path, "utf8");
|
|
38167
38307
|
} catch {
|
|
38168
38308
|
return g;
|
|
38169
38309
|
}
|
|
@@ -38825,7 +38965,7 @@ function resolveFuncId(name, summaries) {
|
|
|
38825
38965
|
function analyzeDataFlow2(path) {
|
|
38826
38966
|
let cacheCode = "";
|
|
38827
38967
|
try {
|
|
38828
|
-
cacheCode =
|
|
38968
|
+
cacheCode = readFileSync15(path, "utf8");
|
|
38829
38969
|
} catch {}
|
|
38830
38970
|
if (cacheCode) {
|
|
38831
38971
|
const cached = getCache("eagle2", cacheCode);
|
|
@@ -38847,7 +38987,7 @@ function analyzeDataFlow2(path) {
|
|
|
38847
38987
|
};
|
|
38848
38988
|
let code;
|
|
38849
38989
|
try {
|
|
38850
|
-
code = cacheCode ||
|
|
38990
|
+
code = cacheCode || readFileSync15(path, "utf8");
|
|
38851
38991
|
} catch {
|
|
38852
38992
|
return result;
|
|
38853
38993
|
}
|
|
@@ -57518,7 +57658,7 @@ registerLanguage(rustAdapter);
|
|
|
57518
57658
|
var SOURCE_EXTS = [".php", ".phtml", ".js", ".mjs", ".ts", ".tsx", ".py", ".pyw", ".java"];
|
|
57519
57659
|
function git(repo, args) {
|
|
57520
57660
|
try {
|
|
57521
|
-
return
|
|
57661
|
+
return execFileSync3("git", args.split(/\s+/).filter(Boolean), { cwd: repo, encoding: "utf8", maxBuffer: 50 * 1024 * 1024 }).trim();
|
|
57522
57662
|
} catch {
|
|
57523
57663
|
return "";
|
|
57524
57664
|
}
|
|
@@ -57533,8 +57673,8 @@ function analyzeChangedFiles(repo, base = "HEAD~1", head = "HEAD") {
|
|
|
57533
57673
|
continue;
|
|
57534
57674
|
const lang = detectLanguage(file)?.language;
|
|
57535
57675
|
if (lang === "php") {
|
|
57536
|
-
const dir = mkdtempSync(
|
|
57537
|
-
const tmp =
|
|
57676
|
+
const dir = mkdtempSync(join15(tmpdir(), "blitz-incr-"));
|
|
57677
|
+
const tmp = join15(dir, "changed.php");
|
|
57538
57678
|
writeFileSync4(tmp, code);
|
|
57539
57679
|
try {
|
|
57540
57680
|
const r = analyzeDataFlow2(tmp);
|
|
@@ -57555,7 +57695,7 @@ function analyzeChangedFiles(repo, base = "HEAD~1", head = "HEAD") {
|
|
|
57555
57695
|
// src/taint.ts
|
|
57556
57696
|
init_scanner();
|
|
57557
57697
|
var import_php_parser4 = __toESM(require_src(), 1);
|
|
57558
|
-
import { readFileSync as
|
|
57698
|
+
import { readFileSync as readFileSync16 } from "node:fs";
|
|
57559
57699
|
var parser4 = new import_php_parser4.Engine({
|
|
57560
57700
|
parser: { extractDoc: false, php7: true },
|
|
57561
57701
|
ast: { withPositions: true, withSource: false }
|
|
@@ -57853,7 +57993,7 @@ function analyzeTaint(path) {
|
|
|
57853
57993
|
const result = { file: path, findings: [], suppressed: 0 };
|
|
57854
57994
|
let code;
|
|
57855
57995
|
try {
|
|
57856
|
-
code =
|
|
57996
|
+
code = readFileSync16(path, "utf8");
|
|
57857
57997
|
} catch {
|
|
57858
57998
|
return result;
|
|
57859
57999
|
}
|
|
@@ -57947,15 +58087,15 @@ function taintTree(root, maxFiles = 2000) {
|
|
|
57947
58087
|
}
|
|
57948
58088
|
|
|
57949
58089
|
// src/differential.ts
|
|
57950
|
-
import { writeFileSync as writeFileSync5, rmSync as rmSync2, mkdtempSync as mkdtempSync2, readFileSync as
|
|
57951
|
-
import { join as
|
|
58090
|
+
import { writeFileSync as writeFileSync5, rmSync as rmSync2, mkdtempSync as mkdtempSync2, readFileSync as readFileSync17 } from "node:fs";
|
|
58091
|
+
import { join as join16 } from "node:path";
|
|
57952
58092
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
57953
|
-
import { execFileSync as
|
|
58093
|
+
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
57954
58094
|
init_scanner();
|
|
57955
58095
|
function runEngine(code, language, file) {
|
|
57956
58096
|
if (language === "php") {
|
|
57957
|
-
const dir = mkdtempSync2(
|
|
57958
|
-
const tmp =
|
|
58097
|
+
const dir = mkdtempSync2(join16(tmpdir2(), "blitz-diff-"));
|
|
58098
|
+
const tmp = join16(dir, "old.php");
|
|
57959
58099
|
writeFileSync5(tmp, code);
|
|
57960
58100
|
try {
|
|
57961
58101
|
const r = analyzeDataFlow2(tmp);
|
|
@@ -58109,8 +58249,8 @@ function languageOf(file) {
|
|
|
58109
58249
|
return adapter?.language ?? "php";
|
|
58110
58250
|
}
|
|
58111
58251
|
function analyzeDifferentialFiles(oldPath, newPath, language) {
|
|
58112
|
-
const oldCode =
|
|
58113
|
-
const newCode =
|
|
58252
|
+
const oldCode = readFileSync17(oldPath, "utf8");
|
|
58253
|
+
const newCode = readFileSync17(newPath, "utf8");
|
|
58114
58254
|
const lang = language ?? languageOf(newPath);
|
|
58115
58255
|
return analyzeDifferential(oldCode, newCode, lang, newPath);
|
|
58116
58256
|
}
|
|
@@ -58118,7 +58258,7 @@ var SOURCE_EXTS2 = [".php", ".phtml", ".php5", ".js", ".mjs", ".ts", ".tsx", ".p
|
|
|
58118
58258
|
function analyzeGitDiff(repo, base = "HEAD~1", head = "HEAD") {
|
|
58119
58259
|
const git = (args) => {
|
|
58120
58260
|
try {
|
|
58121
|
-
return
|
|
58261
|
+
return execFileSync4("git", args.split(/\s+/).filter(Boolean), { cwd: repo, encoding: "utf8", maxBuffer: 50 * 1024 * 1024 }).trim();
|
|
58122
58262
|
} catch {
|
|
58123
58263
|
return "";
|
|
58124
58264
|
}
|
|
@@ -58237,27 +58377,27 @@ function analyzePatch(oldCode, newCode, language = "php") {
|
|
|
58237
58377
|
};
|
|
58238
58378
|
}
|
|
58239
58379
|
function analyzePatchFiles(oldPath, newPath, language) {
|
|
58240
|
-
const oldCode =
|
|
58241
|
-
const newCode =
|
|
58380
|
+
const oldCode = readFileSync17(oldPath, "utf8");
|
|
58381
|
+
const newCode = readFileSync17(newPath, "utf8");
|
|
58242
58382
|
const lang = language ?? languageOf(newPath);
|
|
58243
58383
|
return analyzePatch(oldCode, newCode, lang);
|
|
58244
58384
|
}
|
|
58245
58385
|
|
|
58246
58386
|
// src/frameworks.ts
|
|
58247
|
-
import { readFileSync as
|
|
58248
|
-
import { join as
|
|
58387
|
+
import { readFileSync as readFileSync18, existsSync as existsSync9 } from "node:fs";
|
|
58388
|
+
import { join as join17 } from "node:path";
|
|
58249
58389
|
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
58250
|
-
var ROOT4 =
|
|
58251
|
-
var INTEL2 =
|
|
58390
|
+
var ROOT4 = join17(fileURLToPath8(new URL(".", import.meta.url)), "..");
|
|
58391
|
+
var INTEL2 = join17(ROOT4, "intelligence");
|
|
58252
58392
|
var cache = null;
|
|
58253
58393
|
function load3() {
|
|
58254
58394
|
if (cache)
|
|
58255
58395
|
return cache;
|
|
58256
|
-
const p =
|
|
58396
|
+
const p = join17(INTEL2, "frameworks.json");
|
|
58257
58397
|
if (!existsSync9(p))
|
|
58258
58398
|
return null;
|
|
58259
58399
|
try {
|
|
58260
|
-
cache = JSON.parse(
|
|
58400
|
+
cache = JSON.parse(readFileSync18(p, "utf8"));
|
|
58261
58401
|
return cache;
|
|
58262
58402
|
} catch {
|
|
58263
58403
|
return null;
|
|
@@ -58767,9 +58907,9 @@ function nextSteps(ctx) {
|
|
|
58767
58907
|
|
|
58768
58908
|
// src/git-history.ts
|
|
58769
58909
|
init_complex_bugs();
|
|
58770
|
-
import { execFileSync as
|
|
58910
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
58771
58911
|
import { existsSync as existsSync11 } from "node:fs";
|
|
58772
|
-
import { join as
|
|
58912
|
+
import { join as join18 } from "node:path";
|
|
58773
58913
|
function parseDeletions(output) {
|
|
58774
58914
|
const out = [];
|
|
58775
58915
|
let commit = "";
|
|
@@ -58790,7 +58930,7 @@ function parseDeletions(output) {
|
|
|
58790
58930
|
return out;
|
|
58791
58931
|
}
|
|
58792
58932
|
function blobBeforeDeletion(root, commit, path) {
|
|
58793
|
-
return
|
|
58933
|
+
return execFileSync5("git", ["-C", root, "show", `${commit}^:${path}`], {
|
|
58794
58934
|
encoding: "utf8",
|
|
58795
58935
|
maxBuffer: 10 * 1024 * 1024
|
|
58796
58936
|
});
|
|
@@ -58798,12 +58938,12 @@ function blobBeforeDeletion(root, commit, path) {
|
|
|
58798
58938
|
function scanGitHistory(root, opts) {
|
|
58799
58939
|
const limit = opts?.limit ?? 30;
|
|
58800
58940
|
const findings = [];
|
|
58801
|
-
if (!existsSync11(
|
|
58941
|
+
if (!existsSync11(join18(root, ".git"))) {
|
|
58802
58942
|
return { root, is_git_repo: false, total: 0, findings: [], error: "not a git repository (no .git dir)" };
|
|
58803
58943
|
}
|
|
58804
58944
|
if (opts?.secretPattern) {
|
|
58805
58945
|
try {
|
|
58806
|
-
const pick =
|
|
58946
|
+
const pick = execFileSync5("git", ["-C", root, "log", "-S", opts.secretPattern, "--all", "--format=__COMMIT__ %H %s"], { encoding: "utf8", maxBuffer: 5 * 1024 * 1024 });
|
|
58807
58947
|
const commits = pick.split(`
|
|
58808
58948
|
`).filter(Boolean).map((l) => {
|
|
58809
58949
|
const rest = l.startsWith("__COMMIT__ ") ? l.slice("__COMMIT__ ".length) : l;
|
|
@@ -58824,7 +58964,7 @@ function scanGitHistory(root, opts) {
|
|
|
58824
58964
|
}
|
|
58825
58965
|
}
|
|
58826
58966
|
try {
|
|
58827
|
-
const log =
|
|
58967
|
+
const log = execFileSync5("git", ["-C", root, "log", "--all", "--diff-filter=D", "--name-status", "--format=__COMMIT__ %H %s", "--no-renames"], { encoding: "utf8", maxBuffer: 10 * 1024 * 1024 });
|
|
58828
58968
|
const deletions = parseDeletions(log);
|
|
58829
58969
|
const seen = new Set;
|
|
58830
58970
|
let checked = 0;
|
|
@@ -58945,13 +59085,13 @@ async function validateLeakedKey(input) {
|
|
|
58945
59085
|
}
|
|
58946
59086
|
|
|
58947
59087
|
// src/hackbot-arena.ts
|
|
58948
|
-
import { readFileSync as
|
|
58949
|
-
import { join as
|
|
59088
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
59089
|
+
import { join as join19, dirname as dirname3 } from "node:path";
|
|
58950
59090
|
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
58951
59091
|
var __dirname3 = dirname3(fileURLToPath9(import.meta.url));
|
|
58952
59092
|
function load4() {
|
|
58953
59093
|
try {
|
|
58954
|
-
return JSON.parse(
|
|
59094
|
+
return JSON.parse(readFileSync19(join19(__dirname3, "..", "intelligence", "hackbot-arena.json"), "utf8"));
|
|
58955
59095
|
} catch {
|
|
58956
59096
|
return { total: 0, flag_format: "FLAG{nusasec-<32 hex>}", labs: [] };
|
|
58957
59097
|
}
|
|
@@ -59113,13 +59253,13 @@ function listSeverityMap() {
|
|
|
59113
59253
|
}
|
|
59114
59254
|
|
|
59115
59255
|
// src/datarefresh.ts
|
|
59116
|
-
import { readFileSync as
|
|
59117
|
-
import { join as
|
|
59256
|
+
import { readFileSync as readFileSync20 } from "node:fs";
|
|
59257
|
+
import { join as join20, dirname as dirname4 } from "node:path";
|
|
59118
59258
|
import { fileURLToPath as fileURLToPath10 } from "node:url";
|
|
59119
59259
|
var __dirname4 = dirname4(fileURLToPath10(import.meta.url));
|
|
59120
59260
|
function loadJson2(rel) {
|
|
59121
59261
|
try {
|
|
59122
|
-
return JSON.parse(
|
|
59262
|
+
return JSON.parse(readFileSync20(join20(__dirname4, "..", rel), "utf8"));
|
|
59123
59263
|
} catch {
|
|
59124
59264
|
return null;
|
|
59125
59265
|
}
|
|
@@ -59154,18 +59294,18 @@ init_verify_harness();
|
|
|
59154
59294
|
init_variants();
|
|
59155
59295
|
|
|
59156
59296
|
// src/benchmark.ts
|
|
59157
|
-
import { readFileSync as
|
|
59158
|
-
import { join as
|
|
59297
|
+
import { readFileSync as readFileSync22, readdirSync as readdirSync5, writeFileSync as writeFileSync6, rmSync as rmSync3, mkdirSync as mkdirSync7 } from "node:fs";
|
|
59298
|
+
import { join as join22 } from "node:path";
|
|
59159
59299
|
import { fileURLToPath as fileURLToPath12 } from "node:url";
|
|
59160
59300
|
import { tmpdir as tmpdir3, homedir as homedir8 } from "node:os";
|
|
59161
59301
|
init_complex_bugs();
|
|
59162
|
-
var ROOT5 =
|
|
59163
|
-
var CORPUS =
|
|
59164
|
-
var CAPTURED_CORPUS_DIR = process.env.BLITZSTRIKE_CORPUS_DIR ??
|
|
59302
|
+
var ROOT5 = join22(fileURLToPath12(new URL(".", import.meta.url)), "..");
|
|
59303
|
+
var CORPUS = join22(ROOT5, "bench", "corpus");
|
|
59304
|
+
var CAPTURED_CORPUS_DIR = process.env.BLITZSTRIKE_CORPUS_DIR ?? join22(homedir8(), ".blitzstrike", "corpus");
|
|
59165
59305
|
function parseCorpusFile(path) {
|
|
59166
59306
|
let text;
|
|
59167
59307
|
try {
|
|
59168
|
-
text =
|
|
59308
|
+
text = readFileSync22(path, "utf8");
|
|
59169
59309
|
} catch {
|
|
59170
59310
|
return null;
|
|
59171
59311
|
}
|
|
@@ -59221,7 +59361,7 @@ function loadDir(dir) {
|
|
|
59221
59361
|
for (const f of readdirSync5(dir)) {
|
|
59222
59362
|
if (!f.endsWith(".md"))
|
|
59223
59363
|
continue;
|
|
59224
|
-
const e = parseCorpusFile(
|
|
59364
|
+
const e = parseCorpusFile(join22(dir, f));
|
|
59225
59365
|
if (e)
|
|
59226
59366
|
entries.push(e);
|
|
59227
59367
|
}
|
|
@@ -59248,7 +59388,7 @@ function captureFalsePositive(input) {
|
|
|
59248
59388
|
input.code,
|
|
59249
59389
|
"```"
|
|
59250
59390
|
].filter((l) => l !== null);
|
|
59251
|
-
const path =
|
|
59391
|
+
const path = join22(CAPTURED_CORPUS_DIR, `${id}.md`);
|
|
59252
59392
|
writeFileSync6(path, lines.join(`
|
|
59253
59393
|
`) + `
|
|
59254
59394
|
`, "utf8");
|
|
@@ -59270,7 +59410,7 @@ function detect(entry) {
|
|
|
59270
59410
|
}
|
|
59271
59411
|
const lang = entry.language;
|
|
59272
59412
|
if (lang === "php") {
|
|
59273
|
-
const tmp =
|
|
59413
|
+
const tmp = join22(tmpdir3(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
|
|
59274
59414
|
writeFileSync6(tmp, entry.code);
|
|
59275
59415
|
const r = analyzeDataFlow2(tmp);
|
|
59276
59416
|
rmSync3(tmp, { force: true });
|
|
@@ -59339,12 +59479,12 @@ function runBenchmark() {
|
|
|
59339
59479
|
}
|
|
59340
59480
|
|
|
59341
59481
|
// src/obligations.ts
|
|
59342
|
-
import { readFileSync as
|
|
59343
|
-
import { join as
|
|
59482
|
+
import { readFileSync as readFileSync23, appendFileSync as appendFileSync4, mkdirSync as mkdirSync8, existsSync as existsSync12, writeFileSync as writeFileSync7 } from "node:fs";
|
|
59483
|
+
import { join as join23 } from "node:path";
|
|
59344
59484
|
import { createHash as createHash6 } from "node:crypto";
|
|
59345
59485
|
import { homedir as homedir9 } from "node:os";
|
|
59346
|
-
var HOME_DIR4 = process.env.BLITZSTRIKE_HOME ??
|
|
59347
|
-
var OBLIGATION_PATH =
|
|
59486
|
+
var HOME_DIR4 = process.env.BLITZSTRIKE_HOME ?? join23(homedir9(), ".blitzstrike");
|
|
59487
|
+
var OBLIGATION_PATH = join23(HOME_DIR4, "obligations.jsonl");
|
|
59348
59488
|
function ensureDir3() {
|
|
59349
59489
|
if (!existsSync12(HOME_DIR4))
|
|
59350
59490
|
mkdirSync8(HOME_DIR4, { recursive: true });
|
|
@@ -59357,7 +59497,7 @@ function loadObligations() {
|
|
|
59357
59497
|
return [];
|
|
59358
59498
|
try {
|
|
59359
59499
|
const out = [];
|
|
59360
|
-
for (const line of
|
|
59500
|
+
for (const line of readFileSync23(OBLIGATION_PATH, "utf8").split(`
|
|
59361
59501
|
`)) {
|
|
59362
59502
|
const t = line.trim();
|
|
59363
59503
|
if (!t)
|
|
@@ -59461,11 +59601,11 @@ init_framework_security();
|
|
|
59461
59601
|
// src/variant-scan.ts
|
|
59462
59602
|
init_scanner();
|
|
59463
59603
|
init_complex_bugs();
|
|
59464
|
-
import { readFileSync as
|
|
59604
|
+
import { readFileSync as readFileSync25 } from "node:fs";
|
|
59465
59605
|
|
|
59466
59606
|
// src/cross-file.ts
|
|
59467
59607
|
init_scanner();
|
|
59468
|
-
import { readFileSync as
|
|
59608
|
+
import { readFileSync as readFileSync24 } from "node:fs";
|
|
59469
59609
|
var PUBLIC_HOOK_RE = /(?:wp_ajax_nopriv_\w+|admin_post_nopriv_\w+|init|template_redirect|wp_loaded|parse_request|user_register|um_user_register|register_new_user|authenticate)/;
|
|
59470
59610
|
var PRIV_SINK_RE = /\b(set_role|add_role|remove_role|set_user_role|wp_set_current_user|wp_set_auth_cookie|wp_insert_user|wp_create_user|wp_update_user)\s*\(/;
|
|
59471
59611
|
var ROLE_META_RE = /update_user_meta\s*\(\s*[^,]+,\s*['"](wp_capabilities|role|roles|capabilities|wp_user_level)['"]/;
|
|
@@ -59517,7 +59657,7 @@ function detectCrossFilePrivesc(root) {
|
|
|
59517
59657
|
for (const file of files) {
|
|
59518
59658
|
let code;
|
|
59519
59659
|
try {
|
|
59520
|
-
code = stripComments(
|
|
59660
|
+
code = stripComments(readFileSync24(file, "utf8"));
|
|
59521
59661
|
} catch {
|
|
59522
59662
|
continue;
|
|
59523
59663
|
}
|
|
@@ -59558,7 +59698,7 @@ function detectCrossFilePrivesc(root) {
|
|
|
59558
59698
|
for (const file of files) {
|
|
59559
59699
|
let code;
|
|
59560
59700
|
try {
|
|
59561
|
-
code = stripComments(
|
|
59701
|
+
code = stripComments(readFileSync24(file, "utf8"));
|
|
59562
59702
|
} catch {
|
|
59563
59703
|
continue;
|
|
59564
59704
|
}
|
|
@@ -59581,7 +59721,7 @@ function detectCrossFilePrivesc(root) {
|
|
|
59581
59721
|
for (const file of files) {
|
|
59582
59722
|
let code;
|
|
59583
59723
|
try {
|
|
59584
|
-
code = stripComments(
|
|
59724
|
+
code = stripComments(readFileSync24(file, "utf8"));
|
|
59585
59725
|
} catch {
|
|
59586
59726
|
continue;
|
|
59587
59727
|
}
|
|
@@ -59696,7 +59836,7 @@ function variantScan(root, opts = {}) {
|
|
|
59696
59836
|
for (const file of files) {
|
|
59697
59837
|
let code;
|
|
59698
59838
|
try {
|
|
59699
|
-
code =
|
|
59839
|
+
code = readFileSync25(file, "utf8");
|
|
59700
59840
|
} catch {
|
|
59701
59841
|
continue;
|
|
59702
59842
|
}
|
|
@@ -61376,7 +61516,7 @@ function listPocTypes() {
|
|
|
61376
61516
|
import { execSync as execSync3 } from "node:child_process";
|
|
61377
61517
|
import { mkdtempSync as mkdtempSync3, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, rmSync as rmSync4, existsSync as existsSync14 } from "node:fs";
|
|
61378
61518
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
61379
|
-
import { join as
|
|
61519
|
+
import { join as join24 } from "node:path";
|
|
61380
61520
|
function findForge() {
|
|
61381
61521
|
try {
|
|
61382
61522
|
const p = execSync3("which forge 2>/dev/null", { encoding: "utf8", timeout: 3000 }).trim();
|
|
@@ -61425,14 +61565,14 @@ async function foundryRun(input) {
|
|
|
61425
61565
|
};
|
|
61426
61566
|
}
|
|
61427
61567
|
const poc = input.poc_code ?? foundryPoc(input.class_ ?? "missing_access_control", name);
|
|
61428
|
-
const dir = mkdtempSync3(
|
|
61568
|
+
const dir = mkdtempSync3(join24(tmpdir4(), "blitzstrike-foundry-"));
|
|
61429
61569
|
try {
|
|
61430
61570
|
try {
|
|
61431
61571
|
execSync3(`${forge} init --force --no-git ${dir}`, { stdio: "ignore", timeout: 60000 });
|
|
61432
61572
|
} catch {
|
|
61433
|
-
mkdirSync9(
|
|
61434
|
-
mkdirSync9(
|
|
61435
|
-
writeFileSync8(
|
|
61573
|
+
mkdirSync9(join24(dir, "src"), { recursive: true });
|
|
61574
|
+
mkdirSync9(join24(dir, "test"), { recursive: true });
|
|
61575
|
+
writeFileSync8(join24(dir, "foundry.toml"), `[profile.default]
|
|
61436
61576
|
src = "src"
|
|
61437
61577
|
out = "out"
|
|
61438
61578
|
libs = ["lib"]
|
|
@@ -61442,12 +61582,12 @@ solc = "0.8.20"
|
|
|
61442
61582
|
for (const f of ["Counter.sol", "Counter.t.sol", "Counter.s.sol"]) {
|
|
61443
61583
|
for (const sub of ["src", "test", "script"]) {
|
|
61444
61584
|
try {
|
|
61445
|
-
rmSync4(
|
|
61585
|
+
rmSync4(join24(dir, sub, f), { force: true });
|
|
61446
61586
|
} catch {}
|
|
61447
61587
|
}
|
|
61448
61588
|
}
|
|
61449
|
-
writeFileSync8(
|
|
61450
|
-
writeFileSync8(
|
|
61589
|
+
writeFileSync8(join24(dir, "src", `${name}.sol`), input.contract_code);
|
|
61590
|
+
writeFileSync8(join24(dir, "test", `${name}.t.sol`), poc);
|
|
61451
61591
|
const { out } = runForge(forge, dir, input.timeout_ms ?? 60000);
|
|
61452
61592
|
const tests = parseTestResults(out);
|
|
61453
61593
|
const anyFail = tests.some((t) => t.status === "fail");
|
|
@@ -61464,119 +61604,6 @@ solc = "0.8.20"
|
|
|
61464
61604
|
}
|
|
61465
61605
|
}
|
|
61466
61606
|
|
|
61467
|
-
// src/hunting-intel.ts
|
|
61468
|
-
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
61469
|
-
import { resolve4 as resolve42, Resolver } from "node:dns/promises";
|
|
61470
|
-
import { readFileSync as readFileSync25 } from "node:fs";
|
|
61471
|
-
import { join as join24 } from "node:path";
|
|
61472
|
-
var BLOCKING_KEYWORDS = /internet positif|kominfo|\.go\.id\b|pemblokiran|ditutup|dilarang|akses dibatasi|blocked|restricted|illegal|advertencia|coljuegos|regulat|forbidden|this (site|website) has been (blocked|banned)/i;
|
|
61473
|
-
var HOSTNAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i;
|
|
61474
|
-
function isValidHostname(host) {
|
|
61475
|
-
return host.length <= 253 && HOSTNAME_RE.test(host);
|
|
61476
|
-
}
|
|
61477
|
-
async function localResolve(host) {
|
|
61478
|
-
try {
|
|
61479
|
-
return await resolve42(host);
|
|
61480
|
-
} catch {
|
|
61481
|
-
return [];
|
|
61482
|
-
}
|
|
61483
|
-
}
|
|
61484
|
-
async function publicResolve(host) {
|
|
61485
|
-
const r = new Resolver;
|
|
61486
|
-
r.setServers(["8.8.8.8", "1.1.1.1"]);
|
|
61487
|
-
try {
|
|
61488
|
-
return await r.resolve4(host);
|
|
61489
|
-
} catch {
|
|
61490
|
-
return [];
|
|
61491
|
-
}
|
|
61492
|
-
}
|
|
61493
|
-
function probeBlockingPage(host, ip, timeoutMs = 8000) {
|
|
61494
|
-
for (const port of ["443", "80"]) {
|
|
61495
|
-
const scheme = port === "443" ? "https" : "http";
|
|
61496
|
-
try {
|
|
61497
|
-
const out = execFileSync5("curl", ["-sk", "-m", "6", "--resolve", `${host}:${port}:${ip}`, `${scheme}://${host}/`], { encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"] });
|
|
61498
|
-
const preview = out.replace(/\s+/g, " ").trim().slice(0, 300);
|
|
61499
|
-
if (!preview)
|
|
61500
|
-
continue;
|
|
61501
|
-
return { probed: true, sinkhole: BLOCKING_KEYWORDS.test(preview), preview };
|
|
61502
|
-
} catch {}
|
|
61503
|
-
}
|
|
61504
|
-
return { probed: false, sinkhole: false, preview: "" };
|
|
61505
|
-
}
|
|
61506
|
-
async function resolveCheck(host) {
|
|
61507
|
-
const clean = host.replace(/^https?:\/\//, "").split("/")[0];
|
|
61508
|
-
if (!isValidHostname(clean)) {
|
|
61509
|
-
return { host: clean, local_ips: [], public_ips: [], sinkhole: false, verdict: "invalid", evidence: "not a valid hostname — rejected before DNS/subprocess" };
|
|
61510
|
-
}
|
|
61511
|
-
const local = await localResolve(clean);
|
|
61512
|
-
const pub = await publicResolve(clean);
|
|
61513
|
-
if (local.length === 0 && pub.length === 0) {
|
|
61514
|
-
return { host: clean, local_ips: [], public_ips: [], sinkhole: false, verdict: "unresolvable", evidence: "no A record from local or public DNS" };
|
|
61515
|
-
}
|
|
61516
|
-
if (local.length === 0) {
|
|
61517
|
-
return { host: clean, local_ips: [], public_ips: pub, sinkhole: false, verdict: "local-unresolvable", evidence: "local resolver returned no A record; public DNS resolves. Use the public IP to reach the real target." };
|
|
61518
|
-
}
|
|
61519
|
-
const firstLocal = local[0];
|
|
61520
|
-
const probe = probeBlockingPage(clean, firstLocal);
|
|
61521
|
-
if (probe.sinkhole) {
|
|
61522
|
-
return {
|
|
61523
|
-
host: clean,
|
|
61524
|
-
local_ips: local,
|
|
61525
|
-
public_ips: pub,
|
|
61526
|
-
sinkhole: true,
|
|
61527
|
-
verdict: "sinkhole",
|
|
61528
|
-
evidence: `local IP ${firstLocal} serves a blocking/sinkhole page ("${probe.preview.slice(0, 120)}"). Audit the PUBLIC IP (${pub[0] ?? "n/a"}) instead — e.g. curl --resolve ${clean}:443:${pub[0]} https://${clean}/`
|
|
61529
|
-
};
|
|
61530
|
-
}
|
|
61531
|
-
if (!probe.probed) {
|
|
61532
|
-
return { host: clean, local_ips: local, public_ips: pub, sinkhole: false, verdict: "inconclusive", evidence: "local IP did not respond on 443/80 — inconclusive; for sensitive niches prefer the public IP anyway." };
|
|
61533
|
-
}
|
|
61534
|
-
const ipDiff = new Set(local).size !== new Set(pub).size || !pub.every((ip) => local.includes(ip));
|
|
61535
|
-
return {
|
|
61536
|
-
host: clean,
|
|
61537
|
-
local_ips: local,
|
|
61538
|
-
public_ips: pub,
|
|
61539
|
-
sinkhole: false,
|
|
61540
|
-
verdict: ipDiff ? "clean-cdn" : "clean",
|
|
61541
|
-
evidence: ipDiff ? "local and public IPs differ but the local page shows no blocking content (normal for CDN/anycast/round-robin)." : "local resolution matches public DNS and shows no blocking content."
|
|
61542
|
-
};
|
|
61543
|
-
}
|
|
61544
|
-
function loadWatchlist() {
|
|
61545
|
-
const raw = readFileSync25(join24(process.cwd(), "intelligence", "watchlist.json"), "utf8");
|
|
61546
|
-
const data = JSON.parse(raw);
|
|
61547
|
-
return { cwe_frequency: data.cwe_frequency ?? [], cve_watchlist: data.cve_watchlist ?? [] };
|
|
61548
|
-
}
|
|
61549
|
-
function fmtPayout(min, max) {
|
|
61550
|
-
if (min == null || max == null)
|
|
61551
|
-
return "n/a";
|
|
61552
|
-
const f = (n) => n >= 1000 ? `$${(n / 1000).toFixed(n % 1000 === 0 ? 0 : 1)}K` : `$${n}`;
|
|
61553
|
-
return `${f(min)}–${f(max)}`;
|
|
61554
|
-
}
|
|
61555
|
-
function watchlist() {
|
|
61556
|
-
const { cwe_frequency, cve_watchlist } = loadWatchlist();
|
|
61557
|
-
const entries = cwe_frequency.map((e) => {
|
|
61558
|
-
const pat = e.pattern ? patternLookup(e.pattern) : null;
|
|
61559
|
-
const payoutMin = e.payout_min ?? pat?.payout_min ?? null;
|
|
61560
|
-
const payoutMax = e.payout_max ?? pat?.payout_max ?? null;
|
|
61561
|
-
return {
|
|
61562
|
-
cwe: e.cwe,
|
|
61563
|
-
name: e.name,
|
|
61564
|
-
rank: e.rank,
|
|
61565
|
-
saturated: e.saturated,
|
|
61566
|
-
pattern: e.pattern ?? null,
|
|
61567
|
-
payout_min: payoutMin,
|
|
61568
|
-
payout_max: payoutMax,
|
|
61569
|
-
typical_payout: fmtPayout(payoutMin, payoutMax),
|
|
61570
|
-
notes: e.notes,
|
|
61571
|
-
grounded: pat != null
|
|
61572
|
-
};
|
|
61573
|
-
}).sort((a, b) => a.rank - b.rank);
|
|
61574
|
-
const unsaturated = entries.filter((e) => !e.saturated);
|
|
61575
|
-
const topCve = cve_watchlist.filter((c) => (c.epss ?? 0) >= 0.8 || (c.reports ?? 0) >= 100).slice(0, 4).map((c) => c.cve).join(", ");
|
|
61576
|
-
const recommendation = `Prioritize the ${unsaturated.length} non-saturated classes (top: ${unsaturated.slice(0, 3).map((e) => e.cwe).join(", ")}) — high frequency + high payout + not yet saturated. Skip saturated/low-value classes (XSS/CSRF/open-redirect standalone) unless they chain into account takeover. FINGERPRINT FIRST: if the target stack matches a watchlist product (Next.js, Kibana, Langflow, Confluence/Jira, PAN-OS, Grafana), test that specific CVE — top EPSS/report CVEs: ${topCve}.`;
|
|
61577
|
-
return { cwe_frequency: entries, cve_watchlist, recommendation };
|
|
61578
|
-
}
|
|
61579
|
-
|
|
61580
61607
|
// src/enterprise-benchmark.ts
|
|
61581
61608
|
import { existsSync as existsSync15, readFileSync as readFileSync26, readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
|
|
61582
61609
|
import { join as join25, resolve } from "node:path";
|
|
@@ -180,7 +180,8 @@
|
|
|
180
180
|
"class": "rce",
|
|
181
181
|
"epss": 1.0,
|
|
182
182
|
"reports": 2669,
|
|
183
|
-
"fingerprint": "Confluence (Server/Data Center) \u2014 /login.action, confluence headers"
|
|
183
|
+
"fingerprint": "Confluence (Server/Data Center) \u2014 /login.action, confluence headers",
|
|
184
|
+
"match": "confluence|login\\.action|atlassian"
|
|
184
185
|
},
|
|
185
186
|
{
|
|
186
187
|
"cve": "CVE-2025-29927",
|
|
@@ -189,7 +190,8 @@
|
|
|
189
190
|
"class": "auth_bypass",
|
|
190
191
|
"epss": 0.9962,
|
|
191
192
|
"reports": 132,
|
|
192
|
-
"fingerprint": "_next/static/chunks/ bundles; x-middleware-subrequest header"
|
|
193
|
+
"fingerprint": "_next/static/chunks/ bundles; x-middleware-subrequest header",
|
|
194
|
+
"match": "_next/|__next|next\\.js|middleware"
|
|
193
195
|
},
|
|
194
196
|
{
|
|
195
197
|
"cve": "CVE-2025-55182",
|
|
@@ -198,7 +200,8 @@
|
|
|
198
200
|
"class": "rce",
|
|
199
201
|
"epss": 0.9956,
|
|
200
202
|
"reports": 415,
|
|
201
|
-
"fingerprint": "_next/ + Server Function / flight endpoints"
|
|
203
|
+
"fingerprint": "_next/ + Server Function / flight endpoints",
|
|
204
|
+
"match": "_next/|server function|react server|rsc"
|
|
202
205
|
},
|
|
203
206
|
{
|
|
204
207
|
"cve": "CVE-2025-3248",
|
|
@@ -207,7 +210,8 @@
|
|
|
207
210
|
"class": "rce",
|
|
208
211
|
"epss": 0.9997,
|
|
209
212
|
"reports": 29,
|
|
210
|
-
"fingerprint": "/api/v1/validate/code \u2014 AI/agent tooling"
|
|
213
|
+
"fingerprint": "/api/v1/validate/code \u2014 AI/agent tooling",
|
|
214
|
+
"match": "langflow|validate/code"
|
|
211
215
|
},
|
|
212
216
|
{
|
|
213
217
|
"cve": "CVE-2024-34351",
|
|
@@ -216,7 +220,8 @@
|
|
|
216
220
|
"class": "ssrf",
|
|
217
221
|
"epss": 0.0545,
|
|
218
222
|
"reports": 50,
|
|
219
|
-
"fingerprint": "Next.js Server Actions (Host header + relative redirect)"
|
|
223
|
+
"fingerprint": "Next.js Server Actions (Host header + relative redirect)",
|
|
224
|
+
"match": "_next/|server action"
|
|
220
225
|
},
|
|
221
226
|
{
|
|
222
227
|
"cve": "CVE-2025-0133",
|
|
@@ -225,7 +230,8 @@
|
|
|
225
230
|
"class": "xss",
|
|
226
231
|
"epss": 0.4352,
|
|
227
232
|
"reports": 1990,
|
|
228
|
-
"fingerprint": "GlobalProtect captive portal"
|
|
233
|
+
"fingerprint": "GlobalProtect captive portal",
|
|
234
|
+
"match": "globalprotect|palo alto|pan-os|pan os"
|
|
229
235
|
},
|
|
230
236
|
{
|
|
231
237
|
"cve": "CVE-2026-0257",
|
|
@@ -234,7 +240,8 @@
|
|
|
234
240
|
"class": "auth_bypass",
|
|
235
241
|
"epss": 0.8668,
|
|
236
242
|
"reports": 27,
|
|
237
|
-
"fingerprint": "GlobalProtect portal/gateway"
|
|
243
|
+
"fingerprint": "GlobalProtect portal/gateway",
|
|
244
|
+
"match": "globalprotect|palo alto|pan-os|pan os"
|
|
238
245
|
},
|
|
239
246
|
{
|
|
240
247
|
"cve": "CVE-2025-4123",
|
|
@@ -243,7 +250,8 @@
|
|
|
243
250
|
"class": "xss",
|
|
244
251
|
"epss": 0.9781,
|
|
245
252
|
"reports": 220,
|
|
246
|
-
"fingerprint": "Grafana path traversal + open redirect (Image Renderer)"
|
|
253
|
+
"fingerprint": "Grafana path traversal + open redirect (Image Renderer)",
|
|
254
|
+
"match": "grafana|x-grafana"
|
|
247
255
|
},
|
|
248
256
|
{
|
|
249
257
|
"cve": "CVE-2021-20323",
|
|
@@ -252,7 +260,8 @@
|
|
|
252
260
|
"class": "xss",
|
|
253
261
|
"epss": 0.3725,
|
|
254
262
|
"reports": 107,
|
|
255
|
-
"fingerprint": "Keycloak auth flows"
|
|
263
|
+
"fingerprint": "Keycloak auth flows",
|
|
264
|
+
"match": "keycloak|kc-|realms/"
|
|
256
265
|
},
|
|
257
266
|
{
|
|
258
267
|
"cve": "CVE-2018-5230",
|
|
@@ -261,7 +270,8 @@
|
|
|
261
270
|
"class": "xss",
|
|
262
271
|
"epss": 0.3761,
|
|
263
272
|
"reports": 4694,
|
|
264
|
-
"fingerprint": "Jira /issuecollector custom fields"
|
|
273
|
+
"fingerprint": "Jira /issuecollector custom fields",
|
|
274
|
+
"match": "jira|issuecollector|atlassian"
|
|
265
275
|
},
|
|
266
276
|
{
|
|
267
277
|
"cve": "CVE-2026-33461",
|
|
@@ -270,7 +280,8 @@
|
|
|
270
280
|
"class": "auth_bypass",
|
|
271
281
|
"epss": 0.0028,
|
|
272
282
|
"reports": 8,
|
|
273
|
-
"fingerprint": "Kibana internal Fleet endpoints"
|
|
283
|
+
"fingerprint": "Kibana internal Fleet endpoints",
|
|
284
|
+
"match": "kibana|elastic|fleet"
|
|
274
285
|
},
|
|
275
286
|
{
|
|
276
287
|
"cve": "CVE-2026-49093",
|
|
@@ -279,7 +290,8 @@
|
|
|
279
290
|
"class": "ssrf",
|
|
280
291
|
"epss": 0.002,
|
|
281
292
|
"reports": 5,
|
|
282
|
-
"fingerprint": "Kibana connectors / allowlist bypass"
|
|
293
|
+
"fingerprint": "Kibana connectors / allowlist bypass",
|
|
294
|
+
"match": "kibana|elastic|connector"
|
|
283
295
|
},
|
|
284
296
|
{
|
|
285
297
|
"cve": "CVE-2026-26938",
|
|
@@ -288,7 +300,8 @@
|
|
|
288
300
|
"class": "ssti",
|
|
289
301
|
"epss": 0.0025,
|
|
290
302
|
"reports": 5,
|
|
291
|
-
"fingerprint": "Kibana Workflows"
|
|
303
|
+
"fingerprint": "Kibana Workflows",
|
|
304
|
+
"match": "kibana|elastic|workflow"
|
|
292
305
|
},
|
|
293
306
|
{
|
|
294
307
|
"cve": "CVE-2026-33068",
|
|
@@ -297,7 +310,8 @@
|
|
|
297
310
|
"class": "priv_esc",
|
|
298
311
|
"epss": 0.0034,
|
|
299
312
|
"reports": 5,
|
|
300
|
-
"fingerprint": "repo .claude/settings.json"
|
|
313
|
+
"fingerprint": "repo .claude/settings.json",
|
|
314
|
+
"match": "\\.claude/settings"
|
|
301
315
|
},
|
|
302
316
|
{
|
|
303
317
|
"cve": "CVE-2025-25297",
|
|
@@ -306,7 +320,8 @@
|
|
|
306
320
|
"class": "ssrf",
|
|
307
321
|
"epss": 0.0062,
|
|
308
322
|
"reports": 2,
|
|
309
|
-
"fingerprint": "Label Studio S3 integration"
|
|
323
|
+
"fingerprint": "Label Studio S3 integration",
|
|
324
|
+
"match": "label ?studio|labelstudio|label-studio"
|
|
310
325
|
},
|
|
311
326
|
{
|
|
312
327
|
"cve": "CVE-2026-33309",
|
|
@@ -315,7 +330,8 @@
|
|
|
315
330
|
"class": "file_upload",
|
|
316
331
|
"epss": 0.0142,
|
|
317
332
|
"reports": 6,
|
|
318
|
-
"fingerprint": "Langflow multipart upload filename"
|
|
333
|
+
"fingerprint": "Langflow multipart upload filename",
|
|
334
|
+
"match": "langflow"
|
|
319
335
|
},
|
|
320
336
|
{
|
|
321
337
|
"cve": "CVE-2026-45543",
|
|
@@ -324,7 +340,8 @@
|
|
|
324
340
|
"class": "missing_authz",
|
|
325
341
|
"epss": 0.0027,
|
|
326
342
|
"reports": 5,
|
|
327
|
-
"fingerprint": "Nextcloud Forms collaborator removal"
|
|
343
|
+
"fingerprint": "Nextcloud Forms collaborator removal",
|
|
344
|
+
"match": "nextcloud|ocs-provider"
|
|
328
345
|
},
|
|
329
346
|
{
|
|
330
347
|
"cve": "CVE-2017-15277",
|
|
@@ -333,7 +350,8 @@
|
|
|
333
350
|
"class": "infoleak",
|
|
334
351
|
"epss": 0.1919,
|
|
335
352
|
"reports": 5914,
|
|
336
|
-
"fingerprint": "image processing (GIF upload)"
|
|
353
|
+
"fingerprint": "image processing (GIF upload)",
|
|
354
|
+
"match": "imagemagick|graphicsmagick"
|
|
337
355
|
}
|
|
338
356
|
]
|
|
339
357
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.99",
|
|
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": {
|