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 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.175",
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": {