blitzstrike 2.4.93 → 2.4.95
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 +34 -0
- package/dist/index.js +216 -84
- package/intelligence/patterns.json +63 -0
- package/intelligence/watchlist.json +33 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,40 @@ 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.95] — 2026-09-17
|
|
9
|
+
|
|
10
|
+
### Fixed — full interconnection audit (unreachable template + 2 ungrounded classes)
|
|
11
|
+
|
|
12
|
+
- `auto_poc` `open_redirect` template was UNREACHABLE: `canonicalId("open_redirect")`
|
|
13
|
+
resolves to `oauth_misconfig`, but the template key was `open_redirect`, so the
|
|
14
|
+
lookup fell back to the `missing_authz` recipe. Renamed the key to the canonical
|
|
15
|
+
`oauth_misconfig` + added an interconn invariant that every auto-poc key IS its
|
|
16
|
+
own canonical id.
|
|
17
|
+
- `nosql_injection` and `host_header` were referenced across intel/orchestrator/
|
|
18
|
+
writeup/technique-lookup but had no pattern, severity, or synonym (ungrounded).
|
|
19
|
+
Added full patterns (CWE-943 / CWE-644), severity VRT entries (P1 critical /
|
|
20
|
+
P3 medium), and synonyms (nosql/mongo injection; host-header poisoning /
|
|
21
|
+
password-reset poisoning). cwe_map already carried both CWEs.
|
|
22
|
+
- `SINK_TO_VECTOR` mapped the `code_execution` sink to a non-pattern vector name;
|
|
23
|
+
now maps to `command_injection` (consistent with the "code execution" synonym).
|
|
24
|
+
|
|
25
|
+
## [2.4.94] — 2026-09-17
|
|
26
|
+
|
|
27
|
+
### Added — Hunting intel (DNS sinkhole detection + CWE/CVE watchlist)
|
|
28
|
+
|
|
29
|
+
- `resolve_check(host)` — detects when an ISP/country redirects a domain to a
|
|
30
|
+
blocking/sinkhole page (gambling/adult/streaming niches). Compares the local
|
|
31
|
+
resolver against public DNS (8.8.8.8/1.1.1.1) and probes the local IP for
|
|
32
|
+
blocking-page content — the CONTENT is the signal (a differing IP set alone is
|
|
33
|
+
normal for CDN/anycast). A sinkhole verdict tells the agent to audit the PUBLIC
|
|
34
|
+
IP, never the sinkhole.
|
|
35
|
+
- `watchlist()` — the ranked bug-bounty CWE frequency (17 classes) + high-value
|
|
36
|
+
CVE watchlist (9 CVEs), each with payout range + a saturated flag, joined to a
|
|
37
|
+
Blitz Strike detector pattern for grounding. Prioritizes high-frequency /
|
|
38
|
+
high-payout / not-yet-saturated classes (missing-authz, SQLi, SSRF,
|
|
39
|
+
deserialization) and flags saturated low-value ones (XSS/CSRF/open-redirect).
|
|
40
|
+
Data: intelligence/watchlist.json.
|
|
41
|
+
|
|
8
42
|
## [2.4.93] — 2026-09-17
|
|
9
43
|
|
|
10
44
|
### Fixed — reproducible verify harness (flaky wordlist test)
|
package/dist/index.js
CHANGED
|
@@ -17349,8 +17349,8 @@ var init_variants = __esm(() => {
|
|
|
17349
17349
|
});
|
|
17350
17350
|
|
|
17351
17351
|
// src/sync.ts
|
|
17352
|
-
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9, readFileSync as
|
|
17353
|
-
import { join as
|
|
17352
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9, readFileSync as readFileSync27, readdirSync as readdirSync7, statSync as statSync4, rmSync as rmSync5 } from "node:fs";
|
|
17353
|
+
import { join as join26 } from "node:path";
|
|
17354
17354
|
import { homedir as homedir10 } from "node:os";
|
|
17355
17355
|
import { fileURLToPath as fileURLToPath13 } from "node:url";
|
|
17356
17356
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
@@ -17367,7 +17367,7 @@ function countFiles(dir) {
|
|
|
17367
17367
|
const walk = (d) => {
|
|
17368
17368
|
for (const e of readdirSync7(d, { withFileTypes: true })) {
|
|
17369
17369
|
if (e.isDirectory())
|
|
17370
|
-
walk(
|
|
17370
|
+
walk(join26(d, e.name));
|
|
17371
17371
|
else
|
|
17372
17372
|
n++;
|
|
17373
17373
|
}
|
|
@@ -17381,8 +17381,8 @@ function countFiles(dir) {
|
|
|
17381
17381
|
function syncData() {
|
|
17382
17382
|
if (!dirExists(DATA_ROOT2))
|
|
17383
17383
|
mkdirSync10(DATA_ROOT2, { recursive: true });
|
|
17384
|
-
const payloadsPath =
|
|
17385
|
-
const templatesPath =
|
|
17384
|
+
const payloadsPath = join26(DATA_ROOT2, "payloads");
|
|
17385
|
+
const templatesPath = join26(DATA_ROOT2, "templates");
|
|
17386
17386
|
const hasPayloads = dirExists(payloadsPath) && countFiles(payloadsPath) > 0;
|
|
17387
17387
|
const hasTemplates = dirExists(templatesPath) && countFiles(templatesPath) > 0;
|
|
17388
17388
|
if (hasPayloads && hasTemplates) {
|
|
@@ -17392,8 +17392,8 @@ function syncData() {
|
|
|
17392
17392
|
data_root: DATA_ROOT2
|
|
17393
17393
|
};
|
|
17394
17394
|
}
|
|
17395
|
-
const localPayloads =
|
|
17396
|
-
const localTemplates =
|
|
17395
|
+
const localPayloads = join26(ROOT7, "payloads");
|
|
17396
|
+
const localTemplates = join26(ROOT7, "templates");
|
|
17397
17397
|
let payloadsSynced = false;
|
|
17398
17398
|
let templatesSynced = false;
|
|
17399
17399
|
if (!hasPayloads && dirExists(localPayloads)) {
|
|
@@ -17406,7 +17406,7 @@ function syncData() {
|
|
|
17406
17406
|
}
|
|
17407
17407
|
const gitOk = spawnSync4("git", ["--version"]).status === 0;
|
|
17408
17408
|
if (gitOk && (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0 || !dirExists(templatesPath) || countFiles(templatesPath) === 0)) {
|
|
17409
|
-
const tmp =
|
|
17409
|
+
const tmp = join26(DATA_ROOT2, ".tmp-clone");
|
|
17410
17410
|
if (dirExists(tmp))
|
|
17411
17411
|
rmSync5(tmp, { recursive: true, force: true });
|
|
17412
17412
|
const r = spawnSync4("git", ["clone", "--depth", "1", "--filter=blob:none", "--sparse", GITHUB_REPO, tmp], { timeout: 120000 });
|
|
@@ -17414,11 +17414,11 @@ function syncData() {
|
|
|
17414
17414
|
const sparse = spawnSync4("git", ["-C", tmp, "sparse-checkout", "set", "payloads", "templates"], { timeout: 120000 });
|
|
17415
17415
|
if (sparse.status === 0) {
|
|
17416
17416
|
if (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0) {
|
|
17417
|
-
copyDir(
|
|
17417
|
+
copyDir(join26(tmp, "payloads"), payloadsPath);
|
|
17418
17418
|
payloadsSynced = true;
|
|
17419
17419
|
}
|
|
17420
17420
|
if (!dirExists(templatesPath) || countFiles(templatesPath) === 0) {
|
|
17421
|
-
copyDir(
|
|
17421
|
+
copyDir(join26(tmp, "templates"), templatesPath);
|
|
17422
17422
|
templatesSynced = true;
|
|
17423
17423
|
}
|
|
17424
17424
|
}
|
|
@@ -17440,18 +17440,18 @@ function copyDir(src, dst) {
|
|
|
17440
17440
|
function manualCopy(src, dst) {
|
|
17441
17441
|
mkdirSync10(dst, { recursive: true });
|
|
17442
17442
|
for (const e of readdirSync7(src, { withFileTypes: true })) {
|
|
17443
|
-
const s =
|
|
17444
|
-
const d =
|
|
17443
|
+
const s = join26(src, e.name);
|
|
17444
|
+
const d = join26(dst, e.name);
|
|
17445
17445
|
if (e.isDirectory())
|
|
17446
17446
|
manualCopy(s, d);
|
|
17447
17447
|
else
|
|
17448
|
-
writeFileSync9(d,
|
|
17448
|
+
writeFileSync9(d, readFileSync27(s));
|
|
17449
17449
|
}
|
|
17450
17450
|
}
|
|
17451
17451
|
var ROOT7, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
|
|
17452
17452
|
var init_sync = __esm(() => {
|
|
17453
|
-
ROOT7 =
|
|
17454
|
-
DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ??
|
|
17453
|
+
ROOT7 = join26(fileURLToPath13(new URL(".", import.meta.url)), "..");
|
|
17454
|
+
DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ?? join26(homedir10(), ".blitzstrike", "data");
|
|
17455
17455
|
});
|
|
17456
17456
|
|
|
17457
17457
|
// src/update.ts
|
|
@@ -17496,15 +17496,15 @@ var init_update = __esm(() => {
|
|
|
17496
17496
|
});
|
|
17497
17497
|
|
|
17498
17498
|
// src/release.ts
|
|
17499
|
-
import { readFileSync as
|
|
17500
|
-
import { join as
|
|
17499
|
+
import { readFileSync as readFileSync29, existsSync as existsSync16 } from "node:fs";
|
|
17500
|
+
import { join as join27 } from "node:path";
|
|
17501
17501
|
import { fileURLToPath as fileURLToPath14 } from "node:url";
|
|
17502
|
-
import { execSync as
|
|
17502
|
+
import { execSync as execSync5 } from "node:child_process";
|
|
17503
17503
|
function validateRelease() {
|
|
17504
17504
|
const checks = [];
|
|
17505
17505
|
checks.push({ name: "package.json version", ok: true, detail: VERSION });
|
|
17506
17506
|
try {
|
|
17507
|
-
const changelog =
|
|
17507
|
+
const changelog = readFileSync29(join27(ROOT8, "CHANGELOG.md"), "utf8");
|
|
17508
17508
|
const m = changelog.match(/^##\s+\[([0-9]+\.[0-9]+\.[0-9]+)\]/m);
|
|
17509
17509
|
const changelogVersion = m?.[1] ?? null;
|
|
17510
17510
|
checks.push({
|
|
@@ -17516,13 +17516,13 @@ function validateRelease() {
|
|
|
17516
17516
|
checks.push({ name: "CHANGELOG version", ok: false, detail: "CHANGELOG.md not found" });
|
|
17517
17517
|
}
|
|
17518
17518
|
try {
|
|
17519
|
-
const tag =
|
|
17519
|
+
const tag = execSync5("git describe --tags --abbrev=0", { cwd: ROOT8, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
17520
17520
|
checks.push({ name: "Git tag", ok: tag === `v${VERSION}`, detail: tag, soft: true });
|
|
17521
17521
|
} catch {
|
|
17522
17522
|
checks.push({ name: "Git tag", ok: true, detail: "unavailable (no git/tags)", soft: true });
|
|
17523
17523
|
}
|
|
17524
17524
|
try {
|
|
17525
|
-
const published =
|
|
17525
|
+
const published = execSync5("npm view blitzstrike version", { encoding: "utf8", timeout: 8000, stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
17526
17526
|
checks.push({ name: "npm registry version", ok: published === VERSION, detail: published, soft: true });
|
|
17527
17527
|
} catch {
|
|
17528
17528
|
checks.push({ name: "npm registry version", ok: true, detail: "unavailable (offline)", soft: true });
|
|
@@ -17536,22 +17536,22 @@ function validateRelease() {
|
|
|
17536
17536
|
return { version: VERSION, checks, consistent: checks.filter((c) => !c.soft).every((c) => c.ok) };
|
|
17537
17537
|
}
|
|
17538
17538
|
function lockfilePresent() {
|
|
17539
|
-
return existsSync16(
|
|
17539
|
+
return existsSync16(join27(ROOT8, "package-lock.json")) || existsSync16(join27(ROOT8, "bun.lock")) || existsSync16(join27(ROOT8, "bun.lockb"));
|
|
17540
17540
|
}
|
|
17541
17541
|
var ROOT8;
|
|
17542
17542
|
var init_release = __esm(() => {
|
|
17543
17543
|
init_version();
|
|
17544
|
-
ROOT8 =
|
|
17544
|
+
ROOT8 = join27(fileURLToPath14(new URL(".", import.meta.url)), "..");
|
|
17545
17545
|
});
|
|
17546
17546
|
|
|
17547
17547
|
// src/dependency.ts
|
|
17548
|
-
import { readFileSync as
|
|
17549
|
-
import { join as
|
|
17548
|
+
import { readFileSync as readFileSync30 } from "node:fs";
|
|
17549
|
+
import { join as join28 } from "node:path";
|
|
17550
17550
|
import { fileURLToPath as fileURLToPath15 } from "node:url";
|
|
17551
17551
|
function checkDependencies() {
|
|
17552
17552
|
let pkg = {};
|
|
17553
17553
|
try {
|
|
17554
|
-
pkg = JSON.parse(
|
|
17554
|
+
pkg = JSON.parse(readFileSync30(join28(ROOT9, "package.json"), "utf8"));
|
|
17555
17555
|
} catch {
|
|
17556
17556
|
return { install_scripts: [], git_dependencies: [], total_dependencies: 0, safe: false };
|
|
17557
17557
|
}
|
|
@@ -17574,12 +17574,12 @@ function checkDependencies() {
|
|
|
17574
17574
|
}
|
|
17575
17575
|
var ROOT9;
|
|
17576
17576
|
var init_dependency = __esm(() => {
|
|
17577
|
-
ROOT9 =
|
|
17577
|
+
ROOT9 = join28(fileURLToPath15(new URL(".", import.meta.url)), "..");
|
|
17578
17578
|
});
|
|
17579
17579
|
|
|
17580
17580
|
// src/cli.ts
|
|
17581
|
-
import { readFileSync as
|
|
17582
|
-
import { join as
|
|
17581
|
+
import { readFileSync as readFileSync31, existsSync as existsSync17, writeFileSync as writeFileSync10, mkdirSync as mkdirSync11 } from "node:fs";
|
|
17582
|
+
import { join as join29, dirname as dirname6 } from "node:path";
|
|
17583
17583
|
import { homedir as homedir11 } from "node:os";
|
|
17584
17584
|
import { fileURLToPath as fileURLToPath16 } from "node:url";
|
|
17585
17585
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -17624,7 +17624,7 @@ async function runDoctor() {
|
|
|
17624
17624
|
detail: h1 ? `X-HackerOne-Research: ${h1}` : "H1_USERNAME not set",
|
|
17625
17625
|
fix: h1 ? undefined : "export H1_USERNAME=<your-hackerone-username> (adds X-HackerOne-Research to outbound requests)"
|
|
17626
17626
|
});
|
|
17627
|
-
const dataOk = existsSync17(
|
|
17627
|
+
const dataOk = existsSync17(join29(ROOT10, "chains.json")) && existsSync17(join29(ROOT10, "tools-catalog.json"));
|
|
17628
17628
|
issues.push({
|
|
17629
17629
|
name: "Data layers (chains + tools-catalog)",
|
|
17630
17630
|
status: dataOk ? "ok" : "fail",
|
|
@@ -17675,21 +17675,21 @@ BlitzStrike — ${verdict}`);
|
|
|
17675
17675
|
function resolveCommand() {
|
|
17676
17676
|
if (which("blitzstrike"))
|
|
17677
17677
|
return { command: "blitzstrike", args: ["serve", "--mcp"] };
|
|
17678
|
-
const src =
|
|
17678
|
+
const src = join29(ROOT10, "src", "index.ts");
|
|
17679
17679
|
if (which("bun") && existsSync17(src))
|
|
17680
17680
|
return { command: "bun", args: ["run", src, "serve", "--mcp"] };
|
|
17681
17681
|
if (which("npx"))
|
|
17682
17682
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
17683
17683
|
if (which("bunx"))
|
|
17684
17684
|
return { command: "bunx", args: ["blitzstrike", "serve", "--mcp"] };
|
|
17685
|
-
const dist =
|
|
17685
|
+
const dist = join29(ROOT10, "dist", "index.js");
|
|
17686
17686
|
if (existsSync17(dist))
|
|
17687
17687
|
return { command: "bun", args: [dist, "serve", "--mcp"] };
|
|
17688
17688
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
17689
17689
|
}
|
|
17690
17690
|
function readJson(p) {
|
|
17691
17691
|
try {
|
|
17692
|
-
return JSON.parse(
|
|
17692
|
+
return JSON.parse(readFileSync31(p, "utf8"));
|
|
17693
17693
|
} catch {
|
|
17694
17694
|
return null;
|
|
17695
17695
|
}
|
|
@@ -17722,17 +17722,17 @@ function opencodeWrite(p) {
|
|
|
17722
17722
|
};
|
|
17723
17723
|
}
|
|
17724
17724
|
function copyOpenCodeAgents() {
|
|
17725
|
-
const srcDir =
|
|
17726
|
-
const dstDir =
|
|
17725
|
+
const srcDir = join29(ROOT10, "opencode-agents");
|
|
17726
|
+
const dstDir = join29(homedir11(), ".config", "opencode", "agents");
|
|
17727
17727
|
if (!existsSync17(srcDir))
|
|
17728
17728
|
return;
|
|
17729
17729
|
if (!existsSync17(dstDir))
|
|
17730
17730
|
mkdirSync11(dstDir, { recursive: true });
|
|
17731
17731
|
for (const f of ["Blitz Strike.md", "Blitz.md", "Eagle Eye.md", "Strike.md"]) {
|
|
17732
|
-
const src =
|
|
17733
|
-
const dst =
|
|
17732
|
+
const src = join29(srcDir, f);
|
|
17733
|
+
const dst = join29(dstDir, f);
|
|
17734
17734
|
if (existsSync17(src)) {
|
|
17735
|
-
writeFileSync10(dst,
|
|
17735
|
+
writeFileSync10(dst, readFileSync31(src, "utf8"));
|
|
17736
17736
|
}
|
|
17737
17737
|
}
|
|
17738
17738
|
}
|
|
@@ -17741,7 +17741,7 @@ function codexWrite(p) {
|
|
|
17741
17741
|
const dir = dirname6(p);
|
|
17742
17742
|
if (!existsSync17(dir))
|
|
17743
17743
|
mkdirSync11(dir, { recursive: true });
|
|
17744
|
-
let existing = existsSync17(p) ?
|
|
17744
|
+
let existing = existsSync17(p) ? readFileSync31(p, "utf8") : "";
|
|
17745
17745
|
if (!existing.trimEnd().endsWith(`
|
|
17746
17746
|
`))
|
|
17747
17747
|
existing += `
|
|
@@ -17760,7 +17760,7 @@ function hermesWrite(p) {
|
|
|
17760
17760
|
const dir = dirname6(p);
|
|
17761
17761
|
if (!existsSync17(dir))
|
|
17762
17762
|
mkdirSync11(dir, { recursive: true });
|
|
17763
|
-
let existing = existsSync17(p) ?
|
|
17763
|
+
let existing = existsSync17(p) ? readFileSync31(p, "utf8") : "";
|
|
17764
17764
|
existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
|
|
17765
17765
|
if (!existing.trimEnd().endsWith(`
|
|
17766
17766
|
`))
|
|
@@ -17787,74 +17787,74 @@ mcp_servers:
|
|
|
17787
17787
|
function detectAgents() {
|
|
17788
17788
|
const home = homedir11();
|
|
17789
17789
|
const agents = [];
|
|
17790
|
-
const claudeJson =
|
|
17790
|
+
const claudeJson = join29(home, ".claude.json");
|
|
17791
17791
|
agents.push({
|
|
17792
17792
|
name: "Claude Code",
|
|
17793
17793
|
detectPath: claudeJson,
|
|
17794
17794
|
installed: () => existsSync17(claudeJson) || which("claude"),
|
|
17795
17795
|
write: jsonMcpServersWrite(claudeJson)
|
|
17796
17796
|
});
|
|
17797
|
-
const claudeDesktop =
|
|
17797
|
+
const claudeDesktop = join29(home, ".config", "Claude", "claude_desktop_config.json");
|
|
17798
17798
|
agents.push({
|
|
17799
17799
|
name: "Claude Desktop",
|
|
17800
17800
|
detectPath: claudeDesktop,
|
|
17801
17801
|
installed: () => existsSync17(claudeDesktop),
|
|
17802
17802
|
write: jsonMcpServersWrite(claudeDesktop)
|
|
17803
17803
|
});
|
|
17804
|
-
const cursor =
|
|
17804
|
+
const cursor = join29(home, ".cursor", "mcp.json");
|
|
17805
17805
|
agents.push({
|
|
17806
17806
|
name: "Cursor",
|
|
17807
17807
|
detectPath: cursor,
|
|
17808
|
-
installed: () => existsSync17(
|
|
17808
|
+
installed: () => existsSync17(join29(home, ".cursor")) || which("cursor"),
|
|
17809
17809
|
write: jsonMcpServersWrite(cursor)
|
|
17810
17810
|
});
|
|
17811
|
-
const opencode =
|
|
17811
|
+
const opencode = join29(home, ".config", "opencode", "opencode.jsonc");
|
|
17812
17812
|
agents.push({
|
|
17813
17813
|
name: "OpenCode",
|
|
17814
17814
|
detectPath: opencode,
|
|
17815
|
-
installed: () => existsSync17(
|
|
17815
|
+
installed: () => existsSync17(join29(home, ".config", "opencode")) || which("opencode"),
|
|
17816
17816
|
write: opencodeWrite(opencode)
|
|
17817
17817
|
});
|
|
17818
|
-
const codex =
|
|
17818
|
+
const codex = join29(home, ".codex", "config.toml");
|
|
17819
17819
|
agents.push({
|
|
17820
17820
|
name: "Codex",
|
|
17821
17821
|
detectPath: codex,
|
|
17822
|
-
installed: () => existsSync17(
|
|
17822
|
+
installed: () => existsSync17(join29(home, ".codex")) || which("codex"),
|
|
17823
17823
|
write: codexWrite(codex)
|
|
17824
17824
|
});
|
|
17825
|
-
const hermes =
|
|
17825
|
+
const hermes = join29(home, ".hermes", "config.yaml");
|
|
17826
17826
|
agents.push({
|
|
17827
17827
|
name: "Hermes",
|
|
17828
17828
|
detectPath: hermes,
|
|
17829
|
-
installed: () => existsSync17(
|
|
17829
|
+
installed: () => existsSync17(join29(home, ".hermes")) || which("hermes"),
|
|
17830
17830
|
write: hermesWrite(hermes)
|
|
17831
17831
|
});
|
|
17832
|
-
const gemini =
|
|
17832
|
+
const gemini = join29(home, ".gemini", "settings.json");
|
|
17833
17833
|
agents.push({
|
|
17834
17834
|
name: "Gemini CLI",
|
|
17835
17835
|
detectPath: gemini,
|
|
17836
|
-
installed: () => existsSync17(
|
|
17836
|
+
installed: () => existsSync17(join29(home, ".gemini")) || which("gemini"),
|
|
17837
17837
|
write: jsonMcpServersWrite(gemini)
|
|
17838
17838
|
});
|
|
17839
|
-
const windsurf =
|
|
17839
|
+
const windsurf = join29(home, ".codeium", "windsurf", "mcp_config.json");
|
|
17840
17840
|
agents.push({
|
|
17841
17841
|
name: "Windsurf",
|
|
17842
17842
|
detectPath: windsurf,
|
|
17843
|
-
installed: () => existsSync17(
|
|
17843
|
+
installed: () => existsSync17(join29(home, ".codeium")) || which("windsurf"),
|
|
17844
17844
|
write: jsonMcpServersWrite(windsurf)
|
|
17845
17845
|
});
|
|
17846
|
-
const copilot =
|
|
17846
|
+
const copilot = join29(home, ".copilot", "mcp.json");
|
|
17847
17847
|
agents.push({
|
|
17848
17848
|
name: "Copilot",
|
|
17849
17849
|
detectPath: copilot,
|
|
17850
|
-
installed: () => existsSync17(
|
|
17850
|
+
installed: () => existsSync17(join29(home, ".copilot")),
|
|
17851
17851
|
write: jsonMcpServersWrite(copilot)
|
|
17852
17852
|
});
|
|
17853
|
-
const cline =
|
|
17853
|
+
const cline = join29(home, ".cline", "mcp_settings.json");
|
|
17854
17854
|
agents.push({
|
|
17855
17855
|
name: "Cline",
|
|
17856
17856
|
detectPath: cline,
|
|
17857
|
-
installed: () => existsSync17(
|
|
17857
|
+
installed: () => existsSync17(join29(home, ".cline")),
|
|
17858
17858
|
write: jsonMcpServersWrite(cline)
|
|
17859
17859
|
});
|
|
17860
17860
|
return agents;
|
|
@@ -17972,7 +17972,7 @@ var init_cli = __esm(() => {
|
|
|
17972
17972
|
init_release();
|
|
17973
17973
|
init_dependency();
|
|
17974
17974
|
init_mcp_status();
|
|
17975
|
-
ROOT10 =
|
|
17975
|
+
ROOT10 = join29(fileURLToPath16(new URL(".", import.meta.url)), "..");
|
|
17976
17976
|
});
|
|
17977
17977
|
|
|
17978
17978
|
// node_modules/zod/v3/helpers/util.js
|
|
@@ -32610,7 +32610,7 @@ class StdioServerTransport {
|
|
|
32610
32610
|
|
|
32611
32611
|
// src/server.ts
|
|
32612
32612
|
init_scanner();
|
|
32613
|
-
import { readFileSync as
|
|
32613
|
+
import { readFileSync as readFileSync28 } from "node:fs";
|
|
32614
32614
|
|
|
32615
32615
|
// src/recon.ts
|
|
32616
32616
|
var TIMEOUT_MS = 25000;
|
|
@@ -34262,7 +34262,7 @@ var SINK_TO_VECTOR = {
|
|
|
34262
34262
|
sql_execution: "sql_injection",
|
|
34263
34263
|
command_execution: "command_injection",
|
|
34264
34264
|
html_render: "xss",
|
|
34265
|
-
code_execution: "
|
|
34265
|
+
code_execution: "command_injection",
|
|
34266
34266
|
http_request: "ssrf",
|
|
34267
34267
|
deserialization: "deserialization",
|
|
34268
34268
|
file_operations: "path_traversal",
|
|
@@ -34545,7 +34545,16 @@ var ALIASES = {
|
|
|
34545
34545
|
"subdomain takeover": "subdomain_takeover",
|
|
34546
34546
|
takeover: "subdomain_takeover",
|
|
34547
34547
|
"dangling dns": "subdomain_takeover",
|
|
34548
|
-
"dangling cname": "subdomain_takeover"
|
|
34548
|
+
"dangling cname": "subdomain_takeover",
|
|
34549
|
+
"nosql injection": "nosql_injection",
|
|
34550
|
+
nosql: "nosql_injection",
|
|
34551
|
+
nosqli: "nosql_injection",
|
|
34552
|
+
"mongo injection": "nosql_injection",
|
|
34553
|
+
"mongodb injection": "nosql_injection",
|
|
34554
|
+
"host header": "host_header",
|
|
34555
|
+
"host header injection": "host_header",
|
|
34556
|
+
"host header poisoning": "host_header",
|
|
34557
|
+
"password reset poisoning": "host_header"
|
|
34549
34558
|
};
|
|
34550
34559
|
function normalize(term) {
|
|
34551
34560
|
return term.trim().toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -59037,6 +59046,8 @@ var VRT_MAP = {
|
|
|
59037
59046
|
sql_injection: { vrt: "injection > sql_injection", priority: "P1", severity: "critical", rationale: "SQL injection reaches sensitive data or enables admin takeover." },
|
|
59038
59047
|
ssti: { vrt: "injection > server_side_template_injection", priority: "P1", severity: "critical", rationale: "SSTI evaluates expressions server-side → code execution." },
|
|
59039
59048
|
command_injection: { vrt: "injection > os_command_injection", priority: "P1", severity: "critical", rationale: "User input reaches a shell (system/exec/popen) → arbitrary command execution, full host takeover." },
|
|
59049
|
+
nosql_injection: { vrt: "injection > nosql_injection", priority: "P1", severity: "critical", rationale: "NoSQL operator injection reaches a database query → auth bypass or data exfiltration." },
|
|
59050
|
+
host_header: { vrt: "injection > host_header_injection", priority: "P3", severity: "medium", rationale: "A reflected/trusted Host header can poison password-reset links or enable cache poisoning → account takeover in the right flow." },
|
|
59040
59051
|
deserialization: { vrt: "injection > insecure_deserialization", priority: "P1", severity: "critical", rationale: "Deserialization escalated via a gadget chain → code execution." },
|
|
59041
59052
|
xxe: { vrt: "injection > xml_external_entity", priority: "P1", severity: "critical", rationale: "XXE reads local files or reaches internal services." },
|
|
59042
59053
|
file_upload: { vrt: "insecure_design > unrestricted_file_upload", priority: "P1", severity: "critical", rationale: "A web-reachable uploaded file of an executable type → code execution." },
|
|
@@ -61257,7 +61268,7 @@ var POC_TEMPLATES = {
|
|
|
61257
61268
|
expected: `The response contains an injected \`X-Injected: ${MK}\` header.`,
|
|
61258
61269
|
fix: "Strip CR/LF from any value placed into a response header."
|
|
61259
61270
|
},
|
|
61260
|
-
|
|
61271
|
+
oauth_misconfig: {
|
|
61261
61272
|
severity: "medium",
|
|
61262
61273
|
marker: "https://attacker.example",
|
|
61263
61274
|
request: (i) => qs(i, encodeURIComponent("https://attacker.example")),
|
|
@@ -61447,18 +61458,123 @@ solc = "0.8.20"
|
|
|
61447
61458
|
}
|
|
61448
61459
|
}
|
|
61449
61460
|
|
|
61461
|
+
// src/hunting-intel.ts
|
|
61462
|
+
import { execSync as execSync4 } from "node:child_process";
|
|
61463
|
+
import { resolve4 as resolve42, Resolver } from "node:dns/promises";
|
|
61464
|
+
import { readFileSync as readFileSync25 } from "node:fs";
|
|
61465
|
+
import { join as join24 } from "node:path";
|
|
61466
|
+
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;
|
|
61467
|
+
async function localResolve(host) {
|
|
61468
|
+
try {
|
|
61469
|
+
return await resolve42(host);
|
|
61470
|
+
} catch {
|
|
61471
|
+
return [];
|
|
61472
|
+
}
|
|
61473
|
+
}
|
|
61474
|
+
async function publicResolve(host) {
|
|
61475
|
+
const r = new Resolver;
|
|
61476
|
+
r.setServers(["8.8.8.8", "1.1.1.1"]);
|
|
61477
|
+
try {
|
|
61478
|
+
return await r.resolve4(host);
|
|
61479
|
+
} catch {
|
|
61480
|
+
return [];
|
|
61481
|
+
}
|
|
61482
|
+
}
|
|
61483
|
+
function probeBlockingPage(host, ip, timeoutMs = 8000) {
|
|
61484
|
+
for (const port of ["443", "80"]) {
|
|
61485
|
+
const scheme = port === "443" ? "https" : "http";
|
|
61486
|
+
try {
|
|
61487
|
+
const out = execSync4(`curl -sk -m 6 --resolve ${host}:${port}:${ip} ${scheme}://${host}/ 2>/dev/null`, { encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"] });
|
|
61488
|
+
const preview = out.replace(/\s+/g, " ").trim().slice(0, 300);
|
|
61489
|
+
if (!preview)
|
|
61490
|
+
continue;
|
|
61491
|
+
return { probed: true, sinkhole: BLOCKING_KEYWORDS.test(preview), preview };
|
|
61492
|
+
} catch {}
|
|
61493
|
+
}
|
|
61494
|
+
return { probed: false, sinkhole: false, preview: "" };
|
|
61495
|
+
}
|
|
61496
|
+
async function resolveCheck(host) {
|
|
61497
|
+
const clean = host.replace(/^https?:\/\//, "").split("/")[0];
|
|
61498
|
+
const local = await localResolve(clean);
|
|
61499
|
+
const pub = await publicResolve(clean);
|
|
61500
|
+
if (local.length === 0 && pub.length === 0) {
|
|
61501
|
+
return { host: clean, local_ips: [], public_ips: [], sinkhole: false, verdict: "unresolvable", evidence: "no A record from local or public DNS" };
|
|
61502
|
+
}
|
|
61503
|
+
if (local.length === 0) {
|
|
61504
|
+
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." };
|
|
61505
|
+
}
|
|
61506
|
+
const firstLocal = local[0];
|
|
61507
|
+
const probe = probeBlockingPage(clean, firstLocal);
|
|
61508
|
+
if (probe.sinkhole) {
|
|
61509
|
+
return {
|
|
61510
|
+
host: clean,
|
|
61511
|
+
local_ips: local,
|
|
61512
|
+
public_ips: pub,
|
|
61513
|
+
sinkhole: true,
|
|
61514
|
+
verdict: "sinkhole",
|
|
61515
|
+
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}/`
|
|
61516
|
+
};
|
|
61517
|
+
}
|
|
61518
|
+
if (!probe.probed) {
|
|
61519
|
+
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." };
|
|
61520
|
+
}
|
|
61521
|
+
const ipDiff = new Set(local).size !== new Set(pub).size || !pub.every((ip) => local.includes(ip));
|
|
61522
|
+
return {
|
|
61523
|
+
host: clean,
|
|
61524
|
+
local_ips: local,
|
|
61525
|
+
public_ips: pub,
|
|
61526
|
+
sinkhole: false,
|
|
61527
|
+
verdict: ipDiff ? "clean-cdn" : "clean",
|
|
61528
|
+
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."
|
|
61529
|
+
};
|
|
61530
|
+
}
|
|
61531
|
+
function loadWatchlist() {
|
|
61532
|
+
const raw = readFileSync25(join24(process.cwd(), "intelligence", "watchlist.json"), "utf8");
|
|
61533
|
+
const data = JSON.parse(raw);
|
|
61534
|
+
return { cwe_frequency: data.cwe_frequency ?? [], cve_watchlist: data.cve_watchlist ?? [] };
|
|
61535
|
+
}
|
|
61536
|
+
function fmtPayout(min, max) {
|
|
61537
|
+
if (min == null || max == null)
|
|
61538
|
+
return "n/a";
|
|
61539
|
+
const f = (n) => n >= 1000 ? `$${(n / 1000).toFixed(n % 1000 === 0 ? 0 : 1)}K` : `$${n}`;
|
|
61540
|
+
return `${f(min)}–${f(max)}`;
|
|
61541
|
+
}
|
|
61542
|
+
function watchlist() {
|
|
61543
|
+
const { cwe_frequency, cve_watchlist } = loadWatchlist();
|
|
61544
|
+
const entries = cwe_frequency.map((e) => {
|
|
61545
|
+
const pat = e.pattern ? patternLookup(e.pattern) : null;
|
|
61546
|
+
const payoutMin = e.payout_min ?? pat?.payout_min ?? null;
|
|
61547
|
+
const payoutMax = e.payout_max ?? pat?.payout_max ?? null;
|
|
61548
|
+
return {
|
|
61549
|
+
cwe: e.cwe,
|
|
61550
|
+
name: e.name,
|
|
61551
|
+
rank: e.rank,
|
|
61552
|
+
saturated: e.saturated,
|
|
61553
|
+
pattern: e.pattern ?? null,
|
|
61554
|
+
payout_min: payoutMin,
|
|
61555
|
+
payout_max: payoutMax,
|
|
61556
|
+
typical_payout: fmtPayout(payoutMin, payoutMax),
|
|
61557
|
+
notes: e.notes,
|
|
61558
|
+
grounded: pat != null
|
|
61559
|
+
};
|
|
61560
|
+
}).sort((a, b) => a.rank - b.rank);
|
|
61561
|
+
const unsaturated = entries.filter((e) => !e.saturated);
|
|
61562
|
+
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.`;
|
|
61563
|
+
return { cwe_frequency: entries, cve_watchlist, recommendation };
|
|
61564
|
+
}
|
|
61565
|
+
|
|
61450
61566
|
// src/enterprise-benchmark.ts
|
|
61451
|
-
import { existsSync as existsSync15, readFileSync as
|
|
61452
|
-
import { join as
|
|
61567
|
+
import { existsSync as existsSync15, readFileSync as readFileSync26, readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
|
|
61568
|
+
import { join as join25, resolve } from "node:path";
|
|
61453
61569
|
init_complex_bugs();
|
|
61454
61570
|
var ROOT6 = resolve(process.cwd(), "bench", "enterprise");
|
|
61455
61571
|
var ENTERPRISE_FIXTURES = [
|
|
61456
|
-
{ id: "php-laravel", dir:
|
|
61457
|
-
{ id: "js-express", dir:
|
|
61458
|
-
{ id: "py-django", dir:
|
|
61459
|
-
{ id: "java-spring", dir:
|
|
61460
|
-
{ id: "wp-batch-route", dir:
|
|
61461
|
-
{ id: "php-complex", dir:
|
|
61572
|
+
{ id: "php-laravel", dir: join25(ROOT6, "php-laravel"), expected_vulns: 4 },
|
|
61573
|
+
{ id: "js-express", dir: join25(ROOT6, "js-express"), expected_vulns: 2 },
|
|
61574
|
+
{ id: "py-django", dir: join25(ROOT6, "py-django"), expected_vulns: 2 },
|
|
61575
|
+
{ id: "java-spring", dir: join25(ROOT6, "java-spring"), expected_vulns: 2 },
|
|
61576
|
+
{ id: "wp-batch-route", dir: join25(ROOT6, "wp-batch-route"), expected_vulns: 3 },
|
|
61577
|
+
{ id: "php-complex", dir: join25(ROOT6, "php-complex"), expected_vulns: 4 }
|
|
61462
61578
|
];
|
|
61463
61579
|
function sourceFiles(root) {
|
|
61464
61580
|
const out = [];
|
|
@@ -61470,7 +61586,7 @@ function sourceFiles(root) {
|
|
|
61470
61586
|
return;
|
|
61471
61587
|
}
|
|
61472
61588
|
for (const e of entries) {
|
|
61473
|
-
const p =
|
|
61589
|
+
const p = join25(dir, e);
|
|
61474
61590
|
let st;
|
|
61475
61591
|
try {
|
|
61476
61592
|
st = statSync3(p);
|
|
@@ -61491,7 +61607,7 @@ function detectFixture(dir) {
|
|
|
61491
61607
|
for (const f of sourceFiles(dir)) {
|
|
61492
61608
|
let code;
|
|
61493
61609
|
try {
|
|
61494
|
-
code =
|
|
61610
|
+
code = readFileSync26(f, "utf8");
|
|
61495
61611
|
} catch {
|
|
61496
61612
|
continue;
|
|
61497
61613
|
}
|
|
@@ -61704,7 +61820,7 @@ function createServer2() {
|
|
|
61704
61820
|
` + `- A hard block (Cloudflare/WAF/IP-block/captcha) is NOT the end: mark the finding blocked + note the required vantage (residential / authorized / correct-SNI / --resolve direct-to-origin), then CONTINUE to the next lead (origin bypass, non-prod cluster, mobile APK, subdomain).
|
|
61705
61821
|
` + `- Keep trying every lead AND every chain step until each is verified/executed or blocked, THEN generate_report. Never deliver a bare surface map, and never stop at 'found X' when X enables more.
|
|
61706
61822
|
|
|
61707
|
-
` + "TWO PATHS:\\n" + "- Source path on disk -> blitz_scan / taint_file / eagle_eye2 (static).\\n" + "- Live URL -> live_recon / active_scan / strike_verify (web-hunting).\\n\\n" + "DEEPEN A FINDING: attack_plan(target, tech, params) to get the prioritized vector plan (decide what to test next with data); list_chains (escalation), chain_links (compose), technique_lookup(cls), payload_lookup, bypass_lookup (blocked). Ground any detector hit empirically with pattern_lookup(id) — it returns the class's CWE, real-world signature, escalation chains, citable references, per-class bypass techniques, and the high-value assets (crown jewels) that class hits, so a raw hit becomes a class-aware finding. Resolve free-form terminology first with synonym_lookup(term) (IDOR/BOLA/SQLi/prompt-injection → canonical id). For an llm_injection hit, its pattern carries a validation discipline — the RUN-TWICE RULE (reproduce token-for-token across two fresh sessions), NON-GUESSABLE ANCHOR (must leak a real key/URL, not a plausible guess), and OOB PROOF (a verifiable callback log) — apply all three before reporting. BEFORE generating a report, run submission_gate(...) and only ship a finding that passes all 7 questions (reproducible, in-scope, real-impact, no-privileged-assumption, verified-live, evidence-redacted, severity-derived); one false = kill it. Generate the concrete reproduction recipe with auto_poc(type, base_url, endpoint, param) — it emits the exact marker request + negative control + expected differential + Patchstack-style title, so 'reproducible' is executable, not abstract. Calibrate severity with severity_calibrate(type, cvss) to get a P1–P5 tier + VRT-style category. Start each engagement with engagement_scaffold(target, scope) for the folder + scope.md structure, and check data_refresh() to confirm the data layers are consistent. Phase doctrine: read_skill(bs-orchestrate-engagement | bs-web-hunting | bs-source-audit | bs-verify-finding). Campaign plan: read_playbook(target-type). On any failure call retry_guidance(signal) / model_fallback(signal) before giving up. HUNTING DOCTRINE (maximize payout rate): (1) SIBLING RULE — if one endpoint requires auth, check every sibling (/export /delete /share); ~30% of paid IDOR/auth bugs come from siblings — AUTOMATE it with sibling_scan(base_url, endpoint, token_a, token_b), the deterministic live logic-bug prober (sibling enumeration + differential IDOR + auth matrix + mass assignment + rate limit, evidence-first). (2) A→B SIGNAL — a confirmed bug signals a class of developer mistake; hunt B and C (20-min box) before reporting. (3) TWO-ACCOUNT IDOR — always test with attacker account A reading victim account B, and state it in the report. (4) PoC ESCALATION — IDOR shows victim data (not 200), XSS shows cookie exfil (not alert), SSRF shows internal response (not DNS), SQLi shows DB content (not error). (5) FOLLOW THE MONEY — billing/credits/refunds/wallet flows. (6) Kill weak findings with kill_list(type) BEFORE the gate — missing headers, self-XSS, open-redirect-alone, SSRF-DNS-only, GraphQL-introspection-alone are always N/A standalone. (7) CI/CD + SAML/SSO are critical attack surfaces (pull_request_target, expression injection, XML signature wrapping, comment injection). Full doctrine: hunting_doctrine().\n" + "KNOWLEDGE BASE (discover, don't guess): skillLookup(topic) / listSkills for the 40 attack-path + hunting skills (ap-*, hp-*, bs-*); list_attack_vectors / attack_vectors(cat) for the 34-category vector taxonomy; taxonomy(kind) for OWASP/CWE/ASVS/API-top-10; read_playbook(type) for the 17 campaign playbooks; list_manuals / read_tool_manual for 317 tool references; list_tools / run_catalog_tool for the 141-tool catalog. Every tool, skill, and data layer is reachable from these. CROSS-TARGET INTELLIGENCE: strike_resolve auto-records confirmed/false_positive verdicts into the empirical ledger; query it with intelligence(action=hit_rates/top_vectors/recall) so a new target on a known tech inherits historical hit-rates (attack_plan already blends them into success_probability). intelligence(action=recall, tech, target) is the per-engagement pattern recall — it returns which patterns historically CONFIRMED on that tech (prioritize them) versus which only ever produced false positives (avoid them), so a new engagement starts from the empirical playbook of past ones. LIVE PENTEST + TARGETING (black-box, WAF-safe): cache_gap(base_url, endpoint) fuzzes path-normalization + static-extension variants for cache deception/poisoning; blind_oracle(base_url, param, value) detects which SINK a param reaches (sql/ssti/xss) via benign arithmetic (id=1+0, test${7*7}) WITHOUT a malicious payload; param_precedence(base_url, param, A, B) maps duplicate-parameter precedence (first/last-wins) for WAF-bypass. OOB (out-of-band) PROOF for BLIND vulns: oob_start() spins an interactsh listener (unique *.oast.* domain, streams DNS/HTTP interactions — the standard for public targets) or a local HTTP listener for local labs; inject the canary, then oob_poll(id) until the hit arrives; oob_stop(id) to clean up. A blind SSRF/XXE/SQLi/command-injection is CONFIRMED only when the canary actually arrives — never from a guess. SOURCE TARGETING: action_surface(code) maps every wp_ajax_* action to its vuln class + DERIVED priority (what to look for WHERE); security_state(sanitizers, sink) gives a wrong-context sanitization verdict; framework_knowledge(framework) returns auth/nonce primitives for 9 frameworks; variant_scan(root, signature) mass-sweeps an install base for ONE bug family; patch_analyze(old, new) REVERSES a security patch into the 0-day, and its mine_signature feeds variant_scan to sweep unpatched copies. All of these are DETECTION HINTS — verify each live (strike_verify / browser_validate) before it enters the report. NEWEST DETECTORS — understand AND try them: variant_scan also emits priv_esc (privilege escalation — a role/capability/user change fed by request input, traced cross-file via reverse call-graph, e.g. set_role($id, sanitize_key($_POST['um-role'])) or update_user_meta(...,'wp_capabilities', $_POST[...])) and flags uploadAllow=>array('all') unrestricted-upload configs as file_upload (the file-manager/connector shape). TO TRY a priv_esc hit: submit the role field with 'administrator' (or send wp_capabilities meta) and confirm the account actually gains admin. TO TRY an unrestricted-upload hit: POST a .php/.phtml and confirm it lands web-reachable + executes. variant_scan also emits hardcoded_secret (CWE-798) in three tiers: a secret field (api_key/db_password/access_token/aws_*) assigned a literal (HIGH), a known secret FORMAT (JWT eyJ…/AWS AKIA…/GitHub ghp_…/Slack xoxb-…/…) regardless of name (HIGH), or a high-entropy suspect token (MEDIUM — verify before reporting). Verify a frontend/JS secret live via drive_devtools (inspect the JS bundle / network response and confirm the key is actually exposed), and for a backend secret confirm it is reachable/valid. A static hit is a HYPOTHESIS until verified live — never report unverified. For a repo, also scan_git_history(root) — secrets DELETED from HEAD still live in .git history (the removed-later-is-not-fixed trap), and it feeds every recovered pre-deletion blob through the same secret detector. Once you recover a credential, prove it is still ACTIVE with validate_leaked_key(apiBase, endpoints, key|blobUrl) — a READ-ONLY differential test (401-without-key vs 200-with-key = auth bypass PROVEN). Two supply-chain detectors: dependency_confusion (a manifest/build file resolving a package from a public registry — pypi.org/npmjs.org --extra-index-url, or a scoped @company/pkg with no private registry) and ml_supply_chain (torch.load without weights_only=True / joblib.load / np.load(allow_pickle=True) / keras load_model on an untrusted model → pickle RCE). Rust is supported end-to-end: the taint engine (blitz_scan / taint_file) maps Rust sinks (std::process::Command, sqlx/diesel, reqwest/hyper, std::fs, serde/bincode) to the classic classes, plus two Rust-specific detectors — rust_unsafe (transmute / assume_init / from_raw_parts / ptr::* — memory-unsafety surface, CWE-119) and rust_format_string (println!/format! with a variable as the format string — CWE-134). The sqlx query! compile-time macro is treated as safe (suppressed). Two more web-class detectors: jwt_alg_confusion (a JWT verifier that dispatches on the token's alg header and accepts BOTH RS256 and HS256, with the HS256 HMAC secret derived from public/shared key material — CWE-347; the attacker sets alg=HS256 and signs with the public key to forge admin tokens) and cache_deception (a reverse proxy caching static-extension URLs with a cache key that omits the session cookie, over a session-authenticated backend — CWE-525; the victim's cached private response is re-fetched anonymously). TO TRY a jwt_alg_confusion hit: fetch the public key (/.well-known/jwks.json), forge an HS256 token signed with that public key, and confirm admin access. TO TRY a cache_deception hit: request an authenticated path with a static extension appended (.css/.js) and re-fetch it anonymously. Three more detectors: cors_misconfiguration (Access-Control-Allow-Origin reflects the request Origin or is '*' while credentials are true — CWE-942; any site reads the victim's authenticated responses), xss (user input into a DOM sink — innerHTML / dangerouslySetInnerHTML / document.write / .html() / eval — CWE-79), and subdomain_takeover (a dangling CNAME/AliasTarget to a claimable service like *.github.io/*.herokuapp.com/*.amazonaws.com — CWE-404). TO TRY a cors hit: send an evil Origin + confirm the response echoes it with Allow-Credentials:true. TO TRY an xss hit: confirm the sink executes (drive_devtools). TO TRY a takeover hit: verify the CNAME target resolves to an unclaimed/not-found page. WEB3: for Solidity/EVM, call web3_audit(code|path) — a deterministic 13-class smart-contract audit (reentrancy incl. ERC721/ERC1155 callback reentrancy, unchecked return, unchecked arithmetic, tx.origin auth, signature replay, unprotected selfdestruct, delegatecall-to-user-input, missing access control, timestamp dependence, spot-price oracle manipulation incl. slot0/latestRoundData, unbounded loop, abi.encodePacked hash collision, missing address(0) validation) returning ranked findings (class/CWE/severity/line), then foundry_poc(class, contract) to generate an executable Foundry test, and foundry_run(contract_code, poc_code|class_) to actually EXECUTE it — a PASSING PoC test is the proof that the exploit reproduces (deterministic: forge's own test verdict). A web3 hit is a HYPOTHESIS until the Foundry PoC reproduces it.\n" + `PRECISION ANALYSIS — chrome-devtools MCP is MANDATORY, not optional:
|
|
61823
|
+
` + "TWO PATHS:\\n" + "- Source path on disk -> blitz_scan / taint_file / eagle_eye2 (static).\\n" + "- Live URL -> live_recon / active_scan / strike_verify (web-hunting).\\n\\n" + "DEEPEN A FINDING: attack_plan(target, tech, params) to get the prioritized vector plan (decide what to test next with data); list_chains (escalation), chain_links (compose), technique_lookup(cls), payload_lookup, bypass_lookup (blocked). Ground any detector hit empirically with pattern_lookup(id) — it returns the class's CWE, real-world signature, escalation chains, citable references, per-class bypass techniques, and the high-value assets (crown jewels) that class hits, so a raw hit becomes a class-aware finding. Resolve free-form terminology first with synonym_lookup(term) (IDOR/BOLA/SQLi/prompt-injection → canonical id). For an llm_injection hit, its pattern carries a validation discipline — the RUN-TWICE RULE (reproduce token-for-token across two fresh sessions), NON-GUESSABLE ANCHOR (must leak a real key/URL, not a plausible guess), and OOB PROOF (a verifiable callback log) — apply all three before reporting. BEFORE generating a report, run submission_gate(...) and only ship a finding that passes all 7 questions (reproducible, in-scope, real-impact, no-privileged-assumption, verified-live, evidence-redacted, severity-derived); one false = kill it. Generate the concrete reproduction recipe with auto_poc(type, base_url, endpoint, param) — it emits the exact marker request + negative control + expected differential + Patchstack-style title, so 'reproducible' is executable, not abstract. Calibrate severity with severity_calibrate(type, cvss) to get a P1–P5 tier + VRT-style category. Start each engagement with engagement_scaffold(target, scope) for the folder + scope.md structure, and check data_refresh() to confirm the data layers are consistent. Phase doctrine: read_skill(bs-orchestrate-engagement | bs-web-hunting | bs-source-audit | bs-verify-finding). Campaign plan: read_playbook(target-type). On any failure call retry_guidance(signal) / model_fallback(signal) before giving up. HUNTING DOCTRINE (maximize payout rate): (1) SIBLING RULE — if one endpoint requires auth, check every sibling (/export /delete /share); ~30% of paid IDOR/auth bugs come from siblings — AUTOMATE it with sibling_scan(base_url, endpoint, token_a, token_b), the deterministic live logic-bug prober (sibling enumeration + differential IDOR + auth matrix + mass assignment + rate limit, evidence-first). (2) A→B SIGNAL — a confirmed bug signals a class of developer mistake; hunt B and C (20-min box) before reporting. (3) TWO-ACCOUNT IDOR — always test with attacker account A reading victim account B, and state it in the report. (4) PoC ESCALATION — IDOR shows victim data (not 200), XSS shows cookie exfil (not alert), SSRF shows internal response (not DNS), SQLi shows DB content (not error). (5) FOLLOW THE MONEY — billing/credits/refunds/wallet flows. (6) Kill weak findings with kill_list(type) BEFORE the gate — missing headers, self-XSS, open-redirect-alone, SSRF-DNS-only, GraphQL-introspection-alone are always N/A standalone. (7) CI/CD + SAML/SSO are critical attack surfaces (pull_request_target, expression injection, XML signature wrapping, comment injection). Full doctrine: hunting_doctrine(). WHAT TO HUNT NEXT: watchlist() returns the ranked CWE frequency + high-value CVEs with payout + a saturated flag (prioritize high-freq/high-payout/not-saturated: missing-authz, SQLi, SSRF, deserialization; skip saturated XSS/CSRF/open-redirect unless they chain into takeover). PRECISION GUARD: resolve_check(host) detects when an ISP/country redirects the domain to a blocking/sinkhole page (gambling/adult/streaming niches) — audit the PUBLIC IP, never the sinkhole.\n" + "KNOWLEDGE BASE (discover, don't guess): skillLookup(topic) / listSkills for the 40 attack-path + hunting skills (ap-*, hp-*, bs-*); list_attack_vectors / attack_vectors(cat) for the 34-category vector taxonomy; taxonomy(kind) for OWASP/CWE/ASVS/API-top-10; read_playbook(type) for the 17 campaign playbooks; list_manuals / read_tool_manual for 317 tool references; list_tools / run_catalog_tool for the 141-tool catalog. Every tool, skill, and data layer is reachable from these. CROSS-TARGET INTELLIGENCE: strike_resolve auto-records confirmed/false_positive verdicts into the empirical ledger; query it with intelligence(action=hit_rates/top_vectors/recall) so a new target on a known tech inherits historical hit-rates (attack_plan already blends them into success_probability). intelligence(action=recall, tech, target) is the per-engagement pattern recall — it returns which patterns historically CONFIRMED on that tech (prioritize them) versus which only ever produced false positives (avoid them), so a new engagement starts from the empirical playbook of past ones. LIVE PENTEST + TARGETING (black-box, WAF-safe): cache_gap(base_url, endpoint) fuzzes path-normalization + static-extension variants for cache deception/poisoning; blind_oracle(base_url, param, value) detects which SINK a param reaches (sql/ssti/xss) via benign arithmetic (id=1+0, test${7*7}) WITHOUT a malicious payload; param_precedence(base_url, param, A, B) maps duplicate-parameter precedence (first/last-wins) for WAF-bypass. OOB (out-of-band) PROOF for BLIND vulns: oob_start() spins an interactsh listener (unique *.oast.* domain, streams DNS/HTTP interactions — the standard for public targets) or a local HTTP listener for local labs; inject the canary, then oob_poll(id) until the hit arrives; oob_stop(id) to clean up. A blind SSRF/XXE/SQLi/command-injection is CONFIRMED only when the canary actually arrives — never from a guess. SOURCE TARGETING: action_surface(code) maps every wp_ajax_* action to its vuln class + DERIVED priority (what to look for WHERE); security_state(sanitizers, sink) gives a wrong-context sanitization verdict; framework_knowledge(framework) returns auth/nonce primitives for 9 frameworks; variant_scan(root, signature) mass-sweeps an install base for ONE bug family; patch_analyze(old, new) REVERSES a security patch into the 0-day, and its mine_signature feeds variant_scan to sweep unpatched copies. All of these are DETECTION HINTS — verify each live (strike_verify / browser_validate) before it enters the report. NEWEST DETECTORS — understand AND try them: variant_scan also emits priv_esc (privilege escalation — a role/capability/user change fed by request input, traced cross-file via reverse call-graph, e.g. set_role($id, sanitize_key($_POST['um-role'])) or update_user_meta(...,'wp_capabilities', $_POST[...])) and flags uploadAllow=>array('all') unrestricted-upload configs as file_upload (the file-manager/connector shape). TO TRY a priv_esc hit: submit the role field with 'administrator' (or send wp_capabilities meta) and confirm the account actually gains admin. TO TRY an unrestricted-upload hit: POST a .php/.phtml and confirm it lands web-reachable + executes. variant_scan also emits hardcoded_secret (CWE-798) in three tiers: a secret field (api_key/db_password/access_token/aws_*) assigned a literal (HIGH), a known secret FORMAT (JWT eyJ…/AWS AKIA…/GitHub ghp_…/Slack xoxb-…/…) regardless of name (HIGH), or a high-entropy suspect token (MEDIUM — verify before reporting). Verify a frontend/JS secret live via drive_devtools (inspect the JS bundle / network response and confirm the key is actually exposed), and for a backend secret confirm it is reachable/valid. A static hit is a HYPOTHESIS until verified live — never report unverified. For a repo, also scan_git_history(root) — secrets DELETED from HEAD still live in .git history (the removed-later-is-not-fixed trap), and it feeds every recovered pre-deletion blob through the same secret detector. Once you recover a credential, prove it is still ACTIVE with validate_leaked_key(apiBase, endpoints, key|blobUrl) — a READ-ONLY differential test (401-without-key vs 200-with-key = auth bypass PROVEN). Two supply-chain detectors: dependency_confusion (a manifest/build file resolving a package from a public registry — pypi.org/npmjs.org --extra-index-url, or a scoped @company/pkg with no private registry) and ml_supply_chain (torch.load without weights_only=True / joblib.load / np.load(allow_pickle=True) / keras load_model on an untrusted model → pickle RCE). Rust is supported end-to-end: the taint engine (blitz_scan / taint_file) maps Rust sinks (std::process::Command, sqlx/diesel, reqwest/hyper, std::fs, serde/bincode) to the classic classes, plus two Rust-specific detectors — rust_unsafe (transmute / assume_init / from_raw_parts / ptr::* — memory-unsafety surface, CWE-119) and rust_format_string (println!/format! with a variable as the format string — CWE-134). The sqlx query! compile-time macro is treated as safe (suppressed). Two more web-class detectors: jwt_alg_confusion (a JWT verifier that dispatches on the token's alg header and accepts BOTH RS256 and HS256, with the HS256 HMAC secret derived from public/shared key material — CWE-347; the attacker sets alg=HS256 and signs with the public key to forge admin tokens) and cache_deception (a reverse proxy caching static-extension URLs with a cache key that omits the session cookie, over a session-authenticated backend — CWE-525; the victim's cached private response is re-fetched anonymously). TO TRY a jwt_alg_confusion hit: fetch the public key (/.well-known/jwks.json), forge an HS256 token signed with that public key, and confirm admin access. TO TRY a cache_deception hit: request an authenticated path with a static extension appended (.css/.js) and re-fetch it anonymously. Three more detectors: cors_misconfiguration (Access-Control-Allow-Origin reflects the request Origin or is '*' while credentials are true — CWE-942; any site reads the victim's authenticated responses), xss (user input into a DOM sink — innerHTML / dangerouslySetInnerHTML / document.write / .html() / eval — CWE-79), and subdomain_takeover (a dangling CNAME/AliasTarget to a claimable service like *.github.io/*.herokuapp.com/*.amazonaws.com — CWE-404). TO TRY a cors hit: send an evil Origin + confirm the response echoes it with Allow-Credentials:true. TO TRY an xss hit: confirm the sink executes (drive_devtools). TO TRY a takeover hit: verify the CNAME target resolves to an unclaimed/not-found page. WEB3: for Solidity/EVM, call web3_audit(code|path) — a deterministic 13-class smart-contract audit (reentrancy incl. ERC721/ERC1155 callback reentrancy, unchecked return, unchecked arithmetic, tx.origin auth, signature replay, unprotected selfdestruct, delegatecall-to-user-input, missing access control, timestamp dependence, spot-price oracle manipulation incl. slot0/latestRoundData, unbounded loop, abi.encodePacked hash collision, missing address(0) validation) returning ranked findings (class/CWE/severity/line), then foundry_poc(class, contract) to generate an executable Foundry test, and foundry_run(contract_code, poc_code|class_) to actually EXECUTE it — a PASSING PoC test is the proof that the exploit reproduces (deterministic: forge's own test verdict). A web3 hit is a HYPOTHESIS until the Foundry PoC reproduces it.\n" + `PRECISION ANALYSIS — chrome-devtools MCP is MANDATORY, not optional:
|
|
61708
61824
|
` + `- When a lead needs DOM/JS precision — DOM-XSS sink execution, AJAX endpoint + payload interception, JS runtime errors, redirect chains, SSO/token flow, or anything a static HTML glance cannot prove — call drive_devtools(target). It spawns chrome-devtools-mcp and drives a REAL headless browser (navigate -> list_network_requests -> list_console_messages -> evaluate_script), giving reproducible evidence instead of a guess.
|
|
61709
61825
|
` + `- Before calling it, check_mcp() to confirm the browser MCP is available; if not, blitzstrike install-tools or npm i -g chrome-devtools-mcp. Never substitute eyeballing minified JS or a static sink guess when a live browser can PROVE it. This is the precision + consistency layer.
|
|
61710
61826
|
` + "EXTERNAL MCP SERVERS (verified, connect them — NOT one-shot CLIs): chrome-devtools-mcp (29 tools: list_network_requests/get_network_request for raw HTTP interception, list_console_messages for JS errors + DOM-XSS, evaluate_script to run JS, take_screenshot) -> stdio chrome-devtools-mcp --headless --chromeArg=--no-sandbox --chromeArg=--disable-gpu (as root/container this is MANDATORY or the browser crashes; add --proxyServer http://127.0.0.1:8080 to route through Burp, --acceptInsecureCerts for bad TLS). burp-suite-mcp (proxy history + active scan + repeater) -> NOT npm: build the JAR (git clone https://github.com/PortSwigger/mcp-server.git && cd mcp-server && ./gradlew embedProxyJar), load it in Burp Extensions, then connect the client to http://127.0.0.1:9876 (SSE) or the stdio proxy java -jar mcp-proxy-all.jar --sse-url http://127.0.0.1:9876."
|
|
@@ -62585,7 +62701,7 @@ ${statusLine}` : statusLine },
|
|
|
62585
62701
|
let src = code;
|
|
62586
62702
|
if (!src && path) {
|
|
62587
62703
|
try {
|
|
62588
|
-
src =
|
|
62704
|
+
src = readFileSync28(path, "utf8");
|
|
62589
62705
|
} catch {
|
|
62590
62706
|
return { content: [{ type: "text", text: JSON.stringify({ error: "unreadable path" }) }] };
|
|
62591
62707
|
}
|
|
@@ -62666,7 +62782,7 @@ ${statusLine}` : statusLine },
|
|
|
62666
62782
|
let src = code;
|
|
62667
62783
|
if (!src && path) {
|
|
62668
62784
|
try {
|
|
62669
|
-
src =
|
|
62785
|
+
src = readFileSync28(path, "utf8");
|
|
62670
62786
|
} catch {
|
|
62671
62787
|
return { content: [{ type: "text", text: JSON.stringify({ error: "unreadable path" }) }] };
|
|
62672
62788
|
}
|
|
@@ -62714,6 +62830,22 @@ ${statusLine}` : statusLine },
|
|
|
62714
62830
|
}, async () => {
|
|
62715
62831
|
return { content: [{ type: "text", text: JSON.stringify(huntingDoctrine()) }] };
|
|
62716
62832
|
});
|
|
62833
|
+
server.registerTool("resolve_check", {
|
|
62834
|
+
title: "DNS sinkhole / blocking-page detection (precision guard)",
|
|
62835
|
+
description: "Detect when a target domain is redirected by the ISP/country to a blocking/sinkhole page (gambling/adult/streaming niches) instead of the real site. Compares the local resolver against public DNS (8.8.8.8/1.1.1.1) and probes the local IP for blocking-page content (.gov / 'Internet Positif' / 'blocked' keywords). If a sinkhole is detected, audit the PUBLIC IP — never the sinkhole. Content is the signal (a different IP set alone is normal for CDN/anycast). Use before live_recon/active_scan on any sensitive-niche domain.",
|
|
62836
|
+
inputSchema: {
|
|
62837
|
+
host: string2().describe("Domain to check (e.g. indoav.site or https://target.com)")
|
|
62838
|
+
}
|
|
62839
|
+
}, async ({ host }) => {
|
|
62840
|
+
return { content: [{ type: "text", text: JSON.stringify(await resolveCheck(host)) }] };
|
|
62841
|
+
});
|
|
62842
|
+
server.registerTool("watchlist", {
|
|
62843
|
+
title: "Known CWE/CVE frequency + payout watchlist (what to hunt next)",
|
|
62844
|
+
description: "Return the most-reported bug-bounty classes (CWE frequency, ranked) and high-value CVEs, each with payout range + a 'saturated' flag, joined to a Blitz Strike detector pattern for grounding. Use it to decide WHAT to hunt next: prioritize high-frequency, high-payout, NOT-yet-saturated classes (IDOR/missing-authz, SQLi, SSRF, deserialization); skip saturated/low-value classes (XSS/CSRF/open-redirect) unless they chain into account takeover.",
|
|
62845
|
+
inputSchema: {}
|
|
62846
|
+
}, async () => {
|
|
62847
|
+
return { content: [{ type: "text", text: JSON.stringify(watchlist()) }] };
|
|
62848
|
+
});
|
|
62717
62849
|
server.registerTool("auto_poc", {
|
|
62718
62850
|
title: "Auto-PoC — generate a copy-paste-ready reproduction recipe",
|
|
62719
62851
|
description: "Turn a finding into a concrete reproduction recipe (the 'reproducible' gate question made executable). Given a vuln class + target + endpoint + param, emit the exact request (with a MARKER), a NEGATIVE CONTROL, and the expected differential that PROVES the bug — plus a Patchstack-style title, derived severity, and the fix. Covers sql_injection, missing_authz/idor, ssrf, xss, ssti, command_injection, file_upload, mass_assignment, jwt_alg_confusion, crlf, open_redirect, xxe, prototype_pollution, deserialization, hardcoded_secret, cache_deception, cors_misconfiguration, subdomain_takeover. Pass type='list' to enumerate supported classes.",
|
|
@@ -63333,7 +63465,7 @@ ${statusLine}` : statusLine },
|
|
|
63333
63465
|
description: "Route confusion (separate class from source→sink taint): detect attacker-controlled dispatch (call_user_func/forward/dispatch), dynamic method calls ($obj->$m()), dynamic include (LFI), batch/proxy forwarding without per-route auth re-check, and route matching without path normalization.",
|
|
63334
63466
|
inputSchema: { path: string2().describe("Source file path (PHP)") }
|
|
63335
63467
|
}, async ({ path }) => {
|
|
63336
|
-
const code =
|
|
63468
|
+
const code = readFileSync28(path, "utf8");
|
|
63337
63469
|
const findings = detectRouteConfusion(code, path);
|
|
63338
63470
|
return {
|
|
63339
63471
|
content: [{
|
|
@@ -63347,7 +63479,7 @@ ${statusLine}` : statusLine },
|
|
|
63347
63479
|
description: "Complex-bug detection beyond taint: deserialization→POP gadget chain (unserialize + magic method), type juggling (loose ==/!= vs hash/secret), mass assignment (extract/parse_str without a safe flag), prototype pollution (unsafe merge of request data), CRLF/header injection (user input into a header), path confusion (user input into a file path without canonicalization), SSRF (user-controlled URL with no host allowlist), XXE (user XML parsed without disabling entities), SSTI (user input into a template render with no sandbox), wrong-context sanitization (sanitize_text_field() into a SQL query), missing authorization/nonce on WordPress entry points (wp_ajax_* / admin_post_* / register_rest_route without current_user_can, nonce verification, or permission_callback), CORS misconfiguration (reflected/wildcard origin + credentials), XSS (user input into a DOM sink: innerHTML / dangerouslySetInnerHTML / document.write / .html / eval), and subdomain takeover (CNAME/AliasTarget to a claimable service).",
|
|
63348
63480
|
inputSchema: { path: string2().describe("Source file path") }
|
|
63349
63481
|
}, async ({ path }) => {
|
|
63350
|
-
const code =
|
|
63482
|
+
const code = readFileSync28(path, "utf8");
|
|
63351
63483
|
const findings = detectComplexBugs(code, path);
|
|
63352
63484
|
return {
|
|
63353
63485
|
content: [{
|
|
@@ -63433,7 +63565,7 @@ ${statusLine}` : statusLine },
|
|
|
63433
63565
|
let lang = language;
|
|
63434
63566
|
if (path && !code) {
|
|
63435
63567
|
try {
|
|
63436
|
-
src =
|
|
63568
|
+
src = readFileSync28(path, "utf8");
|
|
63437
63569
|
} catch {
|
|
63438
63570
|
return { content: [{ type: "text", text: JSON.stringify({ error: `cannot read ${path}` }) }] };
|
|
63439
63571
|
}
|
|
@@ -63682,15 +63814,15 @@ async function serve() {
|
|
|
63682
63814
|
|
|
63683
63815
|
// src/index.ts
|
|
63684
63816
|
init_cli();
|
|
63685
|
-
import { readFileSync as
|
|
63686
|
-
import { join as
|
|
63817
|
+
import { readFileSync as readFileSync32, existsSync as existsSync18 } from "node:fs";
|
|
63818
|
+
import { join as join30 } from "node:path";
|
|
63687
63819
|
import { fileURLToPath as fileURLToPath17 } from "node:url";
|
|
63688
|
-
var ROOT11 =
|
|
63820
|
+
var ROOT11 = join30(fileURLToPath17(new URL(".", import.meta.url)), "..");
|
|
63689
63821
|
function readVersion() {
|
|
63690
63822
|
try {
|
|
63691
|
-
const p =
|
|
63823
|
+
const p = join30(ROOT11, "package.json");
|
|
63692
63824
|
if (existsSync18(p))
|
|
63693
|
-
return JSON.parse(
|
|
63825
|
+
return JSON.parse(readFileSync32(p, "utf8")).version ?? "1.0.0";
|
|
63694
63826
|
} catch {}
|
|
63695
63827
|
return "1.0.0";
|
|
63696
63828
|
}
|
|
@@ -988,6 +988,69 @@
|
|
|
988
988
|
"payout_min": 1000,
|
|
989
989
|
"payout_max": 15000,
|
|
990
990
|
"typical_payout": "$1K\u2013$15K"
|
|
991
|
+
},
|
|
992
|
+
{
|
|
993
|
+
"id": "nosql_injection",
|
|
994
|
+
"name": "NoSQL Injection",
|
|
995
|
+
"cwe": "CWE-943",
|
|
996
|
+
"real_world": "User input reaches a NoSQL query (MongoDB $where/$gt/$ne, an unsanitized find/aggregate). Attackers inject operators to bypass authentication or dump collections.",
|
|
997
|
+
"detection_signatures": [
|
|
998
|
+
"unsanitized input into a NoSQL find/where/aggregate",
|
|
999
|
+
"operator injection ($ne/$gt/$where) from request input"
|
|
1000
|
+
],
|
|
1001
|
+
"chain_templates": [
|
|
1002
|
+
"nosql_injection_to_data_theft"
|
|
1003
|
+
],
|
|
1004
|
+
"references": [
|
|
1005
|
+
"CWE-943",
|
|
1006
|
+
"OWASP A03:2021 Injection"
|
|
1007
|
+
],
|
|
1008
|
+
"bypass_techniques": [
|
|
1009
|
+
"operator injection ($ne/$gt/$where)",
|
|
1010
|
+
"JSON-body operator injection",
|
|
1011
|
+
"array/object type juggling to slip past input filters",
|
|
1012
|
+
"blind extraction via regex/timing"
|
|
1013
|
+
],
|
|
1014
|
+
"crown_jewels": [
|
|
1015
|
+
"user/account collections",
|
|
1016
|
+
"password hashes",
|
|
1017
|
+
"admin documents"
|
|
1018
|
+
],
|
|
1019
|
+
"severity": "critical",
|
|
1020
|
+
"payout_min": 1000,
|
|
1021
|
+
"payout_max": 15000,
|
|
1022
|
+
"typical_payout": "$1K\u2013$15K"
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
"id": "host_header",
|
|
1026
|
+
"name": "Host Header Injection",
|
|
1027
|
+
"cwe": "CWE-644",
|
|
1028
|
+
"real_world": "The app trusts the Host header for link generation, password-reset URLs, or routing. An attacker injects a malicious host to poison password-reset links or bypass access controls.",
|
|
1029
|
+
"detection_signatures": [
|
|
1030
|
+
"Host header reflected into a generated URL / Location / Set-Cookie",
|
|
1031
|
+
"Host header used in password-reset link generation"
|
|
1032
|
+
],
|
|
1033
|
+
"chain_templates": [
|
|
1034
|
+
"http_smuggling_to_auth_bypass"
|
|
1035
|
+
],
|
|
1036
|
+
"references": [
|
|
1037
|
+
"CWE-644"
|
|
1038
|
+
],
|
|
1039
|
+
"bypass_techniques": [
|
|
1040
|
+
"Host header override via X-Forwarded-Host",
|
|
1041
|
+
"absolute-URL host injection",
|
|
1042
|
+
"password-reset link poisoning",
|
|
1043
|
+
"web cache poisoning via Host"
|
|
1044
|
+
],
|
|
1045
|
+
"crown_jewels": [
|
|
1046
|
+
"password-reset tokens",
|
|
1047
|
+
"account takeover via poisoned links",
|
|
1048
|
+
"cache poisoning"
|
|
1049
|
+
],
|
|
1050
|
+
"severity": "medium",
|
|
1051
|
+
"payout_min": 500,
|
|
1052
|
+
"payout_max": 5000,
|
|
1053
|
+
"typical_payout": "$500\u2013$5K"
|
|
991
1054
|
}
|
|
992
1055
|
]
|
|
993
1056
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "Bug-bounty disclosure frequency + payout watchlist. Derived from public disclosure statistics (HackerOne Hacktivity / CWE Top 25) and typical bounty ranges — a 'what to hunt next' signal, not a vulnerability database. Each CWE entry links to a Blitz Strike detector pattern when one exists (grounding via pattern_lookup).",
|
|
3
|
+
"cwe_frequency": [
|
|
4
|
+
{ "cwe": "CWE-79", "name": "Cross-site Scripting (XSS)", "rank": 1, "saturated": true, "pattern": "xss", "payout_min": 100, "payout_max": 2500, "notes": "Most-reported class; heavily automated and low-paying — hunt only for high-impact variants (stored/account-takeover)." },
|
|
5
|
+
{ "cwe": "CWE-862", "name": "Missing Authorization / IDOR", "rank": 2, "saturated": false, "pattern": "missing_authz", "payout_min": 1000, "payout_max": 10000, "notes": "~30% of paid access-control bugs; sibling rule + two-account test are the highest-ROI moves." },
|
|
6
|
+
{ "cwe": "CWE-284", "name": "Improper Access Control (priv-esc)", "rank": 3, "saturated": false, "pattern": "priv_esc", "payout_min": 1000, "payout_max": 15000, "notes": "Role/privilege escalation; mass assignment + forced membership are under-hunted." },
|
|
7
|
+
{ "cwe": "CWE-89", "name": "SQL Injection", "rank": 4, "saturated": false, "pattern": "sql_injection", "payout_min": 1000, "payout_max": 15000, "notes": "Still high-value where it reaches a real query; WAF bypass + second-order matter." },
|
|
8
|
+
{ "cwe": "CWE-918", "name": "Server-Side Request Forgery (SSRF)", "rank": 5, "saturated": false, "pattern": "ssrf", "payout_min": 1000, "payout_max": 15000, "notes": "P1 when it hits cloud metadata; proof = internal response or OOB callback." },
|
|
9
|
+
{ "cwe": "CWE-78", "name": "OS Command Injection", "rank": 6, "saturated": false, "pattern": "command_injection", "payout_min": 1000, "payout_max": 15000, "notes": "Critical class; less common than XSS/SQLi but very high impact." },
|
|
10
|
+
{ "cwe": "CWE-502", "name": "Insecure Deserialization", "rank": 7, "saturated": false, "pattern": "deserialization", "payout_min": 5000, "payout_max": 30000, "notes": "Highest per-finding payout; needs a gadget chain but very rewarded." },
|
|
11
|
+
{ "cwe": "CWE-94", "name": "Code Injection / eval", "rank": 8, "saturated": false, "pattern": "command_injection", "payout_min": 1000, "payout_max": 15000, "notes": "eval/exec with user input; folds into RCE umbrella." },
|
|
12
|
+
{ "cwe": "CWE-434", "name": "Unrestricted File Upload", "rank": 9, "saturated": false, "pattern": "file_upload", "payout_min": 1000, "payout_max": 10000, "notes": "Upload → RCE chains pay well; confirm the file actually executes." },
|
|
13
|
+
{ "cwe": "CWE-611", "name": "XML External Entity (XXE)", "rank": 10, "saturated": false, "pattern": "xxe", "payout_min": 1000, "payout_max": 10000, "notes": "Less common post-2020 but still found in SOAP/XML upload flows." },
|
|
14
|
+
{ "cwe": "CWE-287", "name": "Authentication Bypass", "rank": 11, "saturated": false, "pattern": "type_juggling", "payout_min": 1000, "payout_max": 15000, "notes": "Loose comparison / JWT confusion / OAuth misconfig — account takeover class." },
|
|
15
|
+
{ "cwe": "CWE-798", "name": "Hardcoded Credentials", "rank": 12, "saturated": false, "pattern": "hardcoded_secret", "payout_min": 500, "payout_max": 10000, "notes": "Committed keys are common in JS bundles + .git history; prove the key is ACTIVE." },
|
|
16
|
+
{ "cwe": "CWE-22", "name": "Path Traversal / LFI", "rank": 13, "saturated": true, "pattern": "path_confusion", "payout_min": 500, "payout_max": 5000, "notes": "Saturated; value only when it chains to RCE (log poisoning, session files)." },
|
|
17
|
+
{ "cwe": "CWE-601", "name": "Open Redirect", "rank": 14, "saturated": true, "pattern": "oauth_misconfig", "payout_min": 100, "payout_max": 1500, "notes": "Standalone is usually N/A; value only chained into OAuth/token theft." },
|
|
18
|
+
{ "cwe": "CWE-352", "name": "Cross-Site Request Forgery (CSRF)", "rank": 15, "saturated": true, "pattern": "missing_nonce", "payout_min": 100, "payout_max": 2000, "notes": "Saturated + low; only for high-impact state changes (password/email/transfer)." },
|
|
19
|
+
{ "cwe": "CWE-200", "name": "Information Exposure", "rank": 16, "saturated": true, "pattern": "hardcoded_secret", "payout_min": 100, "payout_max": 2000, "notes": "Excessive data exposure / verbose errors; usually a chain enabler, not a standalone bounty." },
|
|
20
|
+
{ "cwe": "CWE-400", "name": "Denial of Service / rate-limit", "rank": 17, "saturated": true, "pattern": null, "payout_min": 100, "payout_max": 1000, "notes": "Usually out of scope standalone; relevant only for business-critical flows." }
|
|
21
|
+
],
|
|
22
|
+
"cve_watchlist": [
|
|
23
|
+
{ "cve": "CVE-2021-44228", "name": "Log4Shell", "product": "Apache Log4j 2", "class": "RCE", "payout_hint": "critical" },
|
|
24
|
+
{ "cve": "CVE-2022-22965", "name": "Spring4Shell", "product": "Spring Framework", "class": "RCE", "payout_hint": "critical" },
|
|
25
|
+
{ "cve": "CVE-2021-41773", "name": "Apache path traversal", "product": "Apache httpd 2.4.49", "class": "Path traversal / RCE", "payout_hint": "high" },
|
|
26
|
+
{ "cve": "CVE-2023-22518", "name": "Confluence auth bypass", "product": "Atlassian Confluence", "class": "Auth bypass", "payout_hint": "high" },
|
|
27
|
+
{ "cve": "CVE-2024-27198", "name": "TeamCity auth bypass", "product": "JetBrains TeamCity", "class": "Auth bypass / RCE", "payout_hint": "high" },
|
|
28
|
+
{ "cve": "CVE-2023-34362", "name": "MOVEit SQLi", "product": "Progress MOVEit", "class": "SQL injection", "payout_hint": "critical" },
|
|
29
|
+
{ "cve": "CVE-2022-42475", "name": "Fortinet SSL-VPN", "product": "FortiOS", "class": "RCE", "payout_hint": "critical" },
|
|
30
|
+
{ "cve": "CVE-2023-27997", "name": "Fortinet SSL-VPN pre-auth", "product": "FortiOS", "class": "RCE", "payout_hint": "critical" },
|
|
31
|
+
{ "cve": "CVE-2024-6387", "name": "regreSSHion", "product": "OpenSSH", "class": "RCE (race)", "payout_hint": "critical" }
|
|
32
|
+
]
|
|
33
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.95",
|
|
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": {
|