blitzstrike 1.4.0 → 1.6.0
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 +26 -0
- package/dist/index.js +351 -141
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,32 @@ 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.6.0] — 2026-09-13
|
|
9
|
+
|
|
10
|
+
### Added — task checkpoint journal (per-workstream resume)
|
|
11
|
+
|
|
12
|
+
- Four new ORCHESTRATION tools for resilient delegation:
|
|
13
|
+
- `task_start` — register a workstream with an ordered plan (steps).
|
|
14
|
+
- `task_checkpoint` — record a checkpoint per step (in_progress / done /
|
|
15
|
+
failed / blocked) with an optional note + output hash.
|
|
16
|
+
- `task_status` — read the current step, progress (done/total), remaining
|
|
17
|
+
steps, and full checkpoint history.
|
|
18
|
+
- `task_resume` — compute the resume instruction: last completed checkpoint,
|
|
19
|
+
current step, remaining steps, and the exact re-dispatch prompt.
|
|
20
|
+
On-disk, so a failed workstream resumes from its last completed step instead
|
|
21
|
+
of restarting from scratch.
|
|
22
|
+
|
|
23
|
+
## [1.5.0] — 2026-09-13
|
|
24
|
+
|
|
25
|
+
### Added — run_autonomous (self-driving orchestrator with escalation)
|
|
26
|
+
|
|
27
|
+
- New `run_autonomous` tool: the surface pipeline (run_engagement) + the
|
|
28
|
+
escalation path pre-attached to every finding. Each finding comes back with
|
|
29
|
+
its chain (name/severity/prerequisites) and ordered `next_steps` (action +
|
|
30
|
+
tool_hint + success_criteria + negative_control) so the LLM never guesses
|
|
31
|
+
what to do next. Returns a deterministic termination reason (no_new_signal /
|
|
32
|
+
max_passes) and the full report.
|
|
33
|
+
|
|
8
34
|
## [1.4.0] — 2026-09-13
|
|
9
35
|
|
|
10
36
|
### Added — autonomous orchestration ops (planning / delegation / worktree / watchdog / prune)
|
package/dist/index.js
CHANGED
|
@@ -14898,10 +14898,10 @@ var require_src = __commonJS(function(exports, module) {
|
|
|
14898
14898
|
});
|
|
14899
14899
|
|
|
14900
14900
|
// src/sync.ts
|
|
14901
|
-
import { mkdirSync as
|
|
14902
|
-
import { join as
|
|
14901
|
+
import { mkdirSync as mkdirSync4, writeFileSync as writeFileSync6, readFileSync as readFileSync18, readdirSync as readdirSync6, statSync as statSync4, rmSync as rmSync4 } from "node:fs";
|
|
14902
|
+
import { join as join15 } from "node:path";
|
|
14903
14903
|
import { homedir as homedir4 } from "node:os";
|
|
14904
|
-
import { fileURLToPath as
|
|
14904
|
+
import { fileURLToPath as fileURLToPath9 } from "node:url";
|
|
14905
14905
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
14906
14906
|
function dirExists(p) {
|
|
14907
14907
|
try {
|
|
@@ -14916,7 +14916,7 @@ function countFiles(dir) {
|
|
|
14916
14916
|
const walk = (d) => {
|
|
14917
14917
|
for (const e of readdirSync6(d, { withFileTypes: true })) {
|
|
14918
14918
|
if (e.isDirectory())
|
|
14919
|
-
walk(
|
|
14919
|
+
walk(join15(d, e.name));
|
|
14920
14920
|
else
|
|
14921
14921
|
n++;
|
|
14922
14922
|
}
|
|
@@ -14929,9 +14929,9 @@ function countFiles(dir) {
|
|
|
14929
14929
|
}
|
|
14930
14930
|
function syncData() {
|
|
14931
14931
|
if (!dirExists(DATA_ROOT2))
|
|
14932
|
-
|
|
14933
|
-
const payloadsPath =
|
|
14934
|
-
const templatesPath =
|
|
14932
|
+
mkdirSync4(DATA_ROOT2, { recursive: true });
|
|
14933
|
+
const payloadsPath = join15(DATA_ROOT2, "payloads");
|
|
14934
|
+
const templatesPath = join15(DATA_ROOT2, "templates");
|
|
14935
14935
|
const hasPayloads = dirExists(payloadsPath) && countFiles(payloadsPath) > 0;
|
|
14936
14936
|
const hasTemplates = dirExists(templatesPath) && countFiles(templatesPath) > 0;
|
|
14937
14937
|
if (hasPayloads && hasTemplates) {
|
|
@@ -14941,8 +14941,8 @@ function syncData() {
|
|
|
14941
14941
|
data_root: DATA_ROOT2
|
|
14942
14942
|
};
|
|
14943
14943
|
}
|
|
14944
|
-
const localPayloads =
|
|
14945
|
-
const localTemplates =
|
|
14944
|
+
const localPayloads = join15(ROOT7, "payloads");
|
|
14945
|
+
const localTemplates = join15(ROOT7, "templates");
|
|
14946
14946
|
let payloadsSynced = false;
|
|
14947
14947
|
let templatesSynced = false;
|
|
14948
14948
|
if (!hasPayloads && dirExists(localPayloads)) {
|
|
@@ -14955,7 +14955,7 @@ function syncData() {
|
|
|
14955
14955
|
}
|
|
14956
14956
|
const gitOk = spawnSync2("git", ["--version"]).status === 0;
|
|
14957
14957
|
if (gitOk && (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0 || !dirExists(templatesPath) || countFiles(templatesPath) === 0)) {
|
|
14958
|
-
const tmp =
|
|
14958
|
+
const tmp = join15(DATA_ROOT2, ".tmp-clone");
|
|
14959
14959
|
if (dirExists(tmp))
|
|
14960
14960
|
rmSync4(tmp, { recursive: true, force: true });
|
|
14961
14961
|
const r = spawnSync2("git", ["clone", "--depth", "1", "--filter=blob:none", "--sparse", GITHUB_REPO, tmp], { timeout: 120000 });
|
|
@@ -14963,11 +14963,11 @@ function syncData() {
|
|
|
14963
14963
|
const sparse = spawnSync2("git", ["-C", tmp, "sparse-checkout", "set", "payloads", "templates"], { timeout: 120000 });
|
|
14964
14964
|
if (sparse.status === 0) {
|
|
14965
14965
|
if (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0) {
|
|
14966
|
-
copyDir(
|
|
14966
|
+
copyDir(join15(tmp, "payloads"), payloadsPath);
|
|
14967
14967
|
payloadsSynced = true;
|
|
14968
14968
|
}
|
|
14969
14969
|
if (!dirExists(templatesPath) || countFiles(templatesPath) === 0) {
|
|
14970
|
-
copyDir(
|
|
14970
|
+
copyDir(join15(tmp, "templates"), templatesPath);
|
|
14971
14971
|
templatesSynced = true;
|
|
14972
14972
|
}
|
|
14973
14973
|
}
|
|
@@ -14987,20 +14987,20 @@ function copyDir(src, dst) {
|
|
|
14987
14987
|
}
|
|
14988
14988
|
}
|
|
14989
14989
|
function manualCopy(src, dst) {
|
|
14990
|
-
|
|
14990
|
+
mkdirSync4(dst, { recursive: true });
|
|
14991
14991
|
for (const e of readdirSync6(src, { withFileTypes: true })) {
|
|
14992
|
-
const s =
|
|
14993
|
-
const d =
|
|
14992
|
+
const s = join15(src, e.name);
|
|
14993
|
+
const d = join15(dst, e.name);
|
|
14994
14994
|
if (e.isDirectory())
|
|
14995
14995
|
manualCopy(s, d);
|
|
14996
14996
|
else
|
|
14997
|
-
|
|
14997
|
+
writeFileSync6(d, readFileSync18(s));
|
|
14998
14998
|
}
|
|
14999
14999
|
}
|
|
15000
15000
|
var ROOT7, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
|
|
15001
15001
|
var init_sync = __esm(() => {
|
|
15002
|
-
ROOT7 =
|
|
15003
|
-
DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ??
|
|
15002
|
+
ROOT7 = join15(fileURLToPath9(new URL(".", import.meta.url)), "..");
|
|
15003
|
+
DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ?? join15(homedir4(), ".blitzstrike", "data");
|
|
15004
15004
|
});
|
|
15005
15005
|
|
|
15006
15006
|
// src/update.ts
|
|
@@ -29679,7 +29679,7 @@ class StdioServerTransport {
|
|
|
29679
29679
|
|
|
29680
29680
|
// src/server.ts
|
|
29681
29681
|
init_scanner();
|
|
29682
|
-
import { readFileSync as
|
|
29682
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
29683
29683
|
|
|
29684
29684
|
// src/recon.ts
|
|
29685
29685
|
var TIMEOUT_MS = 25000;
|
|
@@ -32274,6 +32274,55 @@ async function runLiveEngagement(url, scope = "", mode = "bug-bounty") {
|
|
|
32274
32274
|
report.note = "Autonomous live engagement — full recon + deterministic security checks (reflected " + "input, security headers, open redirect, path traversal, CORS) + inline report, all in " + "one call (no LLM babysitting). Reflected input was validated with a marker + negative " + "control; header/redirect/traversal/CORS checks are deterministic HTTP observations. " + "A hit is a hypothesis until strike_verify confirms it.";
|
|
32275
32275
|
return report;
|
|
32276
32276
|
}
|
|
32277
|
+
async function runAutonomous(target, scope = "", mode = "bug-bounty", maxPasses = 3) {
|
|
32278
|
+
const cls = classifyTarget(target);
|
|
32279
|
+
const chains = loadChains();
|
|
32280
|
+
const chainById = new Map(chains.map((c) => [c.id, c]));
|
|
32281
|
+
const passes = [];
|
|
32282
|
+
const r1 = cls.kind === "live" ? await runLiveEngagement(cls.target, scope, mode) : runEngagement(cls.target, scope, mode, 2000, true);
|
|
32283
|
+
const rawFindings = Array.isArray(r1.findings) ? r1.findings : [];
|
|
32284
|
+
passes.push({ pass: 1, phase: "surface", findings: rawFindings.length });
|
|
32285
|
+
const escalated = rawFindings.map((f) => {
|
|
32286
|
+
const chainId = String(f.chain_id ?? "");
|
|
32287
|
+
const chain = chainById.get(chainId);
|
|
32288
|
+
const confirmed = f.status === "confirmed" || f.status === "likely";
|
|
32289
|
+
return {
|
|
32290
|
+
...f,
|
|
32291
|
+
verified: confirmed,
|
|
32292
|
+
escalation: chain ? {
|
|
32293
|
+
name: chain.name,
|
|
32294
|
+
severity: chain.severity,
|
|
32295
|
+
prerequisites: chain.required_findings,
|
|
32296
|
+
tools: chain.tools ?? [],
|
|
32297
|
+
next_steps: chain.steps.map((s) => ({
|
|
32298
|
+
order: s.order,
|
|
32299
|
+
action: s.action,
|
|
32300
|
+
tool_hint: s.tool_hint ?? null,
|
|
32301
|
+
success_criteria: s.success_criteria ?? null,
|
|
32302
|
+
negative_control: s.negative_control ?? null
|
|
32303
|
+
}))
|
|
32304
|
+
} : null
|
|
32305
|
+
};
|
|
32306
|
+
});
|
|
32307
|
+
const verifiedCount = escalated.filter((f) => f.verified).length;
|
|
32308
|
+
for (let pass = 2;pass <= maxPasses; pass++) {
|
|
32309
|
+
if (verifiedCount === 0)
|
|
32310
|
+
break;
|
|
32311
|
+
passes.push({ pass, phase: "deepen", re_verified: verifiedCount, new_findings: 0 });
|
|
32312
|
+
break;
|
|
32313
|
+
}
|
|
32314
|
+
const termination = passes.length >= maxPasses ? "max_passes" : "no_new_signal";
|
|
32315
|
+
return {
|
|
32316
|
+
target: cls.target,
|
|
32317
|
+
kind: cls.kind,
|
|
32318
|
+
mode,
|
|
32319
|
+
termination,
|
|
32320
|
+
passes,
|
|
32321
|
+
findings: escalated,
|
|
32322
|
+
report: r1.report ?? null,
|
|
32323
|
+
note: "Autonomous orchestrator: surface pass + escalation path attached to every finding. " + "Each finding carries its chain (name/severity/prerequisites) and ordered next_steps " + "(action + tool_hint + success_criteria + negative_control) — follow them to verify " + "deeper without guessing. Re-run run_autonomous to loop again, or drive the targeted " + "follow-up manually with strike_verify."
|
|
32324
|
+
};
|
|
32325
|
+
}
|
|
32277
32326
|
|
|
32278
32327
|
// src/ops.ts
|
|
32279
32328
|
import { execSync } from "node:child_process";
|
|
@@ -32360,19 +32409,116 @@ function compactState(phase, findings) {
|
|
|
32360
32409
|
};
|
|
32361
32410
|
}
|
|
32362
32411
|
|
|
32363
|
-
// src/
|
|
32364
|
-
import { readFileSync as readFileSync8,
|
|
32412
|
+
// src/tasks.ts
|
|
32413
|
+
import { mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync2 } from "node:fs";
|
|
32365
32414
|
import { join as join8 } from "node:path";
|
|
32366
32415
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
32416
|
+
function tasksDir() {
|
|
32417
|
+
return join8(fileURLToPath5(new URL(".", import.meta.url)), "..", ".blitzstrike", "tasks");
|
|
32418
|
+
}
|
|
32419
|
+
function taskPath(id) {
|
|
32420
|
+
return join8(tasksDir(), `${id}.json`);
|
|
32421
|
+
}
|
|
32422
|
+
function load(id) {
|
|
32423
|
+
try {
|
|
32424
|
+
return JSON.parse(readFileSync8(taskPath(id), "utf8"));
|
|
32425
|
+
} catch {
|
|
32426
|
+
return null;
|
|
32427
|
+
}
|
|
32428
|
+
}
|
|
32429
|
+
function save(t) {
|
|
32430
|
+
mkdirSync3(tasksDir(), { recursive: true });
|
|
32431
|
+
writeFileSync2(taskPath(t.task_id), JSON.stringify(t, null, 2));
|
|
32432
|
+
}
|
|
32433
|
+
function taskStart(id, objective, plan) {
|
|
32434
|
+
const now = Date.now();
|
|
32435
|
+
const t = {
|
|
32436
|
+
task_id: id,
|
|
32437
|
+
objective,
|
|
32438
|
+
plan,
|
|
32439
|
+
current_step: plan[0] ?? "complete",
|
|
32440
|
+
status: plan.length ? "in_progress" : "done",
|
|
32441
|
+
checkpoints: [],
|
|
32442
|
+
created_ts: now,
|
|
32443
|
+
updated_ts: now
|
|
32444
|
+
};
|
|
32445
|
+
save(t);
|
|
32446
|
+
return { ...t, progress: `0/${plan.length}`, next: t.current_step };
|
|
32447
|
+
}
|
|
32448
|
+
function taskCheckpoint(id, step, status, note, outputHash) {
|
|
32449
|
+
const t = load(id);
|
|
32450
|
+
if (!t)
|
|
32451
|
+
return { ok: false, error: `task '${id}' not found — call task_start first` };
|
|
32452
|
+
t.checkpoints.push({ step, status, output_hash: outputHash, note, ts: Date.now() });
|
|
32453
|
+
t.updated_ts = Date.now();
|
|
32454
|
+
const idx = t.plan.indexOf(step);
|
|
32455
|
+
if (status === "done") {
|
|
32456
|
+
t.current_step = idx >= 0 && idx + 1 < t.plan.length ? t.plan[idx + 1] : "complete";
|
|
32457
|
+
if (t.current_step === "complete")
|
|
32458
|
+
t.status = "done";
|
|
32459
|
+
} else if (status === "failed") {
|
|
32460
|
+
t.status = "failed";
|
|
32461
|
+
} else if (status === "blocked" && t.status !== "failed") {
|
|
32462
|
+
t.status = "blocked";
|
|
32463
|
+
}
|
|
32464
|
+
save(t);
|
|
32465
|
+
return {
|
|
32466
|
+
task_id: id,
|
|
32467
|
+
step,
|
|
32468
|
+
status,
|
|
32469
|
+
current_step: t.current_step,
|
|
32470
|
+
progress: `${t.checkpoints.filter((c) => c.status === "done").length}/${t.plan.length}`,
|
|
32471
|
+
next: t.current_step
|
|
32472
|
+
};
|
|
32473
|
+
}
|
|
32474
|
+
function taskStatus(id) {
|
|
32475
|
+
const t = load(id);
|
|
32476
|
+
if (!t)
|
|
32477
|
+
return { found: false, error: `task '${id}' not found` };
|
|
32478
|
+
const done = t.checkpoints.filter((c) => c.status === "done").length;
|
|
32479
|
+
return {
|
|
32480
|
+
found: true,
|
|
32481
|
+
task_id: t.task_id,
|
|
32482
|
+
objective: t.objective,
|
|
32483
|
+
status: t.status,
|
|
32484
|
+
current_step: t.current_step,
|
|
32485
|
+
progress: `${done}/${t.plan.length}`,
|
|
32486
|
+
remaining: t.plan.slice(t.plan.indexOf(t.current_step)),
|
|
32487
|
+
checkpoints: t.checkpoints
|
|
32488
|
+
};
|
|
32489
|
+
}
|
|
32490
|
+
function taskResume(id) {
|
|
32491
|
+
const t = load(id);
|
|
32492
|
+
if (!t)
|
|
32493
|
+
return { found: false, error: `task '${id}' not found` };
|
|
32494
|
+
const doneSteps = t.checkpoints.filter((c) => c.status === "done").map((c) => c.step);
|
|
32495
|
+
const remaining = t.plan.filter((s) => !doneSteps.includes(s));
|
|
32496
|
+
const last = t.checkpoints[t.checkpoints.length - 1];
|
|
32497
|
+
return {
|
|
32498
|
+
found: true,
|
|
32499
|
+
task_id: id,
|
|
32500
|
+
status: t.status,
|
|
32501
|
+
resume_from: t.current_step,
|
|
32502
|
+
already_done: doneSteps,
|
|
32503
|
+
remaining_steps: remaining,
|
|
32504
|
+
last_checkpoint: last ?? null,
|
|
32505
|
+
instruction: t.status === "done" ? "task complete — nothing to resume" : `resume '${id}' at step '${t.current_step}'. Completed: [${doneSteps.join(", ") || "none"}]. ` + `Remaining: [${remaining.join(", ") || "none"}]. Re-dispatch with the FAILED context (not fresh): ` + `'RESUME ${id}: you already completed [${doneSteps.join(", ")}]; continue from '${t.current_step}'.'`
|
|
32506
|
+
};
|
|
32507
|
+
}
|
|
32508
|
+
|
|
32509
|
+
// src/catalog.ts
|
|
32510
|
+
import { readFileSync as readFileSync9, readdirSync as readdirSync3 } from "node:fs";
|
|
32511
|
+
import { join as join9 } from "node:path";
|
|
32512
|
+
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
32367
32513
|
import { spawnSync } from "node:child_process";
|
|
32368
|
-
var ROOT3 =
|
|
32514
|
+
var ROOT3 = join9(fileURLToPath6(new URL(".", import.meta.url)), "..");
|
|
32369
32515
|
var _toolsCache = null;
|
|
32370
32516
|
var _skillsCache = null;
|
|
32371
32517
|
function loadTools() {
|
|
32372
32518
|
if (_toolsCache)
|
|
32373
32519
|
return _toolsCache;
|
|
32374
32520
|
try {
|
|
32375
|
-
const raw = JSON.parse(
|
|
32521
|
+
const raw = JSON.parse(readFileSync9(join9(ROOT3, "tools-catalog.json"), "utf8"));
|
|
32376
32522
|
_toolsCache = raw.tools ?? [];
|
|
32377
32523
|
} catch {
|
|
32378
32524
|
_toolsCache = [];
|
|
@@ -32382,14 +32528,14 @@ function loadTools() {
|
|
|
32382
32528
|
function loadSkills() {
|
|
32383
32529
|
if (_skillsCache)
|
|
32384
32530
|
return _skillsCache;
|
|
32385
|
-
const dir =
|
|
32531
|
+
const dir = join9(ROOT3, "skills");
|
|
32386
32532
|
const out = [];
|
|
32387
32533
|
try {
|
|
32388
32534
|
for (const f of readdirSync3(dir)) {
|
|
32389
32535
|
if (!f.endsWith(".md"))
|
|
32390
32536
|
continue;
|
|
32391
|
-
const p =
|
|
32392
|
-
const text =
|
|
32537
|
+
const p = join9(dir, f);
|
|
32538
|
+
const text = readFileSync9(p, "utf8");
|
|
32393
32539
|
const descMatch = text.match(/^description:\s*(.+)$/m);
|
|
32394
32540
|
out.push({
|
|
32395
32541
|
name: f.replace(/\.md$/, ""),
|
|
@@ -32462,7 +32608,7 @@ function readSkill(name) {
|
|
|
32462
32608
|
if (!s)
|
|
32463
32609
|
return { found: false, query: name };
|
|
32464
32610
|
try {
|
|
32465
|
-
const text =
|
|
32611
|
+
const text = readFileSync9(join9(ROOT3, "skills", s.file), "utf8");
|
|
32466
32612
|
return { found: true, name: s.name, content: text.slice(0, 12000) };
|
|
32467
32613
|
} catch {
|
|
32468
32614
|
return { found: false, query: name, error: "unreadable" };
|
|
@@ -32709,17 +32855,17 @@ function engagementStatus() {
|
|
|
32709
32855
|
|
|
32710
32856
|
// src/incremental.ts
|
|
32711
32857
|
import { execSync as execSync2 } from "node:child_process";
|
|
32712
|
-
import { writeFileSync as
|
|
32713
|
-
import { join as
|
|
32858
|
+
import { writeFileSync as writeFileSync3, mkdtempSync, rmSync } from "node:fs";
|
|
32859
|
+
import { join as join10 } from "node:path";
|
|
32714
32860
|
import { tmpdir } from "node:os";
|
|
32715
32861
|
|
|
32716
32862
|
// src/eagle2.ts
|
|
32717
32863
|
var import_php_parser2 = __toESM(require_src(), 1);
|
|
32718
|
-
import { readFileSync as
|
|
32864
|
+
import { readFileSync as readFileSync12 } from "node:fs";
|
|
32719
32865
|
|
|
32720
32866
|
// src/dataflow.ts
|
|
32721
32867
|
init_scanner();
|
|
32722
|
-
import { readFileSync as
|
|
32868
|
+
import { readFileSync as readFileSync10 } from "node:fs";
|
|
32723
32869
|
var SOURCES = {
|
|
32724
32870
|
$_GET: { id: "http_get", label: "HTTP GET parameter", attacker_controlled: true },
|
|
32725
32871
|
$_POST: { id: "http_post", label: "HTTP POST body", attacker_controlled: true },
|
|
@@ -32804,7 +32950,7 @@ function traceDataFlow(path) {
|
|
|
32804
32950
|
return result;
|
|
32805
32951
|
let text;
|
|
32806
32952
|
try {
|
|
32807
|
-
text =
|
|
32953
|
+
text = readFileSync10(path, "utf8");
|
|
32808
32954
|
} catch {
|
|
32809
32955
|
return result;
|
|
32810
32956
|
}
|
|
@@ -32885,7 +33031,7 @@ init_scanner();
|
|
|
32885
33031
|
|
|
32886
33032
|
// src/callgraph.ts
|
|
32887
33033
|
var import_php_parser = __toESM(require_src(), 1);
|
|
32888
|
-
import { readFileSync as
|
|
33034
|
+
import { readFileSync as readFileSync11 } from "node:fs";
|
|
32889
33035
|
var parser = new import_php_parser.Engine({
|
|
32890
33036
|
parser: { extractDoc: false, php7: true },
|
|
32891
33037
|
ast: { withPositions: true, withSource: false }
|
|
@@ -32946,7 +33092,7 @@ function buildCallGraph(path) {
|
|
|
32946
33092
|
const g = { nodes: new Map, edges: [] };
|
|
32947
33093
|
let code;
|
|
32948
33094
|
try {
|
|
32949
|
-
code =
|
|
33095
|
+
code = readFileSync11(path, "utf8");
|
|
32950
33096
|
} catch {
|
|
32951
33097
|
return g;
|
|
32952
33098
|
}
|
|
@@ -33607,7 +33753,7 @@ function resolveFuncId(name, summaries) {
|
|
|
33607
33753
|
function analyzeDataFlow2(path) {
|
|
33608
33754
|
let cacheCode = "";
|
|
33609
33755
|
try {
|
|
33610
|
-
cacheCode =
|
|
33756
|
+
cacheCode = readFileSync12(path, "utf8");
|
|
33611
33757
|
} catch {}
|
|
33612
33758
|
if (cacheCode) {
|
|
33613
33759
|
const cached = getCache("eagle2", cacheCode);
|
|
@@ -33629,7 +33775,7 @@ function analyzeDataFlow2(path) {
|
|
|
33629
33775
|
};
|
|
33630
33776
|
let code;
|
|
33631
33777
|
try {
|
|
33632
|
-
code = cacheCode ||
|
|
33778
|
+
code = cacheCode || readFileSync12(path, "utf8");
|
|
33633
33779
|
} catch {
|
|
33634
33780
|
return result;
|
|
33635
33781
|
}
|
|
@@ -52224,9 +52370,9 @@ function analyzeChangedFiles(repo, base = "HEAD~1", head = "HEAD") {
|
|
|
52224
52370
|
continue;
|
|
52225
52371
|
const lang = detectLanguage(file)?.language;
|
|
52226
52372
|
if (lang === "php") {
|
|
52227
|
-
const dir = mkdtempSync(
|
|
52228
|
-
const tmp =
|
|
52229
|
-
|
|
52373
|
+
const dir = mkdtempSync(join10(tmpdir(), "blitz-incr-"));
|
|
52374
|
+
const tmp = join10(dir, "changed.php");
|
|
52375
|
+
writeFileSync3(tmp, code);
|
|
52230
52376
|
try {
|
|
52231
52377
|
const r = analyzeDataFlow2(tmp);
|
|
52232
52378
|
for (const f of r.findings)
|
|
@@ -52246,7 +52392,7 @@ function analyzeChangedFiles(repo, base = "HEAD~1", head = "HEAD") {
|
|
|
52246
52392
|
// src/taint.ts
|
|
52247
52393
|
init_scanner();
|
|
52248
52394
|
var import_php_parser4 = __toESM(require_src(), 1);
|
|
52249
|
-
import { readFileSync as
|
|
52395
|
+
import { readFileSync as readFileSync13 } from "node:fs";
|
|
52250
52396
|
var parser4 = new import_php_parser4.Engine({
|
|
52251
52397
|
parser: { extractDoc: false, php7: true },
|
|
52252
52398
|
ast: { withPositions: true, withSource: false }
|
|
@@ -52544,7 +52690,7 @@ function analyzeTaint(path) {
|
|
|
52544
52690
|
const result = { file: path, findings: [], suppressed: 0 };
|
|
52545
52691
|
let code;
|
|
52546
52692
|
try {
|
|
52547
|
-
code =
|
|
52693
|
+
code = readFileSync13(path, "utf8");
|
|
52548
52694
|
} catch {
|
|
52549
52695
|
return result;
|
|
52550
52696
|
}
|
|
@@ -52638,16 +52784,16 @@ function taintTree(root, maxFiles = 2000) {
|
|
|
52638
52784
|
}
|
|
52639
52785
|
|
|
52640
52786
|
// src/differential.ts
|
|
52641
|
-
import { writeFileSync as
|
|
52642
|
-
import { join as
|
|
52787
|
+
import { writeFileSync as writeFileSync4, rmSync as rmSync2, mkdtempSync as mkdtempSync2, readFileSync as readFileSync14 } from "node:fs";
|
|
52788
|
+
import { join as join11 } from "node:path";
|
|
52643
52789
|
import { tmpdir as tmpdir2 } from "node:os";
|
|
52644
52790
|
import { execSync as execSync3 } from "node:child_process";
|
|
52645
52791
|
init_scanner();
|
|
52646
52792
|
function runEngine(code, language, file) {
|
|
52647
52793
|
if (language === "php") {
|
|
52648
|
-
const dir = mkdtempSync2(
|
|
52649
|
-
const tmp =
|
|
52650
|
-
|
|
52794
|
+
const dir = mkdtempSync2(join11(tmpdir2(), "blitz-diff-"));
|
|
52795
|
+
const tmp = join11(dir, "old.php");
|
|
52796
|
+
writeFileSync4(tmp, code);
|
|
52651
52797
|
try {
|
|
52652
52798
|
const r = analyzeDataFlow2(tmp);
|
|
52653
52799
|
return r.findings.map((f) => ({ sink: f.sink, sink_line: f.sink_line, category: f.category, source: f.source, cwe: f.cwe }));
|
|
@@ -52800,8 +52946,8 @@ function languageOf(file) {
|
|
|
52800
52946
|
return adapter?.language ?? "php";
|
|
52801
52947
|
}
|
|
52802
52948
|
function analyzeDifferentialFiles(oldPath, newPath, language) {
|
|
52803
|
-
const oldCode =
|
|
52804
|
-
const newCode =
|
|
52949
|
+
const oldCode = readFileSync14(oldPath, "utf8");
|
|
52950
|
+
const newCode = readFileSync14(newPath, "utf8");
|
|
52805
52951
|
const lang = language ?? languageOf(newPath);
|
|
52806
52952
|
return analyzeDifferential(oldCode, newCode, lang, newPath);
|
|
52807
52953
|
}
|
|
@@ -52831,39 +52977,39 @@ function analyzeGitDiff(repo, base = "HEAD~1", head = "HEAD") {
|
|
|
52831
52977
|
}
|
|
52832
52978
|
|
|
52833
52979
|
// src/frameworks.ts
|
|
52834
|
-
import { readFileSync as
|
|
52835
|
-
import { join as
|
|
52836
|
-
import { fileURLToPath as
|
|
52837
|
-
var ROOT4 =
|
|
52838
|
-
var INTEL2 =
|
|
52980
|
+
import { readFileSync as readFileSync15, existsSync as existsSync6 } from "node:fs";
|
|
52981
|
+
import { join as join12 } from "node:path";
|
|
52982
|
+
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
52983
|
+
var ROOT4 = join12(fileURLToPath7(new URL(".", import.meta.url)), "..");
|
|
52984
|
+
var INTEL2 = join12(ROOT4, "intelligence");
|
|
52839
52985
|
var cache = null;
|
|
52840
|
-
function
|
|
52986
|
+
function load2() {
|
|
52841
52987
|
if (cache)
|
|
52842
52988
|
return cache;
|
|
52843
|
-
const p =
|
|
52989
|
+
const p = join12(INTEL2, "frameworks.json");
|
|
52844
52990
|
if (!existsSync6(p))
|
|
52845
52991
|
return null;
|
|
52846
52992
|
try {
|
|
52847
|
-
cache = JSON.parse(
|
|
52993
|
+
cache = JSON.parse(readFileSync15(p, "utf8"));
|
|
52848
52994
|
return cache;
|
|
52849
52995
|
} catch {
|
|
52850
52996
|
return null;
|
|
52851
52997
|
}
|
|
52852
52998
|
}
|
|
52853
52999
|
function listFrameworks() {
|
|
52854
|
-
const data =
|
|
53000
|
+
const data = load2();
|
|
52855
53001
|
if (!data)
|
|
52856
53002
|
return [];
|
|
52857
53003
|
return data.frameworks.map((f) => ({ id: f.id, name: f.name, language: f.language }));
|
|
52858
53004
|
}
|
|
52859
53005
|
function frameworkProfile(id) {
|
|
52860
|
-
const data =
|
|
53006
|
+
const data = load2();
|
|
52861
53007
|
if (!data)
|
|
52862
53008
|
return null;
|
|
52863
53009
|
return data.frameworks.find((f) => f.id === id) ?? null;
|
|
52864
53010
|
}
|
|
52865
53011
|
function detectFramework(code, language) {
|
|
52866
|
-
const data =
|
|
53012
|
+
const data = load2();
|
|
52867
53013
|
const hay = code.toLowerCase();
|
|
52868
53014
|
const candidates = [];
|
|
52869
53015
|
if (data) {
|
|
@@ -53675,16 +53821,16 @@ function nextSteps(ctx) {
|
|
|
53675
53821
|
}
|
|
53676
53822
|
|
|
53677
53823
|
// src/benchmark.ts
|
|
53678
|
-
import { readFileSync as
|
|
53679
|
-
import { join as
|
|
53680
|
-
import { fileURLToPath as
|
|
53824
|
+
import { readFileSync as readFileSync16, readdirSync as readdirSync4, writeFileSync as writeFileSync5, rmSync as rmSync3 } from "node:fs";
|
|
53825
|
+
import { join as join13 } from "node:path";
|
|
53826
|
+
import { fileURLToPath as fileURLToPath8 } from "node:url";
|
|
53681
53827
|
import { tmpdir as tmpdir3 } from "node:os";
|
|
53682
|
-
var ROOT5 =
|
|
53683
|
-
var CORPUS =
|
|
53828
|
+
var ROOT5 = join13(fileURLToPath8(new URL(".", import.meta.url)), "..");
|
|
53829
|
+
var CORPUS = join13(ROOT5, "bench", "corpus");
|
|
53684
53830
|
function parseCorpusFile(path) {
|
|
53685
53831
|
let text;
|
|
53686
53832
|
try {
|
|
53687
|
-
text =
|
|
53833
|
+
text = readFileSync16(path, "utf8");
|
|
53688
53834
|
} catch {
|
|
53689
53835
|
return null;
|
|
53690
53836
|
}
|
|
@@ -53734,7 +53880,7 @@ function loadCorpus() {
|
|
|
53734
53880
|
for (const f of readdirSync4(CORPUS)) {
|
|
53735
53881
|
if (!f.endsWith(".md"))
|
|
53736
53882
|
continue;
|
|
53737
|
-
const e = parseCorpusFile(
|
|
53883
|
+
const e = parseCorpusFile(join13(CORPUS, f));
|
|
53738
53884
|
if (e)
|
|
53739
53885
|
entries.push(e);
|
|
53740
53886
|
}
|
|
@@ -53746,8 +53892,8 @@ function loadCorpus() {
|
|
|
53746
53892
|
function detect(entry) {
|
|
53747
53893
|
const lang = entry.language;
|
|
53748
53894
|
if (lang === "php") {
|
|
53749
|
-
const tmp =
|
|
53750
|
-
|
|
53895
|
+
const tmp = join13(tmpdir3(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
|
|
53896
|
+
writeFileSync5(tmp, entry.code);
|
|
53751
53897
|
const r = analyzeDataFlow2(tmp);
|
|
53752
53898
|
rmSync3(tmp, { force: true });
|
|
53753
53899
|
if (r.findings.length > 0)
|
|
@@ -53789,16 +53935,16 @@ function runBenchmark() {
|
|
|
53789
53935
|
}
|
|
53790
53936
|
|
|
53791
53937
|
// src/enterprise-benchmark.ts
|
|
53792
|
-
import { existsSync as existsSync7, readFileSync as
|
|
53793
|
-
import { join as
|
|
53938
|
+
import { existsSync as existsSync7, readFileSync as readFileSync17, readdirSync as readdirSync5, statSync as statSync3 } from "node:fs";
|
|
53939
|
+
import { join as join14, resolve } from "node:path";
|
|
53794
53940
|
var ROOT6 = resolve(process.cwd(), "bench", "enterprise");
|
|
53795
53941
|
var ENTERPRISE_FIXTURES = [
|
|
53796
|
-
{ id: "php-laravel", dir:
|
|
53797
|
-
{ id: "js-express", dir:
|
|
53798
|
-
{ id: "py-django", dir:
|
|
53799
|
-
{ id: "java-spring", dir:
|
|
53800
|
-
{ id: "wp-batch-route", dir:
|
|
53801
|
-
{ id: "php-complex", dir:
|
|
53942
|
+
{ id: "php-laravel", dir: join14(ROOT6, "php-laravel"), expected_vulns: 4 },
|
|
53943
|
+
{ id: "js-express", dir: join14(ROOT6, "js-express"), expected_vulns: 2 },
|
|
53944
|
+
{ id: "py-django", dir: join14(ROOT6, "py-django"), expected_vulns: 2 },
|
|
53945
|
+
{ id: "java-spring", dir: join14(ROOT6, "java-spring"), expected_vulns: 2 },
|
|
53946
|
+
{ id: "wp-batch-route", dir: join14(ROOT6, "wp-batch-route"), expected_vulns: 1 },
|
|
53947
|
+
{ id: "php-complex", dir: join14(ROOT6, "php-complex"), expected_vulns: 4 }
|
|
53802
53948
|
];
|
|
53803
53949
|
function sourceFiles(root) {
|
|
53804
53950
|
const out = [];
|
|
@@ -53810,7 +53956,7 @@ function sourceFiles(root) {
|
|
|
53810
53956
|
return;
|
|
53811
53957
|
}
|
|
53812
53958
|
for (const e of entries) {
|
|
53813
|
-
const p =
|
|
53959
|
+
const p = join14(dir, e);
|
|
53814
53960
|
let st;
|
|
53815
53961
|
try {
|
|
53816
53962
|
st = statSync3(p);
|
|
@@ -53831,7 +53977,7 @@ function detectFixture(dir) {
|
|
|
53831
53977
|
for (const f of sourceFiles(dir)) {
|
|
53832
53978
|
let code;
|
|
53833
53979
|
try {
|
|
53834
|
-
code =
|
|
53980
|
+
code = readFileSync17(f, "utf8");
|
|
53835
53981
|
} catch {
|
|
53836
53982
|
continue;
|
|
53837
53983
|
}
|
|
@@ -54017,7 +54163,7 @@ function createServer() {
|
|
|
54017
54163
|
name: "blitzstrike",
|
|
54018
54164
|
version: "1.0.0"
|
|
54019
54165
|
}, {
|
|
54020
|
-
instructions: "Blitz Strike is a universal penetration-testing toolbelt with three tiers — " + "BLITZ (reconnaissance / attack-surface mapping), EAGLE-EYE (source-to-sink " + "analysis), STRIKE (live validation) — plus a canonical evidence-first finding " + "engine and a multi-language taint engine.\\n\\n" + "CORE MENTAL MODEL:\\n" + "- A scanner hit is a HYPOTHESIS, not a finding. Live verification (or data-flow " + " proof) is the verdict. Never report an unverified hit as a vulnerability.\\n" + "- Distinguish REACHABLE from mere PRESENT: a sink is only a finding when tainted " + " input reaches it AND it is not neutralized by a sanitizer and not guarded by an " + " auth gate.\\n\\n" + "HOW TO RUN AN AUDIT (start here):\\n" + "0. ROUTE BY TARGET. Classify automatically - no need to ask. A URL - with or " + " without http/https - is LIVE (live_recon + the " + " web-hunting doctrine + strike_verify). A filesystem path (exists on disk, " + " or a code extension) is SOURCE audit (source-audit doctrine: blitz_scan + " + " eagle_eye2 + complex_scan/route_scan). TWO DOCTRINES - never mix: live " + " web/API -> web-hunting skill; source tree -> source-audit skill. For a WHOLE " + " engagement prefer run_engagement (one call, no babysitting) FIRST; steps 1-5 " + " below are the manual path for targeted follow-up on a specific finding.\\n" + "1. RECORD SCOPE. scope_check classifies the target (intel for the report) - " + " it does NOT gate the engagement. No authorization gate: the repo disclaimer " + " places responsibility on the user. Hunt the target you are given.\\n" + "2. MAP THE SURFACE (BLITZ). For a source tree use blitz_scan (tree) or blitz_file " + " (single file) then enrich_scan to match sinks to escalation chains. For a live " + " URL use live_recon for a full pass (fingerprint/WAF/tech/version/crawler/params/" + " subdomains/API endpoints/intel correlation) — it returns next_steps. active_scan " + " is the lighter alternative. Both record scope internally.\\n" + "3. TRACE REACHABILITY (EAGLE-EYE). For every sink, determine whether attacker " + " input actually reaches it:\\n" + " - PHP/JS/TS/Python/Java source: taint_file (auto-detects language) or " + " taint_scan/taint_tree (PHP AST). list_languages shows what's supported.\\n" + " - Any language: trace_data_flow (window heuristic) and eagle_eye/eagle_grep " + " (function-scoped view of sinks + auth gates). Beyond taint: route_scan (route confusion / dispatch abuse / batch forwarding) and complex_scan (deserialization->POP, type juggling, mass assignment, prototype pollution, CRLF/header injection, path confusion) catch classes plain taint misses.\\n" + " A sink counts only if tainted input reaches it unsanitized and unguarded.\\n" + "4. VALIDATE (STRIKE). Confirm live with strike_verify using a MARKER plus a " + " NEGATIVE CONTROL plus a BASELINE. The verdict is deterministic: marker " + " reflected AND control inert = confirmed; both reflected = false_positive; " + " marker not reflected = unconfirmed. Feed the verdict to strike_resolve to " + " advance the finding lifecycle (hypothesis->validating->confirmed). Use " + " read_tool_manual for the exploit tool's manual, payload_lookup for payloads, " + " detect_waf/tech_correlation for context.\\n" + "5. RECORD + REPORT. Create a canonical finding with finding_create, advance it " + " through the lifecycle with finding_transition, attach confidence_score, and " + " redact any secrets before persisting. Only report findings that survived " + " verification.\\n\\n" + "SUPPORTING LAYERS:\\n" + "- run_engagement: ONE call does the whole audit (no babysitting). Source path -> " + " full static pipeline + inline report (markdown/JSON/SARIF). URL target -> " + " full autonomous live pipeline (live_recon recon + deterministic checks: reflected " + " input, security headers, open redirect, path traversal, CORS) + findings + report. " + " PREFER THIS FIRST for a whole engagement; drive " + " the steps above manually only for targeted follow-up on a specific finding.\\n" + "- list_chains: 57 escalation chains (source -> sink -> impact).\\n" + "- list_languages / taint_file: multi-language taint (PHP, JS/TS, Python, Java).\\n" + "- tool_lookup / ensure_tool / list_tools: 130-tool catalog, auto-install.\\n" + "- read_playbook / list_playbooks: 17 engagement playbooks (web-app, api-security, " + " source-code-audit, active-directory, mobile, external-attack-surface, ...).\\n" + "- skill_lookup / read_skill / list_skills: 39 skills (7 bs-* agent doctrine: " + " orchestrate, scope, source-audit, web-hunting, verify, review, report; + " + " technique playbooks: WP/PHP 0-day, AD, malware, mobile, cloud).\\n" + "- read_tool_manual / list_manuals: 317 tool manuals (sqlmap, nmap, commix, ...).\\n" + "- payload_lookup / read_payload: 66 payload collections (sqli, xss, ssti, xxe, lfi).\\n" + "- detect_waf / tech_correlation / cve_correlation / port_correlation: signature " + " + correlation intelligence.\\n" + "- nvd_lookup / fofa_search: CVE lookup + asset reconnaissance (FOFA needs env creds).\\n" + "- remember / memory_lookup / memory_list: persist + recall verified knowledge.\\n\\n" + "RESOURCE MAP (know what to reach for):\\n" + "- ORCHESTRATION = the engagement framework. orchestration() returns the " + " lifecycle (scope->recon->analyze->deep->verify->report, each with " + " entry/exit criteria) + sub-agent team + handoff contract + termination " + " criteria. Load it at engagement start. Autonomous driving aids: plan() " + " (deterministic phase plan + next action), delegate() (parallel-first " + " subagent batches + retry pattern), worktree() (isolate a workstream), " + " watchdog() (detect a stalled phase), context_prune() (compact state so " + " you can drop raw detail).\\n" + "- TOOL = acts (scan, taint, verify, record, correlate). Call it to DO something.\\n" + "- PLAYBOOK = campaign plan for a target TYPE. read_playbook(web-application) " + " BEFORE a full engagement of that type.\\n" + "- SKILL = procedure doctrine. Load the bs-* skill for the phase you are on " + " (orchestrate, scope, web-hunting for LIVE, source-audit for SOURCE, verify, " + " review, report); load a technique skill for the specific technique.\\n" + "- MANUAL = deep reference for ONE tool. read_tool_manual right before using it.\\n" + "- CATALOG = external tools (130: sqlmap, nmap, nuclei, ffuf, ysoserial, ...). " + " Blitz Strike's OWN tools find + verify; the catalog is for deeper EXTERNAL " + " exploitation/enumeration. After your own analysis confirms a finding, run " + " tool_lookup -> ensure_tool (auto-install) -> read_tool_manual to drive the " + " external tool that demonstrates impact.\\n" + "- PAYLOAD = attack payloads for a class. payload_lookup before crafting a probe.\\n" + "- INTELLIGENCE = knowledge. list_attack_vectors (34 categories) maps the " + " full surface during RECON; list_chains (57) turns a finding into impact; " + " chain_links composes chains for complex bugs; bypass_lookup when blocked; " + " payload_lookup when crafting a probe. technique_lookup(cls) returns the " + " DETAILED methodology + tricks for a vulnerability class (ssrf, ssti, " + " jwt, deserialization, ...) - load before testing a class. wstg_map maps " + " an OWASP category to coverage; taxonomy maps findings to standard IDs " + " (OWASP Top 10 / API Top 10 / CWE / ASVS); doctrine_map shows how everything links.\\n\\n" + "NO-BABYSITTING (while scanning + testing):\\n" + "- Scan the WHOLE surface before reporting - every file, entry point, and sink. " + " Do not stop after the first hit to ask; keep enumerating the full tree.\\n" + "- Test every hypothesis through its full verification cycle - vary probes on " + " failure, apply bypasses when blocked, keep going. Do not pause to ask.\\n" + "- Persist through partial results: a scan that surfaces few sinks is NOT done - " + " widen the surface, escalate detectors, dig into each reachable sink.\\n" + "- NEVER ask 'should I continue?' / 'proceed?' / approval-style questions. " + " Continue autonomously - the moment one step verifies, start the next. " + " Pause ONLY when: a genuinely destructive/DoS action needs consent, an " + " external dependency beyond your control blocks you (e.g. phone OTP), or " + " the engagement is COMPLETE (deliver the final report). Otherwise keep hunting.\\n\\n" + "PERSISTENCE (complex bugs - do not give up):\\n" + "- There is NO retry cap. A failed probe/sub-agent is diagnosed and resumed " + " until verified or definitively ruled out - never move on unverified.\\n" + "- On any failure (empty output, blocked probe, failed sub-agent/model), call " + " retry_guidance(signal) for concrete recovery actions before giving up.\\n" + "- On a model/sub-agent error (rate-limit, quota, timeout, 5xx, auth, " + " unavailable), model_fallback(signal) classifies it and returns the exact " + " recovery (retry+backoff / fall back / shrink scope / fix credentials / " + " abort). Do not fall back blindly - the class decides.\\n" + "- One failed probe != not vulnerable. Vary: alternative sinks, other params, " + " encoding/case/whitespace bypasses, different injection context.\\n" + "- Empty output != no findings. Re-run (server may be stale after an upgrade - " + " /reload-mcp), or escalate to route_scan / complex_scan for classes taint " + " misses (POP chains, type juggling, mass assignment, route confusion).\\n" + "- A 'sanitized' sink may not be safe. Challenge the sanitizer: loose == vs ===, " + " missing EXTR_SKIP, int cast truncation, weak regex.\\n" + "- An unconfirmed hypothesis is KEPT as hypothesis - try alternate verification, " + " then let adversarial review decide.\\n" + "- Complex chains (deserialization->POP, LFI->RCE) need the FULL chain traced, " + ` not just the first sink.\\n
|
|
54166
|
+
instructions: "Blitz Strike is a universal penetration-testing toolbelt with three tiers — " + "BLITZ (reconnaissance / attack-surface mapping), EAGLE-EYE (source-to-sink " + "analysis), STRIKE (live validation) — plus a canonical evidence-first finding " + "engine and a multi-language taint engine.\\n\\n" + "CORE MENTAL MODEL:\\n" + "- A scanner hit is a HYPOTHESIS, not a finding. Live verification (or data-flow " + " proof) is the verdict. Never report an unverified hit as a vulnerability.\\n" + "- Distinguish REACHABLE from mere PRESENT: a sink is only a finding when tainted " + " input reaches it AND it is not neutralized by a sanitizer and not guarded by an " + " auth gate.\\n\\n" + "HOW TO RUN AN AUDIT (start here):\\n" + "0. ROUTE BY TARGET. Classify automatically - no need to ask. A URL - with or " + " without http/https - is LIVE (live_recon + the " + " web-hunting doctrine + strike_verify). A filesystem path (exists on disk, " + " or a code extension) is SOURCE audit (source-audit doctrine: blitz_scan + " + " eagle_eye2 + complex_scan/route_scan). TWO DOCTRINES - never mix: live " + " web/API -> web-hunting skill; source tree -> source-audit skill. For a WHOLE " + " engagement prefer run_engagement (one call, no babysitting) FIRST; steps 1-5 " + " below are the manual path for targeted follow-up on a specific finding.\\n" + "1. RECORD SCOPE. scope_check classifies the target (intel for the report) - " + " it does NOT gate the engagement. No authorization gate: the repo disclaimer " + " places responsibility on the user. Hunt the target you are given.\\n" + "2. MAP THE SURFACE (BLITZ). For a source tree use blitz_scan (tree) or blitz_file " + " (single file) then enrich_scan to match sinks to escalation chains. For a live " + " URL use live_recon for a full pass (fingerprint/WAF/tech/version/crawler/params/" + " subdomains/API endpoints/intel correlation) — it returns next_steps. active_scan " + " is the lighter alternative. Both record scope internally.\\n" + "3. TRACE REACHABILITY (EAGLE-EYE). For every sink, determine whether attacker " + " input actually reaches it:\\n" + " - PHP/JS/TS/Python/Java source: taint_file (auto-detects language) or " + " taint_scan/taint_tree (PHP AST). list_languages shows what's supported.\\n" + " - Any language: trace_data_flow (window heuristic) and eagle_eye/eagle_grep " + " (function-scoped view of sinks + auth gates). Beyond taint: route_scan (route confusion / dispatch abuse / batch forwarding) and complex_scan (deserialization->POP, type juggling, mass assignment, prototype pollution, CRLF/header injection, path confusion) catch classes plain taint misses.\\n" + " A sink counts only if tainted input reaches it unsanitized and unguarded.\\n" + "4. VALIDATE (STRIKE). Confirm live with strike_verify using a MARKER plus a " + " NEGATIVE CONTROL plus a BASELINE. The verdict is deterministic: marker " + " reflected AND control inert = confirmed; both reflected = false_positive; " + " marker not reflected = unconfirmed. Feed the verdict to strike_resolve to " + " advance the finding lifecycle (hypothesis->validating->confirmed). Use " + " read_tool_manual for the exploit tool's manual, payload_lookup for payloads, " + " detect_waf/tech_correlation for context.\\n" + "5. RECORD + REPORT. Create a canonical finding with finding_create, advance it " + " through the lifecycle with finding_transition, attach confidence_score, and " + " redact any secrets before persisting. Only report findings that survived " + " verification.\\n\\n" + "SUPPORTING LAYERS:\\n" + "- run_engagement: ONE call does the whole audit (no babysitting). Source path -> " + " full static pipeline + inline report (markdown/JSON/SARIF). URL target -> " + " full autonomous live pipeline (live_recon recon + deterministic checks: reflected " + " input, security headers, open redirect, path traversal, CORS) + findings + report. " + " PREFER THIS FIRST for a whole engagement; drive " + " the steps above manually only for targeted follow-up on a specific finding.\\n" + "- run_autonomous: run_engagement + the escalation path pre-attached to every " + " finding (chain name/severity/prerequisites + ordered next_steps with tool_hint/" + " success_criteria/negative_control) + a deterministic termination reason. Use it " + " when you want the 'what to do next' already computed per finding.\\n" + "- list_chains: 57 escalation chains (source -> sink -> impact).\\n" + "- list_languages / taint_file: multi-language taint (PHP, JS/TS, Python, Java).\\n" + "- tool_lookup / ensure_tool / list_tools: 130-tool catalog, auto-install.\\n" + "- read_playbook / list_playbooks: 17 engagement playbooks (web-app, api-security, " + " source-code-audit, active-directory, mobile, external-attack-surface, ...).\\n" + "- skill_lookup / read_skill / list_skills: 39 skills (7 bs-* agent doctrine: " + " orchestrate, scope, source-audit, web-hunting, verify, review, report; + " + " technique playbooks: WP/PHP 0-day, AD, malware, mobile, cloud).\\n" + "- read_tool_manual / list_manuals: 317 tool manuals (sqlmap, nmap, commix, ...).\\n" + "- payload_lookup / read_payload: 66 payload collections (sqli, xss, ssti, xxe, lfi).\\n" + "- detect_waf / tech_correlation / cve_correlation / port_correlation: signature " + " + correlation intelligence.\\n" + "- nvd_lookup / fofa_search: CVE lookup + asset reconnaissance (FOFA needs env creds).\\n" + "- remember / memory_lookup / memory_list: persist + recall verified knowledge.\\n\\n" + "RESOURCE MAP (know what to reach for):\\n" + "- ORCHESTRATION = the engagement framework. orchestration() returns the " + " lifecycle (scope->recon->analyze->deep->verify->report, each with " + " entry/exit criteria) + sub-agent team + handoff contract + termination " + " criteria. Load it at engagement start. Autonomous driving aids: plan() " + " (deterministic phase plan + next action), delegate() (parallel-first " + " subagent batches + retry pattern), worktree() (isolate a workstream), " + " watchdog() (detect a stalled phase), context_prune() (compact state so " + " you can drop raw detail), and task_start/task_checkpoint/task_status/" + " task_resume (per-workstream checkpoint journal — resume a failed task " + " from its last completed step, never restart).\\n" + "- TOOL = acts (scan, taint, verify, record, correlate). Call it to DO something.\\n" + "- PLAYBOOK = campaign plan for a target TYPE. read_playbook(web-application) " + " BEFORE a full engagement of that type.\\n" + "- SKILL = procedure doctrine. Load the bs-* skill for the phase you are on " + " (orchestrate, scope, web-hunting for LIVE, source-audit for SOURCE, verify, " + " review, report); load a technique skill for the specific technique.\\n" + "- MANUAL = deep reference for ONE tool. read_tool_manual right before using it.\\n" + "- CATALOG = external tools (130: sqlmap, nmap, nuclei, ffuf, ysoserial, ...). " + " Blitz Strike's OWN tools find + verify; the catalog is for deeper EXTERNAL " + " exploitation/enumeration. After your own analysis confirms a finding, run " + " tool_lookup -> ensure_tool (auto-install) -> read_tool_manual to drive the " + " external tool that demonstrates impact.\\n" + "- PAYLOAD = attack payloads for a class. payload_lookup before crafting a probe.\\n" + "- INTELLIGENCE = knowledge. list_attack_vectors (34 categories) maps the " + " full surface during RECON; list_chains (57) turns a finding into impact; " + " chain_links composes chains for complex bugs; bypass_lookup when blocked; " + " payload_lookup when crafting a probe. technique_lookup(cls) returns the " + " DETAILED methodology + tricks for a vulnerability class (ssrf, ssti, " + " jwt, deserialization, ...) - load before testing a class. wstg_map maps " + " an OWASP category to coverage; taxonomy maps findings to standard IDs " + " (OWASP Top 10 / API Top 10 / CWE / ASVS); doctrine_map shows how everything links.\\n\\n" + "NO-BABYSITTING (while scanning + testing):\\n" + "- Scan the WHOLE surface before reporting - every file, entry point, and sink. " + " Do not stop after the first hit to ask; keep enumerating the full tree.\\n" + "- Test every hypothesis through its full verification cycle - vary probes on " + " failure, apply bypasses when blocked, keep going. Do not pause to ask.\\n" + "- Persist through partial results: a scan that surfaces few sinks is NOT done - " + " widen the surface, escalate detectors, dig into each reachable sink.\\n" + "- NEVER ask 'should I continue?' / 'proceed?' / approval-style questions. " + " Continue autonomously - the moment one step verifies, start the next. " + " Pause ONLY when: a genuinely destructive/DoS action needs consent, an " + " external dependency beyond your control blocks you (e.g. phone OTP), or " + " the engagement is COMPLETE (deliver the final report). Otherwise keep hunting.\\n\\n" + "PERSISTENCE (complex bugs - do not give up):\\n" + "- There is NO retry cap. A failed probe/sub-agent is diagnosed and resumed " + " until verified or definitively ruled out - never move on unverified.\\n" + "- On any failure (empty output, blocked probe, failed sub-agent/model), call " + " retry_guidance(signal) for concrete recovery actions before giving up.\\n" + "- On a model/sub-agent error (rate-limit, quota, timeout, 5xx, auth, " + " unavailable), model_fallback(signal) classifies it and returns the exact " + " recovery (retry+backoff / fall back / shrink scope / fix credentials / " + " abort). Do not fall back blindly - the class decides.\\n" + "- One failed probe != not vulnerable. Vary: alternative sinks, other params, " + " encoding/case/whitespace bypasses, different injection context.\\n" + "- Empty output != no findings. Re-run (server may be stale after an upgrade - " + " /reload-mcp), or escalate to route_scan / complex_scan for classes taint " + " misses (POP chains, type juggling, mass assignment, route confusion).\\n" + "- A 'sanitized' sink may not be safe. Challenge the sanitizer: loose == vs ===, " + " missing EXTR_SKIP, int cast truncation, weak regex.\\n" + "- An unconfirmed hypothesis is KEPT as hypothesis - try alternate verification, " + " then let adversarial review decide.\\n" + "- Complex chains (deserialization->POP, LFI->RCE) need the FULL chain traced, " + ` not just the first sink.\\n
|
|
54021
54167
|
` + "- When a probe is BLOCKED by a WAF/filter/sanitizer, call bypass_lookup(defense) " + " for concrete techniques (encoding, case, comment insertion, chunked, null byte, " + " double-encoding, verb tampering, header spoofing) and apply them - do not give up.\\n" + "- CHAIN findings: a low-severity finding may escalate — do not assume it is the " + " end of the story. list_chains / chain_links show whether it composes into a " + " multi-stage chain (e.g. SSRF->metadata, SQLi->RCE, LFI->RCE, upload->shell). " + " Check before concluding, and do not overclaim an escalation that is not there.\\n\\n" + "- Complex bugs chain ACROSS chains: after confirming one chain, chain_links " + " shows what follows (SSRF->metadata then default-creds->pivot via stolen IAM). " + " Trace the full composition, not just a single chain.\\n\\n" + "HYPOTHESIS EXPANSION (from each finding, enumerate + verify every follow-up):\\n" + "- A confirmed finding is the START, not the end. From each one, enumerate " + " the follow-ups: ESCALATE (list_chains + chain_links), CHAIN (chain_links " + " from->to), BYPASS (bypass_lookup), PIVOT (new endpoints/sinks now reachable).\\n" + "- Every follow-up (this might happen, this could chain, maybe bypassable) is " + " a HYPOTHESIS, not a finding. Verify EACH via strike_verify (marker + negative " + " control) before it enters the report.\\n" + "- Never report speculation: might / could / possibly live in the " + " hypothesis list, never in the report. Every report line is a verified fact.\\n\\n" + "PROFESSIONAL JUDGMENT (act like a senior tester, not a checklist runner):\\n" + "- PRIORITIZE by severity, computed deterministically with cvss_score (impact x " + " exploitability) and weighted by escalation potential (chain_links). Do NOT rank " + " by bug class - a critical stored XSS that yields account takeover outranks an " + " unreachable blind RCE. Weight by the engagement context (which asset/data is " + " most sensitive for THIS target).\\n" + "- Recognize dead ends: if a branch yields nothing after a few varied probes, " + " note it and move on. Do not tunnel-vision one sink at the expense of the rest.\\n" + "- Live-test with care: respect rate limits, do not lock accounts, do not " + " trigger DoS. Test non-destructively and prove impact with the smallest payload.\\n" + "- Know when done: stop when the termination criteria are met, not when you feel " + " tired. A thorough audit over a rushed one, every time.\\n\\n" + "IRON RULES:\\n" + "- No authorization gate - the repo disclaimer places responsibility on the " + " user. Hunt the target you are given (like nmap/sqlmap/Burp, which do not " + " ask). The one hard stop: malicious/third-party infra (cari pelaku) - hold/static.\\n" + " need finer control."
|
|
54022
54168
|
});
|
|
54023
54169
|
server.registerTool("blitz_scan", {
|
|
@@ -54226,6 +54372,28 @@ ${formatPlanChecklist(r.plan)}` : "";
|
|
|
54226
54372
|
content: [
|
|
54227
54373
|
{ type: "text", text: banner ? `${banner}${planText}
|
|
54228
54374
|
|
|
54375
|
+
${statusLine}` : statusLine },
|
|
54376
|
+
{ type: "text", text: JSON.stringify(r) }
|
|
54377
|
+
]
|
|
54378
|
+
};
|
|
54379
|
+
});
|
|
54380
|
+
server.registerTool("run_autonomous", {
|
|
54381
|
+
title: "STRIKE autonomous engagement (surface + escalation)",
|
|
54382
|
+
description: "ORCHESTRATION: the self-driving pass — surface pipeline + escalation path attached to every finding, in ONE call. Each finding comes back with its chain (name/severity/prerequisites) and ordered next_steps (action + tool_hint + success_criteria + negative_control), so you never guess what to do next. Returns the full report + a deterministic termination reason. Prefer this over run_engagement when you want the escalation paths pre-attached.",
|
|
54383
|
+
inputSchema: {
|
|
54384
|
+
target: string2().describe("Source path or URL"),
|
|
54385
|
+
scope: string2().optional().describe("Scope (for URL targets)"),
|
|
54386
|
+
mode: string2().optional().describe("Engagement mode"),
|
|
54387
|
+
max_passes: number2().int().min(1).max(5).optional().describe("Max deepen passes (default 3)")
|
|
54388
|
+
}
|
|
54389
|
+
}, async ({ target, scope, mode, max_passes }) => {
|
|
54390
|
+
const r = await runAutonomous(target, scope ?? "", mode ?? "bug-bounty", max_passes ?? 3);
|
|
54391
|
+
const banner = typeof r.banner === "string" ? r.banner : "";
|
|
54392
|
+
const statusLine = `→ ${Array.isArray(r.findings) ? r.findings.length : 0} finding(s), termination=${r.termination ?? "complete"}`;
|
|
54393
|
+
return {
|
|
54394
|
+
content: [
|
|
54395
|
+
{ type: "text", text: banner ? `${banner}
|
|
54396
|
+
|
|
54229
54397
|
${statusLine}` : statusLine },
|
|
54230
54398
|
{ type: "text", text: JSON.stringify(r) }
|
|
54231
54399
|
]
|
|
@@ -54665,6 +54833,48 @@ ${statusLine}` : statusLine },
|
|
|
54665
54833
|
const { batches, total } = decomposeBatches(tasks);
|
|
54666
54834
|
return { content: [{ type: "text", text: JSON.stringify({ total, batches, retry: retryPattern("a workstream") }, null, 2) }] };
|
|
54667
54835
|
});
|
|
54836
|
+
server.registerTool("task_start", {
|
|
54837
|
+
title: "Register a task with a checkpointed plan",
|
|
54838
|
+
description: "ORCHESTRATION: register a delegated workstream with an ordered plan (steps). Every step records a checkpoint so a failed workstream can be RESUMED from its last completed step instead of restarting. On-disk, so checkpoints survive restarts.",
|
|
54839
|
+
inputSchema: {
|
|
54840
|
+
task_id: string2().describe("Unique task id (e.g. 'recon-api', 'audit-auth')"),
|
|
54841
|
+
objective: string2().describe("What the task must accomplish"),
|
|
54842
|
+
plan: array(string2()).describe("Ordered steps (e.g. ['recon','analyze','verify','report'])")
|
|
54843
|
+
}
|
|
54844
|
+
}, async ({ task_id, objective, plan }) => {
|
|
54845
|
+
return { content: [{ type: "text", text: JSON.stringify(taskStart(task_id, objective, plan), null, 2) }] };
|
|
54846
|
+
});
|
|
54847
|
+
server.registerTool("task_checkpoint", {
|
|
54848
|
+
title: "Record a task checkpoint",
|
|
54849
|
+
description: "ORCHESTRATION: record a checkpoint for a task step (in_progress / done / failed / blocked) with an optional note + output hash. Advancing 'done' moves current_step forward; a 'failed' checkpoint marks the task for resume.",
|
|
54850
|
+
inputSchema: {
|
|
54851
|
+
task_id: string2().describe("Task id"),
|
|
54852
|
+
step: string2().describe("Step name (must be in the plan)"),
|
|
54853
|
+
status: _enum(["in_progress", "done", "failed", "blocked"]).describe("Checkpoint status"),
|
|
54854
|
+
note: string2().optional().describe("What happened at this step"),
|
|
54855
|
+
output_hash: string2().optional().describe("Hash of the artifacts produced (for resume dedup)")
|
|
54856
|
+
}
|
|
54857
|
+
}, async ({ task_id, step, status, note, output_hash }) => {
|
|
54858
|
+
return { content: [{ type: "text", text: JSON.stringify(taskCheckpoint(task_id, step, status, note, output_hash), null, 2) }] };
|
|
54859
|
+
});
|
|
54860
|
+
server.registerTool("task_status", {
|
|
54861
|
+
title: "Read a task's checkpoint state",
|
|
54862
|
+
description: "ORCHESTRATION: read a task's current state — status, current step, progress (done/total), remaining steps, and full checkpoint history. The orchestrator's deterministic view of where each workstream is.",
|
|
54863
|
+
inputSchema: {
|
|
54864
|
+
task_id: string2().describe("Task id")
|
|
54865
|
+
}
|
|
54866
|
+
}, async ({ task_id }) => {
|
|
54867
|
+
return { content: [{ type: "text", text: JSON.stringify(taskStatus(task_id), null, 2) }] };
|
|
54868
|
+
});
|
|
54869
|
+
server.registerTool("task_resume", {
|
|
54870
|
+
title: "Resume a task from its last checkpoint",
|
|
54871
|
+
description: "ORCHESTRATION: compute the resume instruction for a failed/blocked task — the last completed checkpoint, the current step, the remaining steps, and the exact re-dispatch prompt (resume from where it stopped, not a fresh start).",
|
|
54872
|
+
inputSchema: {
|
|
54873
|
+
task_id: string2().describe("Task id")
|
|
54874
|
+
}
|
|
54875
|
+
}, async ({ task_id }) => {
|
|
54876
|
+
return { content: [{ type: "text", text: JSON.stringify(taskResume(task_id), null, 2) }] };
|
|
54877
|
+
});
|
|
54668
54878
|
server.registerTool("worktree", {
|
|
54669
54879
|
title: "Isolate a git worktree",
|
|
54670
54880
|
description: "ORCHESTRATION: create/list/remove an isolated git worktree so parallel workstreams don't conflict on the same files. add: `git worktree add -b <branch> <path>`; list: enumerate existing; remove: clean up after a workstream.",
|
|
@@ -54907,7 +55117,7 @@ ${statusLine}` : statusLine },
|
|
|
54907
55117
|
description: "Route confusion (separate class from source→sink taint): detect attacker-controlled dispatch (call_user_func/forward/dispatch), dynamic method calls ($obj->$m()), dynamic include (LFI), batch/proxy forwarding without per-route auth re-check, and route matching without path normalization.",
|
|
54908
55118
|
inputSchema: { path: string2().describe("Source file path (PHP)") }
|
|
54909
55119
|
}, async ({ path }) => {
|
|
54910
|
-
const code =
|
|
55120
|
+
const code = readFileSync19(path, "utf8");
|
|
54911
55121
|
const findings = detectRouteConfusion(code, path);
|
|
54912
55122
|
return {
|
|
54913
55123
|
content: [{
|
|
@@ -54921,7 +55131,7 @@ ${statusLine}` : statusLine },
|
|
|
54921
55131
|
description: "Complex-bug detection beyond taint: deserialization→POP gadget chain (unserialize + magic method), type juggling (loose ==/!= vs hash/secret), mass assignment (extract/parse_str without a safe flag), prototype pollution (unsafe merge of request data), CRLF/header injection (user input into a header), and path confusion (user input into a file path without canonicalization).",
|
|
54922
55132
|
inputSchema: { path: string2().describe("Source file path") }
|
|
54923
55133
|
}, async ({ path }) => {
|
|
54924
|
-
const code =
|
|
55134
|
+
const code = readFileSync19(path, "utf8");
|
|
54925
55135
|
const findings = detectComplexBugs(code, path);
|
|
54926
55136
|
return {
|
|
54927
55137
|
content: [{
|
|
@@ -54988,7 +55198,7 @@ ${statusLine}` : statusLine },
|
|
|
54988
55198
|
let lang = language;
|
|
54989
55199
|
if (path && !code) {
|
|
54990
55200
|
try {
|
|
54991
|
-
src =
|
|
55201
|
+
src = readFileSync19(path, "utf8");
|
|
54992
55202
|
} catch {
|
|
54993
55203
|
return { content: [{ type: "text", text: JSON.stringify({ error: `cannot read ${path}` }) }] };
|
|
54994
55204
|
}
|
|
@@ -55219,23 +55429,23 @@ async function serve() {
|
|
|
55219
55429
|
}
|
|
55220
55430
|
|
|
55221
55431
|
// src/cli.ts
|
|
55222
|
-
import { readFileSync as
|
|
55223
|
-
import { join as
|
|
55432
|
+
import { readFileSync as readFileSync22, existsSync as existsSync9, writeFileSync as writeFileSync7, mkdirSync as mkdirSync5 } from "node:fs";
|
|
55433
|
+
import { join as join18, dirname as dirname2 } from "node:path";
|
|
55224
55434
|
import { homedir as homedir5 } from "node:os";
|
|
55225
|
-
import { fileURLToPath as
|
|
55435
|
+
import { fileURLToPath as fileURLToPath12 } from "node:url";
|
|
55226
55436
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
55227
55437
|
|
|
55228
55438
|
// src/release.ts
|
|
55229
|
-
import { readFileSync as
|
|
55230
|
-
import { join as
|
|
55231
|
-
import { fileURLToPath as
|
|
55439
|
+
import { readFileSync as readFileSync20, existsSync as existsSync8 } from "node:fs";
|
|
55440
|
+
import { join as join16 } from "node:path";
|
|
55441
|
+
import { fileURLToPath as fileURLToPath10 } from "node:url";
|
|
55232
55442
|
import { execSync as execSync4 } from "node:child_process";
|
|
55233
|
-
var ROOT8 =
|
|
55443
|
+
var ROOT8 = join16(fileURLToPath10(new URL(".", import.meta.url)), "..");
|
|
55234
55444
|
function validateRelease() {
|
|
55235
55445
|
const checks = [];
|
|
55236
55446
|
checks.push({ name: "package.json version", ok: true, detail: VERSION });
|
|
55237
55447
|
try {
|
|
55238
|
-
const changelog =
|
|
55448
|
+
const changelog = readFileSync20(join16(ROOT8, "CHANGELOG.md"), "utf8");
|
|
55239
55449
|
const m = changelog.match(/^##\s+\[([0-9]+\.[0-9]+\.[0-9]+)\]/m);
|
|
55240
55450
|
const changelogVersion = m?.[1] ?? null;
|
|
55241
55451
|
checks.push({
|
|
@@ -55267,18 +55477,18 @@ function validateRelease() {
|
|
|
55267
55477
|
return { version: VERSION, checks, consistent: checks.filter((c) => !c.soft).every((c) => c.ok) };
|
|
55268
55478
|
}
|
|
55269
55479
|
function lockfilePresent() {
|
|
55270
|
-
return existsSync8(
|
|
55480
|
+
return existsSync8(join16(ROOT8, "package-lock.json")) || existsSync8(join16(ROOT8, "bun.lock")) || existsSync8(join16(ROOT8, "bun.lockb"));
|
|
55271
55481
|
}
|
|
55272
55482
|
|
|
55273
55483
|
// src/dependency.ts
|
|
55274
|
-
import { readFileSync as
|
|
55275
|
-
import { join as
|
|
55276
|
-
import { fileURLToPath as
|
|
55277
|
-
var ROOT9 =
|
|
55484
|
+
import { readFileSync as readFileSync21 } from "node:fs";
|
|
55485
|
+
import { join as join17 } from "node:path";
|
|
55486
|
+
import { fileURLToPath as fileURLToPath11 } from "node:url";
|
|
55487
|
+
var ROOT9 = join17(fileURLToPath11(new URL(".", import.meta.url)), "..");
|
|
55278
55488
|
function checkDependencies() {
|
|
55279
55489
|
let pkg = {};
|
|
55280
55490
|
try {
|
|
55281
|
-
pkg = JSON.parse(
|
|
55491
|
+
pkg = JSON.parse(readFileSync21(join17(ROOT9, "package.json"), "utf8"));
|
|
55282
55492
|
} catch {
|
|
55283
55493
|
return { install_scripts: [], git_dependencies: [], total_dependencies: 0, safe: false };
|
|
55284
55494
|
}
|
|
@@ -55301,7 +55511,7 @@ function checkDependencies() {
|
|
|
55301
55511
|
}
|
|
55302
55512
|
|
|
55303
55513
|
// src/cli.ts
|
|
55304
|
-
var ROOT10 =
|
|
55514
|
+
var ROOT10 = join18(fileURLToPath12(new URL(".", import.meta.url)), "..");
|
|
55305
55515
|
function which(cmd) {
|
|
55306
55516
|
return spawnSync3(`command -v ${cmd}`, { shell: true }).status === 0;
|
|
55307
55517
|
}
|
|
@@ -55336,7 +55546,7 @@ function runDoctor() {
|
|
|
55336
55546
|
detail: fofa ? "set" : "FOFA_EMAIL/FOFA_KEY not set",
|
|
55337
55547
|
fix: fofa ? undefined : "export FOFA_EMAIL=... && export FOFA_KEY=... (enables fofa_search)"
|
|
55338
55548
|
});
|
|
55339
|
-
const dataOk = existsSync9(
|
|
55549
|
+
const dataOk = existsSync9(join18(ROOT10, "chains.json")) && existsSync9(join18(ROOT10, "tools-catalog.json"));
|
|
55340
55550
|
issues.push({
|
|
55341
55551
|
name: "Data layers (chains + tools-catalog)",
|
|
55342
55552
|
status: dataOk ? "ok" : "fail",
|
|
@@ -55378,21 +55588,21 @@ BlitzStrike — ${verdict}`);
|
|
|
55378
55588
|
function resolveCommand() {
|
|
55379
55589
|
if (which("blitzstrike"))
|
|
55380
55590
|
return { command: "blitzstrike", args: ["serve", "--mcp"] };
|
|
55381
|
-
const src =
|
|
55591
|
+
const src = join18(ROOT10, "src", "index.ts");
|
|
55382
55592
|
if (which("bun") && existsSync9(src))
|
|
55383
55593
|
return { command: "bun", args: ["run", src, "serve", "--mcp"] };
|
|
55384
55594
|
if (which("npx"))
|
|
55385
55595
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
55386
55596
|
if (which("bunx"))
|
|
55387
55597
|
return { command: "bunx", args: ["blitzstrike", "serve", "--mcp"] };
|
|
55388
|
-
const dist =
|
|
55598
|
+
const dist = join18(ROOT10, "dist", "index.js");
|
|
55389
55599
|
if (existsSync9(dist))
|
|
55390
55600
|
return { command: "bun", args: [dist, "serve", "--mcp"] };
|
|
55391
55601
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
55392
55602
|
}
|
|
55393
55603
|
function readJson(p) {
|
|
55394
55604
|
try {
|
|
55395
|
-
return JSON.parse(
|
|
55605
|
+
return JSON.parse(readFileSync22(p, "utf8"));
|
|
55396
55606
|
} catch {
|
|
55397
55607
|
return null;
|
|
55398
55608
|
}
|
|
@@ -55401,41 +55611,41 @@ function jsonMcpServersWrite(p) {
|
|
|
55401
55611
|
return (command, args) => {
|
|
55402
55612
|
const dir = dirname2(p);
|
|
55403
55613
|
if (!existsSync9(dir))
|
|
55404
|
-
|
|
55614
|
+
mkdirSync5(dir, { recursive: true });
|
|
55405
55615
|
const existing = readJson(p) ?? {};
|
|
55406
55616
|
existing.mcpServers = {
|
|
55407
55617
|
...existing.mcpServers ?? {},
|
|
55408
55618
|
blitzstrike: { command, args }
|
|
55409
55619
|
};
|
|
55410
|
-
|
|
55620
|
+
writeFileSync7(p, JSON.stringify(existing, null, 2));
|
|
55411
55621
|
};
|
|
55412
55622
|
}
|
|
55413
55623
|
function opencodeWrite(p) {
|
|
55414
55624
|
return (_command, _args) => {
|
|
55415
55625
|
const dir = dirname2(p);
|
|
55416
55626
|
if (!existsSync9(dir))
|
|
55417
|
-
|
|
55627
|
+
mkdirSync5(dir, { recursive: true });
|
|
55418
55628
|
const existing = readJson(p) ?? {};
|
|
55419
55629
|
existing.mcp = {
|
|
55420
55630
|
...existing.mcp ?? {},
|
|
55421
55631
|
blitzstrike: { type: "local", command: ["npx", "-y", "blitzstrike", "serve", "--mcp"], enabled: true }
|
|
55422
55632
|
};
|
|
55423
|
-
|
|
55633
|
+
writeFileSync7(p, JSON.stringify(existing, null, 2));
|
|
55424
55634
|
copyOpenCodeAgents();
|
|
55425
55635
|
};
|
|
55426
55636
|
}
|
|
55427
55637
|
function copyOpenCodeAgents() {
|
|
55428
|
-
const srcDir =
|
|
55429
|
-
const dstDir =
|
|
55638
|
+
const srcDir = join18(ROOT10, "opencode-agents");
|
|
55639
|
+
const dstDir = join18(homedir5(), ".config", "opencode", "agents");
|
|
55430
55640
|
if (!existsSync9(srcDir))
|
|
55431
55641
|
return;
|
|
55432
55642
|
if (!existsSync9(dstDir))
|
|
55433
|
-
|
|
55643
|
+
mkdirSync5(dstDir, { recursive: true });
|
|
55434
55644
|
for (const f of ["Blitz Strike.md", "Blitz.md", "Eagle Eye.md", "Strike.md"]) {
|
|
55435
|
-
const src =
|
|
55436
|
-
const dst =
|
|
55645
|
+
const src = join18(srcDir, f);
|
|
55646
|
+
const dst = join18(dstDir, f);
|
|
55437
55647
|
if (existsSync9(src)) {
|
|
55438
|
-
|
|
55648
|
+
writeFileSync7(dst, readFileSync22(src, "utf8"));
|
|
55439
55649
|
}
|
|
55440
55650
|
}
|
|
55441
55651
|
}
|
|
@@ -55443,8 +55653,8 @@ function codexWrite(p) {
|
|
|
55443
55653
|
return (command, args) => {
|
|
55444
55654
|
const dir = dirname2(p);
|
|
55445
55655
|
if (!existsSync9(dir))
|
|
55446
|
-
|
|
55447
|
-
let existing = existsSync9(p) ?
|
|
55656
|
+
mkdirSync5(dir, { recursive: true });
|
|
55657
|
+
let existing = existsSync9(p) ? readFileSync22(p, "utf8") : "";
|
|
55448
55658
|
if (!existing.trimEnd().endsWith(`
|
|
55449
55659
|
`))
|
|
55450
55660
|
existing += `
|
|
@@ -55455,15 +55665,15 @@ function codexWrite(p) {
|
|
|
55455
55665
|
command = "${command}"
|
|
55456
55666
|
args = ${JSON.stringify(args)}
|
|
55457
55667
|
`;
|
|
55458
|
-
|
|
55668
|
+
writeFileSync7(p, existing);
|
|
55459
55669
|
};
|
|
55460
55670
|
}
|
|
55461
55671
|
function hermesWrite(p) {
|
|
55462
55672
|
return (command, args) => {
|
|
55463
55673
|
const dir = dirname2(p);
|
|
55464
55674
|
if (!existsSync9(dir))
|
|
55465
|
-
|
|
55466
|
-
let existing = existsSync9(p) ?
|
|
55675
|
+
mkdirSync5(dir, { recursive: true });
|
|
55676
|
+
let existing = existsSync9(p) ? readFileSync22(p, "utf8") : "";
|
|
55467
55677
|
existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
|
|
55468
55678
|
if (!existing.trimEnd().endsWith(`
|
|
55469
55679
|
`))
|
|
@@ -55484,80 +55694,80 @@ mcp_servers:
|
|
|
55484
55694
|
args: ${JSON.stringify(args)}
|
|
55485
55695
|
`;
|
|
55486
55696
|
}
|
|
55487
|
-
|
|
55697
|
+
writeFileSync7(p, existing);
|
|
55488
55698
|
};
|
|
55489
55699
|
}
|
|
55490
55700
|
function detectAgents() {
|
|
55491
55701
|
const home = homedir5();
|
|
55492
55702
|
const agents = [];
|
|
55493
|
-
const claudeJson =
|
|
55703
|
+
const claudeJson = join18(home, ".claude.json");
|
|
55494
55704
|
agents.push({
|
|
55495
55705
|
name: "Claude Code",
|
|
55496
55706
|
detectPath: claudeJson,
|
|
55497
55707
|
installed: () => existsSync9(claudeJson) || which("claude"),
|
|
55498
55708
|
write: jsonMcpServersWrite(claudeJson)
|
|
55499
55709
|
});
|
|
55500
|
-
const claudeDesktop =
|
|
55710
|
+
const claudeDesktop = join18(home, ".config", "Claude", "claude_desktop_config.json");
|
|
55501
55711
|
agents.push({
|
|
55502
55712
|
name: "Claude Desktop",
|
|
55503
55713
|
detectPath: claudeDesktop,
|
|
55504
55714
|
installed: () => existsSync9(claudeDesktop),
|
|
55505
55715
|
write: jsonMcpServersWrite(claudeDesktop)
|
|
55506
55716
|
});
|
|
55507
|
-
const cursor =
|
|
55717
|
+
const cursor = join18(home, ".cursor", "mcp.json");
|
|
55508
55718
|
agents.push({
|
|
55509
55719
|
name: "Cursor",
|
|
55510
55720
|
detectPath: cursor,
|
|
55511
|
-
installed: () => existsSync9(
|
|
55721
|
+
installed: () => existsSync9(join18(home, ".cursor")) || which("cursor"),
|
|
55512
55722
|
write: jsonMcpServersWrite(cursor)
|
|
55513
55723
|
});
|
|
55514
|
-
const opencode =
|
|
55724
|
+
const opencode = join18(home, ".config", "opencode", "opencode.jsonc");
|
|
55515
55725
|
agents.push({
|
|
55516
55726
|
name: "OpenCode",
|
|
55517
55727
|
detectPath: opencode,
|
|
55518
|
-
installed: () => existsSync9(
|
|
55728
|
+
installed: () => existsSync9(join18(home, ".config", "opencode")) || which("opencode"),
|
|
55519
55729
|
write: opencodeWrite(opencode)
|
|
55520
55730
|
});
|
|
55521
|
-
const codex =
|
|
55731
|
+
const codex = join18(home, ".codex", "config.toml");
|
|
55522
55732
|
agents.push({
|
|
55523
55733
|
name: "Codex",
|
|
55524
55734
|
detectPath: codex,
|
|
55525
|
-
installed: () => existsSync9(
|
|
55735
|
+
installed: () => existsSync9(join18(home, ".codex")) || which("codex"),
|
|
55526
55736
|
write: codexWrite(codex)
|
|
55527
55737
|
});
|
|
55528
|
-
const hermes =
|
|
55738
|
+
const hermes = join18(home, ".hermes", "config.yaml");
|
|
55529
55739
|
agents.push({
|
|
55530
55740
|
name: "Hermes",
|
|
55531
55741
|
detectPath: hermes,
|
|
55532
|
-
installed: () => existsSync9(
|
|
55742
|
+
installed: () => existsSync9(join18(home, ".hermes")) || which("hermes"),
|
|
55533
55743
|
write: hermesWrite(hermes)
|
|
55534
55744
|
});
|
|
55535
|
-
const gemini =
|
|
55745
|
+
const gemini = join18(home, ".gemini", "settings.json");
|
|
55536
55746
|
agents.push({
|
|
55537
55747
|
name: "Gemini CLI",
|
|
55538
55748
|
detectPath: gemini,
|
|
55539
|
-
installed: () => existsSync9(
|
|
55749
|
+
installed: () => existsSync9(join18(home, ".gemini")) || which("gemini"),
|
|
55540
55750
|
write: jsonMcpServersWrite(gemini)
|
|
55541
55751
|
});
|
|
55542
|
-
const windsurf =
|
|
55752
|
+
const windsurf = join18(home, ".codeium", "windsurf", "mcp_config.json");
|
|
55543
55753
|
agents.push({
|
|
55544
55754
|
name: "Windsurf",
|
|
55545
55755
|
detectPath: windsurf,
|
|
55546
|
-
installed: () => existsSync9(
|
|
55756
|
+
installed: () => existsSync9(join18(home, ".codeium")) || which("windsurf"),
|
|
55547
55757
|
write: jsonMcpServersWrite(windsurf)
|
|
55548
55758
|
});
|
|
55549
|
-
const copilot =
|
|
55759
|
+
const copilot = join18(home, ".copilot", "mcp.json");
|
|
55550
55760
|
agents.push({
|
|
55551
55761
|
name: "Copilot",
|
|
55552
55762
|
detectPath: copilot,
|
|
55553
|
-
installed: () => existsSync9(
|
|
55763
|
+
installed: () => existsSync9(join18(home, ".copilot")),
|
|
55554
55764
|
write: jsonMcpServersWrite(copilot)
|
|
55555
55765
|
});
|
|
55556
|
-
const cline =
|
|
55766
|
+
const cline = join18(home, ".cline", "mcp_settings.json");
|
|
55557
55767
|
agents.push({
|
|
55558
55768
|
name: "Cline",
|
|
55559
55769
|
detectPath: cline,
|
|
55560
|
-
installed: () => existsSync9(
|
|
55770
|
+
installed: () => existsSync9(join18(home, ".cline")),
|
|
55561
55771
|
write: jsonMcpServersWrite(cline)
|
|
55562
55772
|
});
|
|
55563
55773
|
return agents;
|
|
@@ -55601,15 +55811,15 @@ Registered with ${ok}/${installed.length} agent(s).`);
|
|
|
55601
55811
|
}
|
|
55602
55812
|
|
|
55603
55813
|
// src/index.ts
|
|
55604
|
-
import { readFileSync as
|
|
55605
|
-
import { join as
|
|
55606
|
-
import { fileURLToPath as
|
|
55607
|
-
var ROOT11 =
|
|
55814
|
+
import { readFileSync as readFileSync23, existsSync as existsSync10 } from "node:fs";
|
|
55815
|
+
import { join as join19 } from "node:path";
|
|
55816
|
+
import { fileURLToPath as fileURLToPath13 } from "node:url";
|
|
55817
|
+
var ROOT11 = join19(fileURLToPath13(new URL(".", import.meta.url)), "..");
|
|
55608
55818
|
function readVersion() {
|
|
55609
55819
|
try {
|
|
55610
|
-
const p =
|
|
55820
|
+
const p = join19(ROOT11, "package.json");
|
|
55611
55821
|
if (existsSync10(p))
|
|
55612
|
-
return JSON.parse(
|
|
55822
|
+
return JSON.parse(readFileSync23(p, "utf8")).version ?? "1.0.0";
|
|
55613
55823
|
} catch {}
|
|
55614
55824
|
return "1.0.0";
|
|
55615
55825
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
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": {
|