blitzstrike 1.0.47 → 1.0.48

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,24 @@ 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.48] — 2026-09-13
9
+
10
+ ### Added — Autonomous Security Engine (phase 8: coverage-driven scheduler)
11
+
12
+ - **Coverage-driven Scheduler** (`src/mission/coverage.ts`, §13):
13
+ - `computeCoverage()` — 10 dimensions (attack surface, endpoints, technologies,
14
+ source files, sink classes, auth/authorization boundaries, vulnerability
15
+ classes, attack vectors, escalation chains).
16
+ - `coverageGaps()` — dimensions below a threshold.
17
+ - `scheduleCoverageTasks()` — auto-generates tasks for under-covered
18
+ dimensions, skipping already-attempted agents and mode-inappropriate ones
19
+ (e.g. no `@web` for source audits; endpoints/technologies resolve locally).
20
+ - `gapAgent()` — mode-aware dimension→agent mapping.
21
+ - Mission Control's `replan` now runs the coverage scheduler and persists
22
+ per-dimension coverage into `mission.coverage.dimensions`.
23
+ - `@recon` now links discovered endpoints into the Security Graph (so coverage
24
+ reflects real endpoint exploration).
25
+
8
26
  ## [1.0.47] — 2026-09-13
9
27
 
10
28
  ### Added — Autonomous Security Engine (phase 7: adaptive planner)
package/dist/index.js CHANGED
@@ -18301,11 +18301,18 @@ var init_agents = __esm(() => {
18301
18301
  };
18302
18302
  }
18303
18303
  const r = enrichScan(ctx.mission.target, 500);
18304
+ let graph = { nodes: [], edges: [] };
18305
+ const tid = `target:${ctx.mission.target}`;
18306
+ for (const ep of r.hits.filter((h) => h.type === "endpoint").slice(0, 50)) {
18307
+ const epId = `endpoint:${ep.file}:${ep.line}`;
18308
+ graph = upsertNode(graph, epId, "endpoint", { file: ep.file, line: ep.line, hook: ep.detail });
18309
+ graph = addEdge2(graph, tid, epId, "exposes");
18310
+ }
18304
18311
  return {
18305
18312
  ok: true,
18306
18313
  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
18314
  hypotheses: [],
18308
- graph: emptyGraph()
18315
+ graph
18309
18316
  };
18310
18317
  }
18311
18318
  };
@@ -18582,6 +18589,71 @@ ${m.hypotheses.map((h) => `- [${h.status}] ${h.type} @ ${h.target} (confidence $
18582
18589
  };
18583
18590
  });
18584
18591
 
18592
+ // src/mission/coverage.ts
18593
+ function clamp012(n) {
18594
+ return Math.max(0, Math.min(1, n));
18595
+ }
18596
+ function computeCoverage(mission) {
18597
+ const byType = graphStats(mission.graph).by_type;
18598
+ const hyps = mission.hypotheses;
18599
+ const sinkCategories = new Set(mission.graph.nodes.filter((n) => n.type === "sink").map((n) => String(n.properties.category ?? n.properties.sink ?? "unknown")));
18600
+ const hypTypes = new Set(hyps.map((h) => h.type));
18601
+ return {
18602
+ attack_surface: clamp012(mission.graph.nodes.length / 25),
18603
+ endpoints: clamp012((byType.endpoint ?? 0) / 10),
18604
+ technologies: clamp012(mission.technologies.length / 6),
18605
+ source_files: clamp012((byType.sink ?? 0) / 15),
18606
+ sink_classes: clamp012(sinkCategories.size / 10),
18607
+ auth_boundaries: hyps.length ? clamp012(hyps.filter((h) => /auth/i.test(h.evidence)).length / hyps.length) : 0,
18608
+ authorization_boundaries: hyps.length ? clamp012(hyps.filter((h) => /missing_authorization/i.test(h.type)).length / hyps.length) : 0,
18609
+ vulnerability_classes: clamp012(hypTypes.size / 10),
18610
+ attack_vectors: clamp012(planNextActions(hyps, mission).length / 20),
18611
+ escalation_chains: clamp012((byType.attack_chain ?? 0) / 57)
18612
+ };
18613
+ }
18614
+ function coverageGaps(coverage, threshold = 0.5) {
18615
+ return Object.entries(coverage).filter(([, v]) => v < threshold).map(([k]) => k);
18616
+ }
18617
+ function gapAgent(dimension, isUrl) {
18618
+ if (dimension === "endpoints" && !isUrl)
18619
+ return "recon";
18620
+ if (dimension === "technologies" && !isUrl)
18621
+ return "source";
18622
+ return GAP_AGENT[dimension] ?? "analysis";
18623
+ }
18624
+ function scheduleCoverageTasks(mission, threshold = 0.5) {
18625
+ const coverage = computeCoverage(mission);
18626
+ const gaps = coverageGaps(coverage, threshold);
18627
+ const attempted = new Set(mission.tasks.map((t) => t.agent_type));
18628
+ const isUrl = /^https?:\/\//i.test(mission.target);
18629
+ const tasks = [];
18630
+ for (const gap of gaps) {
18631
+ const agent = gapAgent(gap, isUrl);
18632
+ if (attempted.has(agent))
18633
+ continue;
18634
+ if (!isUrl && agent === "web")
18635
+ continue;
18636
+ tasks.push(createTask({ objective: `close coverage gap: ${gap} (${coverage[gap].toFixed(2)})`, agent_type: agent, priority: 8 }));
18637
+ }
18638
+ return tasks;
18639
+ }
18640
+ var GAP_AGENT;
18641
+ var init_coverage = __esm(() => {
18642
+ init_planner();
18643
+ GAP_AGENT = {
18644
+ attack_surface: "recon",
18645
+ endpoints: "api",
18646
+ technologies: "web",
18647
+ source_files: "source",
18648
+ sink_classes: "source",
18649
+ auth_boundaries: "auth",
18650
+ authorization_boundaries: "auth",
18651
+ vulnerability_classes: "analysis",
18652
+ attack_vectors: "analysis",
18653
+ escalation_chains: "commander"
18654
+ };
18655
+ });
18656
+
18585
18657
  // src/mission/control.ts
18586
18658
  import { existsSync as existsSync8, statSync as statSync4 } from "node:fs";
18587
18659
  function scopeAllowed(mission) {
@@ -18634,7 +18706,12 @@ function replan(mission) {
18634
18706
  tasks = [...tasks, createTask({ objective: "generate the final report", agent_type: "reporter", risk: "low" })];
18635
18707
  }
18636
18708
  }
18637
- return { ...mission, tasks };
18709
+ const coverage = computeCoverage(mission);
18710
+ const coverageTasks = scheduleCoverageTasks(mission);
18711
+ if (coverageTasks.length > 0) {
18712
+ tasks = [...tasks, ...coverageTasks];
18713
+ }
18714
+ return { ...mission, tasks, coverage: { dimensions: { ...mission.coverage.dimensions, ...coverage } } };
18638
18715
  }
18639
18716
  async function executeTask(task, mission, scope) {
18640
18717
  const agent = agentFor(task.agent_type);
@@ -18738,6 +18815,7 @@ var init_control = __esm(() => {
18738
18815
  init_state();
18739
18816
  init_agents();
18740
18817
  init_planner();
18818
+ init_coverage();
18741
18819
  MODE_PROFILES = {
18742
18820
  web: { agents: ["recon", "web", "api", "auth", "validator", "reviewer", "reporter"], verify: true, risk: "medium" },
18743
18821
  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.47",
3
+ "version": "1.0.48",
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": {