blitzstrike 2.4.7 → 2.4.8
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 +11 -0
- package/dist/index.js +175 -127
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,17 @@ 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.8] — 2026-09-14
|
|
9
|
+
|
|
10
|
+
### Added — MCP integration health checks
|
|
11
|
+
|
|
12
|
+
- New `checkMcpServers` (`src/mcp-status.ts`): verifies external MCP
|
|
13
|
+
integrations are actually present/connected — chrome-devtools-mcp (stdio:
|
|
14
|
+
binary or npx auto-install) and burp-suite-mcp (SSE: alive only when Burp is
|
|
15
|
+
running with the extension loaded).
|
|
16
|
+
- `blitzstrike doctor` now reports both MCP servers with availability + fix hints.
|
|
17
|
+
- New MCP tool `check_mcp` to probe MCP integration status on demand.
|
|
18
|
+
|
|
8
19
|
## [2.4.7] — 2026-09-14
|
|
9
20
|
|
|
10
21
|
### Added — deterministic arbitrary-file-read verification (no bare hypotheses)
|
package/dist/index.js
CHANGED
|
@@ -7667,6 +7667,95 @@ var init_catalog = __esm(() => {
|
|
|
7667
7667
|
ROOT2 = join6(fileURLToPath3(new URL(".", import.meta.url)), "..");
|
|
7668
7668
|
});
|
|
7669
7669
|
|
|
7670
|
+
// src/burp-mcp.ts
|
|
7671
|
+
async function fetchTimeout(url, init = {}, timeoutMs = 8000) {
|
|
7672
|
+
const ctrl = new AbortController;
|
|
7673
|
+
const t = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
7674
|
+
try {
|
|
7675
|
+
return await fetch(url, { ...init, signal: ctrl.signal });
|
|
7676
|
+
} finally {
|
|
7677
|
+
clearTimeout(t);
|
|
7678
|
+
}
|
|
7679
|
+
}
|
|
7680
|
+
function sseMessage(text) {
|
|
7681
|
+
const m = text.match(/event:\s*message[\s\S]*?data:\s*([^\n]+)/);
|
|
7682
|
+
return m ? m[1].trim() : null;
|
|
7683
|
+
}
|
|
7684
|
+
async function driveBurpMCP(baseUrl = DEFAULT_BURP_URL, timeoutMs = 1e4) {
|
|
7685
|
+
const base = baseUrl.replace(/\/+$/, "");
|
|
7686
|
+
let sseRes;
|
|
7687
|
+
try {
|
|
7688
|
+
sseRes = await fetchTimeout(`${base}/sse`, { headers: { Accept: "text/event-stream" } }, timeoutMs);
|
|
7689
|
+
} catch (e) {
|
|
7690
|
+
return { ok: false, alive: false, base_url: base, error: `Burp MCP not reachable at ${base}/sse (${String(e)}) — start Burp + load the MCP extension` };
|
|
7691
|
+
}
|
|
7692
|
+
if (!sseRes.ok) {
|
|
7693
|
+
return {
|
|
7694
|
+
ok: false,
|
|
7695
|
+
alive: false,
|
|
7696
|
+
base_url: base,
|
|
7697
|
+
error: `Burp MCP SSE endpoint returned HTTP ${sseRes.status} — is the extension enabled in Burp → MCP?`
|
|
7698
|
+
};
|
|
7699
|
+
}
|
|
7700
|
+
let endpoint = null;
|
|
7701
|
+
let serverInfo = "";
|
|
7702
|
+
let toolsRaw = "";
|
|
7703
|
+
try {
|
|
7704
|
+
const reader = sseRes.body.getReader();
|
|
7705
|
+
const decoder = new TextDecoder;
|
|
7706
|
+
let buf = "";
|
|
7707
|
+
const deadline = Date.now() + timeoutMs;
|
|
7708
|
+
while (Date.now() < deadline) {
|
|
7709
|
+
const { value, done } = await reader.read();
|
|
7710
|
+
if (done)
|
|
7711
|
+
break;
|
|
7712
|
+
buf += decoder.decode(value, { stream: true });
|
|
7713
|
+
if (!endpoint) {
|
|
7714
|
+
const em = buf.match(/event:\s*endpoint[\s\S]*?data:\s*([^\n]+)/);
|
|
7715
|
+
if (em)
|
|
7716
|
+
endpoint = em[1].trim();
|
|
7717
|
+
}
|
|
7718
|
+
if (endpoint)
|
|
7719
|
+
break;
|
|
7720
|
+
}
|
|
7721
|
+
if (endpoint) {
|
|
7722
|
+
const postUrl = new URL(endpoint, base).toString();
|
|
7723
|
+
const initRes = await fetchTimeout(postUrl, {
|
|
7724
|
+
method: "POST",
|
|
7725
|
+
headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
|
|
7726
|
+
body: JSON.stringify({
|
|
7727
|
+
jsonrpc: "2.0",
|
|
7728
|
+
id: 1,
|
|
7729
|
+
method: "initialize",
|
|
7730
|
+
params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "blitzstrike", version: "2" } }
|
|
7731
|
+
})
|
|
7732
|
+
}, timeoutMs);
|
|
7733
|
+
const initText = await initRes.text();
|
|
7734
|
+
const initMsg = sseMessage(initText) ?? (initText.includes("result") ? initText : "");
|
|
7735
|
+
serverInfo = initMsg.slice(0, 600);
|
|
7736
|
+
const toolsRes = await fetchTimeout(postUrl, {
|
|
7737
|
+
method: "POST",
|
|
7738
|
+
headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
|
|
7739
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} })
|
|
7740
|
+
}, timeoutMs);
|
|
7741
|
+
const toolsText = await toolsRes.text();
|
|
7742
|
+
toolsRaw = (sseMessage(toolsText) ?? toolsText).slice(0, 3000);
|
|
7743
|
+
}
|
|
7744
|
+
} catch (e) {
|
|
7745
|
+
return { ok: true, alive: true, base_url: base, endpoint, error: `SSE stream opened but handshake failed: ${String(e)}` };
|
|
7746
|
+
}
|
|
7747
|
+
if (!endpoint) {
|
|
7748
|
+
return {
|
|
7749
|
+
ok: true,
|
|
7750
|
+
alive: true,
|
|
7751
|
+
base_url: base,
|
|
7752
|
+
error: "SSE stream open but no endpoint event received — Burp MCP extension may not be fully enabled"
|
|
7753
|
+
};
|
|
7754
|
+
}
|
|
7755
|
+
return { ok: true, alive: true, base_url: base, endpoint, server: serverInfo, tools: toolsRaw };
|
|
7756
|
+
}
|
|
7757
|
+
var DEFAULT_BURP_URL = "http://127.0.0.1:9876";
|
|
7758
|
+
|
|
7670
7759
|
// node_modules/php-parser/src/lexer/attribute.js
|
|
7671
7760
|
var require_attribute = __commonJS(function(exports, module) {
|
|
7672
7761
|
module.exports = {
|
|
@@ -15295,12 +15384,39 @@ var require_src = __commonJS(function(exports, module) {
|
|
|
15295
15384
|
module.exports.default = Engine;
|
|
15296
15385
|
});
|
|
15297
15386
|
|
|
15387
|
+
// src/mcp-status.ts
|
|
15388
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
15389
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
15390
|
+
async function checkMcpServers() {
|
|
15391
|
+
const out = [];
|
|
15392
|
+
const bin = ["/usr/bin/chrome-devtools-mcp", "/usr/local/bin/chrome-devtools-mcp"].find((p) => existsSync8(p));
|
|
15393
|
+
const npxOk = spawnSync3("npx", ["--version"], { timeout: 8000 }).error === undefined;
|
|
15394
|
+
const devtoolsOk = Boolean(bin) || npxOk;
|
|
15395
|
+
out.push({
|
|
15396
|
+
name: "chrome-devtools-mcp",
|
|
15397
|
+
available: devtoolsOk,
|
|
15398
|
+
kind: "stdio",
|
|
15399
|
+
detail: bin ? `installed at ${bin}` : npxOk ? "not installed — will auto-install via npx on first use" : "not installed (no npx either)",
|
|
15400
|
+
fix: devtoolsOk ? undefined : "install Node/npm, then `npm i -g chrome-devtools-mcp`"
|
|
15401
|
+
});
|
|
15402
|
+
const burp = await driveBurpMCP("http://127.0.0.1:9876", 4000);
|
|
15403
|
+
out.push({
|
|
15404
|
+
name: "burp-suite-mcp",
|
|
15405
|
+
available: burp.alive,
|
|
15406
|
+
kind: "sse",
|
|
15407
|
+
detail: burp.alive ? `alive at ${burp.base_url}${burp.endpoint ? ` (endpoint ${burp.endpoint})` : ""}` : burp.error ?? "not reachable",
|
|
15408
|
+
fix: burp.alive ? undefined : "build PortSwigger/mcp-server (`./gradlew embedProxyJar`) + load the JAR in Burp → Extensions, then enable the MCP extension"
|
|
15409
|
+
});
|
|
15410
|
+
return out;
|
|
15411
|
+
}
|
|
15412
|
+
var init_mcp_status = () => {};
|
|
15413
|
+
|
|
15298
15414
|
// src/sync.ts
|
|
15299
15415
|
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync6, readFileSync as readFileSync18, readdirSync as readdirSync7, statSync as statSync4, rmSync as rmSync4 } from "node:fs";
|
|
15300
15416
|
import { join as join16 } from "node:path";
|
|
15301
15417
|
import { homedir as homedir5 } from "node:os";
|
|
15302
15418
|
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
15303
|
-
import { spawnSync as
|
|
15419
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
15304
15420
|
function dirExists(p) {
|
|
15305
15421
|
try {
|
|
15306
15422
|
return statSync4(p).isDirectory();
|
|
@@ -15351,14 +15467,14 @@ function syncData() {
|
|
|
15351
15467
|
copyDir(localTemplates, templatesPath);
|
|
15352
15468
|
templatesSynced = true;
|
|
15353
15469
|
}
|
|
15354
|
-
const gitOk =
|
|
15470
|
+
const gitOk = spawnSync4("git", ["--version"]).status === 0;
|
|
15355
15471
|
if (gitOk && (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0 || !dirExists(templatesPath) || countFiles(templatesPath) === 0)) {
|
|
15356
15472
|
const tmp = join16(DATA_ROOT2, ".tmp-clone");
|
|
15357
15473
|
if (dirExists(tmp))
|
|
15358
15474
|
rmSync4(tmp, { recursive: true, force: true });
|
|
15359
|
-
const r =
|
|
15475
|
+
const r = spawnSync4("git", ["clone", "--depth", "1", "--filter=blob:none", "--sparse", GITHUB_REPO, tmp], { timeout: 120000 });
|
|
15360
15476
|
if (r.status === 0) {
|
|
15361
|
-
const sparse =
|
|
15477
|
+
const sparse = spawnSync4("git", ["-C", tmp, "sparse-checkout", "set", "payloads", "templates"], { timeout: 120000 });
|
|
15362
15478
|
if (sparse.status === 0) {
|
|
15363
15479
|
if (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0) {
|
|
15364
15480
|
copyDir(join16(tmp, "payloads"), payloadsPath);
|
|
@@ -15379,7 +15495,7 @@ function syncData() {
|
|
|
15379
15495
|
};
|
|
15380
15496
|
}
|
|
15381
15497
|
function copyDir(src, dst) {
|
|
15382
|
-
const r =
|
|
15498
|
+
const r = spawnSync4("cp", ["-r", src, dst], { timeout: 120000 });
|
|
15383
15499
|
if (r.status !== 0) {
|
|
15384
15500
|
manualCopy(src, dst);
|
|
15385
15501
|
}
|
|
@@ -15443,7 +15559,7 @@ var init_update = __esm(() => {
|
|
|
15443
15559
|
});
|
|
15444
15560
|
|
|
15445
15561
|
// src/release.ts
|
|
15446
|
-
import { readFileSync as readFileSync20, existsSync as
|
|
15562
|
+
import { readFileSync as readFileSync20, existsSync as existsSync10 } from "node:fs";
|
|
15447
15563
|
import { join as join17 } from "node:path";
|
|
15448
15564
|
import { fileURLToPath as fileURLToPath10 } from "node:url";
|
|
15449
15565
|
import { execSync as execSync4 } from "node:child_process";
|
|
@@ -15483,7 +15599,7 @@ function validateRelease() {
|
|
|
15483
15599
|
return { version: VERSION, checks, consistent: checks.filter((c) => !c.soft).every((c) => c.ok) };
|
|
15484
15600
|
}
|
|
15485
15601
|
function lockfilePresent() {
|
|
15486
|
-
return
|
|
15602
|
+
return existsSync10(join17(ROOT8, "package-lock.json")) || existsSync10(join17(ROOT8, "bun.lock")) || existsSync10(join17(ROOT8, "bun.lockb"));
|
|
15487
15603
|
}
|
|
15488
15604
|
var ROOT8;
|
|
15489
15605
|
var init_release = __esm(() => {
|
|
@@ -15525,20 +15641,20 @@ var init_dependency = __esm(() => {
|
|
|
15525
15641
|
});
|
|
15526
15642
|
|
|
15527
15643
|
// src/cli.ts
|
|
15528
|
-
import { readFileSync as readFileSync22, existsSync as
|
|
15644
|
+
import { readFileSync as readFileSync22, existsSync as existsSync11, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "node:fs";
|
|
15529
15645
|
import { join as join19, dirname as dirname2 } from "node:path";
|
|
15530
15646
|
import { homedir as homedir6 } from "node:os";
|
|
15531
15647
|
import { fileURLToPath as fileURLToPath12 } from "node:url";
|
|
15532
|
-
import { spawnSync as
|
|
15648
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
15533
15649
|
function which(cmd) {
|
|
15534
|
-
return
|
|
15650
|
+
return spawnSync5(`command -v ${cmd}`, { shell: true }).status === 0;
|
|
15535
15651
|
}
|
|
15536
15652
|
function installedCount() {
|
|
15537
15653
|
const tools = loadTools();
|
|
15538
15654
|
const installed = tools.filter((t) => which(t.name)).map((t) => t.name);
|
|
15539
15655
|
return { total: tools.length, installed, missing: tools.map((t) => t.name).filter((n) => !installed.includes(n)) };
|
|
15540
15656
|
}
|
|
15541
|
-
function runDoctor() {
|
|
15657
|
+
async function runDoctor() {
|
|
15542
15658
|
console.log(`BlitzStrike doctor — environment health check
|
|
15543
15659
|
`);
|
|
15544
15660
|
const issues = [];
|
|
@@ -15571,7 +15687,7 @@ function runDoctor() {
|
|
|
15571
15687
|
detail: h1 ? `X-HackerOne-Research: ${h1}` : "H1_USERNAME not set",
|
|
15572
15688
|
fix: h1 ? undefined : "export H1_USERNAME=<your-hackerone-username> (adds X-HackerOne-Research to outbound requests)"
|
|
15573
15689
|
});
|
|
15574
|
-
const dataOk =
|
|
15690
|
+
const dataOk = existsSync11(join19(ROOT10, "chains.json")) && existsSync11(join19(ROOT10, "tools-catalog.json"));
|
|
15575
15691
|
issues.push({
|
|
15576
15692
|
name: "Data layers (chains + tools-catalog)",
|
|
15577
15693
|
status: dataOk ? "ok" : "fail",
|
|
@@ -15592,6 +15708,15 @@ function runDoctor() {
|
|
|
15592
15708
|
detail: deps.safe ? `${deps.total_dependencies} deps, no install scripts` : `${deps.install_scripts.length} install script(s) + ${deps.git_dependencies.length} git dep(s)`,
|
|
15593
15709
|
fix: deps.safe ? undefined : "remove install-time scripts and git dependencies"
|
|
15594
15710
|
});
|
|
15711
|
+
const mcps = await checkMcpServers();
|
|
15712
|
+
for (const m of mcps) {
|
|
15713
|
+
issues.push({
|
|
15714
|
+
name: `MCP: ${m.name}`,
|
|
15715
|
+
status: m.available ? "ok" : "warn",
|
|
15716
|
+
detail: m.detail,
|
|
15717
|
+
fix: m.fix
|
|
15718
|
+
});
|
|
15719
|
+
}
|
|
15595
15720
|
const symbol = { ok: "OK ", warn: "WARN", fail: "FAIL" };
|
|
15596
15721
|
for (const i of issues) {
|
|
15597
15722
|
console.log(`${symbol[i.status]} ${i.name} ${i.detail}`);
|
|
@@ -15614,14 +15739,14 @@ function resolveCommand() {
|
|
|
15614
15739
|
if (which("blitzstrike"))
|
|
15615
15740
|
return { command: "blitzstrike", args: ["serve", "--mcp"] };
|
|
15616
15741
|
const src = join19(ROOT10, "src", "index.ts");
|
|
15617
|
-
if (which("bun") &&
|
|
15742
|
+
if (which("bun") && existsSync11(src))
|
|
15618
15743
|
return { command: "bun", args: ["run", src, "serve", "--mcp"] };
|
|
15619
15744
|
if (which("npx"))
|
|
15620
15745
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
15621
15746
|
if (which("bunx"))
|
|
15622
15747
|
return { command: "bunx", args: ["blitzstrike", "serve", "--mcp"] };
|
|
15623
15748
|
const dist = join19(ROOT10, "dist", "index.js");
|
|
15624
|
-
if (
|
|
15749
|
+
if (existsSync11(dist))
|
|
15625
15750
|
return { command: "bun", args: [dist, "serve", "--mcp"] };
|
|
15626
15751
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
15627
15752
|
}
|
|
@@ -15635,7 +15760,7 @@ function readJson(p) {
|
|
|
15635
15760
|
function jsonMcpServersWrite(p) {
|
|
15636
15761
|
return (command, args) => {
|
|
15637
15762
|
const dir = dirname2(p);
|
|
15638
|
-
if (!
|
|
15763
|
+
if (!existsSync11(dir))
|
|
15639
15764
|
mkdirSync5(dir, { recursive: true });
|
|
15640
15765
|
const existing = readJson(p) ?? {};
|
|
15641
15766
|
existing.mcpServers = {
|
|
@@ -15648,7 +15773,7 @@ function jsonMcpServersWrite(p) {
|
|
|
15648
15773
|
function opencodeWrite(p) {
|
|
15649
15774
|
return (command, args) => {
|
|
15650
15775
|
const dir = dirname2(p);
|
|
15651
|
-
if (!
|
|
15776
|
+
if (!existsSync11(dir))
|
|
15652
15777
|
mkdirSync5(dir, { recursive: true });
|
|
15653
15778
|
const existing = readJson(p) ?? {};
|
|
15654
15779
|
existing.mcp = {
|
|
@@ -15662,14 +15787,14 @@ function opencodeWrite(p) {
|
|
|
15662
15787
|
function copyOpenCodeAgents() {
|
|
15663
15788
|
const srcDir = join19(ROOT10, "opencode-agents");
|
|
15664
15789
|
const dstDir = join19(homedir6(), ".config", "opencode", "agents");
|
|
15665
|
-
if (!
|
|
15790
|
+
if (!existsSync11(srcDir))
|
|
15666
15791
|
return;
|
|
15667
|
-
if (!
|
|
15792
|
+
if (!existsSync11(dstDir))
|
|
15668
15793
|
mkdirSync5(dstDir, { recursive: true });
|
|
15669
15794
|
for (const f of ["Blitz Strike.md", "Blitz.md", "Eagle Eye.md", "Strike.md"]) {
|
|
15670
15795
|
const src = join19(srcDir, f);
|
|
15671
15796
|
const dst = join19(dstDir, f);
|
|
15672
|
-
if (
|
|
15797
|
+
if (existsSync11(src)) {
|
|
15673
15798
|
writeFileSync7(dst, readFileSync22(src, "utf8"));
|
|
15674
15799
|
}
|
|
15675
15800
|
}
|
|
@@ -15677,9 +15802,9 @@ function copyOpenCodeAgents() {
|
|
|
15677
15802
|
function codexWrite(p) {
|
|
15678
15803
|
return (command, args) => {
|
|
15679
15804
|
const dir = dirname2(p);
|
|
15680
|
-
if (!
|
|
15805
|
+
if (!existsSync11(dir))
|
|
15681
15806
|
mkdirSync5(dir, { recursive: true });
|
|
15682
|
-
let existing =
|
|
15807
|
+
let existing = existsSync11(p) ? readFileSync22(p, "utf8") : "";
|
|
15683
15808
|
if (!existing.trimEnd().endsWith(`
|
|
15684
15809
|
`))
|
|
15685
15810
|
existing += `
|
|
@@ -15696,9 +15821,9 @@ args = ${JSON.stringify(args)}
|
|
|
15696
15821
|
function hermesWrite(p) {
|
|
15697
15822
|
return (command, args) => {
|
|
15698
15823
|
const dir = dirname2(p);
|
|
15699
|
-
if (!
|
|
15824
|
+
if (!existsSync11(dir))
|
|
15700
15825
|
mkdirSync5(dir, { recursive: true });
|
|
15701
|
-
let existing =
|
|
15826
|
+
let existing = existsSync11(p) ? readFileSync22(p, "utf8") : "";
|
|
15702
15827
|
existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
|
|
15703
15828
|
if (!existing.trimEnd().endsWith(`
|
|
15704
15829
|
`))
|
|
@@ -15729,70 +15854,70 @@ function detectAgents() {
|
|
|
15729
15854
|
agents.push({
|
|
15730
15855
|
name: "Claude Code",
|
|
15731
15856
|
detectPath: claudeJson,
|
|
15732
|
-
installed: () =>
|
|
15857
|
+
installed: () => existsSync11(claudeJson) || which("claude"),
|
|
15733
15858
|
write: jsonMcpServersWrite(claudeJson)
|
|
15734
15859
|
});
|
|
15735
15860
|
const claudeDesktop = join19(home, ".config", "Claude", "claude_desktop_config.json");
|
|
15736
15861
|
agents.push({
|
|
15737
15862
|
name: "Claude Desktop",
|
|
15738
15863
|
detectPath: claudeDesktop,
|
|
15739
|
-
installed: () =>
|
|
15864
|
+
installed: () => existsSync11(claudeDesktop),
|
|
15740
15865
|
write: jsonMcpServersWrite(claudeDesktop)
|
|
15741
15866
|
});
|
|
15742
15867
|
const cursor = join19(home, ".cursor", "mcp.json");
|
|
15743
15868
|
agents.push({
|
|
15744
15869
|
name: "Cursor",
|
|
15745
15870
|
detectPath: cursor,
|
|
15746
|
-
installed: () =>
|
|
15871
|
+
installed: () => existsSync11(join19(home, ".cursor")) || which("cursor"),
|
|
15747
15872
|
write: jsonMcpServersWrite(cursor)
|
|
15748
15873
|
});
|
|
15749
15874
|
const opencode = join19(home, ".config", "opencode", "opencode.jsonc");
|
|
15750
15875
|
agents.push({
|
|
15751
15876
|
name: "OpenCode",
|
|
15752
15877
|
detectPath: opencode,
|
|
15753
|
-
installed: () =>
|
|
15878
|
+
installed: () => existsSync11(join19(home, ".config", "opencode")) || which("opencode"),
|
|
15754
15879
|
write: opencodeWrite(opencode)
|
|
15755
15880
|
});
|
|
15756
15881
|
const codex = join19(home, ".codex", "config.toml");
|
|
15757
15882
|
agents.push({
|
|
15758
15883
|
name: "Codex",
|
|
15759
15884
|
detectPath: codex,
|
|
15760
|
-
installed: () =>
|
|
15885
|
+
installed: () => existsSync11(join19(home, ".codex")) || which("codex"),
|
|
15761
15886
|
write: codexWrite(codex)
|
|
15762
15887
|
});
|
|
15763
15888
|
const hermes = join19(home, ".hermes", "config.yaml");
|
|
15764
15889
|
agents.push({
|
|
15765
15890
|
name: "Hermes",
|
|
15766
15891
|
detectPath: hermes,
|
|
15767
|
-
installed: () =>
|
|
15892
|
+
installed: () => existsSync11(join19(home, ".hermes")) || which("hermes"),
|
|
15768
15893
|
write: hermesWrite(hermes)
|
|
15769
15894
|
});
|
|
15770
15895
|
const gemini = join19(home, ".gemini", "settings.json");
|
|
15771
15896
|
agents.push({
|
|
15772
15897
|
name: "Gemini CLI",
|
|
15773
15898
|
detectPath: gemini,
|
|
15774
|
-
installed: () =>
|
|
15899
|
+
installed: () => existsSync11(join19(home, ".gemini")) || which("gemini"),
|
|
15775
15900
|
write: jsonMcpServersWrite(gemini)
|
|
15776
15901
|
});
|
|
15777
15902
|
const windsurf = join19(home, ".codeium", "windsurf", "mcp_config.json");
|
|
15778
15903
|
agents.push({
|
|
15779
15904
|
name: "Windsurf",
|
|
15780
15905
|
detectPath: windsurf,
|
|
15781
|
-
installed: () =>
|
|
15906
|
+
installed: () => existsSync11(join19(home, ".codeium")) || which("windsurf"),
|
|
15782
15907
|
write: jsonMcpServersWrite(windsurf)
|
|
15783
15908
|
});
|
|
15784
15909
|
const copilot = join19(home, ".copilot", "mcp.json");
|
|
15785
15910
|
agents.push({
|
|
15786
15911
|
name: "Copilot",
|
|
15787
15912
|
detectPath: copilot,
|
|
15788
|
-
installed: () =>
|
|
15913
|
+
installed: () => existsSync11(join19(home, ".copilot")),
|
|
15789
15914
|
write: jsonMcpServersWrite(copilot)
|
|
15790
15915
|
});
|
|
15791
15916
|
const cline = join19(home, ".cline", "mcp_settings.json");
|
|
15792
15917
|
agents.push({
|
|
15793
15918
|
name: "Cline",
|
|
15794
15919
|
detectPath: cline,
|
|
15795
|
-
installed: () =>
|
|
15920
|
+
installed: () => existsSync11(join19(home, ".cline")),
|
|
15796
15921
|
write: jsonMcpServersWrite(cline)
|
|
15797
15922
|
});
|
|
15798
15923
|
return agents;
|
|
@@ -15874,6 +15999,7 @@ var init_cli = __esm(() => {
|
|
|
15874
15999
|
init_catalog();
|
|
15875
16000
|
init_release();
|
|
15876
16001
|
init_dependency();
|
|
16002
|
+
init_mcp_status();
|
|
15877
16003
|
ROOT10 = join19(fileURLToPath12(new URL(".", import.meta.url)), "..");
|
|
15878
16004
|
});
|
|
15879
16005
|
|
|
@@ -31781,95 +31907,6 @@ async function driveChromeDevtools(target, timeoutMs = 25000) {
|
|
|
31781
31907
|
}
|
|
31782
31908
|
}
|
|
31783
31909
|
|
|
31784
|
-
// src/burp-mcp.ts
|
|
31785
|
-
var DEFAULT_BURP_URL = "http://127.0.0.1:9876";
|
|
31786
|
-
async function fetchTimeout(url, init = {}, timeoutMs = 8000) {
|
|
31787
|
-
const ctrl = new AbortController;
|
|
31788
|
-
const t = setTimeout(() => ctrl.abort(), timeoutMs);
|
|
31789
|
-
try {
|
|
31790
|
-
return await fetch(url, { ...init, signal: ctrl.signal });
|
|
31791
|
-
} finally {
|
|
31792
|
-
clearTimeout(t);
|
|
31793
|
-
}
|
|
31794
|
-
}
|
|
31795
|
-
function sseMessage(text) {
|
|
31796
|
-
const m = text.match(/event:\s*message[\s\S]*?data:\s*([^\n]+)/);
|
|
31797
|
-
return m ? m[1].trim() : null;
|
|
31798
|
-
}
|
|
31799
|
-
async function driveBurpMCP(baseUrl = DEFAULT_BURP_URL, timeoutMs = 1e4) {
|
|
31800
|
-
const base = baseUrl.replace(/\/+$/, "");
|
|
31801
|
-
let sseRes;
|
|
31802
|
-
try {
|
|
31803
|
-
sseRes = await fetchTimeout(`${base}/sse`, { headers: { Accept: "text/event-stream" } }, timeoutMs);
|
|
31804
|
-
} catch (e) {
|
|
31805
|
-
return { ok: false, alive: false, base_url: base, error: `Burp MCP not reachable at ${base}/sse (${String(e)}) — start Burp + load the MCP extension` };
|
|
31806
|
-
}
|
|
31807
|
-
if (!sseRes.ok) {
|
|
31808
|
-
return {
|
|
31809
|
-
ok: false,
|
|
31810
|
-
alive: false,
|
|
31811
|
-
base_url: base,
|
|
31812
|
-
error: `Burp MCP SSE endpoint returned HTTP ${sseRes.status} — is the extension enabled in Burp → MCP?`
|
|
31813
|
-
};
|
|
31814
|
-
}
|
|
31815
|
-
let endpoint = null;
|
|
31816
|
-
let serverInfo = "";
|
|
31817
|
-
let toolsRaw = "";
|
|
31818
|
-
try {
|
|
31819
|
-
const reader = sseRes.body.getReader();
|
|
31820
|
-
const decoder = new TextDecoder;
|
|
31821
|
-
let buf = "";
|
|
31822
|
-
const deadline = Date.now() + timeoutMs;
|
|
31823
|
-
while (Date.now() < deadline) {
|
|
31824
|
-
const { value, done } = await reader.read();
|
|
31825
|
-
if (done)
|
|
31826
|
-
break;
|
|
31827
|
-
buf += decoder.decode(value, { stream: true });
|
|
31828
|
-
if (!endpoint) {
|
|
31829
|
-
const em = buf.match(/event:\s*endpoint[\s\S]*?data:\s*([^\n]+)/);
|
|
31830
|
-
if (em)
|
|
31831
|
-
endpoint = em[1].trim();
|
|
31832
|
-
}
|
|
31833
|
-
if (endpoint)
|
|
31834
|
-
break;
|
|
31835
|
-
}
|
|
31836
|
-
if (endpoint) {
|
|
31837
|
-
const postUrl = new URL(endpoint, base).toString();
|
|
31838
|
-
const initRes = await fetchTimeout(postUrl, {
|
|
31839
|
-
method: "POST",
|
|
31840
|
-
headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
|
|
31841
|
-
body: JSON.stringify({
|
|
31842
|
-
jsonrpc: "2.0",
|
|
31843
|
-
id: 1,
|
|
31844
|
-
method: "initialize",
|
|
31845
|
-
params: { protocolVersion: "2024-11-05", capabilities: {}, clientInfo: { name: "blitzstrike", version: "2" } }
|
|
31846
|
-
})
|
|
31847
|
-
}, timeoutMs);
|
|
31848
|
-
const initText = await initRes.text();
|
|
31849
|
-
const initMsg = sseMessage(initText) ?? (initText.includes("result") ? initText : "");
|
|
31850
|
-
serverInfo = initMsg.slice(0, 600);
|
|
31851
|
-
const toolsRes = await fetchTimeout(postUrl, {
|
|
31852
|
-
method: "POST",
|
|
31853
|
-
headers: { "Content-Type": "application/json", Accept: "application/json, text/event-stream" },
|
|
31854
|
-
body: JSON.stringify({ jsonrpc: "2.0", id: 2, method: "tools/list", params: {} })
|
|
31855
|
-
}, timeoutMs);
|
|
31856
|
-
const toolsText = await toolsRes.text();
|
|
31857
|
-
toolsRaw = (sseMessage(toolsText) ?? toolsText).slice(0, 3000);
|
|
31858
|
-
}
|
|
31859
|
-
} catch (e) {
|
|
31860
|
-
return { ok: true, alive: true, base_url: base, endpoint, error: `SSE stream opened but handshake failed: ${String(e)}` };
|
|
31861
|
-
}
|
|
31862
|
-
if (!endpoint) {
|
|
31863
|
-
return {
|
|
31864
|
-
ok: true,
|
|
31865
|
-
alive: true,
|
|
31866
|
-
base_url: base,
|
|
31867
|
-
error: "SSE stream open but no endpoint event received — Burp MCP extension may not be fully enabled"
|
|
31868
|
-
};
|
|
31869
|
-
}
|
|
31870
|
-
return { ok: true, alive: true, base_url: base, endpoint, server: serverInfo, tools: toolsRaw };
|
|
31871
|
-
}
|
|
31872
|
-
|
|
31873
31910
|
// src/chain-executor.ts
|
|
31874
31911
|
var BLITZ_FINDING_TOOLS = {
|
|
31875
31912
|
strike_verify: verifyFinding,
|
|
@@ -55414,6 +55451,9 @@ function browserDiagnostics() {
|
|
|
55414
55451
|
return { console_errors: [...consoleErrors], page_errors: [...pageErrors] };
|
|
55415
55452
|
}
|
|
55416
55453
|
|
|
55454
|
+
// src/server.ts
|
|
55455
|
+
init_mcp_status();
|
|
55456
|
+
|
|
55417
55457
|
// src/route-confusion.ts
|
|
55418
55458
|
var SOURCE_TOKENS = /\$_(GET|POST|REQUEST|COOKIE|FILES|SERVER)\b|\$request->|\$req->|request\.(get|input|query|post)|getParameter\(|req\.(query|body|params)/;
|
|
55419
55459
|
function lines(code) {
|
|
@@ -55988,7 +56028,7 @@ function runBenchmark() {
|
|
|
55988
56028
|
}
|
|
55989
56029
|
|
|
55990
56030
|
// src/enterprise-benchmark.ts
|
|
55991
|
-
import { existsSync as
|
|
56031
|
+
import { existsSync as existsSync9, readFileSync as readFileSync17, readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
|
|
55992
56032
|
import { join as join15, resolve } from "node:path";
|
|
55993
56033
|
var ROOT6 = resolve(process.cwd(), "bench", "enterprise");
|
|
55994
56034
|
var ENTERPRISE_FIXTURES = [
|
|
@@ -56048,7 +56088,7 @@ function detectFixture(dir) {
|
|
|
56048
56088
|
function runEnterpriseBenchmark() {
|
|
56049
56089
|
const fixtures = [];
|
|
56050
56090
|
for (const fx of ENTERPRISE_FIXTURES) {
|
|
56051
|
-
if (!
|
|
56091
|
+
if (!existsSync9(fx.dir)) {
|
|
56052
56092
|
fixtures.push({ id: fx.id, available: false, detected: 0, expected: fx.expected_vulns, recall: 0 });
|
|
56053
56093
|
continue;
|
|
56054
56094
|
}
|
|
@@ -56680,6 +56720,14 @@ ${statusLine}` : statusLine },
|
|
|
56680
56720
|
}, async ({ target }) => {
|
|
56681
56721
|
return { content: [{ type: "text", text: JSON.stringify(await driveChromeDevtools(target)) }] };
|
|
56682
56722
|
});
|
|
56723
|
+
server.registerTool("check_mcp", {
|
|
56724
|
+
title: "Check external MCP integrations (chrome-devtools-mcp + burp-suite-mcp)",
|
|
56725
|
+
description: "Verify every external MCP integration is actually present/connected: chrome-devtools-mcp (stdio — installed or npx auto-install) and burp-suite-mcp (SSE — only alive when Burp is running with the extension loaded). Returns availability + fix hints, so you know before you need them.",
|
|
56726
|
+
inputSchema: {}
|
|
56727
|
+
}, async () => {
|
|
56728
|
+
const status = await checkMcpServers();
|
|
56729
|
+
return { content: [{ type: "text", text: JSON.stringify({ mcp_servers: status }) }] };
|
|
56730
|
+
});
|
|
56683
56731
|
server.registerTool("install_all_tools", {
|
|
56684
56732
|
title: "Install every catalog tool at once",
|
|
56685
56733
|
description: "CATALOG: install every non-MCP-server catalog tool in parallel (bounded concurrency), skipping MCP servers (they are connected, not installed). Returns a summary (installed / already_installed / failed) + per-tool result. Optionally filter by category (recon, web, api, cloud, system, ...). Use this to mass-provision the toolbelt instead of ensure_tool one-by-one.",
|
|
@@ -57623,14 +57671,14 @@ async function serve() {
|
|
|
57623
57671
|
|
|
57624
57672
|
// src/index.ts
|
|
57625
57673
|
init_cli();
|
|
57626
|
-
import { readFileSync as readFileSync23, existsSync as
|
|
57674
|
+
import { readFileSync as readFileSync23, existsSync as existsSync12 } from "node:fs";
|
|
57627
57675
|
import { join as join20 } from "node:path";
|
|
57628
57676
|
import { fileURLToPath as fileURLToPath13 } from "node:url";
|
|
57629
57677
|
var ROOT11 = join20(fileURLToPath13(new URL(".", import.meta.url)), "..");
|
|
57630
57678
|
function readVersion() {
|
|
57631
57679
|
try {
|
|
57632
57680
|
const p = join20(ROOT11, "package.json");
|
|
57633
|
-
if (
|
|
57681
|
+
if (existsSync12(p))
|
|
57634
57682
|
return JSON.parse(readFileSync23(p, "utf8")).version ?? "1.0.0";
|
|
57635
57683
|
} catch {}
|
|
57636
57684
|
return "1.0.0";
|
|
@@ -57662,7 +57710,7 @@ async function main() {
|
|
|
57662
57710
|
return;
|
|
57663
57711
|
}
|
|
57664
57712
|
if (args[0] === "doctor") {
|
|
57665
|
-
runDoctor();
|
|
57713
|
+
await runDoctor();
|
|
57666
57714
|
return;
|
|
57667
57715
|
}
|
|
57668
57716
|
if (args[0] === "install") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.8",
|
|
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": {
|