blitzstrike 2.4.175 → 2.4.177
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 +20 -0
- package/dist/index.js +124 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,26 @@ 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.177] — 2026-10-11
|
|
9
|
+
|
|
10
|
+
### Added — surface coverage tracker (quantitative progress)
|
|
11
|
+
|
|
12
|
+
- New `surface-coverage` engine + 3 tools: `surface_add` (register a discovered
|
|
13
|
+
endpoint/param/host/subdomain/route/class), `surface_verdict` (mark a terminal
|
|
14
|
+
verdict: confirmed/dead/partial/skipped), and `coverage` (total/terminal/pending
|
|
15
|
+
+ coverage % + by-kind/by-verdict breakdown).
|
|
16
|
+
- Gives the agent a measurable "how done are we" number — a goal like
|
|
17
|
+
"coverage >= 80%" is now verifiable against the measured pct before prove_goal.
|
|
18
|
+
|
|
19
|
+
## [2.4.176] — 2026-10-11
|
|
20
|
+
|
|
21
|
+
### Added — retest context (completes the retest loop)
|
|
22
|
+
|
|
23
|
+
- New `retest_context` tool: given a confirmed/still_vulnerable/inconclusive
|
|
24
|
+
finding, return everything needed to re-verify it — finding key fields, PoC
|
|
25
|
+
evidence grouped by role (proof/baseline/verification) + reproducible flag,
|
|
26
|
+
the active operation constraints, and the current goal state.
|
|
27
|
+
|
|
8
28
|
## [2.4.175] — 2026-10-11
|
|
9
29
|
|
|
10
30
|
### Added — evidence roles (structured proof/baseline/verification/supporting)
|
package/dist/index.js
CHANGED
|
@@ -64006,6 +64006,59 @@ function lineage(id) {
|
|
|
64006
64006
|
return { fact, ancestors, descendants };
|
|
64007
64007
|
}
|
|
64008
64008
|
|
|
64009
|
+
// src/surface-coverage.ts
|
|
64010
|
+
var surface = [];
|
|
64011
|
+
var counter4 = 0;
|
|
64012
|
+
function nextId4() {
|
|
64013
|
+
counter4 += 1;
|
|
64014
|
+
return `SRF-${String(counter4).padStart(4, "0")}`;
|
|
64015
|
+
}
|
|
64016
|
+
function surfaceAdd(kind, ref) {
|
|
64017
|
+
const existing = surface.find((s) => s.kind === kind && s.ref === ref);
|
|
64018
|
+
if (existing)
|
|
64019
|
+
return existing;
|
|
64020
|
+
const item = { id: nextId4(), kind, ref, verdict: null, updated: new Date().toISOString() };
|
|
64021
|
+
surface.push(item);
|
|
64022
|
+
return item;
|
|
64023
|
+
}
|
|
64024
|
+
function surfaceVerdict(idOrRef, verdict) {
|
|
64025
|
+
const item = surface.find((s) => s.id === idOrRef || s.ref === idOrRef);
|
|
64026
|
+
if (!item)
|
|
64027
|
+
return { ok: false, error: `no surface item '${idOrRef}'` };
|
|
64028
|
+
item.verdict = verdict;
|
|
64029
|
+
item.updated = new Date().toISOString();
|
|
64030
|
+
return { ok: true, item: { ...item } };
|
|
64031
|
+
}
|
|
64032
|
+
function coverageSummary() {
|
|
64033
|
+
const by_kind = {
|
|
64034
|
+
endpoint: { total: 0, terminal: 0 },
|
|
64035
|
+
param: { total: 0, terminal: 0 },
|
|
64036
|
+
host: { total: 0, terminal: 0 },
|
|
64037
|
+
subdomain: { total: 0, terminal: 0 },
|
|
64038
|
+
route: { total: 0, terminal: 0 },
|
|
64039
|
+
class: { total: 0, terminal: 0 }
|
|
64040
|
+
};
|
|
64041
|
+
const by_verdict = { confirmed: 0, dead: 0, partial: 0, skipped: 0 };
|
|
64042
|
+
let terminal = 0;
|
|
64043
|
+
for (const s of surface) {
|
|
64044
|
+
by_kind[s.kind].total += 1;
|
|
64045
|
+
if (s.verdict) {
|
|
64046
|
+
terminal += 1;
|
|
64047
|
+
by_kind[s.kind].terminal += 1;
|
|
64048
|
+
by_verdict[s.verdict] += 1;
|
|
64049
|
+
}
|
|
64050
|
+
}
|
|
64051
|
+
const total = surface.length;
|
|
64052
|
+
return {
|
|
64053
|
+
total,
|
|
64054
|
+
terminal,
|
|
64055
|
+
pending: total - terminal,
|
|
64056
|
+
pct: total === 0 ? 0 : Math.round(terminal / total * 1000) / 10,
|
|
64057
|
+
by_kind,
|
|
64058
|
+
by_verdict
|
|
64059
|
+
};
|
|
64060
|
+
}
|
|
64061
|
+
|
|
64009
64062
|
// src/sibling-scan.ts
|
|
64010
64063
|
var SIBLING_SUFFIXES = [
|
|
64011
64064
|
"export",
|
|
@@ -66000,6 +66053,35 @@ ${statusLine}` : statusLine },
|
|
|
66000
66053
|
}, async ({ id }) => {
|
|
66001
66054
|
return { content: [{ type: "text", text: JSON.stringify(lineage(id)) }] };
|
|
66002
66055
|
});
|
|
66056
|
+
server.registerTool("surface_add", {
|
|
66057
|
+
title: "Register a discovered attack-surface item",
|
|
66058
|
+
description: "COVERAGE: register a discovered surface item (endpoint/param/host/subdomain/route/class) so the coverage tracker can measure progress. Deduped by kind+ref. Pair with surface_verdict (mark terminal verdict) and coverage (the % done). Use every time recon/param_surface/asset_discovery surfaces a new endpoint, param, host, or class.",
|
|
66059
|
+
inputSchema: {
|
|
66060
|
+
kind: _enum(["endpoint", "param", "host", "subdomain", "route", "class"]).describe("What kind of surface item"),
|
|
66061
|
+
ref: string2().describe("The endpoint/param/host/etc. (e.g. '/api/users', 'id', 'app.example.com')")
|
|
66062
|
+
}
|
|
66063
|
+
}, async ({ kind, ref }) => {
|
|
66064
|
+
const item = surfaceAdd(kind, ref);
|
|
66065
|
+
return { content: [{ type: "text", text: JSON.stringify({ ...item, ...coverageSummary() }) }] };
|
|
66066
|
+
});
|
|
66067
|
+
server.registerTool("surface_verdict", {
|
|
66068
|
+
title: "Mark a terminal verdict on a surface item",
|
|
66069
|
+
description: "COVERAGE: mark a surface item's terminal verdict — confirmed (a verified finding), dead (tested + disproved/blocked), partial (tested, some shapes remain), or skipped (out of scope / tool blocked). An item stays PENDING until it has a verdict; pending items drag the coverage % down. Accepts the item id (SRF-XXXX) or its ref.",
|
|
66070
|
+
inputSchema: {
|
|
66071
|
+
id_or_ref: string2().describe("Surface item id (SRF-XXXX) or its ref"),
|
|
66072
|
+
verdict: _enum(["confirmed", "dead", "partial", "skipped"]).describe("Terminal verdict")
|
|
66073
|
+
}
|
|
66074
|
+
}, async ({ id_or_ref, verdict }) => {
|
|
66075
|
+
const r = surfaceVerdict(id_or_ref, verdict);
|
|
66076
|
+
return { content: [{ type: "text", text: JSON.stringify({ ...r, ...coverageSummary() }) }] };
|
|
66077
|
+
});
|
|
66078
|
+
server.registerTool("coverage", {
|
|
66079
|
+
title: "Coverage % of the discovered attack surface",
|
|
66080
|
+
description: "COVERAGE: return the quantitative progress — total surface items, how many have a terminal verdict, coverage % (terminal/total), pending count, and the breakdown by kind and by verdict. Check this before proving a goal like 'coverage >= 80%'; a goal with a quantitative condition must be checked against this measured pct before prove_goal.",
|
|
66081
|
+
inputSchema: {}
|
|
66082
|
+
}, async () => {
|
|
66083
|
+
return { content: [{ type: "text", text: JSON.stringify(coverageSummary()) }] };
|
|
66084
|
+
});
|
|
66003
66085
|
server.registerTool("hunting_doctrine", {
|
|
66004
66086
|
title: "High-ROI hunting doctrine (sibling rule, A→B, follow-the-money)",
|
|
66005
66087
|
description: "Return the hunting doctrine — the high-ROI heuristics the agent applies while selecting targets/endpoints/classes: the Sibling Rule (check every sibling endpoint — ~30% of paid IDOR/auth bugs), A→B Signal Method, impact-first, follow-the-money, less-saturated classes, new==unreviewed, two-account IDOR test, PoC escalation, credential-proof, CI/CD + SAML/SSO attack surface, 20-minute rotation, plus the always-rejected kill-list. Use when deciding WHAT to hunt next.",
|
|
@@ -66811,6 +66893,48 @@ ${statusLine}` : statusLine },
|
|
|
66811
66893
|
].filter(Boolean);
|
|
66812
66894
|
return { content: [{ type: "text", text: JSON.stringify({ ...roles, missing: missing.length ? missing : undefined, hint: roles.reproducible ? "reproducible — evidence complete" : "NOT reproducible — attach the missing roles via finding_attach_evidence" }) }] };
|
|
66813
66895
|
});
|
|
66896
|
+
server.registerTool("retest_context", {
|
|
66897
|
+
title: "Build the retest context for a confirmed finding",
|
|
66898
|
+
description: "RETEST: given a CONFIRMED (or still_vulnerable/inconclusive) finding, return everything needed to re-verify it in one shot: the finding's key fields (target/method/param/severity), its PoC evidence grouped by role (proof/baseline/verification) + reproducible flag, the active operation constraints (red lines), and the current goal state. Then re-run the proof request and record the verdict with retest_finding.",
|
|
66899
|
+
inputSchema: {
|
|
66900
|
+
finding: string2().describe("JSON of the Finding object to retest")
|
|
66901
|
+
}
|
|
66902
|
+
}, async ({ finding }) => {
|
|
66903
|
+
let f;
|
|
66904
|
+
try {
|
|
66905
|
+
f = JSON.parse(finding);
|
|
66906
|
+
} catch {
|
|
66907
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "invalid finding JSON" }) }] };
|
|
66908
|
+
}
|
|
66909
|
+
const ev = f.evidence ?? [];
|
|
66910
|
+
const roles = evidenceRoles(ev);
|
|
66911
|
+
const byRole = { proof: [], baseline: [], verification: [], supporting: [] };
|
|
66912
|
+
for (const e of ev)
|
|
66913
|
+
byRole[e.role].push({ evidence_id: e.evidence_id, type: e.type, description: e.description, artifacts: e.artifacts.map((a) => a.name) });
|
|
66914
|
+
const missing = ["proof", "baseline", "verification"].filter((r) => !roles[r]);
|
|
66915
|
+
const goals = goalStatus();
|
|
66916
|
+
return {
|
|
66917
|
+
content: [{
|
|
66918
|
+
type: "text",
|
|
66919
|
+
text: JSON.stringify({
|
|
66920
|
+
finding: {
|
|
66921
|
+
id: f.id,
|
|
66922
|
+
status: f.status,
|
|
66923
|
+
title: f.title,
|
|
66924
|
+
severity: f.classification?.severity,
|
|
66925
|
+
cwe: f.classification?.cwe,
|
|
66926
|
+
target: f.target,
|
|
66927
|
+
method: f.target?.method,
|
|
66928
|
+
param: f.target?.param
|
|
66929
|
+
},
|
|
66930
|
+
poc: { ...byRole, reproducible: roles.reproducible, missing: missing.length ? missing : undefined },
|
|
66931
|
+
constraints: listConstraints(),
|
|
66932
|
+
goals: { all_met: goals.all_met, met: goals.met, total: goals.total },
|
|
66933
|
+
retest_hint: "Re-run the proof request (or the original PoC path) against the CURRENT target. Still reproduces → retest_finding(verdict=still_vulnerable); no longer reproduces → fixed; a blocker (WAF/target down/missing login) prevents a verdict → inconclusive."
|
|
66934
|
+
})
|
|
66935
|
+
}]
|
|
66936
|
+
};
|
|
66937
|
+
});
|
|
66814
66938
|
server.registerTool("redact", {
|
|
66815
66939
|
title: "Redact secrets from text",
|
|
66816
66940
|
description: "EVIDENCE: redact passwords/API keys/tokens/cookies/private keys from text before persisting evidence or reporting.",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.177",
|
|
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": {
|