blitzstrike 2.4.8 → 2.4.10
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 +28 -0
- package/dist/index.js +61 -17
- package/package.json +1 -1
- package/tools-catalog.json +5633 -5631
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,34 @@ 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.10] — 2026-09-14
|
|
9
|
+
|
|
10
|
+
### Added — auto-install external MCP servers to every agent
|
|
11
|
+
|
|
12
|
+
- chrome-devtools-mcp is now flagged `installable` in the catalog: `install-tools`
|
|
13
|
+
/ `install_all_tools` provision it (npm i -g) instead of skipping all MCP
|
|
14
|
+
servers. burp-suite-mcp stays skipped (GUI/daemon — build + run manually).
|
|
15
|
+
- `blitzstrike install` (register to every agent) now also auto-installs
|
|
16
|
+
chrome-devtools-mcp so all agents can drive a real browser immediately.
|
|
17
|
+
|
|
18
|
+
### Fixed — doctor git stderr leak
|
|
19
|
+
|
|
20
|
+
- `validateRelease` suppresses git/npm stderr, so `blitzstrike doctor` no longer
|
|
21
|
+
prints a stray `fatal: not a git repository` / `No tags can describe` line when
|
|
22
|
+
run outside a git checkout.
|
|
23
|
+
|
|
24
|
+
## [2.4.9] — 2026-09-14
|
|
25
|
+
|
|
26
|
+
### Added — evidence-first enforcement (findings are never born empty)
|
|
27
|
+
|
|
28
|
+
- `makeFinding` + `finding_create` now accept `evidence` at creation — a finding
|
|
29
|
+
can carry its observed artifact (headers/URL/response) the moment it is
|
|
30
|
+
created, instead of relying on a separate attach call that gets skipped.
|
|
31
|
+
- `finding_create` warns when a finding is created with no evidence.
|
|
32
|
+
- `reportMarkdown` now flags evidence-less findings with an explicit
|
|
33
|
+
"Evidence-first violation" block (ids listed) — detection without proof is
|
|
34
|
+
surfaced instead of silently shipped.
|
|
35
|
+
|
|
8
36
|
## [2.4.8] — 2026-09-14
|
|
9
37
|
|
|
10
38
|
### Added — MCP integration health checks
|
package/dist/index.js
CHANGED
|
@@ -7633,9 +7633,9 @@ function ensureToolAsync(name, timeoutMs = 120000) {
|
|
|
7633
7633
|
}
|
|
7634
7634
|
async function installAllTools(opts = {}) {
|
|
7635
7635
|
const tools = loadTools();
|
|
7636
|
-
const
|
|
7636
|
+
const skippedMcp = tools.filter((t) => t.category === "mcp-server" && t.installable !== true).map((t) => t.name);
|
|
7637
7637
|
const cat = opts.category?.toLowerCase();
|
|
7638
|
-
const targets = tools.filter((t) => t.category !== "mcp-server" && (!cat || t.category === cat));
|
|
7638
|
+
const targets = tools.filter((t) => (t.category !== "mcp-server" || t.installable === true) && (!cat || t.category === cat));
|
|
7639
7639
|
const concurrency = Math.max(1, opts.concurrency ?? 4);
|
|
7640
7640
|
const results = [];
|
|
7641
7641
|
let idx = 0;
|
|
@@ -7657,7 +7657,7 @@ async function installAllTools(opts = {}) {
|
|
|
7657
7657
|
installed,
|
|
7658
7658
|
already_installed: already,
|
|
7659
7659
|
failed,
|
|
7660
|
-
skipped_mcp_servers:
|
|
7660
|
+
skipped_mcp_servers: skippedMcp,
|
|
7661
7661
|
results
|
|
7662
7662
|
};
|
|
7663
7663
|
}
|
|
@@ -15579,13 +15579,13 @@ function validateRelease() {
|
|
|
15579
15579
|
checks.push({ name: "CHANGELOG version", ok: false, detail: "CHANGELOG.md not found" });
|
|
15580
15580
|
}
|
|
15581
15581
|
try {
|
|
15582
|
-
const tag = execSync4("git describe --tags --abbrev=0", { cwd: ROOT8, encoding: "utf8" }).trim();
|
|
15582
|
+
const tag = execSync4("git describe --tags --abbrev=0", { cwd: ROOT8, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
15583
15583
|
checks.push({ name: "Git tag", ok: tag === `v${VERSION}`, detail: tag, soft: true });
|
|
15584
15584
|
} catch {
|
|
15585
|
-
checks.push({ name: "Git tag", ok: true, detail: "unavailable (no git)", soft: true });
|
|
15585
|
+
checks.push({ name: "Git tag", ok: true, detail: "unavailable (no git/tags)", soft: true });
|
|
15586
15586
|
}
|
|
15587
15587
|
try {
|
|
15588
|
-
const published = execSync4("npm view blitzstrike version", { encoding: "utf8", timeout: 8000 }).trim();
|
|
15588
|
+
const published = execSync4("npm view blitzstrike version", { encoding: "utf8", timeout: 8000, stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
15589
15589
|
checks.push({ name: "npm registry version", ok: published === VERSION, detail: published, soft: true });
|
|
15590
15590
|
} catch {
|
|
15591
15591
|
checks.push({ name: "npm registry version", ok: true, detail: "unavailable (offline)", soft: true });
|
|
@@ -15958,6 +15958,11 @@ Agent-specific formats: see docs/installation.md`);
|
|
|
15958
15958
|
console.log(`
|
|
15959
15959
|
Registered with ${ok}/${installed.length} agent(s).`);
|
|
15960
15960
|
console.log("Restart your agent, then call `run_engagement` or any `blitzstrike` tool.");
|
|
15961
|
+
console.log(`
|
|
15962
|
+
External MCP servers:`);
|
|
15963
|
+
const devtools = ensureTool("chrome-devtools-mcp");
|
|
15964
|
+
console.log(` ${devtools.installed ? "OK " : "FAIL"} chrome-devtools-mcp ${devtools.installed ? "installed (browser MCP ready for all agents)" : devtools.command ?? "install failed"}`);
|
|
15965
|
+
console.log(` SKIP burp-suite-mcp GUI/daemon — build manually (PortSwigger/mcp-server) + load in Burp (connect, not install)`);
|
|
15961
15966
|
}
|
|
15962
15967
|
async function runInstallTools(args) {
|
|
15963
15968
|
const getArg = (flag) => {
|
|
@@ -15975,9 +15980,9 @@ async function runInstallTools(args) {
|
|
|
15975
15980
|
await Promise.resolve().then(() => init_catalog());
|
|
15976
15981
|
if (dryRun) {
|
|
15977
15982
|
const tools = loadTools();
|
|
15978
|
-
const mcp = tools.filter((t) => t.category === "mcp-server");
|
|
15979
|
-
const targets = tools.filter((t) => t.category !== "mcp-server" && (!category || t.category === category.toLowerCase()));
|
|
15980
|
-
console.log(`Dry run — would attempt ${targets.length} tool(s); ${mcp.length} MCP server(s) skipped (connect, not install):`);
|
|
15983
|
+
const mcp = tools.filter((t) => t.category === "mcp-server" && t.installable !== true);
|
|
15984
|
+
const targets = tools.filter((t) => (t.category !== "mcp-server" || t.installable === true) && (!category || t.category === category.toLowerCase()));
|
|
15985
|
+
console.log(`Dry run — would attempt ${targets.length} tool(s); ${mcp.length} MCP server(s) skipped (GUI/daemon — connect, not install):`);
|
|
15981
15986
|
for (const t of targets)
|
|
15982
15987
|
console.log(` ${t.category.padEnd(11)} ${t.name}`);
|
|
15983
15988
|
return;
|
|
@@ -31064,7 +31069,7 @@ function makeFinding(input) {
|
|
|
31064
31069
|
const status = input.status ?? "detected";
|
|
31065
31070
|
const factors = confidenceFactorsFor(input);
|
|
31066
31071
|
const confidence = computeConfidence(factors);
|
|
31067
|
-
|
|
31072
|
+
let finding = {
|
|
31068
31073
|
id: nextFindingId(),
|
|
31069
31074
|
status,
|
|
31070
31075
|
title: input.title,
|
|
@@ -31087,6 +31092,16 @@ function makeFinding(input) {
|
|
|
31087
31092
|
remediation: {},
|
|
31088
31093
|
timestamps: { created: now, updated: now }
|
|
31089
31094
|
};
|
|
31095
|
+
if (input.evidence?.length) {
|
|
31096
|
+
for (const e of input.evidence) {
|
|
31097
|
+
const ev = makeEvidence({
|
|
31098
|
+
type: e.type,
|
|
31099
|
+
description: e.description,
|
|
31100
|
+
artifacts: e.content ? [{ name: "artifact", kind: "evidence", content: e.content }] : []
|
|
31101
|
+
});
|
|
31102
|
+
finding = attachEvidence(finding, ev);
|
|
31103
|
+
}
|
|
31104
|
+
}
|
|
31090
31105
|
recordAudit("finding_created", { tool: "eagle-eye", target: input.target.host ?? input.target.endpoint, result: status, correlationId: finding.id });
|
|
31091
31106
|
return finding;
|
|
31092
31107
|
}
|
|
@@ -33009,11 +33024,15 @@ function summarize(findings) {
|
|
|
33009
33024
|
const byStatus = {};
|
|
33010
33025
|
const bySeverity = {};
|
|
33011
33026
|
let evidenceArtifacts = 0;
|
|
33027
|
+
let evidenceLess = 0;
|
|
33012
33028
|
for (const f of findings) {
|
|
33013
33029
|
byStatus[f.status ?? "detected"] = (byStatus[f.status ?? "detected"] ?? 0) + 1;
|
|
33014
33030
|
const sev = f.classification?.severity ?? "informational";
|
|
33015
33031
|
bySeverity[sev] = (bySeverity[sev] ?? 0) + 1;
|
|
33016
|
-
|
|
33032
|
+
const arts = f.evidence?.reduce((n, e) => n + (e.artifacts?.length ?? 0), 0) ?? 0;
|
|
33033
|
+
evidenceArtifacts += arts;
|
|
33034
|
+
if ((f.evidence ?? []).length === 0)
|
|
33035
|
+
evidenceLess += 1;
|
|
33017
33036
|
}
|
|
33018
33037
|
const groups = dedupFindings(findings);
|
|
33019
33038
|
return {
|
|
@@ -33023,7 +33042,8 @@ function summarize(findings) {
|
|
|
33023
33042
|
by_severity: bySeverity,
|
|
33024
33043
|
confirmed: byStatus["confirmed"] ?? 0,
|
|
33025
33044
|
false_positives: byStatus["false_positive"] ?? 0,
|
|
33026
|
-
evidence_artifacts: evidenceArtifacts
|
|
33045
|
+
evidence_artifacts: evidenceArtifacts,
|
|
33046
|
+
evidence_less_findings: evidenceLess
|
|
33027
33047
|
};
|
|
33028
33048
|
}
|
|
33029
33049
|
function reportMarkdown(findings, opts = {}) {
|
|
@@ -33047,6 +33067,11 @@ function reportMarkdown(findings, opts = {}) {
|
|
|
33047
33067
|
}
|
|
33048
33068
|
lines.push(`Detailed findings, reproduction steps, impact, and remediation follow in section 5.`);
|
|
33049
33069
|
lines.push("");
|
|
33070
|
+
if (summary.evidence_less_findings > 0) {
|
|
33071
|
+
const evLess = findings.filter((f) => (f.evidence ?? []).length === 0).map((f) => f.id).join(", ");
|
|
33072
|
+
lines.push(`> ⚠️ **Evidence-first violation:** **${summary.evidence_less_findings}** finding(s) carry NO evidence artifacts (${evLess}). ` + `Detection is not proof — every finding must carry the observed artifact (headers, URL, response body). Attach evidence via finding_attach_evidence before finalizing.`);
|
|
33073
|
+
lines.push("");
|
|
33074
|
+
}
|
|
33050
33075
|
lines.push("## 2. Scope");
|
|
33051
33076
|
lines.push("");
|
|
33052
33077
|
lines.push(`- **Target(s):** ${scope}`);
|
|
@@ -57170,7 +57195,7 @@ ${statusLine}` : statusLine },
|
|
|
57170
57195
|
});
|
|
57171
57196
|
server.registerTool("finding_create", {
|
|
57172
57197
|
title: "Create a canonical finding",
|
|
57173
|
-
description: "FINDINGS: create a canonical, evidence-first finding. Severity describes impact; confidence (deterministic) describes certainty. Default status=detected (hypothesis-pending).",
|
|
57198
|
+
description: "FINDINGS: create a canonical, evidence-first finding. Severity describes impact; confidence (deterministic) describes certainty. Default status=detected (hypothesis-pending). Pass `evidence` (JSON array of {type, description, content}) to attach observed artifacts AT CREATION — a finding must never be born empty when you already hold the artifact (headers, URL, response body).",
|
|
57174
57199
|
inputSchema: {
|
|
57175
57200
|
title: string2().describe("Finding title"),
|
|
57176
57201
|
target_type: string2().optional().describe("Target type (web/api/source/mobile/network/other)"),
|
|
@@ -57182,9 +57207,18 @@ ${statusLine}` : statusLine },
|
|
|
57182
57207
|
source_name: string2().describe("Source name"),
|
|
57183
57208
|
sink_type: string2().describe("Sink type (e.g. sql_execution, command_execution, file_operations)"),
|
|
57184
57209
|
sink_symbol: string2().optional().describe("Sink symbol (e.g. '->query(')"),
|
|
57185
|
-
chain_id: string2().optional().describe("Escalation chain id")
|
|
57210
|
+
chain_id: string2().optional().describe("Escalation chain id"),
|
|
57211
|
+
evidence: string2().optional().describe("JSON array of evidence entries: [{type, description, content?}] — attached at creation (redacted + SHA-256 tagged)")
|
|
57212
|
+
}
|
|
57213
|
+
}, async ({ title, target_type, host, endpoint, severity, cwe, source_type, source_name, sink_type, sink_symbol, chain_id, evidence }) => {
|
|
57214
|
+
let evs = [];
|
|
57215
|
+
if (evidence) {
|
|
57216
|
+
try {
|
|
57217
|
+
evs = JSON.parse(evidence);
|
|
57218
|
+
} catch {
|
|
57219
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "invalid evidence JSON" }) }] };
|
|
57220
|
+
}
|
|
57186
57221
|
}
|
|
57187
|
-
}, async ({ title, target_type, host, endpoint, severity, cwe, source_type, source_name, sink_type, sink_symbol, chain_id }) => {
|
|
57188
57222
|
const f = makeFinding({
|
|
57189
57223
|
title,
|
|
57190
57224
|
target: { type: target_type ?? "web", host, endpoint },
|
|
@@ -57193,9 +57227,19 @@ ${statusLine}` : statusLine },
|
|
|
57193
57227
|
source: { type: source_type, name: source_name },
|
|
57194
57228
|
sink: { type: sink_type, symbol: sink_symbol },
|
|
57195
57229
|
chainId: chain_id ?? null,
|
|
57196
|
-
status: "detected"
|
|
57230
|
+
status: "detected",
|
|
57231
|
+
evidence: evs
|
|
57197
57232
|
});
|
|
57198
|
-
|
|
57233
|
+
const empty = (f.evidence ?? []).length === 0;
|
|
57234
|
+
return {
|
|
57235
|
+
content: [{
|
|
57236
|
+
type: "text",
|
|
57237
|
+
text: JSON.stringify({
|
|
57238
|
+
...f,
|
|
57239
|
+
_warning: empty ? "finding created with NO evidence — if you hold the observed artifact (headers/URL/body), attach it now via `evidence` or finding_attach_evidence" : undefined
|
|
57240
|
+
})
|
|
57241
|
+
}]
|
|
57242
|
+
};
|
|
57199
57243
|
});
|
|
57200
57244
|
server.registerTool("finding_transition", {
|
|
57201
57245
|
title: "Advance a finding lifecycle",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.10",
|
|
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": {
|