blitzstrike 2.4.173 → 2.4.175
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 +112 -4
- 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.175] — 2026-10-11
|
|
9
|
+
|
|
10
|
+
### Added — evidence roles (structured proof/baseline/verification/supporting)
|
|
11
|
+
|
|
12
|
+
- Evidence records now carry a `role` (proof = trigger request/response, baseline =
|
|
13
|
+
normal request, verification = negative control/marker, supporting = anything else).
|
|
14
|
+
- `finding_attach_evidence` accepts a `role`; new `finding_evidence_roles` tool
|
|
15
|
+
returns the role breakdown + a `reproducible` flag (true only when a finding has
|
|
16
|
+
proof + baseline + verification — the three roles a HackerOne-grade PoC needs).
|
|
17
|
+
|
|
18
|
+
## [2.4.174] — 2026-10-11
|
|
19
|
+
|
|
20
|
+
### Added — observation ledger + lineage (third write-back channel)
|
|
21
|
+
|
|
22
|
+
- New `facts` engine + 3 tools: `record_fact` (record an exploration observation
|
|
23
|
+
with observed/inferred confidence + optional parent_ids), `list_facts`, and
|
|
24
|
+
`lineage` (trace a fact's ancestors up + descendants down).
|
|
25
|
+
- Completes the three write-back channels: findings (vulns), assets (asset_discovery),
|
|
26
|
+
and facts (neutral/negative observations that would otherwise be lost).
|
|
27
|
+
|
|
8
28
|
## [2.4.173] — 2026-10-11
|
|
9
29
|
|
|
10
30
|
### Added — goal tracking + prove_goal (completion signal)
|
package/dist/index.js
CHANGED
|
@@ -33211,6 +33211,7 @@ function makeEvidence(input) {
|
|
|
33211
33211
|
return {
|
|
33212
33212
|
evidence_id: nextEvidenceId(),
|
|
33213
33213
|
type: input.type,
|
|
33214
|
+
role: input.role ?? "supporting",
|
|
33214
33215
|
description: redactSecrets(input.description),
|
|
33215
33216
|
source: input.source ? redactObject(input.source) : undefined,
|
|
33216
33217
|
sink: input.sink ? redactObject(input.sink) : undefined,
|
|
@@ -33236,6 +33237,18 @@ function verifyEvidence(evidence) {
|
|
|
33236
33237
|
}
|
|
33237
33238
|
return { ok: failed.length === 0, failed };
|
|
33238
33239
|
}
|
|
33240
|
+
function evidenceRoles(evidence) {
|
|
33241
|
+
const roles = { proof: 0, baseline: 0, verification: 0, supporting: 0 };
|
|
33242
|
+
for (const e of evidence)
|
|
33243
|
+
roles[e.role] = (roles[e.role] ?? 0) + 1;
|
|
33244
|
+
return {
|
|
33245
|
+
roles,
|
|
33246
|
+
proof: roles.proof > 0,
|
|
33247
|
+
baseline: roles.baseline > 0,
|
|
33248
|
+
verification: roles.verification > 0,
|
|
33249
|
+
reproducible: roles.proof > 0 && roles.baseline > 0 && roles.verification > 0
|
|
33250
|
+
};
|
|
33251
|
+
}
|
|
33239
33252
|
|
|
33240
33253
|
// src/invariants.ts
|
|
33241
33254
|
function checkFindingInvariants(finding) {
|
|
@@ -63949,6 +63962,50 @@ function goalStatus() {
|
|
|
63949
63962
|
return { goals: [...goals], all_met: goals.length > 0 && met === goals.length, met, total: goals.length };
|
|
63950
63963
|
}
|
|
63951
63964
|
|
|
63965
|
+
// src/facts.ts
|
|
63966
|
+
var facts = [];
|
|
63967
|
+
var counter3 = 0;
|
|
63968
|
+
function nextId3() {
|
|
63969
|
+
counter3 += 1;
|
|
63970
|
+
return `FACT-${String(counter3).padStart(3, "0")}`;
|
|
63971
|
+
}
|
|
63972
|
+
function recordFact(input) {
|
|
63973
|
+
const fact = {
|
|
63974
|
+
id: nextId3(),
|
|
63975
|
+
text: input.text,
|
|
63976
|
+
confidence: input.confidence,
|
|
63977
|
+
target: input.target,
|
|
63978
|
+
parent_ids: input.parent_ids ?? [],
|
|
63979
|
+
created: new Date().toISOString()
|
|
63980
|
+
};
|
|
63981
|
+
facts.push(fact);
|
|
63982
|
+
return fact;
|
|
63983
|
+
}
|
|
63984
|
+
function listFacts() {
|
|
63985
|
+
return [...facts];
|
|
63986
|
+
}
|
|
63987
|
+
function lineage(id) {
|
|
63988
|
+
const fact = facts.find((f) => f.id === id);
|
|
63989
|
+
if (!fact)
|
|
63990
|
+
return { error: `no fact '${id}'` };
|
|
63991
|
+
const ancestors = [];
|
|
63992
|
+
const seen = new Set([id]);
|
|
63993
|
+
const queue = [...fact.parent_ids];
|
|
63994
|
+
while (queue.length) {
|
|
63995
|
+
const pid = queue.shift();
|
|
63996
|
+
if (seen.has(pid))
|
|
63997
|
+
continue;
|
|
63998
|
+
seen.add(pid);
|
|
63999
|
+
const p = facts.find((f) => f.id === pid);
|
|
64000
|
+
if (p) {
|
|
64001
|
+
ancestors.push(p);
|
|
64002
|
+
queue.push(...p.parent_ids);
|
|
64003
|
+
}
|
|
64004
|
+
}
|
|
64005
|
+
const descendants = facts.filter((f) => f.parent_ids.includes(id));
|
|
64006
|
+
return { fact, ancestors, descendants };
|
|
64007
|
+
}
|
|
64008
|
+
|
|
63952
64009
|
// src/sibling-scan.ts
|
|
63953
64010
|
var SIBLING_SUFFIXES = [
|
|
63954
64011
|
"export",
|
|
@@ -65914,6 +65971,35 @@ ${statusLine}` : statusLine },
|
|
|
65914
65971
|
}, async () => {
|
|
65915
65972
|
return { content: [{ type: "text", text: JSON.stringify(goalStatus()) }] };
|
|
65916
65973
|
});
|
|
65974
|
+
server.registerTool("record_fact", {
|
|
65975
|
+
title: "Record an exploration observation (fact)",
|
|
65976
|
+
description: "FACTS: record an observation/conclusion that is NOT a vuln (finding_create) and NOT an asset (asset_discovery) — e.g. 'admin panel is behind SSO', 'upload strips .php', 'param id is reflected'. Pass text, confidence (observed = you triggered it; inferred = deduced from a fingerprint/diff — never report inferred as confirmed), optional target, and optional parent_ids (facts this derives from). Use this so negative/neutral observations are written down instead of lost.",
|
|
65977
|
+
inputSchema: {
|
|
65978
|
+
text: string2().describe("The observation/conclusion"),
|
|
65979
|
+
confidence: _enum(["observed", "inferred"]).describe("observed (directly triggered) or inferred (deduced, not yet triggered)"),
|
|
65980
|
+
target: string2().optional().describe("Optional host/endpoint/param the fact is about"),
|
|
65981
|
+
parent_ids: array(string2()).optional().describe("Optional ids of facts this one derives from")
|
|
65982
|
+
}
|
|
65983
|
+
}, async ({ text, confidence, target, parent_ids }) => {
|
|
65984
|
+
const fact = recordFact({ text, confidence, target, parent_ids });
|
|
65985
|
+
return { content: [{ type: "text", text: JSON.stringify(fact) }] };
|
|
65986
|
+
});
|
|
65987
|
+
server.registerTool("list_facts", {
|
|
65988
|
+
title: "List all recorded facts",
|
|
65989
|
+
description: "FACTS: return every recorded observation with its confidence + lineage parent_ids. Use before re-testing a direction — a solid observed fact (or a recorded dead-end) saves re-probing the same thing.",
|
|
65990
|
+
inputSchema: {}
|
|
65991
|
+
}, async () => {
|
|
65992
|
+
return { content: [{ type: "text", text: JSON.stringify(listFacts()) }] };
|
|
65993
|
+
});
|
|
65994
|
+
server.registerTool("lineage", {
|
|
65995
|
+
title: "Trace a fact's lineage (ancestors + descendants)",
|
|
65996
|
+
description: "FACTS: walk a fact's lineage — its ancestors (the facts it derived from, up the parent chain) and descendants (facts derived from it). Use to understand where a conclusion came from and what it enabled.",
|
|
65997
|
+
inputSchema: {
|
|
65998
|
+
id: string2().describe("Fact id (FACT-XXX) to trace")
|
|
65999
|
+
}
|
|
66000
|
+
}, async ({ id }) => {
|
|
66001
|
+
return { content: [{ type: "text", text: JSON.stringify(lineage(id)) }] };
|
|
66002
|
+
});
|
|
65917
66003
|
server.registerTool("hunting_doctrine", {
|
|
65918
66004
|
title: "High-ROI hunting doctrine (sibling rule, A→B, follow-the-money)",
|
|
65919
66005
|
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.",
|
|
@@ -66685,23 +66771,45 @@ ${statusLine}` : statusLine },
|
|
|
66685
66771
|
});
|
|
66686
66772
|
server.registerTool("finding_attach_evidence", {
|
|
66687
66773
|
title: "Attach evidence to a finding",
|
|
66688
|
-
description: "FINDINGS: attach a redacted, SHA-256-tagged evidence record to an existing canonical finding (without advancing its lifecycle).",
|
|
66774
|
+
description: "FINDINGS: attach a redacted, SHA-256-tagged evidence record to an existing canonical finding (without advancing its lifecycle). Pass role (proof = the request/response that triggered it; baseline = the normal request showing the differential; verification = the negative control/marker; supporting = anything else) so the finding's evidence stays structured. A reproducible PoC needs proof + baseline + verification.",
|
|
66689
66775
|
inputSchema: {
|
|
66690
66776
|
finding: string2().describe("JSON of the Finding object"),
|
|
66691
66777
|
type: string2().describe("Evidence type (source_location/response/validation_result/negative_control/...)"),
|
|
66692
66778
|
description: string2().describe("Evidence description"),
|
|
66779
|
+
role: _enum(["proof", "baseline", "verification", "supporting"]).optional().describe("Evidence role (default supporting)"),
|
|
66693
66780
|
content: string2().optional().describe("Evidence artifact content")
|
|
66694
66781
|
}
|
|
66695
|
-
}, async ({ finding, type, description, content }) => {
|
|
66782
|
+
}, async ({ finding, type, description, role, content }) => {
|
|
66696
66783
|
let f;
|
|
66697
66784
|
try {
|
|
66698
66785
|
f = JSON.parse(finding);
|
|
66699
66786
|
} catch {
|
|
66700
66787
|
return { content: [{ type: "text", text: JSON.stringify({ error: "invalid finding JSON" }) }] };
|
|
66701
66788
|
}
|
|
66702
|
-
const ev = makeEvidence({ type, description, artifacts: content ? [{ name: "artifact", kind: "evidence", content }] : [] });
|
|
66789
|
+
const ev = makeEvidence({ type, role, description, artifacts: content ? [{ name: "artifact", kind: "evidence", content }] : [] });
|
|
66703
66790
|
const updated = attachEvidence(f, ev);
|
|
66704
|
-
return { content: [{ type: "text", text: JSON.stringify(updated) }] };
|
|
66791
|
+
return { content: [{ type: "text", text: JSON.stringify({ ...updated, roles: evidenceRoles(updated.evidence) }) }] };
|
|
66792
|
+
});
|
|
66793
|
+
server.registerTool("finding_evidence_roles", {
|
|
66794
|
+
title: "Summarize a finding's evidence by role",
|
|
66795
|
+
description: "FINDINGS: given a finding, return its evidence broken down by role (proof/baseline/verification/supporting) + a `reproducible` flag (true only when it has proof + baseline + verification). Check this before submitting a report: a finding that is not `reproducible` is missing the trigger/differential/negative-control evidence a HackerOne-grade PoC needs.",
|
|
66796
|
+
inputSchema: {
|
|
66797
|
+
finding: string2().describe("JSON of the Finding object")
|
|
66798
|
+
}
|
|
66799
|
+
}, async ({ finding }) => {
|
|
66800
|
+
let f;
|
|
66801
|
+
try {
|
|
66802
|
+
f = JSON.parse(finding);
|
|
66803
|
+
} catch {
|
|
66804
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "invalid finding JSON" }) }] };
|
|
66805
|
+
}
|
|
66806
|
+
const roles = evidenceRoles(f.evidence ?? []);
|
|
66807
|
+
const missing = [
|
|
66808
|
+
!roles.proof ? "proof (trigger request/response)" : null,
|
|
66809
|
+
!roles.baseline ? "baseline (normal request)" : null,
|
|
66810
|
+
!roles.verification ? "verification (negative control/marker)" : null
|
|
66811
|
+
].filter(Boolean);
|
|
66812
|
+
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" }) }] };
|
|
66705
66813
|
});
|
|
66706
66814
|
server.registerTool("redact", {
|
|
66707
66815
|
title: "Redact secrets from text",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.175",
|
|
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": {
|