blitzstrike 1.0.86 → 1.0.87
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 +14 -0
- package/dist/index.js +105 -0
- package/package.json +1 -1
- package/skills/bs-orchestrate-engagement.md +6 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,20 @@ 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
|
+
## [1.0.87] — 2026-09-13
|
|
9
|
+
|
|
10
|
+
### Added — engagement state (deterministic orchestration bookkeeping)
|
|
11
|
+
|
|
12
|
+
- New `engagement.ts` module + 4 MCP tools: `engagement_start` (open an
|
|
13
|
+
engagement), `engagement_phase` (advance the lifecycle phase),
|
|
14
|
+
`engagement_track` (record each hypothesis as pending/confirmed/rejected),
|
|
15
|
+
`engagement_status` (deterministic "am I done?" — returns the exact unmet
|
|
16
|
+
termination criteria instead of a feeling).
|
|
17
|
+
- This is Blitz Strike's own orchestration-state answer: the LLM is the brain;
|
|
18
|
+
these tools are the hands that keep the phase + hypotheses + termination
|
|
19
|
+
state so the LLM never relies on its own memory for progress or completion.
|
|
20
|
+
- `bs-orchestrate-engagement` documents the track-state loop.
|
|
21
|
+
|
|
8
22
|
## [1.0.86] — 2026-09-13
|
|
9
23
|
|
|
10
24
|
### Added — orchestration framework (`orchestration`)
|
package/dist/index.js
CHANGED
|
@@ -31848,6 +31848,75 @@ async function activeScan(target, scope = "", mode = "bug-bounty", allowed = und
|
|
|
31848
31848
|
return result;
|
|
31849
31849
|
}
|
|
31850
31850
|
|
|
31851
|
+
// src/engagement.ts
|
|
31852
|
+
var PHASES = ["scope", "recon", "analyze", "deep", "verify", "report"];
|
|
31853
|
+
var current = null;
|
|
31854
|
+
function ts() {
|
|
31855
|
+
return new Date().toISOString();
|
|
31856
|
+
}
|
|
31857
|
+
function startEngagement(target, kind = "source") {
|
|
31858
|
+
current = {
|
|
31859
|
+
id: `eng-${Date.now().toString(36)}`,
|
|
31860
|
+
target,
|
|
31861
|
+
kind,
|
|
31862
|
+
phase: "scope",
|
|
31863
|
+
startedAt: ts(),
|
|
31864
|
+
hypotheses: []
|
|
31865
|
+
};
|
|
31866
|
+
return { started: true, ...current };
|
|
31867
|
+
}
|
|
31868
|
+
function setPhase(phase) {
|
|
31869
|
+
if (!current)
|
|
31870
|
+
return { found: false, error: "no active engagement — call engagement_start first" };
|
|
31871
|
+
const p = phase.toLowerCase().trim();
|
|
31872
|
+
if (!PHASES.includes(p)) {
|
|
31873
|
+
return { found: false, error: `unknown phase '${phase}'`, valid: PHASES };
|
|
31874
|
+
}
|
|
31875
|
+
current.phase = p;
|
|
31876
|
+
return { found: true, phase: current.phase };
|
|
31877
|
+
}
|
|
31878
|
+
function trackHypothesis(sink, status = "pending") {
|
|
31879
|
+
if (!current)
|
|
31880
|
+
return { found: false, error: "no active engagement — call engagement_start first" };
|
|
31881
|
+
const existing = current.hypotheses.find((h) => h.sink === sink);
|
|
31882
|
+
if (existing)
|
|
31883
|
+
existing.status = status;
|
|
31884
|
+
else
|
|
31885
|
+
current.hypotheses.push({ sink, status });
|
|
31886
|
+
return { found: true, tracked: current.hypotheses.length };
|
|
31887
|
+
}
|
|
31888
|
+
function engagementStatus() {
|
|
31889
|
+
if (!current)
|
|
31890
|
+
return { found: false, error: "no active engagement — call engagement_start first" };
|
|
31891
|
+
const pending = current.hypotheses.filter((h) => h.status === "pending").length;
|
|
31892
|
+
const confirmed = current.hypotheses.filter((h) => h.status === "confirmed").length;
|
|
31893
|
+
const rejected = current.hypotheses.filter((h) => h.status === "rejected").length;
|
|
31894
|
+
const unmet = [];
|
|
31895
|
+
if (current.phase !== "report") {
|
|
31896
|
+
unmet.push(`phase is '${current.phase}', not 'report' — the report has not been emitted`);
|
|
31897
|
+
}
|
|
31898
|
+
if (pending > 0) {
|
|
31899
|
+
unmet.push(`${pending} hypothesis(es) still pending — verify or reject each one`);
|
|
31900
|
+
}
|
|
31901
|
+
if (current.hypotheses.length === 0) {
|
|
31902
|
+
unmet.push("no hypotheses tracked — nothing was verified");
|
|
31903
|
+
}
|
|
31904
|
+
const idx = PHASES.indexOf(current.phase);
|
|
31905
|
+
const next = idx >= PHASES.length - 1 ? "done" : PHASES[idx + 1];
|
|
31906
|
+
return {
|
|
31907
|
+
found: true,
|
|
31908
|
+
id: current.id,
|
|
31909
|
+
target: current.target,
|
|
31910
|
+
kind: current.kind,
|
|
31911
|
+
phase: current.phase,
|
|
31912
|
+
startedAt: current.startedAt,
|
|
31913
|
+
hypotheses: current.hypotheses,
|
|
31914
|
+
counts: { pending, confirmed, rejected, total: current.hypotheses.length },
|
|
31915
|
+
termination: { done: unmet.length === 0, unmet },
|
|
31916
|
+
next_phase: next
|
|
31917
|
+
};
|
|
31918
|
+
}
|
|
31919
|
+
|
|
31851
31920
|
// src/dataflow.ts
|
|
31852
31921
|
init_scanner();
|
|
31853
31922
|
import { readFileSync as readFileSync9 } from "node:fs";
|
|
@@ -53986,6 +54055,42 @@ ${statusLine}` : statusLine },
|
|
|
53986
54055
|
}, async ({ part }) => {
|
|
53987
54056
|
return { content: [{ type: "text", text: JSON.stringify(orchestration(part)) }] };
|
|
53988
54057
|
});
|
|
54058
|
+
server.registerTool("engagement_start", {
|
|
54059
|
+
title: "Start an engagement",
|
|
54060
|
+
description: "ORCHESTRATION: open an engagement and track its state. Call at the start of a full audit so subsequent engagement_phase / engagement_track / engagement_status calls have a state to update. The LLM is the brain; this is the deterministic bookkeeping hand.",
|
|
54061
|
+
inputSchema: {
|
|
54062
|
+
target: string2().describe("The target (source path or live URL)."),
|
|
54063
|
+
kind: _enum(["source", "live"]).optional().describe("source path or live URL. Defaults to source.")
|
|
54064
|
+
}
|
|
54065
|
+
}, async ({ target, kind }) => {
|
|
54066
|
+
return { content: [{ type: "text", text: JSON.stringify(startEngagement(target, kind)) }] };
|
|
54067
|
+
});
|
|
54068
|
+
server.registerTool("engagement_phase", {
|
|
54069
|
+
title: "Advance the engagement phase",
|
|
54070
|
+
description: "ORCHESTRATION: move the engagement to the next lifecycle phase (scope, recon, analyze, deep, verify, report). The phase tracks where the audit is; engagement_status uses it for the termination check.",
|
|
54071
|
+
inputSchema: {
|
|
54072
|
+
phase: string2().describe("One of: scope, recon, analyze, deep, verify, report.")
|
|
54073
|
+
}
|
|
54074
|
+
}, async ({ phase }) => {
|
|
54075
|
+
return { content: [{ type: "text", text: JSON.stringify(setPhase(phase)) }] };
|
|
54076
|
+
});
|
|
54077
|
+
server.registerTool("engagement_track", {
|
|
54078
|
+
title: "Track a hypothesis",
|
|
54079
|
+
description: "ORCHESTRATION: record a hypothesis (a candidate finding keyed by its sink) with its status — pending, confirmed, or rejected. engagement_status derives the termination check from these; an untracked hypothesis is one the LLM has to remember, which is exactly what this avoids.",
|
|
54080
|
+
inputSchema: {
|
|
54081
|
+
sink: string2().describe("The sink / candidate identifier (e.g. 'POST /upload -> eval()')."),
|
|
54082
|
+
status: _enum(["pending", "confirmed", "rejected"]).optional().describe("Defaults to pending.")
|
|
54083
|
+
}
|
|
54084
|
+
}, async ({ sink, status }) => {
|
|
54085
|
+
return { content: [{ type: "text", text: JSON.stringify(trackHypothesis(sink, status)) }] };
|
|
54086
|
+
});
|
|
54087
|
+
server.registerTool("engagement_status", {
|
|
54088
|
+
title: "Engagement status + termination check",
|
|
54089
|
+
description: "ORCHESTRATION: return the current engagement state — phase, hypotheses, counts — and a deterministic termination check (done or the exact unmet criteria). Call this instead of asking yourself 'am I done?'. Also drives blitz_status-style progress.",
|
|
54090
|
+
inputSchema: {}
|
|
54091
|
+
}, async () => {
|
|
54092
|
+
return { content: [{ type: "text", text: JSON.stringify(engagementStatus()) }] };
|
|
54093
|
+
});
|
|
53989
54094
|
server.registerTool("read_payload", {
|
|
53990
54095
|
title: "Read a payload collection",
|
|
53991
54096
|
description: "INTEL: read the full payload collection for a category (or a specific file).",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.87",
|
|
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": {
|
|
@@ -84,6 +84,12 @@ reference — do not improvise the shape of the engagement:
|
|
|
84
84
|
the surface is wider than the scan, hypotheses remain unverified, or a
|
|
85
85
|
low-severity finding hasn't been checked for escalation (`chain_links`).
|
|
86
86
|
|
|
87
|
+
Track the state deterministically so you never rely on your own memory:
|
|
88
|
+
`engagement_start` (open the engagement) → `engagement_phase` (advance the
|
|
89
|
+
phase) → `engagement_track` (record each hypothesis with pending/confirmed/
|
|
90
|
+
rejected) → `engagement_status` (deterministic "am I done?" — returns the exact
|
|
91
|
+
unmet criteria, not a feeling).
|
|
92
|
+
|
|
87
93
|
## Orchestration pattern (native plan + sub-agents)
|
|
88
94
|
|
|
89
95
|
You are the orchestrator — use your platform's native planning and delegation
|