blitzstrike 1.0.47 → 1.0.49
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 +33 -0
- package/dist/index.js +167 -5
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,39 @@ 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.49] — 2026-09-13
|
|
9
|
+
|
|
10
|
+
### Added — Autonomous Security Engine (phase 9: failure recovery)
|
|
11
|
+
|
|
12
|
+
- **Failure Recovery** (`src/mission/recovery.ts`, §17):
|
|
13
|
+
- `diagnoseError()` — categorizes failures (network / permission /
|
|
14
|
+
missing_tool / invalid_args / scope / unknown).
|
|
15
|
+
- `recoveryDecision()` — network → retry (bounded); missing_tool → route to an
|
|
16
|
+
alternative agent; scope / permission / invalid_args → record (no retry).
|
|
17
|
+
- `AGENT_ALTERNATIVES` — fallback specialization per agent type.
|
|
18
|
+
- `recordFailure()` — a failed-approach record with a `future_policy` so the
|
|
19
|
+
autonomous system never blindly repeats the same mistake.
|
|
20
|
+
- Mission Control now routes task failures through the recovery engine and
|
|
21
|
+
persists `failed_approaches` into the mission state.
|
|
22
|
+
|
|
23
|
+
## [1.0.48] — 2026-09-13
|
|
24
|
+
|
|
25
|
+
### Added — Autonomous Security Engine (phase 8: coverage-driven scheduler)
|
|
26
|
+
|
|
27
|
+
- **Coverage-driven Scheduler** (`src/mission/coverage.ts`, §13):
|
|
28
|
+
- `computeCoverage()` — 10 dimensions (attack surface, endpoints, technologies,
|
|
29
|
+
source files, sink classes, auth/authorization boundaries, vulnerability
|
|
30
|
+
classes, attack vectors, escalation chains).
|
|
31
|
+
- `coverageGaps()` — dimensions below a threshold.
|
|
32
|
+
- `scheduleCoverageTasks()` — auto-generates tasks for under-covered
|
|
33
|
+
dimensions, skipping already-attempted agents and mode-inappropriate ones
|
|
34
|
+
(e.g. no `@web` for source audits; endpoints/technologies resolve locally).
|
|
35
|
+
- `gapAgent()` — mode-aware dimension→agent mapping.
|
|
36
|
+
- Mission Control's `replan` now runs the coverage scheduler and persists
|
|
37
|
+
per-dimension coverage into `mission.coverage.dimensions`.
|
|
38
|
+
- `@recon` now links discovered endpoints into the Security Graph (so coverage
|
|
39
|
+
reflects real endpoint exploration).
|
|
40
|
+
|
|
8
41
|
## [1.0.47] — 2026-09-13
|
|
9
42
|
|
|
10
43
|
### Added — Autonomous Security Engine (phase 7: adaptive planner)
|
package/dist/index.js
CHANGED
|
@@ -17903,7 +17903,8 @@ function newMission(id, target, scope, mode, budget) {
|
|
|
17903
17903
|
coverage: { dimensions: {} },
|
|
17904
17904
|
memory_refs: [],
|
|
17905
17905
|
termination_reason: null,
|
|
17906
|
-
decision_log: []
|
|
17906
|
+
decision_log: [],
|
|
17907
|
+
failed_approaches: []
|
|
17907
17908
|
};
|
|
17908
17909
|
}
|
|
17909
17910
|
|
|
@@ -18301,11 +18302,18 @@ var init_agents = __esm(() => {
|
|
|
18301
18302
|
};
|
|
18302
18303
|
}
|
|
18303
18304
|
const r = enrichScan(ctx.mission.target, 500);
|
|
18305
|
+
let graph = { nodes: [], edges: [] };
|
|
18306
|
+
const tid = `target:${ctx.mission.target}`;
|
|
18307
|
+
for (const ep of r.hits.filter((h) => h.type === "endpoint").slice(0, 50)) {
|
|
18308
|
+
const epId = `endpoint:${ep.file}:${ep.line}`;
|
|
18309
|
+
graph = upsertNode(graph, epId, "endpoint", { file: ep.file, line: ep.line, hook: ep.detail });
|
|
18310
|
+
graph = addEdge2(graph, tid, epId, "exposes");
|
|
18311
|
+
}
|
|
18304
18312
|
return {
|
|
18305
18313
|
ok: true,
|
|
18306
18314
|
output: { files_scanned: r.files_scanned, hits: r.hits.length, sink_signals: r.sink_signals, endpoints: r.hits.filter((h) => h.type === "endpoint").length },
|
|
18307
18315
|
hypotheses: [],
|
|
18308
|
-
graph
|
|
18316
|
+
graph
|
|
18309
18317
|
};
|
|
18310
18318
|
}
|
|
18311
18319
|
};
|
|
@@ -18582,6 +18590,132 @@ ${m.hypotheses.map((h) => `- [${h.status}] ${h.type} @ ${h.target} (confidence $
|
|
|
18582
18590
|
};
|
|
18583
18591
|
});
|
|
18584
18592
|
|
|
18593
|
+
// src/mission/coverage.ts
|
|
18594
|
+
function clamp012(n) {
|
|
18595
|
+
return Math.max(0, Math.min(1, n));
|
|
18596
|
+
}
|
|
18597
|
+
function computeCoverage(mission) {
|
|
18598
|
+
const byType = graphStats(mission.graph).by_type;
|
|
18599
|
+
const hyps = mission.hypotheses;
|
|
18600
|
+
const sinkCategories = new Set(mission.graph.nodes.filter((n) => n.type === "sink").map((n) => String(n.properties.category ?? n.properties.sink ?? "unknown")));
|
|
18601
|
+
const hypTypes = new Set(hyps.map((h) => h.type));
|
|
18602
|
+
return {
|
|
18603
|
+
attack_surface: clamp012(mission.graph.nodes.length / 25),
|
|
18604
|
+
endpoints: clamp012((byType.endpoint ?? 0) / 10),
|
|
18605
|
+
technologies: clamp012(mission.technologies.length / 6),
|
|
18606
|
+
source_files: clamp012((byType.sink ?? 0) / 15),
|
|
18607
|
+
sink_classes: clamp012(sinkCategories.size / 10),
|
|
18608
|
+
auth_boundaries: hyps.length ? clamp012(hyps.filter((h) => /auth/i.test(h.evidence)).length / hyps.length) : 0,
|
|
18609
|
+
authorization_boundaries: hyps.length ? clamp012(hyps.filter((h) => /missing_authorization/i.test(h.type)).length / hyps.length) : 0,
|
|
18610
|
+
vulnerability_classes: clamp012(hypTypes.size / 10),
|
|
18611
|
+
attack_vectors: clamp012(planNextActions(hyps, mission).length / 20),
|
|
18612
|
+
escalation_chains: clamp012((byType.attack_chain ?? 0) / 57)
|
|
18613
|
+
};
|
|
18614
|
+
}
|
|
18615
|
+
function coverageGaps(coverage, threshold = 0.5) {
|
|
18616
|
+
return Object.entries(coverage).filter(([, v]) => v < threshold).map(([k]) => k);
|
|
18617
|
+
}
|
|
18618
|
+
function gapAgent(dimension, isUrl) {
|
|
18619
|
+
if (dimension === "endpoints" && !isUrl)
|
|
18620
|
+
return "recon";
|
|
18621
|
+
if (dimension === "technologies" && !isUrl)
|
|
18622
|
+
return "source";
|
|
18623
|
+
return GAP_AGENT[dimension] ?? "analysis";
|
|
18624
|
+
}
|
|
18625
|
+
function scheduleCoverageTasks(mission, threshold = 0.5) {
|
|
18626
|
+
const coverage = computeCoverage(mission);
|
|
18627
|
+
const gaps = coverageGaps(coverage, threshold);
|
|
18628
|
+
const attempted = new Set(mission.tasks.map((t) => t.agent_type));
|
|
18629
|
+
const isUrl = /^https?:\/\//i.test(mission.target);
|
|
18630
|
+
const tasks = [];
|
|
18631
|
+
for (const gap of gaps) {
|
|
18632
|
+
const agent = gapAgent(gap, isUrl);
|
|
18633
|
+
if (attempted.has(agent))
|
|
18634
|
+
continue;
|
|
18635
|
+
if (!isUrl && agent === "web")
|
|
18636
|
+
continue;
|
|
18637
|
+
tasks.push(createTask({ objective: `close coverage gap: ${gap} (${coverage[gap].toFixed(2)})`, agent_type: agent, priority: 8 }));
|
|
18638
|
+
}
|
|
18639
|
+
return tasks;
|
|
18640
|
+
}
|
|
18641
|
+
var GAP_AGENT;
|
|
18642
|
+
var init_coverage = __esm(() => {
|
|
18643
|
+
init_planner();
|
|
18644
|
+
GAP_AGENT = {
|
|
18645
|
+
attack_surface: "recon",
|
|
18646
|
+
endpoints: "api",
|
|
18647
|
+
technologies: "web",
|
|
18648
|
+
source_files: "source",
|
|
18649
|
+
sink_classes: "source",
|
|
18650
|
+
auth_boundaries: "auth",
|
|
18651
|
+
authorization_boundaries: "auth",
|
|
18652
|
+
vulnerability_classes: "analysis",
|
|
18653
|
+
attack_vectors: "analysis",
|
|
18654
|
+
escalation_chains: "commander"
|
|
18655
|
+
};
|
|
18656
|
+
});
|
|
18657
|
+
|
|
18658
|
+
// src/mission/recovery.ts
|
|
18659
|
+
function diagnoseError(error) {
|
|
18660
|
+
const e = error.toLowerCase();
|
|
18661
|
+
if (/network|connect|timeout|econn|enotfound|unreachable|dns|resolve|refused|abort/i.test(e))
|
|
18662
|
+
return "network";
|
|
18663
|
+
if (/permission|eacces|denied|forbidden|401|403/i.test(e))
|
|
18664
|
+
return "permission";
|
|
18665
|
+
if (/not (installed|found)|no such|command not found|missing tool/i.test(e))
|
|
18666
|
+
return "missing_tool";
|
|
18667
|
+
if (/invalid|argument|usage|required|schema|type/i.test(e))
|
|
18668
|
+
return "invalid_args";
|
|
18669
|
+
if (/scope|authorized|out of scope|unauthorized/i.test(e))
|
|
18670
|
+
return "scope";
|
|
18671
|
+
return "unknown";
|
|
18672
|
+
}
|
|
18673
|
+
function recoveryDecision(task, error) {
|
|
18674
|
+
const category = diagnoseError(error);
|
|
18675
|
+
switch (category) {
|
|
18676
|
+
case "network":
|
|
18677
|
+
return task.attempts < 3 ? { action: "retry", reason: "transient network failure — retry", alternative_agent: null } : { action: "record", reason: "persistent network failure", alternative_agent: null };
|
|
18678
|
+
case "missing_tool":
|
|
18679
|
+
return { action: "alternative", reason: "tool unavailable — try alternative", alternative_agent: AGENT_ALTERNATIVES[task.agent_type]?.[0] ?? null };
|
|
18680
|
+
case "invalid_args":
|
|
18681
|
+
return { action: "record", reason: "invalid arguments — cannot auto-recover", alternative_agent: null };
|
|
18682
|
+
case "scope":
|
|
18683
|
+
return { action: "record", reason: "out of scope — do not retry", alternative_agent: null };
|
|
18684
|
+
case "permission":
|
|
18685
|
+
return { action: "record", reason: "permission denied — cannot auto-recover", alternative_agent: null };
|
|
18686
|
+
default:
|
|
18687
|
+
return task.attempts < 2 ? { action: "retry", reason: "unknown failure — retry once", alternative_agent: null } : { action: "record", reason: "unknown failure — give up", alternative_agent: null };
|
|
18688
|
+
}
|
|
18689
|
+
}
|
|
18690
|
+
function recordFailure(task, error) {
|
|
18691
|
+
const category = diagnoseError(error);
|
|
18692
|
+
return {
|
|
18693
|
+
technique: `${task.agent_type}:${task.objective}`,
|
|
18694
|
+
target: task.objective,
|
|
18695
|
+
category,
|
|
18696
|
+
reason: error,
|
|
18697
|
+
attempts: task.attempts,
|
|
18698
|
+
future_policy: category === "scope" ? "do not retry — out of scope" : category === "missing_tool" ? "route to an alternative agent next time" : category === "network" ? "retry with backoff" : "lower priority for this technique"
|
|
18699
|
+
};
|
|
18700
|
+
}
|
|
18701
|
+
var AGENT_ALTERNATIVES;
|
|
18702
|
+
var init_recovery = __esm(() => {
|
|
18703
|
+
AGENT_ALTERNATIVES = {
|
|
18704
|
+
commander: [],
|
|
18705
|
+
recon: ["api", "web"],
|
|
18706
|
+
web: ["api", "recon"],
|
|
18707
|
+
api: ["recon", "web"],
|
|
18708
|
+
source: ["analysis"],
|
|
18709
|
+
auth: ["analysis", "source"],
|
|
18710
|
+
mobile: ["source"],
|
|
18711
|
+
cloud: ["source"],
|
|
18712
|
+
analysis: ["source"],
|
|
18713
|
+
validator: ["reviewer"],
|
|
18714
|
+
reviewer: ["validator"],
|
|
18715
|
+
reporter: []
|
|
18716
|
+
};
|
|
18717
|
+
});
|
|
18718
|
+
|
|
18585
18719
|
// src/mission/control.ts
|
|
18586
18720
|
import { existsSync as existsSync8, statSync as statSync4 } from "node:fs";
|
|
18587
18721
|
function scopeAllowed(mission) {
|
|
@@ -18634,7 +18768,12 @@ function replan(mission) {
|
|
|
18634
18768
|
tasks = [...tasks, createTask({ objective: "generate the final report", agent_type: "reporter", risk: "low" })];
|
|
18635
18769
|
}
|
|
18636
18770
|
}
|
|
18637
|
-
|
|
18771
|
+
const coverage = computeCoverage(mission);
|
|
18772
|
+
const coverageTasks = scheduleCoverageTasks(mission);
|
|
18773
|
+
if (coverageTasks.length > 0) {
|
|
18774
|
+
tasks = [...tasks, ...coverageTasks];
|
|
18775
|
+
}
|
|
18776
|
+
return { ...mission, tasks, coverage: { dimensions: { ...mission.coverage.dimensions, ...coverage } } };
|
|
18638
18777
|
}
|
|
18639
18778
|
async function executeTask(task, mission, scope) {
|
|
18640
18779
|
const agent = agentFor(task.agent_type);
|
|
@@ -18697,8 +18836,29 @@ async function runMission(mission) {
|
|
|
18697
18836
|
m = { ...m, report: r.output };
|
|
18698
18837
|
}
|
|
18699
18838
|
} else {
|
|
18700
|
-
const
|
|
18701
|
-
|
|
18839
|
+
const rec = recoveryDecision(r.task, r.error ?? "unknown error");
|
|
18840
|
+
const stamp = { at: new Date().toISOString(), agent: r.task.agent_type, decision };
|
|
18841
|
+
if (rec.action === "retry") {
|
|
18842
|
+
const retried = setTask(m.tasks, r.task.task_id, { status: "retry", error: r.error ?? null });
|
|
18843
|
+
m = { ...m, tasks: retried, decision_log: [...m.decision_log, stamp] };
|
|
18844
|
+
} else if (rec.action === "alternative" && rec.alternative_agent) {
|
|
18845
|
+
const failed = setTask(m.tasks, r.task.task_id, { status: "failed", error: r.error ?? null });
|
|
18846
|
+
const alt = createTask({ objective: `[alternative] ${r.task.objective}`, agent_type: rec.alternative_agent, risk: r.task.risk, priority: r.task.priority - 1 });
|
|
18847
|
+
m = {
|
|
18848
|
+
...m,
|
|
18849
|
+
tasks: [...failed, alt],
|
|
18850
|
+
decision_log: [...m.decision_log, stamp],
|
|
18851
|
+
failed_approaches: [...m.failed_approaches, recordFailure(r.task, r.error ?? "")]
|
|
18852
|
+
};
|
|
18853
|
+
} else {
|
|
18854
|
+
const failed = setTask(m.tasks, r.task.task_id, { status: "failed", error: r.error ?? null });
|
|
18855
|
+
m = {
|
|
18856
|
+
...m,
|
|
18857
|
+
tasks: failed,
|
|
18858
|
+
decision_log: [...m.decision_log, stamp],
|
|
18859
|
+
failed_approaches: [...m.failed_approaches, recordFailure(r.task, r.error ?? "")]
|
|
18860
|
+
};
|
|
18861
|
+
}
|
|
18702
18862
|
}
|
|
18703
18863
|
}
|
|
18704
18864
|
m.budget.used_tasks += batch.length;
|
|
@@ -18738,6 +18898,8 @@ var init_control = __esm(() => {
|
|
|
18738
18898
|
init_state();
|
|
18739
18899
|
init_agents();
|
|
18740
18900
|
init_planner();
|
|
18901
|
+
init_coverage();
|
|
18902
|
+
init_recovery();
|
|
18741
18903
|
MODE_PROFILES = {
|
|
18742
18904
|
web: { agents: ["recon", "web", "api", "auth", "validator", "reviewer", "reporter"], verify: true, risk: "medium" },
|
|
18743
18905
|
api: { agents: ["recon", "api", "auth", "validator", "reviewer", "reporter"], verify: true, risk: "medium" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.49",
|
|
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": {
|