blitzstrike 2.4.107 → 2.4.108
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 +16 -0
- package/dist/index.js +236 -190
- package/intelligence/surface-map.json +159 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,22 @@ 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.108] — 2026-09-17
|
|
9
|
+
|
|
10
|
+
### Added — surface-first map (attack surface → high-value tests)
|
|
11
|
+
|
|
12
|
+
- New `surface_map(hint)` tool + `intelligence/surface-map.json`: maps 8 attack
|
|
13
|
+
surfaces to their highest-value test cases, derived from public target-recon
|
|
14
|
+
dossiers (image proxy → SSRF/URL-bypass/XSS; redirect proxy → open-redirect/
|
|
15
|
+
SSRF/bypass; edge cache → IDOR/cache-poisoning; payment portal →
|
|
16
|
+
price-manipulation/IDOR/subscription; AI agent → prompt-injection→SSRF-RCE/
|
|
17
|
+
deser/stored-XSS; file service → XSS-via-content/SSRF/upload-RCE; Electron →
|
|
18
|
+
HTML-injection→BrowserWindow-RCE/IPC; SSO → OAuth-redirect/JWT/2FA).
|
|
19
|
+
- `matchSurfaces(techs, headers, body)` fingerprints the detected stack against
|
|
20
|
+
the map; `live_recon` now emits `suggested_surfaces` + a SURFACE-FIRST next
|
|
21
|
+
step. Added doctrine rule `surface_first` ("map surfaces before spraying
|
|
22
|
+
payloads").
|
|
23
|
+
|
|
8
24
|
## [2.4.107] — 2026-09-17
|
|
9
25
|
|
|
10
26
|
### Added — `review_harness` (per-file review, full-coverage deterministic rubric)
|
package/dist/index.js
CHANGED
|
@@ -17388,12 +17388,12 @@ document.getElementById('out').innerHTML = u;
|
|
|
17388
17388
|
});
|
|
17389
17389
|
|
|
17390
17390
|
// src/variants.ts
|
|
17391
|
-
import { readFileSync as
|
|
17392
|
-
import { join as
|
|
17393
|
-
import { fileURLToPath as
|
|
17391
|
+
import { readFileSync as readFileSync23 } from "node:fs";
|
|
17392
|
+
import { join as join22, dirname as dirname5 } from "node:path";
|
|
17393
|
+
import { fileURLToPath as fileURLToPath13 } from "node:url";
|
|
17394
17394
|
function loadVariants() {
|
|
17395
17395
|
try {
|
|
17396
|
-
const doc = JSON.parse(
|
|
17396
|
+
const doc = JSON.parse(readFileSync23(join22(__dirname5, "..", "intelligence", "variants.json"), "utf8"));
|
|
17397
17397
|
return doc.variants ?? [];
|
|
17398
17398
|
} catch {
|
|
17399
17399
|
return [];
|
|
@@ -17447,15 +17447,15 @@ function variantCoverageText() {
|
|
|
17447
17447
|
var __dirname5, CROSS_FILE_ONLY;
|
|
17448
17448
|
var init_variants = __esm(() => {
|
|
17449
17449
|
init_complex_bugs();
|
|
17450
|
-
__dirname5 = dirname5(
|
|
17450
|
+
__dirname5 = dirname5(fileURLToPath13(import.meta.url));
|
|
17451
17451
|
CROSS_FILE_ONLY = new Set(["priv_esc"]);
|
|
17452
17452
|
});
|
|
17453
17453
|
|
|
17454
17454
|
// src/sync.ts
|
|
17455
|
-
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9, readFileSync as
|
|
17456
|
-
import { join as
|
|
17455
|
+
import { mkdirSync as mkdirSync10, writeFileSync as writeFileSync9, readFileSync as readFileSync29, readdirSync as readdirSync7, statSync as statSync4, rmSync as rmSync5 } from "node:fs";
|
|
17456
|
+
import { join as join27 } from "node:path";
|
|
17457
17457
|
import { homedir as homedir10 } from "node:os";
|
|
17458
|
-
import { fileURLToPath as
|
|
17458
|
+
import { fileURLToPath as fileURLToPath16 } from "node:url";
|
|
17459
17459
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
17460
17460
|
function dirExists(p) {
|
|
17461
17461
|
try {
|
|
@@ -17470,7 +17470,7 @@ function countFiles(dir) {
|
|
|
17470
17470
|
const walk = (d) => {
|
|
17471
17471
|
for (const e of readdirSync7(d, { withFileTypes: true })) {
|
|
17472
17472
|
if (e.isDirectory())
|
|
17473
|
-
walk(
|
|
17473
|
+
walk(join27(d, e.name));
|
|
17474
17474
|
else
|
|
17475
17475
|
n++;
|
|
17476
17476
|
}
|
|
@@ -17484,8 +17484,8 @@ function countFiles(dir) {
|
|
|
17484
17484
|
function syncData() {
|
|
17485
17485
|
if (!dirExists(DATA_ROOT2))
|
|
17486
17486
|
mkdirSync10(DATA_ROOT2, { recursive: true });
|
|
17487
|
-
const payloadsPath =
|
|
17488
|
-
const templatesPath =
|
|
17487
|
+
const payloadsPath = join27(DATA_ROOT2, "payloads");
|
|
17488
|
+
const templatesPath = join27(DATA_ROOT2, "templates");
|
|
17489
17489
|
const hasPayloads = dirExists(payloadsPath) && countFiles(payloadsPath) > 0;
|
|
17490
17490
|
const hasTemplates = dirExists(templatesPath) && countFiles(templatesPath) > 0;
|
|
17491
17491
|
if (hasPayloads && hasTemplates) {
|
|
@@ -17495,8 +17495,8 @@ function syncData() {
|
|
|
17495
17495
|
data_root: DATA_ROOT2
|
|
17496
17496
|
};
|
|
17497
17497
|
}
|
|
17498
|
-
const localPayloads =
|
|
17499
|
-
const localTemplates =
|
|
17498
|
+
const localPayloads = join27(ROOT9, "payloads");
|
|
17499
|
+
const localTemplates = join27(ROOT9, "templates");
|
|
17500
17500
|
let payloadsSynced = false;
|
|
17501
17501
|
let templatesSynced = false;
|
|
17502
17502
|
if (!hasPayloads && dirExists(localPayloads)) {
|
|
@@ -17509,7 +17509,7 @@ function syncData() {
|
|
|
17509
17509
|
}
|
|
17510
17510
|
const gitOk = spawnSync4("git", ["--version"]).status === 0;
|
|
17511
17511
|
if (gitOk && (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0 || !dirExists(templatesPath) || countFiles(templatesPath) === 0)) {
|
|
17512
|
-
const tmp =
|
|
17512
|
+
const tmp = join27(DATA_ROOT2, ".tmp-clone");
|
|
17513
17513
|
if (dirExists(tmp))
|
|
17514
17514
|
rmSync5(tmp, { recursive: true, force: true });
|
|
17515
17515
|
const r = spawnSync4("git", ["clone", "--depth", "1", "--filter=blob:none", "--sparse", GITHUB_REPO, tmp], { timeout: 120000 });
|
|
@@ -17517,11 +17517,11 @@ function syncData() {
|
|
|
17517
17517
|
const sparse = spawnSync4("git", ["-C", tmp, "sparse-checkout", "set", "payloads", "templates"], { timeout: 120000 });
|
|
17518
17518
|
if (sparse.status === 0) {
|
|
17519
17519
|
if (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0) {
|
|
17520
|
-
copyDir(
|
|
17520
|
+
copyDir(join27(tmp, "payloads"), payloadsPath);
|
|
17521
17521
|
payloadsSynced = true;
|
|
17522
17522
|
}
|
|
17523
17523
|
if (!dirExists(templatesPath) || countFiles(templatesPath) === 0) {
|
|
17524
|
-
copyDir(
|
|
17524
|
+
copyDir(join27(tmp, "templates"), templatesPath);
|
|
17525
17525
|
templatesSynced = true;
|
|
17526
17526
|
}
|
|
17527
17527
|
}
|
|
@@ -17543,18 +17543,18 @@ function copyDir(src, dst) {
|
|
|
17543
17543
|
function manualCopy(src, dst) {
|
|
17544
17544
|
mkdirSync10(dst, { recursive: true });
|
|
17545
17545
|
for (const e of readdirSync7(src, { withFileTypes: true })) {
|
|
17546
|
-
const s =
|
|
17547
|
-
const d =
|
|
17546
|
+
const s = join27(src, e.name);
|
|
17547
|
+
const d = join27(dst, e.name);
|
|
17548
17548
|
if (e.isDirectory())
|
|
17549
17549
|
manualCopy(s, d);
|
|
17550
17550
|
else
|
|
17551
|
-
writeFileSync9(d,
|
|
17551
|
+
writeFileSync9(d, readFileSync29(s));
|
|
17552
17552
|
}
|
|
17553
17553
|
}
|
|
17554
|
-
var
|
|
17554
|
+
var ROOT9, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
|
|
17555
17555
|
var init_sync = __esm(() => {
|
|
17556
|
-
|
|
17557
|
-
DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ??
|
|
17556
|
+
ROOT9 = join27(fileURLToPath16(new URL(".", import.meta.url)), "..");
|
|
17557
|
+
DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ?? join27(homedir10(), ".blitzstrike", "data");
|
|
17558
17558
|
});
|
|
17559
17559
|
|
|
17560
17560
|
// src/update.ts
|
|
@@ -17599,15 +17599,15 @@ var init_update = __esm(() => {
|
|
|
17599
17599
|
});
|
|
17600
17600
|
|
|
17601
17601
|
// src/release.ts
|
|
17602
|
-
import { readFileSync as
|
|
17603
|
-
import { join as
|
|
17604
|
-
import { fileURLToPath as
|
|
17602
|
+
import { readFileSync as readFileSync31, existsSync as existsSync16 } from "node:fs";
|
|
17603
|
+
import { join as join28 } from "node:path";
|
|
17604
|
+
import { fileURLToPath as fileURLToPath17 } from "node:url";
|
|
17605
17605
|
import { execSync as execSync4 } from "node:child_process";
|
|
17606
17606
|
function validateRelease() {
|
|
17607
17607
|
const checks = [];
|
|
17608
17608
|
checks.push({ name: "package.json version", ok: true, detail: VERSION });
|
|
17609
17609
|
try {
|
|
17610
|
-
const changelog =
|
|
17610
|
+
const changelog = readFileSync31(join28(ROOT10, "CHANGELOG.md"), "utf8");
|
|
17611
17611
|
const m = changelog.match(/^##\s+\[([0-9]+\.[0-9]+\.[0-9]+)\]/m);
|
|
17612
17612
|
const changelogVersion = m?.[1] ?? null;
|
|
17613
17613
|
checks.push({
|
|
@@ -17619,7 +17619,7 @@ function validateRelease() {
|
|
|
17619
17619
|
checks.push({ name: "CHANGELOG version", ok: false, detail: "CHANGELOG.md not found" });
|
|
17620
17620
|
}
|
|
17621
17621
|
try {
|
|
17622
|
-
const tag = execSync4("git describe --tags --abbrev=0", { cwd:
|
|
17622
|
+
const tag = execSync4("git describe --tags --abbrev=0", { cwd: ROOT10, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
17623
17623
|
checks.push({ name: "Git tag", ok: tag === `v${VERSION}`, detail: tag, soft: true });
|
|
17624
17624
|
} catch {
|
|
17625
17625
|
checks.push({ name: "Git tag", ok: true, detail: "unavailable (no git/tags)", soft: true });
|
|
@@ -17639,22 +17639,22 @@ function validateRelease() {
|
|
|
17639
17639
|
return { version: VERSION, checks, consistent: checks.filter((c) => !c.soft).every((c) => c.ok) };
|
|
17640
17640
|
}
|
|
17641
17641
|
function lockfilePresent() {
|
|
17642
|
-
return existsSync16(
|
|
17642
|
+
return existsSync16(join28(ROOT10, "package-lock.json")) || existsSync16(join28(ROOT10, "bun.lock")) || existsSync16(join28(ROOT10, "bun.lockb"));
|
|
17643
17643
|
}
|
|
17644
|
-
var
|
|
17644
|
+
var ROOT10;
|
|
17645
17645
|
var init_release = __esm(() => {
|
|
17646
17646
|
init_version();
|
|
17647
|
-
|
|
17647
|
+
ROOT10 = join28(fileURLToPath17(new URL(".", import.meta.url)), "..");
|
|
17648
17648
|
});
|
|
17649
17649
|
|
|
17650
17650
|
// src/dependency.ts
|
|
17651
|
-
import { readFileSync as
|
|
17652
|
-
import { join as
|
|
17653
|
-
import { fileURLToPath as
|
|
17651
|
+
import { readFileSync as readFileSync32 } from "node:fs";
|
|
17652
|
+
import { join as join29 } from "node:path";
|
|
17653
|
+
import { fileURLToPath as fileURLToPath18 } from "node:url";
|
|
17654
17654
|
function checkDependencies() {
|
|
17655
17655
|
let pkg = {};
|
|
17656
17656
|
try {
|
|
17657
|
-
pkg = JSON.parse(
|
|
17657
|
+
pkg = JSON.parse(readFileSync32(join29(ROOT11, "package.json"), "utf8"));
|
|
17658
17658
|
} catch {
|
|
17659
17659
|
return { install_scripts: [], git_dependencies: [], total_dependencies: 0, safe: false };
|
|
17660
17660
|
}
|
|
@@ -17675,16 +17675,16 @@ function checkDependencies() {
|
|
|
17675
17675
|
safe: installScripts.length === 0 && gitDependencies.length === 0
|
|
17676
17676
|
};
|
|
17677
17677
|
}
|
|
17678
|
-
var
|
|
17678
|
+
var ROOT11;
|
|
17679
17679
|
var init_dependency = __esm(() => {
|
|
17680
|
-
|
|
17680
|
+
ROOT11 = join29(fileURLToPath18(new URL(".", import.meta.url)), "..");
|
|
17681
17681
|
});
|
|
17682
17682
|
|
|
17683
17683
|
// src/cli.ts
|
|
17684
|
-
import { readFileSync as
|
|
17685
|
-
import { join as
|
|
17684
|
+
import { readFileSync as readFileSync33, existsSync as existsSync17, writeFileSync as writeFileSync10, mkdirSync as mkdirSync11 } from "node:fs";
|
|
17685
|
+
import { join as join30, dirname as dirname6 } from "node:path";
|
|
17686
17686
|
import { homedir as homedir11 } from "node:os";
|
|
17687
|
-
import { fileURLToPath as
|
|
17687
|
+
import { fileURLToPath as fileURLToPath19 } from "node:url";
|
|
17688
17688
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
17689
17689
|
function which(cmd) {
|
|
17690
17690
|
return spawnSync5("sh", ["-c", 'command -v "$1" >/dev/null 2>&1', "sh", cmd]).status === 0;
|
|
@@ -17727,7 +17727,7 @@ async function runDoctor() {
|
|
|
17727
17727
|
detail: h1 ? `X-HackerOne-Research: ${h1}` : "H1_USERNAME not set",
|
|
17728
17728
|
fix: h1 ? undefined : "export H1_USERNAME=<your-hackerone-username> (adds X-HackerOne-Research to outbound requests)"
|
|
17729
17729
|
});
|
|
17730
|
-
const dataOk = existsSync17(
|
|
17730
|
+
const dataOk = existsSync17(join30(ROOT12, "chains.json")) && existsSync17(join30(ROOT12, "tools-catalog.json"));
|
|
17731
17731
|
issues.push({
|
|
17732
17732
|
name: "Data layers (chains + tools-catalog)",
|
|
17733
17733
|
status: dataOk ? "ok" : "fail",
|
|
@@ -17778,21 +17778,21 @@ BlitzStrike — ${verdict}`);
|
|
|
17778
17778
|
function resolveCommand() {
|
|
17779
17779
|
if (which("blitzstrike"))
|
|
17780
17780
|
return { command: "blitzstrike", args: ["serve", "--mcp"] };
|
|
17781
|
-
const src =
|
|
17781
|
+
const src = join30(ROOT12, "src", "index.ts");
|
|
17782
17782
|
if (which("bun") && existsSync17(src))
|
|
17783
17783
|
return { command: "bun", args: ["run", src, "serve", "--mcp"] };
|
|
17784
17784
|
if (which("npx"))
|
|
17785
17785
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
17786
17786
|
if (which("bunx"))
|
|
17787
17787
|
return { command: "bunx", args: ["blitzstrike", "serve", "--mcp"] };
|
|
17788
|
-
const dist =
|
|
17788
|
+
const dist = join30(ROOT12, "dist", "index.js");
|
|
17789
17789
|
if (existsSync17(dist))
|
|
17790
17790
|
return { command: "bun", args: [dist, "serve", "--mcp"] };
|
|
17791
17791
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
17792
17792
|
}
|
|
17793
17793
|
function readJson(p) {
|
|
17794
17794
|
try {
|
|
17795
|
-
return JSON.parse(
|
|
17795
|
+
return JSON.parse(readFileSync33(p, "utf8"));
|
|
17796
17796
|
} catch {
|
|
17797
17797
|
return null;
|
|
17798
17798
|
}
|
|
@@ -17825,17 +17825,17 @@ function opencodeWrite(p) {
|
|
|
17825
17825
|
};
|
|
17826
17826
|
}
|
|
17827
17827
|
function copyOpenCodeAgents() {
|
|
17828
|
-
const srcDir =
|
|
17829
|
-
const dstDir =
|
|
17828
|
+
const srcDir = join30(ROOT12, "opencode-agents");
|
|
17829
|
+
const dstDir = join30(homedir11(), ".config", "opencode", "agents");
|
|
17830
17830
|
if (!existsSync17(srcDir))
|
|
17831
17831
|
return;
|
|
17832
17832
|
if (!existsSync17(dstDir))
|
|
17833
17833
|
mkdirSync11(dstDir, { recursive: true });
|
|
17834
17834
|
for (const f of ["Blitz Strike.md", "Blitz.md", "Eagle Eye.md", "Strike.md"]) {
|
|
17835
|
-
const src =
|
|
17836
|
-
const dst =
|
|
17835
|
+
const src = join30(srcDir, f);
|
|
17836
|
+
const dst = join30(dstDir, f);
|
|
17837
17837
|
if (existsSync17(src)) {
|
|
17838
|
-
writeFileSync10(dst,
|
|
17838
|
+
writeFileSync10(dst, readFileSync33(src, "utf8"));
|
|
17839
17839
|
}
|
|
17840
17840
|
}
|
|
17841
17841
|
}
|
|
@@ -17844,7 +17844,7 @@ function codexWrite(p) {
|
|
|
17844
17844
|
const dir = dirname6(p);
|
|
17845
17845
|
if (!existsSync17(dir))
|
|
17846
17846
|
mkdirSync11(dir, { recursive: true });
|
|
17847
|
-
let existing = existsSync17(p) ?
|
|
17847
|
+
let existing = existsSync17(p) ? readFileSync33(p, "utf8") : "";
|
|
17848
17848
|
if (!existing.trimEnd().endsWith(`
|
|
17849
17849
|
`))
|
|
17850
17850
|
existing += `
|
|
@@ -17863,7 +17863,7 @@ function hermesWrite(p) {
|
|
|
17863
17863
|
const dir = dirname6(p);
|
|
17864
17864
|
if (!existsSync17(dir))
|
|
17865
17865
|
mkdirSync11(dir, { recursive: true });
|
|
17866
|
-
let existing = existsSync17(p) ?
|
|
17866
|
+
let existing = existsSync17(p) ? readFileSync33(p, "utf8") : "";
|
|
17867
17867
|
existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
|
|
17868
17868
|
if (!existing.trimEnd().endsWith(`
|
|
17869
17869
|
`))
|
|
@@ -17890,74 +17890,74 @@ mcp_servers:
|
|
|
17890
17890
|
function detectAgents() {
|
|
17891
17891
|
const home = homedir11();
|
|
17892
17892
|
const agents = [];
|
|
17893
|
-
const claudeJson =
|
|
17893
|
+
const claudeJson = join30(home, ".claude.json");
|
|
17894
17894
|
agents.push({
|
|
17895
17895
|
name: "Claude Code",
|
|
17896
17896
|
detectPath: claudeJson,
|
|
17897
17897
|
installed: () => existsSync17(claudeJson) || which("claude"),
|
|
17898
17898
|
write: jsonMcpServersWrite(claudeJson)
|
|
17899
17899
|
});
|
|
17900
|
-
const claudeDesktop =
|
|
17900
|
+
const claudeDesktop = join30(home, ".config", "Claude", "claude_desktop_config.json");
|
|
17901
17901
|
agents.push({
|
|
17902
17902
|
name: "Claude Desktop",
|
|
17903
17903
|
detectPath: claudeDesktop,
|
|
17904
17904
|
installed: () => existsSync17(claudeDesktop),
|
|
17905
17905
|
write: jsonMcpServersWrite(claudeDesktop)
|
|
17906
17906
|
});
|
|
17907
|
-
const cursor =
|
|
17907
|
+
const cursor = join30(home, ".cursor", "mcp.json");
|
|
17908
17908
|
agents.push({
|
|
17909
17909
|
name: "Cursor",
|
|
17910
17910
|
detectPath: cursor,
|
|
17911
|
-
installed: () => existsSync17(
|
|
17911
|
+
installed: () => existsSync17(join30(home, ".cursor")) || which("cursor"),
|
|
17912
17912
|
write: jsonMcpServersWrite(cursor)
|
|
17913
17913
|
});
|
|
17914
|
-
const opencode =
|
|
17914
|
+
const opencode = join30(home, ".config", "opencode", "opencode.jsonc");
|
|
17915
17915
|
agents.push({
|
|
17916
17916
|
name: "OpenCode",
|
|
17917
17917
|
detectPath: opencode,
|
|
17918
|
-
installed: () => existsSync17(
|
|
17918
|
+
installed: () => existsSync17(join30(home, ".config", "opencode")) || which("opencode"),
|
|
17919
17919
|
write: opencodeWrite(opencode)
|
|
17920
17920
|
});
|
|
17921
|
-
const codex =
|
|
17921
|
+
const codex = join30(home, ".codex", "config.toml");
|
|
17922
17922
|
agents.push({
|
|
17923
17923
|
name: "Codex",
|
|
17924
17924
|
detectPath: codex,
|
|
17925
|
-
installed: () => existsSync17(
|
|
17925
|
+
installed: () => existsSync17(join30(home, ".codex")) || which("codex"),
|
|
17926
17926
|
write: codexWrite(codex)
|
|
17927
17927
|
});
|
|
17928
|
-
const hermes =
|
|
17928
|
+
const hermes = join30(home, ".hermes", "config.yaml");
|
|
17929
17929
|
agents.push({
|
|
17930
17930
|
name: "Hermes",
|
|
17931
17931
|
detectPath: hermes,
|
|
17932
|
-
installed: () => existsSync17(
|
|
17932
|
+
installed: () => existsSync17(join30(home, ".hermes")) || which("hermes"),
|
|
17933
17933
|
write: hermesWrite(hermes)
|
|
17934
17934
|
});
|
|
17935
|
-
const gemini =
|
|
17935
|
+
const gemini = join30(home, ".gemini", "settings.json");
|
|
17936
17936
|
agents.push({
|
|
17937
17937
|
name: "Gemini CLI",
|
|
17938
17938
|
detectPath: gemini,
|
|
17939
|
-
installed: () => existsSync17(
|
|
17939
|
+
installed: () => existsSync17(join30(home, ".gemini")) || which("gemini"),
|
|
17940
17940
|
write: jsonMcpServersWrite(gemini)
|
|
17941
17941
|
});
|
|
17942
|
-
const windsurf =
|
|
17942
|
+
const windsurf = join30(home, ".codeium", "windsurf", "mcp_config.json");
|
|
17943
17943
|
agents.push({
|
|
17944
17944
|
name: "Windsurf",
|
|
17945
17945
|
detectPath: windsurf,
|
|
17946
|
-
installed: () => existsSync17(
|
|
17946
|
+
installed: () => existsSync17(join30(home, ".codeium")) || which("windsurf"),
|
|
17947
17947
|
write: jsonMcpServersWrite(windsurf)
|
|
17948
17948
|
});
|
|
17949
|
-
const copilot =
|
|
17949
|
+
const copilot = join30(home, ".copilot", "mcp.json");
|
|
17950
17950
|
agents.push({
|
|
17951
17951
|
name: "Copilot",
|
|
17952
17952
|
detectPath: copilot,
|
|
17953
|
-
installed: () => existsSync17(
|
|
17953
|
+
installed: () => existsSync17(join30(home, ".copilot")),
|
|
17954
17954
|
write: jsonMcpServersWrite(copilot)
|
|
17955
17955
|
});
|
|
17956
|
-
const cline =
|
|
17956
|
+
const cline = join30(home, ".cline", "mcp_settings.json");
|
|
17957
17957
|
agents.push({
|
|
17958
17958
|
name: "Cline",
|
|
17959
17959
|
detectPath: cline,
|
|
17960
|
-
installed: () => existsSync17(
|
|
17960
|
+
installed: () => existsSync17(join30(home, ".cline")),
|
|
17961
17961
|
write: jsonMcpServersWrite(cline)
|
|
17962
17962
|
});
|
|
17963
17963
|
return agents;
|
|
@@ -18069,13 +18069,13 @@ Failed (${failedList.length}) — reason per tool:`);
|
|
|
18069
18069
|
Common causes: missing toolchain, pip/git network failures, or very heavy builds. ` + `Re-run after installing the missing toolchain — failed tools are skipped, not fatal.`);
|
|
18070
18070
|
}
|
|
18071
18071
|
}
|
|
18072
|
-
var
|
|
18072
|
+
var ROOT12;
|
|
18073
18073
|
var init_cli = __esm(() => {
|
|
18074
18074
|
init_catalog();
|
|
18075
18075
|
init_release();
|
|
18076
18076
|
init_dependency();
|
|
18077
18077
|
init_mcp_status();
|
|
18078
|
-
|
|
18078
|
+
ROOT12 = join30(fileURLToPath19(new URL(".", import.meta.url)), "..");
|
|
18079
18079
|
});
|
|
18080
18080
|
|
|
18081
18081
|
// node_modules/zod/v3/helpers/util.js
|
|
@@ -32713,7 +32713,7 @@ class StdioServerTransport {
|
|
|
32713
32713
|
|
|
32714
32714
|
// src/server.ts
|
|
32715
32715
|
init_scanner();
|
|
32716
|
-
import { readFileSync as
|
|
32716
|
+
import { readFileSync as readFileSync30 } from "node:fs";
|
|
32717
32717
|
|
|
32718
32718
|
// src/review-harness.ts
|
|
32719
32719
|
init_scanner();
|
|
@@ -32879,9 +32879,9 @@ async function nvdLookup(cveId) {
|
|
|
32879
32879
|
|
|
32880
32880
|
// src/orchestrator.ts
|
|
32881
32881
|
init_scanner();
|
|
32882
|
-
import { readFileSync as
|
|
32883
|
-
import { join as
|
|
32884
|
-
import { fileURLToPath as
|
|
32882
|
+
import { readFileSync as readFileSync13, existsSync as existsSync8, statSync as statSync2 } from "node:fs";
|
|
32883
|
+
import { join as join14 } from "node:path";
|
|
32884
|
+
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
32885
32885
|
|
|
32886
32886
|
// src/memory.ts
|
|
32887
32887
|
import { readFileSync as readFileSync3, appendFileSync, mkdirSync, existsSync, writeFileSync } from "node:fs";
|
|
@@ -36444,6 +36444,33 @@ function watchlistCveMatches(techs, headers = {}, body = "", versions = []) {
|
|
|
36444
36444
|
});
|
|
36445
36445
|
}
|
|
36446
36446
|
|
|
36447
|
+
// src/surface-map.ts
|
|
36448
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
36449
|
+
import { join as join13 } from "node:path";
|
|
36450
|
+
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
36451
|
+
var ROOT5 = join13(fileURLToPath7(new URL(".", import.meta.url)), "..");
|
|
36452
|
+
function load2() {
|
|
36453
|
+
const raw = readFileSync12(join13(ROOT5, "intelligence", "surface-map.json"), "utf8");
|
|
36454
|
+
return JSON.parse(raw);
|
|
36455
|
+
}
|
|
36456
|
+
function surfaceMap(hint = "") {
|
|
36457
|
+
const data = load2();
|
|
36458
|
+
if (!hint)
|
|
36459
|
+
return data.surfaces;
|
|
36460
|
+
const h = hint.toLowerCase();
|
|
36461
|
+
return data.surfaces.filter((s) => s.id.includes(h) || s.name.toLowerCase().includes(h) || s.fingerprints.some((f) => f.toLowerCase().includes(h)));
|
|
36462
|
+
}
|
|
36463
|
+
function matchSurfaces(techs, headers = {}, body = "") {
|
|
36464
|
+
const haystack = `${techs.join(" ")} ${Object.keys(headers).join(" ")} ${Object.values(headers).join(" ")} ${body}`.toLowerCase();
|
|
36465
|
+
const out = [];
|
|
36466
|
+
for (const s of load2().surfaces) {
|
|
36467
|
+
const hits = s.fingerprints.filter((f) => haystack.includes(f.toLowerCase()));
|
|
36468
|
+
if (hits.length > 0)
|
|
36469
|
+
out.push({ ...s, hits });
|
|
36470
|
+
}
|
|
36471
|
+
return out.sort((a, b) => b.hits.length - a.hits.length);
|
|
36472
|
+
}
|
|
36473
|
+
|
|
36447
36474
|
// src/live-recon.ts
|
|
36448
36475
|
import { resolve4 as resolve42 } from "node:dns/promises";
|
|
36449
36476
|
import { connect } from "node:net";
|
|
@@ -36879,6 +36906,7 @@ async function liveRecon(target, includeActive = false) {
|
|
|
36879
36906
|
const techs = detectTech(root.headers, root.body);
|
|
36880
36907
|
const versions = extractVersion(root.headers, root.body);
|
|
36881
36908
|
const suggestedCves = watchlistCveMatches(techs, root.headers, root.body, versions);
|
|
36909
|
+
const surfaces = matchSurfaces(techs, root.headers, root.body);
|
|
36882
36910
|
const { links, scripts } = crawlLinks(base, root.body);
|
|
36883
36911
|
const { robots, sitemap } = await fetchRobotsAndSitemap(base);
|
|
36884
36912
|
const allUrls = [...links, ...scripts, ...robots, ...sitemap];
|
|
@@ -36915,6 +36943,9 @@ async function liveRecon(target, includeActive = false) {
|
|
|
36915
36943
|
if (suggestedCves.length > 0) {
|
|
36916
36944
|
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("; ")}.`);
|
|
36917
36945
|
}
|
|
36946
|
+
if (surfaces.length > 0) {
|
|
36947
|
+
nextSteps.push(`SURFACE-FIRST: detected ${surfaces.length} attack surface(s) — ${surfaces.map((s) => `${s.id} (${s.high_value_tests.length} high-value tests)`).join(", ")}. Map each surface to its tests via surface_map before spraying payloads.`);
|
|
36948
|
+
}
|
|
36918
36949
|
if (crawl.endpoints.length > 0)
|
|
36919
36950
|
nextSteps.push(`Found ${crawl.endpoints.length} endpoints — feed them to taint_file/blitz_scan for source analysis, or strike_verify for live validation.`);
|
|
36920
36951
|
if (params.length > 0)
|
|
@@ -36942,6 +36973,7 @@ async function liveRecon(target, includeActive = false) {
|
|
|
36942
36973
|
tech: { detected: techs, server: fingerprint.server },
|
|
36943
36974
|
versions,
|
|
36944
36975
|
suggested_cves: suggestedCves.map((c) => ({ cve: c.cve, name: c.name, product: c.product, epss: c.epss, reports: c.reports })),
|
|
36976
|
+
suggested_surfaces: surfaces.map((s) => ({ id: s.id, name: s.name, high_value_tests: s.high_value_tests, matched_fingerprints: s.hits })),
|
|
36945
36977
|
crawl,
|
|
36946
36978
|
params,
|
|
36947
36979
|
subdomains,
|
|
@@ -37018,9 +37050,9 @@ function loadChains() {
|
|
|
37018
37050
|
if (_chainsCache)
|
|
37019
37051
|
return _chainsCache;
|
|
37020
37052
|
try {
|
|
37021
|
-
const dir =
|
|
37022
|
-
const path =
|
|
37023
|
-
const raw = JSON.parse(
|
|
37053
|
+
const dir = join14(fileURLToPath8(new URL(".", import.meta.url)), "..");
|
|
37054
|
+
const path = join14(dir, "chains.json");
|
|
37055
|
+
const raw = JSON.parse(readFileSync13(path, "utf8"));
|
|
37024
37056
|
_chainsCache = raw.chains ?? [];
|
|
37025
37057
|
return _chainsCache;
|
|
37026
37058
|
} catch {
|
|
@@ -37801,8 +37833,8 @@ var CHAIN_ID_TO_ESCALATION = {
|
|
|
37801
37833
|
};
|
|
37802
37834
|
function loadChainLinks() {
|
|
37803
37835
|
try {
|
|
37804
|
-
const dir =
|
|
37805
|
-
const data = JSON.parse(
|
|
37836
|
+
const dir = join14(fileURLToPath8(new URL(".", import.meta.url)), "..");
|
|
37837
|
+
const data = JSON.parse(readFileSync13(join14(dir, "intelligence", "chain_links.json"), "utf8"));
|
|
37806
37838
|
return data.links ?? [];
|
|
37807
37839
|
} catch {
|
|
37808
37840
|
return [];
|
|
@@ -37958,18 +37990,18 @@ function compactState(phase, findings) {
|
|
|
37958
37990
|
}
|
|
37959
37991
|
|
|
37960
37992
|
// src/tasks.ts
|
|
37961
|
-
import { mkdirSync as mkdirSync6, readFileSync as
|
|
37962
|
-
import { join as
|
|
37963
|
-
import { fileURLToPath as
|
|
37993
|
+
import { mkdirSync as mkdirSync6, readFileSync as readFileSync14, writeFileSync as writeFileSync3 } from "node:fs";
|
|
37994
|
+
import { join as join15 } from "node:path";
|
|
37995
|
+
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
37964
37996
|
function tasksDir() {
|
|
37965
|
-
return
|
|
37997
|
+
return join15(fileURLToPath9(new URL(".", import.meta.url)), "..", ".blitzstrike", "tasks");
|
|
37966
37998
|
}
|
|
37967
37999
|
function taskPath(id) {
|
|
37968
|
-
return
|
|
38000
|
+
return join15(tasksDir(), `${id}.json`);
|
|
37969
38001
|
}
|
|
37970
|
-
function
|
|
38002
|
+
function load3(id) {
|
|
37971
38003
|
try {
|
|
37972
|
-
return JSON.parse(
|
|
38004
|
+
return JSON.parse(readFileSync14(taskPath(id), "utf8"));
|
|
37973
38005
|
} catch {
|
|
37974
38006
|
return null;
|
|
37975
38007
|
}
|
|
@@ -37994,7 +38026,7 @@ function taskStart(id, objective, plan) {
|
|
|
37994
38026
|
return { ...t, progress: `0/${plan.length}`, next: t.current_step };
|
|
37995
38027
|
}
|
|
37996
38028
|
function taskCheckpoint(id, step, status, note, outputHash) {
|
|
37997
|
-
const t =
|
|
38029
|
+
const t = load3(id);
|
|
37998
38030
|
if (!t)
|
|
37999
38031
|
return { ok: false, error: `task '${id}' not found — call task_start first` };
|
|
38000
38032
|
t.checkpoints.push({ step, status, output_hash: outputHash, note, ts: Date.now() });
|
|
@@ -38020,7 +38052,7 @@ function taskCheckpoint(id, step, status, note, outputHash) {
|
|
|
38020
38052
|
};
|
|
38021
38053
|
}
|
|
38022
38054
|
function taskStatus(id) {
|
|
38023
|
-
const t =
|
|
38055
|
+
const t = load3(id);
|
|
38024
38056
|
if (!t)
|
|
38025
38057
|
return { found: false, error: `task '${id}' not found` };
|
|
38026
38058
|
const done = t.checkpoints.filter((c) => c.status === "done").length;
|
|
@@ -38036,7 +38068,7 @@ function taskStatus(id) {
|
|
|
38036
38068
|
};
|
|
38037
38069
|
}
|
|
38038
38070
|
function taskResume(id) {
|
|
38039
|
-
const t =
|
|
38071
|
+
const t = load3(id);
|
|
38040
38072
|
if (!t)
|
|
38041
38073
|
return { found: false, error: `task '${id}' not found` };
|
|
38042
38074
|
const doneSteps = t.checkpoints.filter((c) => c.status === "done").map((c) => c.step);
|
|
@@ -38309,16 +38341,16 @@ function scaffoldEngagement(target, scope = [], out_of_scope = []) {
|
|
|
38309
38341
|
// src/incremental.ts
|
|
38310
38342
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
38311
38343
|
import { writeFileSync as writeFileSync4, mkdtempSync, rmSync } from "node:fs";
|
|
38312
|
-
import { join as
|
|
38344
|
+
import { join as join16 } from "node:path";
|
|
38313
38345
|
import { tmpdir } from "node:os";
|
|
38314
38346
|
|
|
38315
38347
|
// src/eagle2.ts
|
|
38316
38348
|
var import_php_parser2 = __toESM(require_src(), 1);
|
|
38317
|
-
import { readFileSync as
|
|
38349
|
+
import { readFileSync as readFileSync17 } from "node:fs";
|
|
38318
38350
|
|
|
38319
38351
|
// src/dataflow.ts
|
|
38320
38352
|
init_scanner();
|
|
38321
|
-
import { readFileSync as
|
|
38353
|
+
import { readFileSync as readFileSync15 } from "node:fs";
|
|
38322
38354
|
var SOURCES = {
|
|
38323
38355
|
$_GET: { id: "http_get", label: "HTTP GET parameter", attacker_controlled: true },
|
|
38324
38356
|
$_POST: { id: "http_post", label: "HTTP POST body", attacker_controlled: true },
|
|
@@ -38403,7 +38435,7 @@ function traceDataFlow(path) {
|
|
|
38403
38435
|
return result;
|
|
38404
38436
|
let text;
|
|
38405
38437
|
try {
|
|
38406
|
-
text =
|
|
38438
|
+
text = readFileSync15(path, "utf8");
|
|
38407
38439
|
} catch {
|
|
38408
38440
|
return result;
|
|
38409
38441
|
}
|
|
@@ -38484,7 +38516,7 @@ init_scanner();
|
|
|
38484
38516
|
|
|
38485
38517
|
// src/callgraph.ts
|
|
38486
38518
|
var import_php_parser = __toESM(require_src(), 1);
|
|
38487
|
-
import { readFileSync as
|
|
38519
|
+
import { readFileSync as readFileSync16 } from "node:fs";
|
|
38488
38520
|
var parser = new import_php_parser.Engine({
|
|
38489
38521
|
parser: { extractDoc: false, php7: true },
|
|
38490
38522
|
ast: { withPositions: true, withSource: false }
|
|
@@ -38545,7 +38577,7 @@ function buildCallGraph(path) {
|
|
|
38545
38577
|
const g = { nodes: new Map, edges: [] };
|
|
38546
38578
|
let code;
|
|
38547
38579
|
try {
|
|
38548
|
-
code =
|
|
38580
|
+
code = readFileSync16(path, "utf8");
|
|
38549
38581
|
} catch {
|
|
38550
38582
|
return g;
|
|
38551
38583
|
}
|
|
@@ -39207,7 +39239,7 @@ function resolveFuncId(name, summaries) {
|
|
|
39207
39239
|
function analyzeDataFlow2(path) {
|
|
39208
39240
|
let cacheCode = "";
|
|
39209
39241
|
try {
|
|
39210
|
-
cacheCode =
|
|
39242
|
+
cacheCode = readFileSync17(path, "utf8");
|
|
39211
39243
|
} catch {}
|
|
39212
39244
|
if (cacheCode) {
|
|
39213
39245
|
const cached = getCache("eagle2", cacheCode);
|
|
@@ -39229,7 +39261,7 @@ function analyzeDataFlow2(path) {
|
|
|
39229
39261
|
};
|
|
39230
39262
|
let code;
|
|
39231
39263
|
try {
|
|
39232
|
-
code = cacheCode ||
|
|
39264
|
+
code = cacheCode || readFileSync17(path, "utf8");
|
|
39233
39265
|
} catch {
|
|
39234
39266
|
return result;
|
|
39235
39267
|
}
|
|
@@ -57915,8 +57947,8 @@ function analyzeChangedFiles(repo, base = "HEAD~1", head = "HEAD") {
|
|
|
57915
57947
|
continue;
|
|
57916
57948
|
const lang = detectLanguage(file)?.language;
|
|
57917
57949
|
if (lang === "php") {
|
|
57918
|
-
const dir = mkdtempSync(
|
|
57919
|
-
const tmp =
|
|
57950
|
+
const dir = mkdtempSync(join16(tmpdir(), "blitz-incr-"));
|
|
57951
|
+
const tmp = join16(dir, "changed.php");
|
|
57920
57952
|
writeFileSync4(tmp, code);
|
|
57921
57953
|
try {
|
|
57922
57954
|
const r = analyzeDataFlow2(tmp);
|
|
@@ -57937,7 +57969,7 @@ function analyzeChangedFiles(repo, base = "HEAD~1", head = "HEAD") {
|
|
|
57937
57969
|
// src/taint.ts
|
|
57938
57970
|
init_scanner();
|
|
57939
57971
|
var import_php_parser4 = __toESM(require_src(), 1);
|
|
57940
|
-
import { readFileSync as
|
|
57972
|
+
import { readFileSync as readFileSync18 } from "node:fs";
|
|
57941
57973
|
var parser4 = new import_php_parser4.Engine({
|
|
57942
57974
|
parser: { extractDoc: false, php7: true },
|
|
57943
57975
|
ast: { withPositions: true, withSource: false }
|
|
@@ -58235,7 +58267,7 @@ function analyzeTaint(path) {
|
|
|
58235
58267
|
const result = { file: path, findings: [], suppressed: 0 };
|
|
58236
58268
|
let code;
|
|
58237
58269
|
try {
|
|
58238
|
-
code =
|
|
58270
|
+
code = readFileSync18(path, "utf8");
|
|
58239
58271
|
} catch {
|
|
58240
58272
|
return result;
|
|
58241
58273
|
}
|
|
@@ -58329,15 +58361,15 @@ function taintTree(root, maxFiles = 2000) {
|
|
|
58329
58361
|
}
|
|
58330
58362
|
|
|
58331
58363
|
// src/differential.ts
|
|
58332
|
-
import { writeFileSync as writeFileSync5, rmSync as rmSync2, mkdtempSync as mkdtempSync2, readFileSync as
|
|
58333
|
-
import { join as
|
|
58364
|
+
import { writeFileSync as writeFileSync5, rmSync as rmSync2, mkdtempSync as mkdtempSync2, readFileSync as readFileSync19 } from "node:fs";
|
|
58365
|
+
import { join as join17 } from "node:path";
|
|
58334
58366
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
58335
58367
|
import { execFileSync as execFileSync4 } from "node:child_process";
|
|
58336
58368
|
init_scanner();
|
|
58337
58369
|
function runEngine(code, language, file) {
|
|
58338
58370
|
if (language === "php") {
|
|
58339
|
-
const dir = mkdtempSync2(
|
|
58340
|
-
const tmp =
|
|
58371
|
+
const dir = mkdtempSync2(join17(tmpdir2(), "blitz-diff-"));
|
|
58372
|
+
const tmp = join17(dir, "old.php");
|
|
58341
58373
|
writeFileSync5(tmp, code);
|
|
58342
58374
|
try {
|
|
58343
58375
|
const r = analyzeDataFlow2(tmp);
|
|
@@ -58491,8 +58523,8 @@ function languageOf(file) {
|
|
|
58491
58523
|
return adapter?.language ?? "php";
|
|
58492
58524
|
}
|
|
58493
58525
|
function analyzeDifferentialFiles(oldPath, newPath, language) {
|
|
58494
|
-
const oldCode =
|
|
58495
|
-
const newCode =
|
|
58526
|
+
const oldCode = readFileSync19(oldPath, "utf8");
|
|
58527
|
+
const newCode = readFileSync19(newPath, "utf8");
|
|
58496
58528
|
const lang = language ?? languageOf(newPath);
|
|
58497
58529
|
return analyzeDifferential(oldCode, newCode, lang, newPath);
|
|
58498
58530
|
}
|
|
@@ -58619,46 +58651,46 @@ function analyzePatch(oldCode, newCode, language = "php") {
|
|
|
58619
58651
|
};
|
|
58620
58652
|
}
|
|
58621
58653
|
function analyzePatchFiles(oldPath, newPath, language) {
|
|
58622
|
-
const oldCode =
|
|
58623
|
-
const newCode =
|
|
58654
|
+
const oldCode = readFileSync19(oldPath, "utf8");
|
|
58655
|
+
const newCode = readFileSync19(newPath, "utf8");
|
|
58624
58656
|
const lang = language ?? languageOf(newPath);
|
|
58625
58657
|
return analyzePatch(oldCode, newCode, lang);
|
|
58626
58658
|
}
|
|
58627
58659
|
|
|
58628
58660
|
// src/frameworks.ts
|
|
58629
|
-
import { readFileSync as
|
|
58630
|
-
import { join as
|
|
58631
|
-
import { fileURLToPath as
|
|
58632
|
-
var
|
|
58633
|
-
var INTEL2 =
|
|
58661
|
+
import { readFileSync as readFileSync20, existsSync as existsSync9 } from "node:fs";
|
|
58662
|
+
import { join as join18 } from "node:path";
|
|
58663
|
+
import { fileURLToPath as fileURLToPath10 } from "node:url";
|
|
58664
|
+
var ROOT6 = join18(fileURLToPath10(new URL(".", import.meta.url)), "..");
|
|
58665
|
+
var INTEL2 = join18(ROOT6, "intelligence");
|
|
58634
58666
|
var cache = null;
|
|
58635
|
-
function
|
|
58667
|
+
function load4() {
|
|
58636
58668
|
if (cache)
|
|
58637
58669
|
return cache;
|
|
58638
|
-
const p =
|
|
58670
|
+
const p = join18(INTEL2, "frameworks.json");
|
|
58639
58671
|
if (!existsSync9(p))
|
|
58640
58672
|
return null;
|
|
58641
58673
|
try {
|
|
58642
|
-
cache = JSON.parse(
|
|
58674
|
+
cache = JSON.parse(readFileSync20(p, "utf8"));
|
|
58643
58675
|
return cache;
|
|
58644
58676
|
} catch {
|
|
58645
58677
|
return null;
|
|
58646
58678
|
}
|
|
58647
58679
|
}
|
|
58648
58680
|
function listFrameworks2() {
|
|
58649
|
-
const data =
|
|
58681
|
+
const data = load4();
|
|
58650
58682
|
if (!data)
|
|
58651
58683
|
return [];
|
|
58652
58684
|
return data.frameworks.map((f) => ({ id: f.id, name: f.name, language: f.language }));
|
|
58653
58685
|
}
|
|
58654
58686
|
function frameworkProfile(id) {
|
|
58655
|
-
const data =
|
|
58687
|
+
const data = load4();
|
|
58656
58688
|
if (!data)
|
|
58657
58689
|
return null;
|
|
58658
58690
|
return data.frameworks.find((f) => f.id === id) ?? null;
|
|
58659
58691
|
}
|
|
58660
58692
|
function detectFramework(code, language) {
|
|
58661
|
-
const data =
|
|
58693
|
+
const data = load4();
|
|
58662
58694
|
const hay = code.toLowerCase();
|
|
58663
58695
|
const candidates = [];
|
|
58664
58696
|
if (data) {
|
|
@@ -59151,7 +59183,7 @@ function nextSteps(ctx) {
|
|
|
59151
59183
|
init_complex_bugs();
|
|
59152
59184
|
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
59153
59185
|
import { existsSync as existsSync11 } from "node:fs";
|
|
59154
|
-
import { join as
|
|
59186
|
+
import { join as join19 } from "node:path";
|
|
59155
59187
|
function parseDeletions(output) {
|
|
59156
59188
|
const out = [];
|
|
59157
59189
|
let commit = "";
|
|
@@ -59180,7 +59212,7 @@ function blobBeforeDeletion(root, commit, path) {
|
|
|
59180
59212
|
function scanGitHistory(root, opts) {
|
|
59181
59213
|
const limit = opts?.limit ?? 30;
|
|
59182
59214
|
const findings = [];
|
|
59183
|
-
if (!existsSync11(
|
|
59215
|
+
if (!existsSync11(join19(root, ".git"))) {
|
|
59184
59216
|
return { root, is_git_repo: false, total: 0, findings: [], error: "not a git repository (no .git dir)" };
|
|
59185
59217
|
}
|
|
59186
59218
|
if (opts?.secretPattern) {
|
|
@@ -59327,13 +59359,13 @@ async function validateLeakedKey(input) {
|
|
|
59327
59359
|
}
|
|
59328
59360
|
|
|
59329
59361
|
// src/hackbot-arena.ts
|
|
59330
|
-
import { readFileSync as
|
|
59331
|
-
import { join as
|
|
59332
|
-
import { fileURLToPath as
|
|
59333
|
-
var __dirname3 = dirname3(
|
|
59334
|
-
function
|
|
59362
|
+
import { readFileSync as readFileSync21 } from "node:fs";
|
|
59363
|
+
import { join as join20, dirname as dirname3 } from "node:path";
|
|
59364
|
+
import { fileURLToPath as fileURLToPath11 } from "node:url";
|
|
59365
|
+
var __dirname3 = dirname3(fileURLToPath11(import.meta.url));
|
|
59366
|
+
function load5() {
|
|
59335
59367
|
try {
|
|
59336
|
-
return JSON.parse(
|
|
59368
|
+
return JSON.parse(readFileSync21(join20(__dirname3, "..", "intelligence", "hackbot-arena.json"), "utf8"));
|
|
59337
59369
|
} catch {
|
|
59338
59370
|
return { total: 0, flag_format: "FLAG{nusasec-<32 hex>}", labs: [] };
|
|
59339
59371
|
}
|
|
@@ -59370,10 +59402,10 @@ function mapLab(lab) {
|
|
|
59370
59402
|
}
|
|
59371
59403
|
function findByLab(labId) {
|
|
59372
59404
|
const id = labId.toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
59373
|
-
return
|
|
59405
|
+
return load5().labs.find((l) => l.id.toLowerCase() === id || l.id.toLowerCase() === `labs${id}`) ?? null;
|
|
59374
59406
|
}
|
|
59375
59407
|
function hackbotArenaList() {
|
|
59376
|
-
const doc =
|
|
59408
|
+
const doc = load5();
|
|
59377
59409
|
return {
|
|
59378
59410
|
arena: "Hackbot-Arena (NusaSec)",
|
|
59379
59411
|
total: doc.total,
|
|
@@ -59394,18 +59426,18 @@ function hackbotArenaBrief(labId) {
|
|
|
59394
59426
|
target: `http://localhost:${lab.port}`,
|
|
59395
59427
|
vuln_class: lab.vuln_class,
|
|
59396
59428
|
stack: lab.stack,
|
|
59397
|
-
flag_format:
|
|
59429
|
+
flag_format: load5().flag_format,
|
|
59398
59430
|
attack_plan: {
|
|
59399
59431
|
vectors: plan.vectors,
|
|
59400
59432
|
detectors: plan.detectors,
|
|
59401
59433
|
chains: plan.chains,
|
|
59402
59434
|
tools: plan.tools
|
|
59403
59435
|
},
|
|
59404
|
-
hint: `Start with live_recon(target) to fingerprint, then drive the ${plan.chains.join(" + ") || "detected"} chain. Verify every step live (strike_verify / browser_validate) and recover the flag. Flag format: ${
|
|
59436
|
+
hint: `Start with live_recon(target) to fingerprint, then drive the ${plan.chains.join(" + ") || "detected"} chain. Verify every step live (strike_verify / browser_validate) and recover the flag. Flag format: ${load5().flag_format}.`
|
|
59405
59437
|
};
|
|
59406
59438
|
}
|
|
59407
59439
|
function hackbotArenaCoverage() {
|
|
59408
|
-
const doc =
|
|
59440
|
+
const doc = load5();
|
|
59409
59441
|
const rows = doc.labs.map((l) => {
|
|
59410
59442
|
const plan = mapLab(l);
|
|
59411
59443
|
return { id: l.id, name: l.name, vuln_class: l.vuln_class, vectors: plan.vectors, detectors: plan.detectors, chains: plan.chains };
|
|
@@ -59497,13 +59529,13 @@ function listSeverityMap() {
|
|
|
59497
59529
|
}
|
|
59498
59530
|
|
|
59499
59531
|
// src/datarefresh.ts
|
|
59500
|
-
import { readFileSync as
|
|
59501
|
-
import { join as
|
|
59502
|
-
import { fileURLToPath as
|
|
59503
|
-
var __dirname4 = dirname4(
|
|
59532
|
+
import { readFileSync as readFileSync22 } from "node:fs";
|
|
59533
|
+
import { join as join21, dirname as dirname4 } from "node:path";
|
|
59534
|
+
import { fileURLToPath as fileURLToPath12 } from "node:url";
|
|
59535
|
+
var __dirname4 = dirname4(fileURLToPath12(import.meta.url));
|
|
59504
59536
|
function loadJson2(rel) {
|
|
59505
59537
|
try {
|
|
59506
|
-
return JSON.parse(
|
|
59538
|
+
return JSON.parse(readFileSync22(join21(__dirname4, "..", rel), "utf8"));
|
|
59507
59539
|
} catch {
|
|
59508
59540
|
return null;
|
|
59509
59541
|
}
|
|
@@ -59695,18 +59727,18 @@ async function brokenLinkScan(url, maxHosts = 30) {
|
|
|
59695
59727
|
}
|
|
59696
59728
|
|
|
59697
59729
|
// src/benchmark.ts
|
|
59698
|
-
import { readFileSync as
|
|
59699
|
-
import { join as
|
|
59700
|
-
import { fileURLToPath as
|
|
59730
|
+
import { readFileSync as readFileSync24, readdirSync as readdirSync5, writeFileSync as writeFileSync6, rmSync as rmSync3, mkdirSync as mkdirSync7 } from "node:fs";
|
|
59731
|
+
import { join as join23 } from "node:path";
|
|
59732
|
+
import { fileURLToPath as fileURLToPath14 } from "node:url";
|
|
59701
59733
|
import { tmpdir as tmpdir3, homedir as homedir8 } from "node:os";
|
|
59702
59734
|
init_complex_bugs();
|
|
59703
|
-
var
|
|
59704
|
-
var CORPUS =
|
|
59705
|
-
var CAPTURED_CORPUS_DIR = process.env.BLITZSTRIKE_CORPUS_DIR ??
|
|
59735
|
+
var ROOT7 = join23(fileURLToPath14(new URL(".", import.meta.url)), "..");
|
|
59736
|
+
var CORPUS = join23(ROOT7, "bench", "corpus");
|
|
59737
|
+
var CAPTURED_CORPUS_DIR = process.env.BLITZSTRIKE_CORPUS_DIR ?? join23(homedir8(), ".blitzstrike", "corpus");
|
|
59706
59738
|
function parseCorpusFile(path) {
|
|
59707
59739
|
let text;
|
|
59708
59740
|
try {
|
|
59709
|
-
text =
|
|
59741
|
+
text = readFileSync24(path, "utf8");
|
|
59710
59742
|
} catch {
|
|
59711
59743
|
return null;
|
|
59712
59744
|
}
|
|
@@ -59762,7 +59794,7 @@ function loadDir(dir) {
|
|
|
59762
59794
|
for (const f of readdirSync5(dir)) {
|
|
59763
59795
|
if (!f.endsWith(".md"))
|
|
59764
59796
|
continue;
|
|
59765
|
-
const e = parseCorpusFile(
|
|
59797
|
+
const e = parseCorpusFile(join23(dir, f));
|
|
59766
59798
|
if (e)
|
|
59767
59799
|
entries.push(e);
|
|
59768
59800
|
}
|
|
@@ -59789,7 +59821,7 @@ function captureFalsePositive(input) {
|
|
|
59789
59821
|
input.code,
|
|
59790
59822
|
"```"
|
|
59791
59823
|
].filter((l) => l !== null);
|
|
59792
|
-
const path =
|
|
59824
|
+
const path = join23(CAPTURED_CORPUS_DIR, `${id}.md`);
|
|
59793
59825
|
writeFileSync6(path, lines.join(`
|
|
59794
59826
|
`) + `
|
|
59795
59827
|
`, "utf8");
|
|
@@ -59811,7 +59843,7 @@ function detect(entry) {
|
|
|
59811
59843
|
}
|
|
59812
59844
|
const lang = entry.language;
|
|
59813
59845
|
if (lang === "php") {
|
|
59814
|
-
const tmp =
|
|
59846
|
+
const tmp = join23(tmpdir3(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
|
|
59815
59847
|
writeFileSync6(tmp, entry.code);
|
|
59816
59848
|
const r = analyzeDataFlow2(tmp);
|
|
59817
59849
|
rmSync3(tmp, { force: true });
|
|
@@ -59880,12 +59912,12 @@ function runBenchmark() {
|
|
|
59880
59912
|
}
|
|
59881
59913
|
|
|
59882
59914
|
// src/obligations.ts
|
|
59883
|
-
import { readFileSync as
|
|
59884
|
-
import { join as
|
|
59915
|
+
import { readFileSync as readFileSync25, appendFileSync as appendFileSync4, mkdirSync as mkdirSync8, existsSync as existsSync12, writeFileSync as writeFileSync7 } from "node:fs";
|
|
59916
|
+
import { join as join24 } from "node:path";
|
|
59885
59917
|
import { createHash as createHash6 } from "node:crypto";
|
|
59886
59918
|
import { homedir as homedir9 } from "node:os";
|
|
59887
|
-
var HOME_DIR4 = process.env.BLITZSTRIKE_HOME ??
|
|
59888
|
-
var OBLIGATION_PATH =
|
|
59919
|
+
var HOME_DIR4 = process.env.BLITZSTRIKE_HOME ?? join24(homedir9(), ".blitzstrike");
|
|
59920
|
+
var OBLIGATION_PATH = join24(HOME_DIR4, "obligations.jsonl");
|
|
59889
59921
|
function ensureDir3() {
|
|
59890
59922
|
if (!existsSync12(HOME_DIR4))
|
|
59891
59923
|
mkdirSync8(HOME_DIR4, { recursive: true });
|
|
@@ -59898,7 +59930,7 @@ function loadObligations() {
|
|
|
59898
59930
|
return [];
|
|
59899
59931
|
try {
|
|
59900
59932
|
const out = [];
|
|
59901
|
-
for (const line of
|
|
59933
|
+
for (const line of readFileSync25(OBLIGATION_PATH, "utf8").split(`
|
|
59902
59934
|
`)) {
|
|
59903
59935
|
const t = line.trim();
|
|
59904
59936
|
if (!t)
|
|
@@ -60002,11 +60034,11 @@ init_framework_security();
|
|
|
60002
60034
|
// src/variant-scan.ts
|
|
60003
60035
|
init_scanner();
|
|
60004
60036
|
init_complex_bugs();
|
|
60005
|
-
import { readFileSync as
|
|
60037
|
+
import { readFileSync as readFileSync27 } from "node:fs";
|
|
60006
60038
|
|
|
60007
60039
|
// src/cross-file.ts
|
|
60008
60040
|
init_scanner();
|
|
60009
|
-
import { readFileSync as
|
|
60041
|
+
import { readFileSync as readFileSync26 } from "node:fs";
|
|
60010
60042
|
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)/;
|
|
60011
60043
|
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*\(/;
|
|
60012
60044
|
var ROLE_META_RE = /update_user_meta\s*\(\s*[^,]+,\s*['"](wp_capabilities|role|roles|capabilities|wp_user_level)['"]/;
|
|
@@ -60058,7 +60090,7 @@ function detectCrossFilePrivesc(root) {
|
|
|
60058
60090
|
for (const file of files) {
|
|
60059
60091
|
let code;
|
|
60060
60092
|
try {
|
|
60061
|
-
code = stripComments(
|
|
60093
|
+
code = stripComments(readFileSync26(file, "utf8"));
|
|
60062
60094
|
} catch {
|
|
60063
60095
|
continue;
|
|
60064
60096
|
}
|
|
@@ -60099,7 +60131,7 @@ function detectCrossFilePrivesc(root) {
|
|
|
60099
60131
|
for (const file of files) {
|
|
60100
60132
|
let code;
|
|
60101
60133
|
try {
|
|
60102
|
-
code = stripComments(
|
|
60134
|
+
code = stripComments(readFileSync26(file, "utf8"));
|
|
60103
60135
|
} catch {
|
|
60104
60136
|
continue;
|
|
60105
60137
|
}
|
|
@@ -60122,7 +60154,7 @@ function detectCrossFilePrivesc(root) {
|
|
|
60122
60154
|
for (const file of files) {
|
|
60123
60155
|
let code;
|
|
60124
60156
|
try {
|
|
60125
|
-
code = stripComments(
|
|
60157
|
+
code = stripComments(readFileSync26(file, "utf8"));
|
|
60126
60158
|
} catch {
|
|
60127
60159
|
continue;
|
|
60128
60160
|
}
|
|
@@ -60237,7 +60269,7 @@ function variantScan(root, opts = {}) {
|
|
|
60237
60269
|
for (const file of files) {
|
|
60238
60270
|
let code;
|
|
60239
60271
|
try {
|
|
60240
|
-
code =
|
|
60272
|
+
code = readFileSync27(file, "utf8");
|
|
60241
60273
|
} catch {
|
|
60242
60274
|
continue;
|
|
60243
60275
|
}
|
|
@@ -61489,6 +61521,11 @@ var HUNTING_DOCTRINE = [
|
|
|
61489
61521
|
id: "dedup_before_report",
|
|
61490
61522
|
title: "Search for Duplicates Before Reporting",
|
|
61491
61523
|
rule: "The #1 cause of 'Duplicate'/'N/A' rejections is not searching first. Before writing the report, search Hacktivity (and the program's prior public reports) for the same endpoint/tech/vuln class; an already-reported open S3 bucket, CORS misconfig, or XSS param is not worth a second report. Only submit what is genuinely new."
|
|
61524
|
+
},
|
|
61525
|
+
{
|
|
61526
|
+
id: "surface_first",
|
|
61527
|
+
title: "Map Attack Surfaces Before Spraying Payloads",
|
|
61528
|
+
rule: "A large target is won by mapping its attack SURFACES (image proxy → SSRF/URL-bypass; redirect proxy → open-redirect/SSRF; edge cache → IDOR/cache-poisoning; payment portal → price-manipulation/IDOR; AI agent → prompt-injection→SSRF-RCE; file service → XSS-via-content/SSRF/upload-RCE; Electron app → HTML-injection→BrowserWindow-RCE; SSO → OAuth-redirect/JWT). Use surface_map(surface) to get each surface's high-value tests, then test those FIRST — not a payload spray across every endpoint."
|
|
61492
61529
|
}
|
|
61493
61530
|
];
|
|
61494
61531
|
function huntingDoctrine() {
|
|
@@ -61917,7 +61954,7 @@ function listPocTypes() {
|
|
|
61917
61954
|
import { execSync as execSync3 } from "node:child_process";
|
|
61918
61955
|
import { mkdtempSync as mkdtempSync3, mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, rmSync as rmSync4, existsSync as existsSync14 } from "node:fs";
|
|
61919
61956
|
import { tmpdir as tmpdir4 } from "node:os";
|
|
61920
|
-
import { join as
|
|
61957
|
+
import { join as join25 } from "node:path";
|
|
61921
61958
|
function findForge() {
|
|
61922
61959
|
try {
|
|
61923
61960
|
const p = execSync3("which forge 2>/dev/null", { encoding: "utf8", timeout: 3000 }).trim();
|
|
@@ -61966,14 +62003,14 @@ async function foundryRun(input) {
|
|
|
61966
62003
|
};
|
|
61967
62004
|
}
|
|
61968
62005
|
const poc = input.poc_code ?? foundryPoc(input.class_ ?? "missing_access_control", name);
|
|
61969
|
-
const dir = mkdtempSync3(
|
|
62006
|
+
const dir = mkdtempSync3(join25(tmpdir4(), "blitzstrike-foundry-"));
|
|
61970
62007
|
try {
|
|
61971
62008
|
try {
|
|
61972
62009
|
execSync3(`${forge} init --force --no-git ${dir}`, { stdio: "ignore", timeout: 60000 });
|
|
61973
62010
|
} catch {
|
|
61974
|
-
mkdirSync9(
|
|
61975
|
-
mkdirSync9(
|
|
61976
|
-
writeFileSync8(
|
|
62011
|
+
mkdirSync9(join25(dir, "src"), { recursive: true });
|
|
62012
|
+
mkdirSync9(join25(dir, "test"), { recursive: true });
|
|
62013
|
+
writeFileSync8(join25(dir, "foundry.toml"), `[profile.default]
|
|
61977
62014
|
src = "src"
|
|
61978
62015
|
out = "out"
|
|
61979
62016
|
libs = ["lib"]
|
|
@@ -61983,12 +62020,12 @@ solc = "0.8.20"
|
|
|
61983
62020
|
for (const f of ["Counter.sol", "Counter.t.sol", "Counter.s.sol"]) {
|
|
61984
62021
|
for (const sub of ["src", "test", "script"]) {
|
|
61985
62022
|
try {
|
|
61986
|
-
rmSync4(
|
|
62023
|
+
rmSync4(join25(dir, sub, f), { force: true });
|
|
61987
62024
|
} catch {}
|
|
61988
62025
|
}
|
|
61989
62026
|
}
|
|
61990
|
-
writeFileSync8(
|
|
61991
|
-
writeFileSync8(
|
|
62027
|
+
writeFileSync8(join25(dir, "src", `${name}.sol`), input.contract_code);
|
|
62028
|
+
writeFileSync8(join25(dir, "test", `${name}.t.sol`), poc);
|
|
61992
62029
|
const { out } = runForge(forge, dir, input.timeout_ms ?? 60000);
|
|
61993
62030
|
const tests = parseTestResults(out);
|
|
61994
62031
|
const anyFail = tests.some((t) => t.status === "fail");
|
|
@@ -62006,18 +62043,18 @@ solc = "0.8.20"
|
|
|
62006
62043
|
}
|
|
62007
62044
|
|
|
62008
62045
|
// src/enterprise-benchmark.ts
|
|
62009
|
-
import { existsSync as existsSync15, readFileSync as
|
|
62010
|
-
import { join as
|
|
62011
|
-
import { fileURLToPath as
|
|
62046
|
+
import { existsSync as existsSync15, readFileSync as readFileSync28, readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
|
|
62047
|
+
import { join as join26, resolve } from "node:path";
|
|
62048
|
+
import { fileURLToPath as fileURLToPath15 } from "node:url";
|
|
62012
62049
|
init_complex_bugs();
|
|
62013
|
-
var
|
|
62050
|
+
var ROOT8 = resolve(fileURLToPath15(new URL(".", import.meta.url)), "..", "bench", "enterprise");
|
|
62014
62051
|
var ENTERPRISE_FIXTURES = [
|
|
62015
|
-
{ id: "php-laravel", dir:
|
|
62016
|
-
{ id: "js-express", dir:
|
|
62017
|
-
{ id: "py-django", dir:
|
|
62018
|
-
{ id: "java-spring", dir:
|
|
62019
|
-
{ id: "wp-batch-route", dir:
|
|
62020
|
-
{ id: "php-complex", dir:
|
|
62052
|
+
{ id: "php-laravel", dir: join26(ROOT8, "php-laravel"), expected_vulns: 4 },
|
|
62053
|
+
{ id: "js-express", dir: join26(ROOT8, "js-express"), expected_vulns: 2 },
|
|
62054
|
+
{ id: "py-django", dir: join26(ROOT8, "py-django"), expected_vulns: 2 },
|
|
62055
|
+
{ id: "java-spring", dir: join26(ROOT8, "java-spring"), expected_vulns: 2 },
|
|
62056
|
+
{ id: "wp-batch-route", dir: join26(ROOT8, "wp-batch-route"), expected_vulns: 3 },
|
|
62057
|
+
{ id: "php-complex", dir: join26(ROOT8, "php-complex"), expected_vulns: 4 }
|
|
62021
62058
|
];
|
|
62022
62059
|
function sourceFiles(root) {
|
|
62023
62060
|
const out = [];
|
|
@@ -62029,7 +62066,7 @@ function sourceFiles(root) {
|
|
|
62029
62066
|
return;
|
|
62030
62067
|
}
|
|
62031
62068
|
for (const e of entries) {
|
|
62032
|
-
const p =
|
|
62069
|
+
const p = join26(dir, e);
|
|
62033
62070
|
let st;
|
|
62034
62071
|
try {
|
|
62035
62072
|
st = statSync3(p);
|
|
@@ -62050,7 +62087,7 @@ function detectFixture(dir) {
|
|
|
62050
62087
|
for (const f of sourceFiles(dir)) {
|
|
62051
62088
|
let code;
|
|
62052
62089
|
try {
|
|
62053
|
-
code =
|
|
62090
|
+
code = readFileSync28(f, "utf8");
|
|
62054
62091
|
} catch {
|
|
62055
62092
|
continue;
|
|
62056
62093
|
}
|
|
@@ -63154,7 +63191,7 @@ ${statusLine}` : statusLine },
|
|
|
63154
63191
|
let src = code;
|
|
63155
63192
|
if (!src && path) {
|
|
63156
63193
|
try {
|
|
63157
|
-
src =
|
|
63194
|
+
src = readFileSync30(path, "utf8");
|
|
63158
63195
|
} catch {
|
|
63159
63196
|
return { content: [{ type: "text", text: JSON.stringify({ error: "unreadable path" }) }] };
|
|
63160
63197
|
}
|
|
@@ -63235,7 +63272,7 @@ ${statusLine}` : statusLine },
|
|
|
63235
63272
|
let src = code;
|
|
63236
63273
|
if (!src && path) {
|
|
63237
63274
|
try {
|
|
63238
|
-
src =
|
|
63275
|
+
src = readFileSync30(path, "utf8");
|
|
63239
63276
|
} catch {
|
|
63240
63277
|
return { content: [{ type: "text", text: JSON.stringify({ error: "unreadable path" }) }] };
|
|
63241
63278
|
}
|
|
@@ -63308,6 +63345,15 @@ ${statusLine}` : statusLine },
|
|
|
63308
63345
|
}, async ({ url }) => {
|
|
63309
63346
|
return { content: [{ type: "text", text: JSON.stringify(await brokenLinkScan(url)) }] };
|
|
63310
63347
|
});
|
|
63348
|
+
server.registerTool("surface_map", {
|
|
63349
|
+
title: "Attack-surface → high-value-test map (surface-first targeting)",
|
|
63350
|
+
description: "Map an attack surface to its highest-value test cases (derived from public target-recon dossiers). Surfaces: image_proxy (SSRF/URL-bypass/XSS), redirect_proxy (open-redirect/SSRF/bypass), edge_cache (IDOR/cache-poisoning), payment_portal (price-manipulation/IDOR/subscription), ai_agent (prompt-injection→SSRF-RCE/deser/stored-XSS), file_service (XSS-via-content/SSRF/upload-RCE), electron_desktop (HTML-injection→BrowserWindow-RCE/IPC), auth_sso (OAuth-redirect/JWT/2FA). Pass a hint to filter, or no arg to list all. SURFACE-FIRST: a large target is won by mapping surfaces to tests, not by spraying payloads.",
|
|
63351
|
+
inputSchema: {
|
|
63352
|
+
hint: string2().optional().describe("Surface id/keyword to filter (e.g. 'image_proxy', 'payment', 'ai')")
|
|
63353
|
+
}
|
|
63354
|
+
}, async ({ hint }) => {
|
|
63355
|
+
return { content: [{ type: "text", text: JSON.stringify(surfaceMap(hint ?? "")) }] };
|
|
63356
|
+
});
|
|
63311
63357
|
server.registerTool("auto_poc", {
|
|
63312
63358
|
title: "Auto-PoC — generate a copy-paste-ready reproduction recipe",
|
|
63313
63359
|
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.",
|
|
@@ -63927,7 +63973,7 @@ ${statusLine}` : statusLine },
|
|
|
63927
63973
|
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.",
|
|
63928
63974
|
inputSchema: { path: string2().describe("Source file path (PHP)") }
|
|
63929
63975
|
}, async ({ path }) => {
|
|
63930
|
-
const code =
|
|
63976
|
+
const code = readFileSync30(path, "utf8");
|
|
63931
63977
|
const findings = detectRouteConfusion(code, path);
|
|
63932
63978
|
return {
|
|
63933
63979
|
content: [{
|
|
@@ -63941,7 +63987,7 @@ ${statusLine}` : statusLine },
|
|
|
63941
63987
|
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).",
|
|
63942
63988
|
inputSchema: { path: string2().describe("Source file path") }
|
|
63943
63989
|
}, async ({ path }) => {
|
|
63944
|
-
const code =
|
|
63990
|
+
const code = readFileSync30(path, "utf8");
|
|
63945
63991
|
const findings = detectComplexBugs(code, path);
|
|
63946
63992
|
return {
|
|
63947
63993
|
content: [{
|
|
@@ -64027,7 +64073,7 @@ ${statusLine}` : statusLine },
|
|
|
64027
64073
|
let lang = language;
|
|
64028
64074
|
if (path && !code) {
|
|
64029
64075
|
try {
|
|
64030
|
-
src =
|
|
64076
|
+
src = readFileSync30(path, "utf8");
|
|
64031
64077
|
} catch {
|
|
64032
64078
|
return { content: [{ type: "text", text: JSON.stringify({ error: `cannot read ${path}` }) }] };
|
|
64033
64079
|
}
|
|
@@ -64276,15 +64322,15 @@ async function serve() {
|
|
|
64276
64322
|
|
|
64277
64323
|
// src/index.ts
|
|
64278
64324
|
init_cli();
|
|
64279
|
-
import { readFileSync as
|
|
64280
|
-
import { join as
|
|
64281
|
-
import { fileURLToPath as
|
|
64282
|
-
var
|
|
64325
|
+
import { readFileSync as readFileSync34, existsSync as existsSync18 } from "node:fs";
|
|
64326
|
+
import { join as join31 } from "node:path";
|
|
64327
|
+
import { fileURLToPath as fileURLToPath20 } from "node:url";
|
|
64328
|
+
var ROOT13 = join31(fileURLToPath20(new URL(".", import.meta.url)), "..");
|
|
64283
64329
|
function readVersion() {
|
|
64284
64330
|
try {
|
|
64285
|
-
const p =
|
|
64331
|
+
const p = join31(ROOT13, "package.json");
|
|
64286
64332
|
if (existsSync18(p))
|
|
64287
|
-
return JSON.parse(
|
|
64333
|
+
return JSON.parse(readFileSync34(p, "utf8")).version ?? "1.0.0";
|
|
64288
64334
|
} catch {}
|
|
64289
64335
|
return "1.0.0";
|
|
64290
64336
|
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
{
|
|
2
|
+
"surfaces": [
|
|
3
|
+
{
|
|
4
|
+
"id": "image_proxy",
|
|
5
|
+
"name": "Image proxy / URL-rewrite service",
|
|
6
|
+
"fingerprints": [
|
|
7
|
+
"imgproxy",
|
|
8
|
+
"image proxy",
|
|
9
|
+
"url unfurling",
|
|
10
|
+
"image resize",
|
|
11
|
+
"?url=",
|
|
12
|
+
"slack-imgs",
|
|
13
|
+
"image-resize",
|
|
14
|
+
"proxy.php?url"
|
|
15
|
+
],
|
|
16
|
+
"high_value_tests": [
|
|
17
|
+
"SSRF \u2014 the url= param reaches the proxy; point it at 169.254.169.254 or an internal range",
|
|
18
|
+
"URL validation bypass \u2014 encoded/internal-scheme (file://, gopher://, 127.0.0.1) variants",
|
|
19
|
+
"XSS via image content \u2014 SVG/HTML payloads rendered inline"
|
|
20
|
+
]
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
"id": "redirect_proxy",
|
|
24
|
+
"name": "URL redirect / safety proxy",
|
|
25
|
+
"fingerprints": [
|
|
26
|
+
"redirect",
|
|
27
|
+
"safety proxy",
|
|
28
|
+
"redir",
|
|
29
|
+
"url shortener",
|
|
30
|
+
"outbound link",
|
|
31
|
+
"?to=",
|
|
32
|
+
"?redirect=",
|
|
33
|
+
"?url="
|
|
34
|
+
],
|
|
35
|
+
"high_value_tests": [
|
|
36
|
+
"Open redirect \u2014 bypass the safety check to cloak phishing links",
|
|
37
|
+
"SSRF \u2014 the proxy fetches the target URL server-side",
|
|
38
|
+
"URL validation bypass \u2014 scheme/domain confusion (//, @, encoded chars)"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"id": "edge_cache",
|
|
43
|
+
"name": "Edge cache / entity data service",
|
|
44
|
+
"fingerprints": [
|
|
45
|
+
"edge",
|
|
46
|
+
"cache",
|
|
47
|
+
"conditional fetch",
|
|
48
|
+
"timestamp",
|
|
49
|
+
"entity",
|
|
50
|
+
"flannel",
|
|
51
|
+
"graphql"
|
|
52
|
+
],
|
|
53
|
+
"high_value_tests": [
|
|
54
|
+
"IDOR \u2014 read other entities (user/channel/tenant) by id without auth",
|
|
55
|
+
"Cache poisoning \u2014 inject a key that serves attacker content to other users",
|
|
56
|
+
"Unauthorized entity enumeration \u2014 bulk read via timestamp/resource pattern"
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"id": "payment_portal",
|
|
61
|
+
"name": "Payment / billing portal",
|
|
62
|
+
"fingerprints": [
|
|
63
|
+
"payment",
|
|
64
|
+
"billing",
|
|
65
|
+
"checkout",
|
|
66
|
+
"invoice",
|
|
67
|
+
"subscription",
|
|
68
|
+
"stripe",
|
|
69
|
+
"paypal",
|
|
70
|
+
"braintree",
|
|
71
|
+
"plan",
|
|
72
|
+
"pricing"
|
|
73
|
+
],
|
|
74
|
+
"high_value_tests": [
|
|
75
|
+
"Price/amount manipulation \u2014 alter the client-side total before checkout",
|
|
76
|
+
"IDOR on invoices/payment records \u2014 read or modify other users' billing",
|
|
77
|
+
"Subscription tampering \u2014 upgrade/entitlement without payment",
|
|
78
|
+
"Auth bypass on checkout/order endpoints"
|
|
79
|
+
]
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"id": "ai_agent",
|
|
83
|
+
"name": "AI agent / no-code app builder",
|
|
84
|
+
"fingerprints": [
|
|
85
|
+
"agent",
|
|
86
|
+
"ai",
|
|
87
|
+
"llm",
|
|
88
|
+
"no-code",
|
|
89
|
+
"builder",
|
|
90
|
+
"prompt",
|
|
91
|
+
"gpt",
|
|
92
|
+
"assistant",
|
|
93
|
+
"horizons"
|
|
94
|
+
],
|
|
95
|
+
"high_value_tests": [
|
|
96
|
+
"Prompt injection \u2192 SSRF / RCE / data exfiltration via tool-call abuse",
|
|
97
|
+
"Insecure deserialization of tool-call arguments",
|
|
98
|
+
"Stored XSS in agent-generated output rendered in the dashboard"
|
|
99
|
+
]
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"id": "file_service",
|
|
103
|
+
"name": "File upload / download service",
|
|
104
|
+
"fingerprints": [
|
|
105
|
+
"file",
|
|
106
|
+
"upload",
|
|
107
|
+
"download",
|
|
108
|
+
"supra",
|
|
109
|
+
"miata",
|
|
110
|
+
"attachment",
|
|
111
|
+
"?file=",
|
|
112
|
+
"?download="
|
|
113
|
+
],
|
|
114
|
+
"high_value_tests": [
|
|
115
|
+
"XSS via file content \u2014 SVG/HTML served inline",
|
|
116
|
+
"SSRF via file URL / remote file fetch",
|
|
117
|
+
"Upload \u2192 RCE \u2014 executable file landing web-reachable"
|
|
118
|
+
]
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
"id": "electron_desktop",
|
|
122
|
+
"name": "Electron / desktop app",
|
|
123
|
+
"fingerprints": [
|
|
124
|
+
"electron",
|
|
125
|
+
"browserwindow",
|
|
126
|
+
"desktop",
|
|
127
|
+
"chromium",
|
|
128
|
+
"node.js desktop",
|
|
129
|
+
"app.asar"
|
|
130
|
+
],
|
|
131
|
+
"high_value_tests": [
|
|
132
|
+
"HTML injection \u2192 BrowserWindow RCE (nodeIntegration + remote content)",
|
|
133
|
+
"Insecure IPC / preload bridge \u2014 expose privileged APIs to renderer",
|
|
134
|
+
"Remote content loading \u2014 hot-update/CDN resources replaced"
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"id": "auth_sso",
|
|
139
|
+
"name": "Authentication / SSO portal",
|
|
140
|
+
"fingerprints": [
|
|
141
|
+
"auth",
|
|
142
|
+
"sso",
|
|
143
|
+
"oauth",
|
|
144
|
+
"saml",
|
|
145
|
+
"login",
|
|
146
|
+
"passport",
|
|
147
|
+
"jwt",
|
|
148
|
+
"token",
|
|
149
|
+
"signin"
|
|
150
|
+
],
|
|
151
|
+
"high_value_tests": [
|
|
152
|
+
"OAuth open redirect via redirect_uri manipulation",
|
|
153
|
+
"JWT alg:none / weak secret / missing expiry",
|
|
154
|
+
"Token leakage / account enumeration / 2FA bypass"
|
|
155
|
+
]
|
|
156
|
+
}
|
|
157
|
+
],
|
|
158
|
+
"note": "Generalized attack-surface \u2192 high-value-test map, derived from public target-recon dossiers. Each surface is matched by fingerprint keywords against the detected tech stack/headers/body; when a surface is present, test its high-value cases FIRST (surface-first, not payload-spraying)."
|
|
159
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.108",
|
|
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": {
|