blitzstrike 1.0.40 → 1.0.43
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 +102 -0
- package/dist/index.js +436 -51
- package/package.json +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,59 @@ 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.43] — 2026-09-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Phase 8 — Production Hardening**:
|
|
13
|
+
- **Caching** (`src/cache.ts`, §46) — versioned result cache (`sha256(source) + engine_version`);
|
|
14
|
+
wired into `analyzeDataFlow2` so unchanged files skip re-parsing.
|
|
15
|
+
- **SARIF export** (`src/sarif.ts`, §32) — `toSarif()` emits a SARIF 2.1.0 document
|
|
16
|
+
(GitHub Code Scanning / GitLab / Azure DevOps).
|
|
17
|
+
- **Release validation** (`src/release.ts`, §49) — `validateRelease()` checks
|
|
18
|
+
package.json vs CHANGELOG (hard) + git tag / npm registry / lockfile (soft).
|
|
19
|
+
- **Dependency security** (`src/dependency.ts`, §50) — `checkDependencies()`
|
|
20
|
+
inspects install-time scripts and git/unusual dependency sources.
|
|
21
|
+
- **Incremental analysis** (`src/incremental.ts`, §47) — `analyzeChangedFiles()`
|
|
22
|
+
re-analyzes only the files a commit touched (git diff → changed files → engine).
|
|
23
|
+
- **CI/CD** (`.github/workflows/ci.yml`, §48) — typecheck, tests, build, npm pack,
|
|
24
|
+
release-consistency, and dependency-surface gates.
|
|
25
|
+
- `blitzstrike doctor` now reports release-metadata and dependency-surface health.
|
|
26
|
+
|
|
27
|
+
## [1.0.42] — 2026-09-13
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
- **Phase 7 — MCP Orchestration** (`src/orchestration.ts`, wired into `run_engagement`):
|
|
32
|
+
- A data-driven **state machine** (10 states: scope_check → plan → recon →
|
|
33
|
+
analyze → hypothesis → select_chain → validate → evidence → finding → report).
|
|
34
|
+
- A deterministic **planner** (`buildPlan`) that emits an ordered plan with
|
|
35
|
+
per-step tool, risk level, evidence requirements, and done/pending/blocked status.
|
|
36
|
+
- A **risk policy** (`actionPermitted`) that gates high-risk actions
|
|
37
|
+
(strike_verify, confirm_finding) behind scope.
|
|
38
|
+
- **§34 agent guidance** (`computeGuidance`) — every result now exposes
|
|
39
|
+
current_state / confidence / blocking_reason / required_evidence /
|
|
40
|
+
recommended_next_action / recommended_tool.
|
|
41
|
+
- `run_engagement` output now includes `state`, `plan`, `guidance`, and
|
|
42
|
+
`risk_policy` (no new MCP tools added — the surface was deepened, not widened).
|
|
43
|
+
|
|
44
|
+
## [1.0.41] — 2026-09-13
|
|
45
|
+
|
|
46
|
+
### Added
|
|
47
|
+
|
|
48
|
+
- **Phase 6 — Browser Validation** (`src/browser.ts` + MCP tool `browser_validate`):
|
|
49
|
+
- Playwright-based validation of static-analysis hypotheses (not a scanner —
|
|
50
|
+
gated, "use only when necessary").
|
|
51
|
+
- Checks: `dom_xss` (marker-payload execution), `open_redirect` (unexpected
|
|
52
|
+
redirect), `auth_bypass` (missing auth enforcement), `csrf` (tokenless
|
|
53
|
+
state-changing form).
|
|
54
|
+
- Each check returns structured evidence (verdict + final_url + dom/console/
|
|
55
|
+
page-error signals) suitable for attaching to a finding.
|
|
56
|
+
- `playwright-core` is an **optional peer dependency**; `browser_validate`
|
|
57
|
+
degrades to a clear `unavailable` result when it is not installed.
|
|
58
|
+
- Hermetic browser test suite (`bun run test:browser`, 8 checks) against a
|
|
59
|
+
local vulnerable server.
|
|
60
|
+
|
|
8
61
|
## [1.0.40] — 2026-09-13
|
|
9
62
|
|
|
10
63
|
### Changed (review + spec alignment)
|
|
@@ -116,6 +169,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
116
169
|
now use `\b` word boundaries, so `outer_exec(...)` is no longer misclassified
|
|
117
170
|
as command execution.
|
|
118
171
|
|
|
172
|
+
## [1.0.34] — 2026-09-13
|
|
173
|
+
|
|
174
|
+
### Fixed (Phase 1 review)
|
|
175
|
+
|
|
176
|
+
- `hasSecrets` rewritten as `redactSecrets(text) !== text` — deterministic
|
|
177
|
+
(fixed the `g`-flag `.test()` state bug) and no false positive on already-redacted
|
|
178
|
+
content (`password=[REDACTED]` is no longer flagged).
|
|
179
|
+
- Wired `finding_updated` audit event into `transition()` / `rejectFinding()`.
|
|
180
|
+
|
|
119
181
|
## [1.0.33] — 2026-09-13
|
|
120
182
|
|
|
121
183
|
### Added
|
|
@@ -142,6 +204,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
142
204
|
- Shared `src/version.ts` — single source of truth for the package version
|
|
143
205
|
(removed the duplicate VERSION readers in `server.ts`).
|
|
144
206
|
|
|
207
|
+
## [1.0.32] — 2026-09-13
|
|
208
|
+
|
|
209
|
+
### Added
|
|
210
|
+
|
|
211
|
+
- Self-update: `blitzstrike update` CLI command + `check_update` MCP tool
|
|
212
|
+
(`src/update.ts` with semver-aware version comparison).
|
|
213
|
+
|
|
214
|
+
## [1.0.31] — 2026-09-13
|
|
215
|
+
|
|
216
|
+
### Changed
|
|
217
|
+
|
|
218
|
+
- npm publish pipeline debug (version bump only, no functional change).
|
|
219
|
+
|
|
220
|
+
## [1.0.30] — 2026-09-13
|
|
221
|
+
|
|
222
|
+
### Changed
|
|
223
|
+
|
|
224
|
+
- Removed source attribution from the attack-vector taxonomy data (first-party
|
|
225
|
+
content; `_comment` and notices stripped).
|
|
226
|
+
|
|
227
|
+
## [1.0.29] — 2026-09-13
|
|
228
|
+
|
|
229
|
+
### Added
|
|
230
|
+
|
|
231
|
+
- Attack-vector taxonomy (`intelligence/attack_vectors.json`, 34 categories /
|
|
232
|
+
588 vectors) + `list_attack_vectors` + `attack_vectors` MCP tools.
|
|
233
|
+
|
|
234
|
+
## [1.0.28] — 2026-09-13
|
|
235
|
+
|
|
236
|
+
### Fixed
|
|
237
|
+
|
|
238
|
+
- Benchmark corpus writes to the OS temp directory (hermetic; no writes inside
|
|
239
|
+
the read-only package).
|
|
240
|
+
|
|
241
|
+
## [1.0.27] — 2026-09-13
|
|
242
|
+
|
|
243
|
+
### Added
|
|
244
|
+
|
|
245
|
+
- Documentation milestone: CHANGELOG / ROADMAP / README + docs metrics.
|
|
246
|
+
|
|
145
247
|
## [1.0.26] — 2026-09-13
|
|
146
248
|
|
|
147
249
|
The first hardening milestone: the whole roadmap is implemented, measured, and
|
package/dist/index.js
CHANGED
|
@@ -30238,6 +30238,87 @@ function rejectFinding(finding, status, reason, evidence) {
|
|
|
30238
30238
|
};
|
|
30239
30239
|
}
|
|
30240
30240
|
|
|
30241
|
+
// src/orchestration.ts
|
|
30242
|
+
var STATE_MACHINE = [
|
|
30243
|
+
{ state: "scope_check", description: "Validate target against scope + risk policy.", recommended_tools: ["scope_check"], required_evidence: ["scope_decision"], next: ["plan"], confidence: "high" },
|
|
30244
|
+
{ state: "plan", description: "Derive the engagement plan from surface + scope.", recommended_tools: ["list_chains"], required_evidence: ["plan_steps"], next: ["recon"], confidence: "high" },
|
|
30245
|
+
{ state: "recon", description: "Map the attack surface (endpoints, tech, WAF).", recommended_tools: ["blitz_scan", "active_scan", "fofa_search", "detect_waf"], required_evidence: ["attack_surface"], next: ["analyze"], confidence: "medium" },
|
|
30246
|
+
{ state: "analyze", description: "Trace source→sink flows (interprocedural taint).", recommended_tools: ["eagle_eye2", "blitz_scan", "blitz_file"], required_evidence: ["sink_hits", "taint_flow"], next: ["hypothesis"], confidence: "medium" },
|
|
30247
|
+
{ state: "hypothesis", description: "Form defensible hypotheses (static finding).", recommended_tools: ["blitz_file", "enrich_scan"], required_evidence: ["static_finding"], next: ["select_chain"], blocking: "live_validation_required", confidence: "high" },
|
|
30248
|
+
{ state: "select_chain", description: "Match the hypothesis to an escalation chain.", recommended_tools: ["list_chains", "read_playbook"], required_evidence: ["matched_chain"], next: ["validate"], confidence: "high" },
|
|
30249
|
+
{ state: "validate", description: "Verify live with baseline + marker + negative control.", recommended_tools: ["strike_verify", "browser_validate"], required_evidence: ["baseline", "marker_reflection", "negative_control"], next: ["evidence"], confidence: "medium" },
|
|
30250
|
+
{ state: "evidence", description: "Attach hashable, provenance-bound evidence.", recommended_tools: ["finding_attach_evidence"], required_evidence: ["evidence_artifact"], next: ["finding"], confidence: "high" },
|
|
30251
|
+
{ state: "finding", description: "Confirm the finding (invariants enforced).", recommended_tools: ["confirm_finding"], required_evidence: ["confirmed_finding"], next: ["report"], confidence: "high" },
|
|
30252
|
+
{ state: "report", description: "Produce a reproducible report.", recommended_tools: ["report", "security-report"], required_evidence: ["report"], next: [], confidence: "high" }
|
|
30253
|
+
];
|
|
30254
|
+
function stateDefinition(state) {
|
|
30255
|
+
return STATE_MACHINE.find((s) => s.state === state);
|
|
30256
|
+
}
|
|
30257
|
+
var RISK_POLICY = [
|
|
30258
|
+
{ action: "scope_check", risk: "low", requires_scope: false, destructive: false },
|
|
30259
|
+
{ action: "blitz_scan", risk: "low", requires_scope: false, destructive: false },
|
|
30260
|
+
{ action: "blitz_file", risk: "low", requires_scope: false, destructive: false },
|
|
30261
|
+
{ action: "eagle_eye2", risk: "low", requires_scope: false, destructive: false },
|
|
30262
|
+
{ action: "enrich_scan", risk: "low", requires_scope: false, destructive: false },
|
|
30263
|
+
{ action: "detect_waf", risk: "low", requires_scope: false, destructive: false },
|
|
30264
|
+
{ action: "fofa_search", risk: "low", requires_scope: false, destructive: false },
|
|
30265
|
+
{ action: "list_chains", risk: "low", requires_scope: false, destructive: false },
|
|
30266
|
+
{ action: "read_playbook", risk: "low", requires_scope: false, destructive: false },
|
|
30267
|
+
{ action: "active_scan", risk: "medium", requires_scope: true, destructive: false },
|
|
30268
|
+
{ action: "browser_validate", risk: "medium", requires_scope: true, destructive: false },
|
|
30269
|
+
{ action: "strike_verify", risk: "high", requires_scope: true, destructive: false },
|
|
30270
|
+
{ action: "confirm_finding", risk: "high", requires_scope: true, destructive: false }
|
|
30271
|
+
];
|
|
30272
|
+
function riskOf(action) {
|
|
30273
|
+
return RISK_POLICY.find((r) => r.action === action) ?? null;
|
|
30274
|
+
}
|
|
30275
|
+
function actionPermitted(action, scopeAllowed, mode) {
|
|
30276
|
+
const rule = riskOf(action);
|
|
30277
|
+
if (!rule)
|
|
30278
|
+
return true;
|
|
30279
|
+
if (mode === "ctf" || mode === "offensive" || mode === "reverse-engineering")
|
|
30280
|
+
return true;
|
|
30281
|
+
if (rule.requires_scope && !scopeAllowed)
|
|
30282
|
+
return false;
|
|
30283
|
+
return true;
|
|
30284
|
+
}
|
|
30285
|
+
function buildPlan(scopeAllowed, mode, executedState) {
|
|
30286
|
+
const order = ["scope_check", "plan", "recon", "analyze", "hypothesis", "select_chain", "validate", "evidence", "finding", "report"];
|
|
30287
|
+
const executedIdx = order.indexOf(executedState);
|
|
30288
|
+
return order.map((state, i) => {
|
|
30289
|
+
const def = stateDefinition(state);
|
|
30290
|
+
const tool = def.recommended_tools[0] ?? "";
|
|
30291
|
+
const rule = riskOf(tool);
|
|
30292
|
+
const permitted = actionPermitted(tool, scopeAllowed, mode);
|
|
30293
|
+
const status = i <= executedIdx ? "done" : permitted ? "pending" : "blocked";
|
|
30294
|
+
return {
|
|
30295
|
+
order: i + 1,
|
|
30296
|
+
state,
|
|
30297
|
+
action: def.description,
|
|
30298
|
+
tool,
|
|
30299
|
+
evidence_required: def.required_evidence,
|
|
30300
|
+
risk: rule?.risk ?? "low",
|
|
30301
|
+
permitted,
|
|
30302
|
+
status
|
|
30303
|
+
};
|
|
30304
|
+
});
|
|
30305
|
+
}
|
|
30306
|
+
function computeGuidance(state, scopeAllowed) {
|
|
30307
|
+
const def = stateDefinition(state);
|
|
30308
|
+
const next = def.next[0];
|
|
30309
|
+
const nextDef = next ? stateDefinition(next) : null;
|
|
30310
|
+
const tool = nextDef?.recommended_tools[0] ?? "";
|
|
30311
|
+
const permitted = actionPermitted(tool, scopeAllowed, "bug-bounty");
|
|
30312
|
+
return {
|
|
30313
|
+
state,
|
|
30314
|
+
confidence: def.confidence,
|
|
30315
|
+
blocking_reason: !scopeAllowed && nextDef && riskOf(tool)?.requires_scope ? "scope_required" : def.blocking ?? null,
|
|
30316
|
+
required_evidence: def.required_evidence,
|
|
30317
|
+
recommended_next_action: nextDef?.description ?? "complete",
|
|
30318
|
+
recommended_tool: permitted ? tool : ""
|
|
30319
|
+
};
|
|
30320
|
+
}
|
|
30321
|
+
|
|
30241
30322
|
// src/orchestrator.ts
|
|
30242
30323
|
var _chainsCache = null;
|
|
30243
30324
|
function loadChains() {
|
|
@@ -30451,6 +30532,17 @@ function runEngagement(target, scope = "", mode = "bug-bounty", maxFiles = 2000,
|
|
|
30451
30532
|
}));
|
|
30452
30533
|
report.status = "COMPLETE";
|
|
30453
30534
|
report.note = "All findings are HYPOTHESES until verified live with strike_verify. " + "Apply each chain's negative_control before reporting. Confidence reflects static evidence only.";
|
|
30535
|
+
const executedState = "select_chain";
|
|
30536
|
+
const scopeAllowed = report.scope?.allowed ?? false;
|
|
30537
|
+
const plan = buildPlan(scopeAllowed, mode, executedState);
|
|
30538
|
+
report.state = executedState;
|
|
30539
|
+
report.state_description = stateDefinition(executedState).description;
|
|
30540
|
+
report.plan = plan;
|
|
30541
|
+
report.guidance = computeGuidance(executedState, scopeAllowed);
|
|
30542
|
+
report.risk_policy = {
|
|
30543
|
+
note: "High-risk actions (strike_verify, confirm_finding) require scope + are never run server-side automatically.",
|
|
30544
|
+
gated: plan.filter((p) => !p.permitted).map((p) => p.tool)
|
|
30545
|
+
};
|
|
30454
30546
|
if (remember) {
|
|
30455
30547
|
let captured = 0;
|
|
30456
30548
|
for (const mc of blitz.matched_chains) {
|
|
@@ -31803,6 +31895,29 @@ function entryPoints(g) {
|
|
|
31803
31895
|
return entries;
|
|
31804
31896
|
}
|
|
31805
31897
|
|
|
31898
|
+
// src/cache.ts
|
|
31899
|
+
import { createHash as createHash3 } from "node:crypto";
|
|
31900
|
+
var mem = new Map;
|
|
31901
|
+
var hits = 0;
|
|
31902
|
+
var misses = 0;
|
|
31903
|
+
function cacheKey(kind, source) {
|
|
31904
|
+
const h = createHash3("sha256").update(source).digest("hex").slice(0, 20);
|
|
31905
|
+
return `${kind}:${VERSION}:${h}`;
|
|
31906
|
+
}
|
|
31907
|
+
function getCache(kind, source) {
|
|
31908
|
+
const k = cacheKey(kind, source);
|
|
31909
|
+
if (mem.has(k)) {
|
|
31910
|
+
hits += 1;
|
|
31911
|
+
return mem.get(k);
|
|
31912
|
+
}
|
|
31913
|
+
misses += 1;
|
|
31914
|
+
return;
|
|
31915
|
+
}
|
|
31916
|
+
function setCache(kind, source, value) {
|
|
31917
|
+
mem.set(cacheKey(kind, source), value);
|
|
31918
|
+
return value;
|
|
31919
|
+
}
|
|
31920
|
+
|
|
31806
31921
|
// src/eagle2.ts
|
|
31807
31922
|
var parser3 = new import_php_parser3.Engine({
|
|
31808
31923
|
parser: { extractDoc: false, php7: true },
|
|
@@ -32230,6 +32345,15 @@ function resolveFuncId(name, summaries) {
|
|
|
32230
32345
|
return null;
|
|
32231
32346
|
}
|
|
32232
32347
|
function analyzeDataFlow2(path) {
|
|
32348
|
+
let cacheCode = "";
|
|
32349
|
+
try {
|
|
32350
|
+
cacheCode = readFileSync12(path, "utf8");
|
|
32351
|
+
} catch {}
|
|
32352
|
+
if (cacheCode) {
|
|
32353
|
+
const cached = getCache("eagle2", cacheCode);
|
|
32354
|
+
if (cached)
|
|
32355
|
+
return cached;
|
|
32356
|
+
}
|
|
32233
32357
|
const cg = buildCallGraph(path);
|
|
32234
32358
|
const entries = entryPoints(cg);
|
|
32235
32359
|
const reachable = new Set;
|
|
@@ -32245,7 +32369,7 @@ function analyzeDataFlow2(path) {
|
|
|
32245
32369
|
};
|
|
32246
32370
|
let code;
|
|
32247
32371
|
try {
|
|
32248
|
-
code = readFileSync12(path, "utf8");
|
|
32372
|
+
code = cacheCode || readFileSync12(path, "utf8");
|
|
32249
32373
|
} catch {
|
|
32250
32374
|
return result;
|
|
32251
32375
|
}
|
|
@@ -32354,6 +32478,8 @@ function analyzeDataFlow2(path) {
|
|
|
32354
32478
|
queue.push({ funcId: calleeId, param: calleeParam, source: item.source, sourceLine: item.sourceLine, viaReturn: false });
|
|
32355
32479
|
}
|
|
32356
32480
|
}
|
|
32481
|
+
if (cacheCode)
|
|
32482
|
+
setCache("eagle2", cacheCode, result);
|
|
32357
32483
|
return result;
|
|
32358
32484
|
}
|
|
32359
32485
|
|
|
@@ -51040,6 +51166,162 @@ function scanFramework(code, frameworkId) {
|
|
|
51040
51166
|
};
|
|
51041
51167
|
}
|
|
51042
51168
|
|
|
51169
|
+
// src/browser.ts
|
|
51170
|
+
var chromiumCache = null;
|
|
51171
|
+
async function ensureChromium() {
|
|
51172
|
+
if (chromiumCache)
|
|
51173
|
+
return chromiumCache;
|
|
51174
|
+
try {
|
|
51175
|
+
chromiumCache = await import("playwright-core");
|
|
51176
|
+
return chromiumCache;
|
|
51177
|
+
} catch {
|
|
51178
|
+
return null;
|
|
51179
|
+
}
|
|
51180
|
+
}
|
|
51181
|
+
function unavailable(check, target, reason) {
|
|
51182
|
+
return {
|
|
51183
|
+
check,
|
|
51184
|
+
target: redactSecrets(target),
|
|
51185
|
+
verdict: "unavailable",
|
|
51186
|
+
detail: reason,
|
|
51187
|
+
evidence: { console_errors: [], page_errors: [] }
|
|
51188
|
+
};
|
|
51189
|
+
}
|
|
51190
|
+
function errorEvidence(check, target, e) {
|
|
51191
|
+
return {
|
|
51192
|
+
check,
|
|
51193
|
+
target: redactSecrets(target),
|
|
51194
|
+
verdict: "error",
|
|
51195
|
+
detail: e instanceof Error ? e.message : String(e),
|
|
51196
|
+
evidence: { console_errors: [], page_errors: [] }
|
|
51197
|
+
};
|
|
51198
|
+
}
|
|
51199
|
+
async function validateDomXss(target, payload = '<img src=x onerror="window.__blitz_xss__=true">') {
|
|
51200
|
+
const chr = await ensureChromium();
|
|
51201
|
+
if (!chr)
|
|
51202
|
+
return unavailable("dom_xss", target, "playwright-core not installed (optional peer dependency)");
|
|
51203
|
+
const browser = await chr.chromium.launch({ headless: true });
|
|
51204
|
+
try {
|
|
51205
|
+
const page = await browser.newPage();
|
|
51206
|
+
const consoleErrors = [];
|
|
51207
|
+
const pageErrors = [];
|
|
51208
|
+
page.on("console", (m) => {
|
|
51209
|
+
if (m.type() === "error")
|
|
51210
|
+
consoleErrors.push(m.text());
|
|
51211
|
+
});
|
|
51212
|
+
page.on("pageerror", (e) => pageErrors.push(String(e)));
|
|
51213
|
+
const url = target.includes("{{payload}}") ? target.replace("{{payload}}", encodeURIComponent(payload)) : target;
|
|
51214
|
+
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 15000 });
|
|
51215
|
+
await page.waitForTimeout(600);
|
|
51216
|
+
const marker = await page.evaluate(() => window.__blitz_xss__ === true);
|
|
51217
|
+
return {
|
|
51218
|
+
check: "dom_xss",
|
|
51219
|
+
target: redactSecrets(target),
|
|
51220
|
+
verdict: marker ? "confirmed" : "not_confirmed",
|
|
51221
|
+
detail: marker ? "payload executed (marker set)" : "payload did not execute (no marker)",
|
|
51222
|
+
evidence: { final_url: page.url(), dom_marker: marker, console_errors: consoleErrors, page_errors: pageErrors }
|
|
51223
|
+
};
|
|
51224
|
+
} catch (e) {
|
|
51225
|
+
return errorEvidence("dom_xss", target, e);
|
|
51226
|
+
} finally {
|
|
51227
|
+
await browser.close();
|
|
51228
|
+
}
|
|
51229
|
+
}
|
|
51230
|
+
async function validateOpenRedirect(target, payload) {
|
|
51231
|
+
const chr = await ensureChromium();
|
|
51232
|
+
if (!chr)
|
|
51233
|
+
return unavailable("open_redirect", target, "playwright-core not installed (optional peer dependency)");
|
|
51234
|
+
const browser = await chr.chromium.launch({ headless: true });
|
|
51235
|
+
try {
|
|
51236
|
+
const page = await browser.newPage();
|
|
51237
|
+
const url = target.includes("{{payload}}") ? target.replace("{{payload}}", encodeURIComponent(payload)) : target;
|
|
51238
|
+
await page.goto(url, { waitUntil: "domcontentloaded", timeout: 15000 });
|
|
51239
|
+
await page.waitForTimeout(400);
|
|
51240
|
+
const finalUrl = page.url();
|
|
51241
|
+
const redirected = finalUrl !== url && finalUrl.includes(payload);
|
|
51242
|
+
return {
|
|
51243
|
+
check: "open_redirect",
|
|
51244
|
+
target: redactSecrets(target),
|
|
51245
|
+
verdict: redirected ? "confirmed" : "not_confirmed",
|
|
51246
|
+
detail: redirected ? `browser redirected to ${finalUrl}` : `no redirect to attacker URL (final: ${finalUrl})`,
|
|
51247
|
+
evidence: { final_url: finalUrl, console_errors: [], page_errors: [] }
|
|
51248
|
+
};
|
|
51249
|
+
} catch (e) {
|
|
51250
|
+
return errorEvidence("open_redirect", target, e);
|
|
51251
|
+
} finally {
|
|
51252
|
+
await browser.close();
|
|
51253
|
+
}
|
|
51254
|
+
}
|
|
51255
|
+
async function validateAuthBypass(target) {
|
|
51256
|
+
const chr = await ensureChromium();
|
|
51257
|
+
if (!chr)
|
|
51258
|
+
return unavailable("auth_bypass", target, "playwright-core not installed (optional peer dependency)");
|
|
51259
|
+
const browser = await chr.chromium.launch({ headless: true });
|
|
51260
|
+
try {
|
|
51261
|
+
const page = await browser.newPage();
|
|
51262
|
+
const resp = await page.goto(target, { waitUntil: "domcontentloaded", timeout: 15000 });
|
|
51263
|
+
const status = resp?.status() ?? 0;
|
|
51264
|
+
const finalUrl = page.url();
|
|
51265
|
+
const enforced = status === 401 || status === 403 || /login|signin|auth/.test(finalUrl);
|
|
51266
|
+
return {
|
|
51267
|
+
check: "auth_bypass",
|
|
51268
|
+
target: redactSecrets(target),
|
|
51269
|
+
verdict: enforced ? "not_confirmed" : "confirmed",
|
|
51270
|
+
detail: enforced ? `auth enforced (status ${status}, final ${finalUrl})` : `no auth enforcement observed (status ${status}, final ${finalUrl})`,
|
|
51271
|
+
evidence: { final_url: finalUrl, status, console_errors: [], page_errors: [] }
|
|
51272
|
+
};
|
|
51273
|
+
} catch (e) {
|
|
51274
|
+
return errorEvidence("auth_bypass", target, e);
|
|
51275
|
+
} finally {
|
|
51276
|
+
await browser.close();
|
|
51277
|
+
}
|
|
51278
|
+
}
|
|
51279
|
+
async function validateCsrf(target) {
|
|
51280
|
+
const chr = await ensureChromium();
|
|
51281
|
+
if (!chr)
|
|
51282
|
+
return unavailable("csrf", target, "playwright-core not installed (optional peer dependency)");
|
|
51283
|
+
const browser = await chr.chromium.launch({ headless: true });
|
|
51284
|
+
try {
|
|
51285
|
+
const page = await browser.newPage();
|
|
51286
|
+
await page.goto(target, { waitUntil: "domcontentloaded", timeout: 15000 });
|
|
51287
|
+
const result = await page.evaluate(() => {
|
|
51288
|
+
const forms = Array.from(document.querySelectorAll("form"));
|
|
51289
|
+
const stateChanging = forms.filter((f) => /post/i.test(f.method) || f.method === "");
|
|
51290
|
+
if (stateChanging.length === 0)
|
|
51291
|
+
return { token: true, count: 0 };
|
|
51292
|
+
for (const f of stateChanging) {
|
|
51293
|
+
const token = f.querySelector('input[name*="csrf"], input[name*="token"], input[name*="_token"], input[name*="nonce"]');
|
|
51294
|
+
if (!token)
|
|
51295
|
+
return { token: false, count: stateChanging.length };
|
|
51296
|
+
}
|
|
51297
|
+
return { token: true, count: stateChanging.length };
|
|
51298
|
+
});
|
|
51299
|
+
return {
|
|
51300
|
+
check: "csrf",
|
|
51301
|
+
target: redactSecrets(target),
|
|
51302
|
+
verdict: result.token ? "not_confirmed" : "confirmed",
|
|
51303
|
+
detail: result.token ? `CSRF token present (${result.count} form(s))` : `state-changing form missing CSRF token (${result.count} form(s))`,
|
|
51304
|
+
evidence: { csrf_token_present: result.token, forms_count: result.count, console_errors: [], page_errors: [] }
|
|
51305
|
+
};
|
|
51306
|
+
} catch (e) {
|
|
51307
|
+
return errorEvidence("csrf", target, e);
|
|
51308
|
+
} finally {
|
|
51309
|
+
await browser.close();
|
|
51310
|
+
}
|
|
51311
|
+
}
|
|
51312
|
+
async function browserValidate(check, target, payload) {
|
|
51313
|
+
switch (check) {
|
|
51314
|
+
case "dom_xss":
|
|
51315
|
+
return validateDomXss(target, payload);
|
|
51316
|
+
case "open_redirect":
|
|
51317
|
+
return validateOpenRedirect(target, payload ?? "https://attacker.example/");
|
|
51318
|
+
case "auth_bypass":
|
|
51319
|
+
return validateAuthBypass(target);
|
|
51320
|
+
case "csrf":
|
|
51321
|
+
return validateCsrf(target);
|
|
51322
|
+
}
|
|
51323
|
+
}
|
|
51324
|
+
|
|
51043
51325
|
// src/guidance.ts
|
|
51044
51326
|
var SINK_DIRECTIVES = {
|
|
51045
51327
|
sql_execution: "Validate the SQL injection live with strike_verify (use a unique MARKER and a NEGATIVE CONTROL that stays inert), or read_tool_manual for sqlmap. Only a reflected marker + inert control = confirmed.",
|
|
@@ -52843,6 +53125,18 @@ function createServer() {
|
|
|
52843
53125
|
out.scan = scanFramework(code, framework);
|
|
52844
53126
|
return { content: [{ type: "text", text: JSON.stringify(out) }] };
|
|
52845
53127
|
});
|
|
53128
|
+
server.registerTool("browser_validate", {
|
|
53129
|
+
title: "Browser validation (Playwright)",
|
|
53130
|
+
description: "PHASE 6: validate a static-analysis HYPOTHESIS by driving a real headless Chromium. Checks: dom_xss, open_redirect, auth_bypass, csrf. Use only when necessary — to confirm a hypothesis with browser evidence, not as a first-pass scan. Requires playwright-core (optional peer dependency).",
|
|
53131
|
+
inputSchema: {
|
|
53132
|
+
check: _enum(["dom_xss", "open_redirect", "auth_bypass", "csrf"]).describe("Validation check to run"),
|
|
53133
|
+
target: string2().describe("Target URL (use {{payload}} placeholder for dom_xss/open_redirect injection)"),
|
|
53134
|
+
payload: string2().optional().describe("Payload to inject (defaults to an XSS marker for dom_xss)")
|
|
53135
|
+
}
|
|
53136
|
+
}, async ({ check, target, payload }) => {
|
|
53137
|
+
const result = await browserValidate(check, target, payload);
|
|
53138
|
+
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
53139
|
+
});
|
|
52846
53140
|
server.registerTool("taint_tree", {
|
|
52847
53141
|
title: "Whole-tree taint scan",
|
|
52848
53142
|
description: "EAGLE-EYE: run inter-procedural taint analysis across an entire source tree.",
|
|
@@ -52996,12 +53290,89 @@ async function serve() {
|
|
|
52996
53290
|
}
|
|
52997
53291
|
|
|
52998
53292
|
// src/cli.ts
|
|
52999
|
-
import { readFileSync as
|
|
53000
|
-
import { join as
|
|
53293
|
+
import { readFileSync as readFileSync20, existsSync as existsSync8, writeFileSync as writeFileSync5, mkdirSync as mkdirSync4 } from "node:fs";
|
|
53294
|
+
import { join as join15, dirname as dirname2 } from "node:path";
|
|
53001
53295
|
import { homedir as homedir5 } from "node:os";
|
|
53002
|
-
import { fileURLToPath as
|
|
53296
|
+
import { fileURLToPath as fileURLToPath11 } from "node:url";
|
|
53003
53297
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
53298
|
+
|
|
53299
|
+
// src/release.ts
|
|
53300
|
+
import { readFileSync as readFileSync18, existsSync as existsSync7 } from "node:fs";
|
|
53301
|
+
import { join as join13 } from "node:path";
|
|
53302
|
+
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
53303
|
+
import { execSync as execSync2 } from "node:child_process";
|
|
53004
53304
|
var ROOT7 = join13(fileURLToPath9(new URL(".", import.meta.url)), "..");
|
|
53305
|
+
function validateRelease() {
|
|
53306
|
+
const checks = [];
|
|
53307
|
+
checks.push({ name: "package.json version", ok: true, detail: VERSION });
|
|
53308
|
+
try {
|
|
53309
|
+
const changelog = readFileSync18(join13(ROOT7, "CHANGELOG.md"), "utf8");
|
|
53310
|
+
const m = changelog.match(/^##\s+\[([0-9]+\.[0-9]+\.[0-9]+)\]/m);
|
|
53311
|
+
const changelogVersion = m?.[1] ?? null;
|
|
53312
|
+
checks.push({
|
|
53313
|
+
name: "CHANGELOG version",
|
|
53314
|
+
ok: changelogVersion === VERSION,
|
|
53315
|
+
detail: changelogVersion ?? "no version header found"
|
|
53316
|
+
});
|
|
53317
|
+
} catch {
|
|
53318
|
+
checks.push({ name: "CHANGELOG version", ok: false, detail: "CHANGELOG.md not found" });
|
|
53319
|
+
}
|
|
53320
|
+
try {
|
|
53321
|
+
const tag = execSync2("git describe --tags --abbrev=0", { cwd: ROOT7, encoding: "utf8" }).trim();
|
|
53322
|
+
checks.push({ name: "Git tag", ok: tag === `v${VERSION}`, detail: tag, soft: true });
|
|
53323
|
+
} catch {
|
|
53324
|
+
checks.push({ name: "Git tag", ok: true, detail: "unavailable (no git)", soft: true });
|
|
53325
|
+
}
|
|
53326
|
+
try {
|
|
53327
|
+
const published = execSync2("npm view blitzstrike version", { encoding: "utf8", timeout: 8000 }).trim();
|
|
53328
|
+
checks.push({ name: "npm registry version", ok: published === VERSION, detail: published, soft: true });
|
|
53329
|
+
} catch {
|
|
53330
|
+
checks.push({ name: "npm registry version", ok: true, detail: "unavailable (offline)", soft: true });
|
|
53331
|
+
}
|
|
53332
|
+
checks.push({
|
|
53333
|
+
name: "Lockfile",
|
|
53334
|
+
ok: lockfilePresent(),
|
|
53335
|
+
detail: lockfilePresent() ? "present (bun.lock / package-lock.json)" : "missing",
|
|
53336
|
+
soft: true
|
|
53337
|
+
});
|
|
53338
|
+
return { version: VERSION, checks, consistent: checks.filter((c) => !c.soft).every((c) => c.ok) };
|
|
53339
|
+
}
|
|
53340
|
+
function lockfilePresent() {
|
|
53341
|
+
return existsSync7(join13(ROOT7, "package-lock.json")) || existsSync7(join13(ROOT7, "bun.lock")) || existsSync7(join13(ROOT7, "bun.lockb"));
|
|
53342
|
+
}
|
|
53343
|
+
|
|
53344
|
+
// src/dependency.ts
|
|
53345
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
53346
|
+
import { join as join14 } from "node:path";
|
|
53347
|
+
import { fileURLToPath as fileURLToPath10 } from "node:url";
|
|
53348
|
+
var ROOT8 = join14(fileURLToPath10(new URL(".", import.meta.url)), "..");
|
|
53349
|
+
function checkDependencies() {
|
|
53350
|
+
let pkg = {};
|
|
53351
|
+
try {
|
|
53352
|
+
pkg = JSON.parse(readFileSync19(join14(ROOT8, "package.json"), "utf8"));
|
|
53353
|
+
} catch {
|
|
53354
|
+
return { install_scripts: [], git_dependencies: [], total_dependencies: 0, safe: false };
|
|
53355
|
+
}
|
|
53356
|
+
const installScriptNames = ["preinstall", "install", "postinstall", "prepare"];
|
|
53357
|
+
const installScripts = installScriptNames.filter((n) => pkg.scripts?.[n]).map((n) => ({ name: n, command: pkg.scripts[n] }));
|
|
53358
|
+
const allDeps = {
|
|
53359
|
+
...pkg.dependencies ?? {},
|
|
53360
|
+
...pkg.devDependencies ?? {},
|
|
53361
|
+
...pkg.peerDependencies ?? {},
|
|
53362
|
+
...pkg.optionalDependencies ?? {}
|
|
53363
|
+
};
|
|
53364
|
+
const gitDependencies = Object.entries(allDeps).filter(([, v]) => /^(git\+|git:|github:|gitlab:|bitbucket:|\w+:\/\/)/.test(v)).map(([name, v]) => `${name}@${v}`);
|
|
53365
|
+
const total = Object.keys(allDeps).length;
|
|
53366
|
+
return {
|
|
53367
|
+
install_scripts: installScripts,
|
|
53368
|
+
git_dependencies: gitDependencies,
|
|
53369
|
+
total_dependencies: total,
|
|
53370
|
+
safe: installScripts.length === 0 && gitDependencies.length === 0
|
|
53371
|
+
};
|
|
53372
|
+
}
|
|
53373
|
+
|
|
53374
|
+
// src/cli.ts
|
|
53375
|
+
var ROOT9 = join15(fileURLToPath11(new URL(".", import.meta.url)), "..");
|
|
53005
53376
|
function which(cmd) {
|
|
53006
53377
|
return spawnSync3(`command -v ${cmd}`, { shell: true }).status === 0;
|
|
53007
53378
|
}
|
|
@@ -53036,13 +53407,27 @@ function runDoctor() {
|
|
|
53036
53407
|
detail: fofa ? "set" : "FOFA_EMAIL/FOFA_KEY not set",
|
|
53037
53408
|
fix: fofa ? undefined : "export FOFA_EMAIL=... && export FOFA_KEY=... (enables fofa_search)"
|
|
53038
53409
|
});
|
|
53039
|
-
const dataOk =
|
|
53410
|
+
const dataOk = existsSync8(join15(ROOT9, "chains.json")) && existsSync8(join15(ROOT9, "tools-catalog.json"));
|
|
53040
53411
|
issues.push({
|
|
53041
53412
|
name: "Data layers (chains + tools-catalog)",
|
|
53042
53413
|
status: dataOk ? "ok" : "fail",
|
|
53043
53414
|
detail: dataOk ? "present" : "chains.json or tools-catalog.json missing",
|
|
53044
53415
|
fix: dataOk ? undefined : "re-install blitzstrike"
|
|
53045
53416
|
});
|
|
53417
|
+
const release = validateRelease();
|
|
53418
|
+
issues.push({
|
|
53419
|
+
name: "Release metadata",
|
|
53420
|
+
status: release.consistent ? "ok" : "fail",
|
|
53421
|
+
detail: release.consistent ? `v${release.version} consistent` : release.checks.filter((c) => !c.ok && !c.soft).map((c) => c.name).join(", ") || "drift",
|
|
53422
|
+
fix: release.consistent ? undefined : "align package.json version with the latest CHANGELOG entry"
|
|
53423
|
+
});
|
|
53424
|
+
const deps = checkDependencies();
|
|
53425
|
+
issues.push({
|
|
53426
|
+
name: "Dependency surface",
|
|
53427
|
+
status: deps.safe ? "ok" : "warn",
|
|
53428
|
+
detail: deps.safe ? `${deps.total_dependencies} deps, no install scripts` : `${deps.install_scripts.length} install script(s) + ${deps.git_dependencies.length} git dep(s)`,
|
|
53429
|
+
fix: deps.safe ? undefined : "remove install-time scripts and git dependencies"
|
|
53430
|
+
});
|
|
53046
53431
|
const symbol = { ok: "OK ", warn: "WARN", fail: "FAIL" };
|
|
53047
53432
|
for (const i of issues) {
|
|
53048
53433
|
console.log(`${symbol[i.status]} ${i.name} ${i.detail}`);
|
|
@@ -53064,21 +53449,21 @@ BlitzStrike — ${verdict}`);
|
|
|
53064
53449
|
function resolveCommand() {
|
|
53065
53450
|
if (which("blitzstrike"))
|
|
53066
53451
|
return { command: "blitzstrike", args: ["serve", "--mcp"] };
|
|
53067
|
-
const src =
|
|
53068
|
-
if (which("bun") &&
|
|
53452
|
+
const src = join15(ROOT9, "src", "index.ts");
|
|
53453
|
+
if (which("bun") && existsSync8(src))
|
|
53069
53454
|
return { command: "bun", args: ["run", src, "serve", "--mcp"] };
|
|
53070
53455
|
if (which("npx"))
|
|
53071
53456
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
53072
53457
|
if (which("bunx"))
|
|
53073
53458
|
return { command: "bunx", args: ["blitzstrike", "serve", "--mcp"] };
|
|
53074
|
-
const dist =
|
|
53075
|
-
if (
|
|
53459
|
+
const dist = join15(ROOT9, "dist", "index.js");
|
|
53460
|
+
if (existsSync8(dist))
|
|
53076
53461
|
return { command: "bun", args: [dist, "serve", "--mcp"] };
|
|
53077
53462
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
53078
53463
|
}
|
|
53079
53464
|
function readJson(p) {
|
|
53080
53465
|
try {
|
|
53081
|
-
return JSON.parse(
|
|
53466
|
+
return JSON.parse(readFileSync20(p, "utf8"));
|
|
53082
53467
|
} catch {
|
|
53083
53468
|
return null;
|
|
53084
53469
|
}
|
|
@@ -53086,7 +53471,7 @@ function readJson(p) {
|
|
|
53086
53471
|
function jsonMcpServersWrite(p) {
|
|
53087
53472
|
return (command, args) => {
|
|
53088
53473
|
const dir = dirname2(p);
|
|
53089
|
-
if (!
|
|
53474
|
+
if (!existsSync8(dir))
|
|
53090
53475
|
mkdirSync4(dir, { recursive: true });
|
|
53091
53476
|
const existing = readJson(p) ?? {};
|
|
53092
53477
|
existing.mcpServers = {
|
|
@@ -53099,7 +53484,7 @@ function jsonMcpServersWrite(p) {
|
|
|
53099
53484
|
function opencodeWrite(p) {
|
|
53100
53485
|
return (_command, _args) => {
|
|
53101
53486
|
const dir = dirname2(p);
|
|
53102
|
-
if (!
|
|
53487
|
+
if (!existsSync8(dir))
|
|
53103
53488
|
mkdirSync4(dir, { recursive: true });
|
|
53104
53489
|
const existing = readJson(p) ?? {};
|
|
53105
53490
|
existing.mcp = {
|
|
@@ -53111,26 +53496,26 @@ function opencodeWrite(p) {
|
|
|
53111
53496
|
};
|
|
53112
53497
|
}
|
|
53113
53498
|
function copyOpenCodeAgents() {
|
|
53114
|
-
const srcDir =
|
|
53115
|
-
const dstDir =
|
|
53116
|
-
if (!
|
|
53499
|
+
const srcDir = join15(ROOT9, "opencode-agents");
|
|
53500
|
+
const dstDir = join15(homedir5(), ".config", "opencode", "agents");
|
|
53501
|
+
if (!existsSync8(srcDir))
|
|
53117
53502
|
return;
|
|
53118
|
-
if (!
|
|
53503
|
+
if (!existsSync8(dstDir))
|
|
53119
53504
|
mkdirSync4(dstDir, { recursive: true });
|
|
53120
53505
|
for (const f of ["Blitz Strike.md", "Blitz.md", "Eagle Eye.md", "Strike.md"]) {
|
|
53121
|
-
const src =
|
|
53122
|
-
const dst =
|
|
53123
|
-
if (
|
|
53124
|
-
writeFileSync5(dst,
|
|
53506
|
+
const src = join15(srcDir, f);
|
|
53507
|
+
const dst = join15(dstDir, f);
|
|
53508
|
+
if (existsSync8(src)) {
|
|
53509
|
+
writeFileSync5(dst, readFileSync20(src, "utf8"));
|
|
53125
53510
|
}
|
|
53126
53511
|
}
|
|
53127
53512
|
}
|
|
53128
53513
|
function codexWrite(p) {
|
|
53129
53514
|
return (command, args) => {
|
|
53130
53515
|
const dir = dirname2(p);
|
|
53131
|
-
if (!
|
|
53516
|
+
if (!existsSync8(dir))
|
|
53132
53517
|
mkdirSync4(dir, { recursive: true });
|
|
53133
|
-
let existing =
|
|
53518
|
+
let existing = existsSync8(p) ? readFileSync20(p, "utf8") : "";
|
|
53134
53519
|
if (!existing.trimEnd().endsWith(`
|
|
53135
53520
|
`))
|
|
53136
53521
|
existing += `
|
|
@@ -53147,9 +53532,9 @@ args = ${JSON.stringify(args)}
|
|
|
53147
53532
|
function hermesWrite(p) {
|
|
53148
53533
|
return (command, args) => {
|
|
53149
53534
|
const dir = dirname2(p);
|
|
53150
|
-
if (!
|
|
53535
|
+
if (!existsSync8(dir))
|
|
53151
53536
|
mkdirSync4(dir, { recursive: true });
|
|
53152
|
-
let existing =
|
|
53537
|
+
let existing = existsSync8(p) ? readFileSync20(p, "utf8") : "";
|
|
53153
53538
|
existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
|
|
53154
53539
|
if (!existing.trimEnd().endsWith(`
|
|
53155
53540
|
`))
|
|
@@ -53176,74 +53561,74 @@ mcp_servers:
|
|
|
53176
53561
|
function detectAgents() {
|
|
53177
53562
|
const home = homedir5();
|
|
53178
53563
|
const agents = [];
|
|
53179
|
-
const claudeJson =
|
|
53564
|
+
const claudeJson = join15(home, ".claude.json");
|
|
53180
53565
|
agents.push({
|
|
53181
53566
|
name: "Claude Code",
|
|
53182
53567
|
detectPath: claudeJson,
|
|
53183
|
-
installed: () =>
|
|
53568
|
+
installed: () => existsSync8(claudeJson) || which("claude"),
|
|
53184
53569
|
write: jsonMcpServersWrite(claudeJson)
|
|
53185
53570
|
});
|
|
53186
|
-
const claudeDesktop =
|
|
53571
|
+
const claudeDesktop = join15(home, ".config", "Claude", "claude_desktop_config.json");
|
|
53187
53572
|
agents.push({
|
|
53188
53573
|
name: "Claude Desktop",
|
|
53189
53574
|
detectPath: claudeDesktop,
|
|
53190
|
-
installed: () =>
|
|
53575
|
+
installed: () => existsSync8(claudeDesktop),
|
|
53191
53576
|
write: jsonMcpServersWrite(claudeDesktop)
|
|
53192
53577
|
});
|
|
53193
|
-
const cursor =
|
|
53578
|
+
const cursor = join15(home, ".cursor", "mcp.json");
|
|
53194
53579
|
agents.push({
|
|
53195
53580
|
name: "Cursor",
|
|
53196
53581
|
detectPath: cursor,
|
|
53197
|
-
installed: () =>
|
|
53582
|
+
installed: () => existsSync8(join15(home, ".cursor")) || which("cursor"),
|
|
53198
53583
|
write: jsonMcpServersWrite(cursor)
|
|
53199
53584
|
});
|
|
53200
|
-
const opencode =
|
|
53585
|
+
const opencode = join15(home, ".config", "opencode", "opencode.jsonc");
|
|
53201
53586
|
agents.push({
|
|
53202
53587
|
name: "OpenCode",
|
|
53203
53588
|
detectPath: opencode,
|
|
53204
|
-
installed: () =>
|
|
53589
|
+
installed: () => existsSync8(join15(home, ".config", "opencode")) || which("opencode"),
|
|
53205
53590
|
write: opencodeWrite(opencode)
|
|
53206
53591
|
});
|
|
53207
|
-
const codex =
|
|
53592
|
+
const codex = join15(home, ".codex", "config.toml");
|
|
53208
53593
|
agents.push({
|
|
53209
53594
|
name: "Codex",
|
|
53210
53595
|
detectPath: codex,
|
|
53211
|
-
installed: () =>
|
|
53596
|
+
installed: () => existsSync8(join15(home, ".codex")) || which("codex"),
|
|
53212
53597
|
write: codexWrite(codex)
|
|
53213
53598
|
});
|
|
53214
|
-
const hermes =
|
|
53599
|
+
const hermes = join15(home, ".hermes", "config.yaml");
|
|
53215
53600
|
agents.push({
|
|
53216
53601
|
name: "Hermes",
|
|
53217
53602
|
detectPath: hermes,
|
|
53218
|
-
installed: () =>
|
|
53603
|
+
installed: () => existsSync8(join15(home, ".hermes")) || which("hermes"),
|
|
53219
53604
|
write: hermesWrite(hermes)
|
|
53220
53605
|
});
|
|
53221
|
-
const gemini =
|
|
53606
|
+
const gemini = join15(home, ".gemini", "settings.json");
|
|
53222
53607
|
agents.push({
|
|
53223
53608
|
name: "Gemini CLI",
|
|
53224
53609
|
detectPath: gemini,
|
|
53225
|
-
installed: () =>
|
|
53610
|
+
installed: () => existsSync8(join15(home, ".gemini")) || which("gemini"),
|
|
53226
53611
|
write: jsonMcpServersWrite(gemini)
|
|
53227
53612
|
});
|
|
53228
|
-
const windsurf =
|
|
53613
|
+
const windsurf = join15(home, ".codeium", "windsurf", "mcp_config.json");
|
|
53229
53614
|
agents.push({
|
|
53230
53615
|
name: "Windsurf",
|
|
53231
53616
|
detectPath: windsurf,
|
|
53232
|
-
installed: () =>
|
|
53617
|
+
installed: () => existsSync8(join15(home, ".codeium")) || which("windsurf"),
|
|
53233
53618
|
write: jsonMcpServersWrite(windsurf)
|
|
53234
53619
|
});
|
|
53235
|
-
const copilot =
|
|
53620
|
+
const copilot = join15(home, ".copilot", "mcp.json");
|
|
53236
53621
|
agents.push({
|
|
53237
53622
|
name: "Copilot",
|
|
53238
53623
|
detectPath: copilot,
|
|
53239
|
-
installed: () =>
|
|
53624
|
+
installed: () => existsSync8(join15(home, ".copilot")),
|
|
53240
53625
|
write: jsonMcpServersWrite(copilot)
|
|
53241
53626
|
});
|
|
53242
|
-
const cline =
|
|
53627
|
+
const cline = join15(home, ".cline", "mcp_settings.json");
|
|
53243
53628
|
agents.push({
|
|
53244
53629
|
name: "Cline",
|
|
53245
53630
|
detectPath: cline,
|
|
53246
|
-
installed: () =>
|
|
53631
|
+
installed: () => existsSync8(join15(home, ".cline")),
|
|
53247
53632
|
write: jsonMcpServersWrite(cline)
|
|
53248
53633
|
});
|
|
53249
53634
|
return agents;
|
|
@@ -53287,15 +53672,15 @@ Registered with ${ok}/${installed.length} agent(s).`);
|
|
|
53287
53672
|
}
|
|
53288
53673
|
|
|
53289
53674
|
// src/index.ts
|
|
53290
|
-
import { readFileSync as
|
|
53291
|
-
import { join as
|
|
53292
|
-
import { fileURLToPath as
|
|
53293
|
-
var
|
|
53675
|
+
import { readFileSync as readFileSync21, existsSync as existsSync9 } from "node:fs";
|
|
53676
|
+
import { join as join16 } from "node:path";
|
|
53677
|
+
import { fileURLToPath as fileURLToPath12 } from "node:url";
|
|
53678
|
+
var ROOT10 = join16(fileURLToPath12(new URL(".", import.meta.url)), "..");
|
|
53294
53679
|
function readVersion() {
|
|
53295
53680
|
try {
|
|
53296
|
-
const p =
|
|
53297
|
-
if (
|
|
53298
|
-
return JSON.parse(
|
|
53681
|
+
const p = join16(ROOT10, "package.json");
|
|
53682
|
+
if (existsSync9(p))
|
|
53683
|
+
return JSON.parse(readFileSync21(p, "utf8")).version ?? "1.0.0";
|
|
53299
53684
|
} catch {}
|
|
53300
53685
|
return "1.0.0";
|
|
53301
53686
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.43",
|
|
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": {
|
|
@@ -25,12 +25,13 @@
|
|
|
25
25
|
"tools-catalog.json"
|
|
26
26
|
],
|
|
27
27
|
"scripts": {
|
|
28
|
-
"build": "bun build src/index.ts --outdir dist --target node --format esm",
|
|
28
|
+
"build": "bun build src/index.ts --outdir dist --target node --format esm --external playwright-core",
|
|
29
29
|
"typecheck": "tsc --noEmit",
|
|
30
30
|
"serve": "bun run src/index.ts serve",
|
|
31
31
|
"doctor": "bun run src/index.ts doctor",
|
|
32
32
|
"compile": "bun build src/index.ts --compile --outfile dist/blitzstrike",
|
|
33
33
|
"test": "bun run test/verify.ts",
|
|
34
|
+
"test:browser": "bun run test/browser-verify.ts",
|
|
34
35
|
"prepublishOnly": "bun run typecheck && bun run build",
|
|
35
36
|
"bench": "bun run test/verify.ts"
|
|
36
37
|
},
|
|
@@ -74,6 +75,15 @@
|
|
|
74
75
|
},
|
|
75
76
|
"devDependencies": {
|
|
76
77
|
"@types/node": "^22.20.2",
|
|
78
|
+
"playwright-core": "1.61.0",
|
|
77
79
|
"typescript": "^5.5.0"
|
|
80
|
+
},
|
|
81
|
+
"peerDependencies": {
|
|
82
|
+
"playwright-core": ">=1.61.0"
|
|
83
|
+
},
|
|
84
|
+
"peerDependenciesMeta": {
|
|
85
|
+
"playwright-core": {
|
|
86
|
+
"optional": true
|
|
87
|
+
}
|
|
78
88
|
}
|
|
79
89
|
}
|