blitzstrike 1.0.49 → 1.0.51
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 +27 -0
- package/dist/index.js +130 -17
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,33 @@ 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.51] — 2026-09-13
|
|
9
|
+
|
|
10
|
+
### Added — Autonomous Security Engine (phase 11: adversarial reviewer)
|
|
11
|
+
|
|
12
|
+
- **Adversarial Reviewer** (`src/mission/reviewer.ts`, §15):
|
|
13
|
+
- `reviewFinding()` — challenges a hypothesis with the ten §15 questions
|
|
14
|
+
(reachability, auth required, authorization elsewhere, middleware, attacker
|
|
15
|
+
control, sink reachability, caching, reproducibility, negative control,
|
|
16
|
+
impact accuracy), each a deterministic check against evidence + Security Graph.
|
|
17
|
+
- Returns CONFIRMED / REJECTED / NEEDS_MORE_EVIDENCE.
|
|
18
|
+
- `reviewValidated()` — reviews every validated hypothesis in a mission.
|
|
19
|
+
- `@reviewer` now uses the full §15 review (rejects on any hard fail; demands
|
|
20
|
+
more evidence when supporting signals are too weak).
|
|
21
|
+
|
|
22
|
+
## [1.0.50] — 2026-09-13
|
|
23
|
+
|
|
24
|
+
### Added — Autonomous Security Engine (phase 10: cross-agent memory)
|
|
25
|
+
|
|
26
|
+
- **Cross-agent Memory** (`src/mission/memory.ts`, §20):
|
|
27
|
+
- `persistMissionMemory()` — persists confirmed findings (verified),
|
|
28
|
+
rejected findings, and failed approaches (with `future_policy`) into the
|
|
29
|
+
long-term memory store; pending hypotheses are NOT persisted (unverified).
|
|
30
|
+
- `recallForTarget()` — retrieves memory relevant to a target, to seed a
|
|
31
|
+
fresh mission.
|
|
32
|
+
- Mission Control's `finalize` now persists reusable knowledge and records the
|
|
33
|
+
saved/deduped count in `memory_refs`.
|
|
34
|
+
|
|
8
35
|
## [1.0.49] — 2026-09-13
|
|
9
36
|
|
|
10
37
|
### Added — Autonomous Security Engine (phase 9: failure recovery)
|
package/dist/index.js
CHANGED
|
@@ -18258,6 +18258,92 @@ var init_planner = __esm(() => {
|
|
|
18258
18258
|
RISK_PENALTY = { low: 0, medium: 0.1, high: 0.2 };
|
|
18259
18259
|
});
|
|
18260
18260
|
|
|
18261
|
+
// src/mission/reviewer.ts
|
|
18262
|
+
function checkAll(h, mission) {
|
|
18263
|
+
const ev = h.evidence.toLowerCase();
|
|
18264
|
+
const sinkNode = mission.graph.nodes.find((n) => n.properties && n.properties.hypothesis === h.id);
|
|
18265
|
+
const reachable = sinkNode ? incoming(mission.graph, sinkNode.id).length > 0 : false;
|
|
18266
|
+
return [
|
|
18267
|
+
{
|
|
18268
|
+
id: "reachable_code",
|
|
18269
|
+
question: "Is the vulnerable code actually reachable?",
|
|
18270
|
+
verdict: reachable ? "pass" : "unknown",
|
|
18271
|
+
note: reachable ? "sink has an incoming flow edge" : "no flow edge in the graph"
|
|
18272
|
+
},
|
|
18273
|
+
{
|
|
18274
|
+
id: "auth_required",
|
|
18275
|
+
question: "Is authentication required?",
|
|
18276
|
+
verdict: /auth-gated|auth_required|login required/i.test(ev) ? "pass" : "unknown",
|
|
18277
|
+
note: /auth-gated|auth_required|login required/i.test(ev) ? "auth gate detected" : "no auth gate signal"
|
|
18278
|
+
},
|
|
18279
|
+
{
|
|
18280
|
+
id: "authz_elsewhere",
|
|
18281
|
+
question: "Is authorization enforced elsewhere?",
|
|
18282
|
+
verdict: /missing_authorization/i.test(h.type) ? "fail" : "unknown",
|
|
18283
|
+
note: /missing_authorization/i.test(h.type) ? "no authorization boundary detected" : "not an authorization finding"
|
|
18284
|
+
},
|
|
18285
|
+
{
|
|
18286
|
+
id: "middleware",
|
|
18287
|
+
question: "Is there middleware protection?",
|
|
18288
|
+
verdict: /sanitized|middleware|waf|firewall/i.test(ev) ? "pass" : "unknown",
|
|
18289
|
+
note: /sanitized|middleware|waf|firewall/i.test(ev) ? "sanitizer/middleware present" : "no protection signal"
|
|
18290
|
+
},
|
|
18291
|
+
{
|
|
18292
|
+
id: "attacker_controlled",
|
|
18293
|
+
question: "Is the input actually attacker-controlled?",
|
|
18294
|
+
verdict: /attacker-controlled|user input|\$_(get|post|request|cookie)/i.test(ev) ? "pass" : "unknown",
|
|
18295
|
+
note: /attacker-controlled|user input|\$_(get|post|request|cookie)/i.test(ev) ? "input is attacker-controlled" : "input provenance unclear"
|
|
18296
|
+
},
|
|
18297
|
+
{
|
|
18298
|
+
id: "sink_reachable",
|
|
18299
|
+
question: "Is the sink reachable?",
|
|
18300
|
+
verdict: sinkNode ? "pass" : "unknown",
|
|
18301
|
+
note: sinkNode ? "sink node present in graph" : "sink not mapped in graph"
|
|
18302
|
+
},
|
|
18303
|
+
{
|
|
18304
|
+
id: "caching",
|
|
18305
|
+
question: "Is the observed behavior caused by caching?",
|
|
18306
|
+
verdict: /cache|etag|304/i.test(ev) ? "fail" : "unknown",
|
|
18307
|
+
note: /cache|etag|304/i.test(ev) ? "possible cache artifact" : "no cache signal"
|
|
18308
|
+
},
|
|
18309
|
+
{
|
|
18310
|
+
id: "reproducible",
|
|
18311
|
+
question: "Is the exploit reproducible?",
|
|
18312
|
+
verdict: /verdict=confirmed|reproducible|marker/i.test(ev) ? "pass" : "unknown",
|
|
18313
|
+
note: /verdict=confirmed|reproducible|marker/i.test(ev) ? "live validation performed" : "no live validation"
|
|
18314
|
+
},
|
|
18315
|
+
{
|
|
18316
|
+
id: "negative_control",
|
|
18317
|
+
question: "Does the negative control remain inert?",
|
|
18318
|
+
verdict: /control (not|did not|inert)|control=false|verdict=confirmed/i.test(ev) ? "pass" : /control.*reflected|benign echo|false_positive/i.test(ev) ? "fail" : "unknown",
|
|
18319
|
+
note: /benign echo|false_positive/i.test(ev) ? "control also reflected" : /verdict=confirmed/i.test(ev) ? "control inert" : "no negative control"
|
|
18320
|
+
},
|
|
18321
|
+
{
|
|
18322
|
+
id: "impact_accurate",
|
|
18323
|
+
question: "Is the impact accurately classified?",
|
|
18324
|
+
verdict: /cwe-\d+/i.test(h.potential_impact) || h.potential_impact !== "unspecified" ? "pass" : "unknown",
|
|
18325
|
+
note: h.potential_impact !== "unspecified" ? `impact: ${h.potential_impact}` : "impact unspecified"
|
|
18326
|
+
}
|
|
18327
|
+
];
|
|
18328
|
+
}
|
|
18329
|
+
function reviewFinding(h, mission) {
|
|
18330
|
+
const questions = checkAll(h, mission);
|
|
18331
|
+
const fails = questions.filter((q) => q.verdict === "fail").length;
|
|
18332
|
+
const passes = questions.filter((q) => q.verdict === "pass").length;
|
|
18333
|
+
let outcome;
|
|
18334
|
+
if (fails > 0)
|
|
18335
|
+
outcome = "REJECTED";
|
|
18336
|
+
else if (passes < 3)
|
|
18337
|
+
outcome = "NEEDS_MORE_EVIDENCE";
|
|
18338
|
+
else
|
|
18339
|
+
outcome = "CONFIRMED";
|
|
18340
|
+
return { hypothesis_id: h.id, outcome, questions };
|
|
18341
|
+
}
|
|
18342
|
+
function reviewValidated(mission) {
|
|
18343
|
+
return mission.hypotheses.filter((h) => h.status === "validated").map((h) => reviewFinding(h, mission));
|
|
18344
|
+
}
|
|
18345
|
+
var init_reviewer = () => {};
|
|
18346
|
+
|
|
18261
18347
|
// src/agents.ts
|
|
18262
18348
|
import { readFileSync as readFileSync18 } from "node:fs";
|
|
18263
18349
|
function emptyGraph() {
|
|
@@ -18276,6 +18362,7 @@ function agentFor(type) {
|
|
|
18276
18362
|
var reconAgent, webAgent, sourceAgent, analysisAgent, authAgent, validatorAgent, reviewerAgent, reporterAgent, mobileAgent, cloudAgent, commanderAgent, apiAgent, AGENTS;
|
|
18277
18363
|
var init_agents = __esm(() => {
|
|
18278
18364
|
init_planner();
|
|
18365
|
+
init_reviewer();
|
|
18279
18366
|
init_orchestrator();
|
|
18280
18367
|
init_scanner();
|
|
18281
18368
|
init_eagle2();
|
|
@@ -18455,25 +18542,18 @@ var init_agents = __esm(() => {
|
|
|
18455
18542
|
responsibility: "Challenge findings before confirmation (reachability, auth, controls).",
|
|
18456
18543
|
tools: ["read_skill", "eagle_eye2"],
|
|
18457
18544
|
async execute(_task, ctx) {
|
|
18458
|
-
const
|
|
18545
|
+
const results = reviewValidated(ctx.mission);
|
|
18459
18546
|
const hypotheses = [];
|
|
18460
|
-
for (const
|
|
18461
|
-
if (
|
|
18547
|
+
for (const r of results) {
|
|
18548
|
+
if (r.outcome !== "REJECTED")
|
|
18462
18549
|
continue;
|
|
18463
|
-
const
|
|
18464
|
-
|
|
18465
|
-
|
|
18466
|
-
|
|
18467
|
-
|
|
18468
|
-
|
|
18469
|
-
|
|
18470
|
-
else if (!/reachability|verification|proof|negative control/i.test(h.required_validation))
|
|
18471
|
-
outcome = "NEEDS_MORE_EVIDENCE";
|
|
18472
|
-
outcomes.push({ hypothesis: h.id, outcome });
|
|
18473
|
-
if (outcome === "REJECTED")
|
|
18474
|
-
hypotheses.push(rejectHypothesis(h, "adversarial review: confidence below threshold"));
|
|
18475
|
-
}
|
|
18476
|
-
return { ok: true, output: { outcomes }, hypotheses, graph: emptyGraph() };
|
|
18550
|
+
const h = ctx.mission.hypotheses.find((x) => x.id === r.hypothesis_id);
|
|
18551
|
+
if (h) {
|
|
18552
|
+
const failIds = r.questions.filter((q) => q.verdict === "fail").map((q) => q.id).join(", ");
|
|
18553
|
+
hypotheses.push(rejectHypothesis(h, `adversarial review rejected: ${failIds || "unexplainable"}`));
|
|
18554
|
+
}
|
|
18555
|
+
}
|
|
18556
|
+
return { ok: true, output: { outcomes: results.map((r) => ({ id: r.hypothesis_id, outcome: r.outcome })) }, hypotheses, graph: emptyGraph() };
|
|
18477
18557
|
}
|
|
18478
18558
|
};
|
|
18479
18559
|
reporterAgent = {
|
|
@@ -18716,6 +18796,34 @@ var init_recovery = __esm(() => {
|
|
|
18716
18796
|
};
|
|
18717
18797
|
});
|
|
18718
18798
|
|
|
18799
|
+
// src/mission/memory.ts
|
|
18800
|
+
function recordHypothesisOutcome(type, target, status, evidence) {
|
|
18801
|
+
return remember(`finding:${type}`, `[${status}] ${type} @ ${target}: ${evidence}`, status === "validated" ? "signature" : "lesson", [type.toLowerCase(), status], "mission", status === "validated").saved === true;
|
|
18802
|
+
}
|
|
18803
|
+
function recordFailedApproach(fa) {
|
|
18804
|
+
return remember(`failed:${fa.technique}`, `${fa.technique} failed (${fa.category}): ${fa.reason} — ${fa.future_policy}`, "lesson", ["failed", fa.category], "mission", false).saved === true;
|
|
18805
|
+
}
|
|
18806
|
+
function persistMissionMemory(mission) {
|
|
18807
|
+
let attempted = 0;
|
|
18808
|
+
let saved = 0;
|
|
18809
|
+
for (const h of mission.hypotheses) {
|
|
18810
|
+
if (h.status !== "validated" && h.status !== "rejected")
|
|
18811
|
+
continue;
|
|
18812
|
+
attempted += 1;
|
|
18813
|
+
if (recordHypothesisOutcome(h.type, h.target, h.status, h.evidence))
|
|
18814
|
+
saved += 1;
|
|
18815
|
+
}
|
|
18816
|
+
for (const fa of mission.failed_approaches) {
|
|
18817
|
+
attempted += 1;
|
|
18818
|
+
if (recordFailedApproach(fa))
|
|
18819
|
+
saved += 1;
|
|
18820
|
+
}
|
|
18821
|
+
return { saved, deduped: attempted - saved };
|
|
18822
|
+
}
|
|
18823
|
+
var init_memory2 = __esm(() => {
|
|
18824
|
+
init_memory();
|
|
18825
|
+
});
|
|
18826
|
+
|
|
18719
18827
|
// src/mission/control.ts
|
|
18720
18828
|
import { existsSync as existsSync8, statSync as statSync4 } from "node:fs";
|
|
18721
18829
|
function scopeAllowed(mission) {
|
|
@@ -18878,6 +18986,10 @@ function finalize2(mission) {
|
|
|
18878
18986
|
tasks: allResolved(m.tasks) ? 1 : 0,
|
|
18879
18987
|
hypotheses_validated: m.hypotheses.length > 0 ? m.hypotheses.filter((h) => h.status === "validated").length / m.hypotheses.length : 0
|
|
18880
18988
|
};
|
|
18989
|
+
const mem = persistMissionMemory(m);
|
|
18990
|
+
if (mem.saved > 0) {
|
|
18991
|
+
m = { ...m, memory_refs: [...m.memory_refs, `persisted:${mem.saved} (${mem.deduped} deduped)`] };
|
|
18992
|
+
}
|
|
18881
18993
|
return saveMission(m);
|
|
18882
18994
|
}
|
|
18883
18995
|
async function startMission(target, mode, opts = {}) {
|
|
@@ -18900,6 +19012,7 @@ var init_control = __esm(() => {
|
|
|
18900
19012
|
init_planner();
|
|
18901
19013
|
init_coverage();
|
|
18902
19014
|
init_recovery();
|
|
19015
|
+
init_memory2();
|
|
18903
19016
|
MODE_PROFILES = {
|
|
18904
19017
|
web: { agents: ["recon", "web", "api", "auth", "validator", "reviewer", "reporter"], verify: true, risk: "medium" },
|
|
18905
19018
|
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.51",
|
|
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": {
|