blitzstrike 2.4.2 → 2.4.5
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 +21 -0
- package/README.md +3 -0
- package/ROADMAP.md +7 -0
- package/dist/index.js +24 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,27 @@ 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
|
+
## [2.4.5] — 2026-09-14
|
|
9
|
+
|
|
10
|
+
### Added — responsible-disclosure header (X-HackerOne-Research)
|
|
11
|
+
|
|
12
|
+
- New `H1_USERNAME` env var: when set, every outbound security-testing request
|
|
13
|
+
(STRIKE verification, live recon, active scan, advanced checks, leak-source
|
|
14
|
+
fetch) carries `X-HackerOne-Research: <username>` so targets/triagers can
|
|
15
|
+
identify the researcher.
|
|
16
|
+
- `src/http.ts` central helper (`researchHeaders` / `withResearchHeaders` /
|
|
17
|
+
`h1Username`); injected across strike, live-recon, active, orchestrator, server.
|
|
18
|
+
- `blitzstrike doctor` now reports the HackerOne research-header status.
|
|
19
|
+
|
|
20
|
+
## [2.4.4] — 2026-09-14
|
|
21
|
+
|
|
22
|
+
### Added — automated publishing via npm trusted publishing (OIDC)
|
|
23
|
+
|
|
24
|
+
- New `.github/workflows/release.yml`: publishes to npm on push to `main`
|
|
25
|
+
(version-gated) using trusted publishing (OIDC) — no npm token, no 2FA.
|
|
26
|
+
- Adds `--provenance` (SLSA attestation) so every release is verifiably built
|
|
27
|
+
from this repo. npm's bypass-2FA token deprecation no longer blocks releases.
|
|
28
|
+
|
|
8
29
|
## [2.4.0] — 2026-09-14
|
|
9
30
|
|
|
10
31
|
### Added — bulk tool installation (install the whole catalog at once)
|
package/README.md
CHANGED
|
@@ -347,6 +347,9 @@ Edit `chains.json` to add knowledge — never hardcode in source.
|
|
|
347
347
|
|---|---|---|
|
|
348
348
|
| `FOFA_EMAIL` | For `fofa_search` | FOFA account email |
|
|
349
349
|
| `FOFA_KEY` | For `fofa_search` | FOFA API key |
|
|
350
|
+
| `H1_USERNAME` | Optional | Your HackerOne username. When set, every outbound security-testing request carries `X-HackerOne-Research: <username>` so targets/triagers can identify you (responsible-disclosure convention). |
|
|
351
|
+
| `BLITZSTRIKE_HOME` | Optional | Override the home directory (default `~/.blitzstrike`). |
|
|
352
|
+
| `BLITZSTRIKE_DATA` | Optional | Override the data-cache directory (default `~/.blitzstrike/data`). |
|
|
350
353
|
|
|
351
354
|
All other tools need no credentials.
|
|
352
355
|
|
package/ROADMAP.md
CHANGED
|
@@ -46,3 +46,10 @@ Blitz Strike is version 1.0. This is where it's headed.
|
|
|
46
46
|
- Everything wired, nothing decorative. A feature that isn't exercised in the
|
|
47
47
|
flow is removed.
|
|
48
48
|
- License-safe. MIT/Apache-2.0 only for vendored data.
|
|
49
|
+
|
|
50
|
+
## Release pipeline
|
|
51
|
+
|
|
52
|
+
Publishing is automated via npm **trusted publishing (OIDC)** — see
|
|
53
|
+
`.github/workflows/release.yml`. No npm token or 2FA is required; push to `main`
|
|
54
|
+
with a bumped `package.json` version and the workflow publishes directly with
|
|
55
|
+
SLSA provenance.
|
package/dist/index.js
CHANGED
|
@@ -7388,6 +7388,15 @@ var init_version = __esm(() => {
|
|
|
7388
7388
|
})();
|
|
7389
7389
|
});
|
|
7390
7390
|
|
|
7391
|
+
// src/http.ts
|
|
7392
|
+
function researchHeaders() {
|
|
7393
|
+
const u = process.env.H1_USERNAME?.trim();
|
|
7394
|
+
return u ? { "X-HackerOne-Research": u } : {};
|
|
7395
|
+
}
|
|
7396
|
+
function h1Username() {
|
|
7397
|
+
return process.env.H1_USERNAME?.trim() ?? "";
|
|
7398
|
+
}
|
|
7399
|
+
|
|
7391
7400
|
// src/catalog.ts
|
|
7392
7401
|
import { readFileSync as readFileSync6, readdirSync as readdirSync2 } from "node:fs";
|
|
7393
7402
|
import { join as join6 } from "node:path";
|
|
@@ -15555,6 +15564,13 @@ function runDoctor() {
|
|
|
15555
15564
|
detail: fofa ? "set" : "FOFA_EMAIL/FOFA_KEY not set",
|
|
15556
15565
|
fix: fofa ? undefined : "export FOFA_EMAIL=... && export FOFA_KEY=... (enables fofa_search)"
|
|
15557
15566
|
});
|
|
15567
|
+
const h1 = h1Username();
|
|
15568
|
+
issues.push({
|
|
15569
|
+
name: "HackerOne research header",
|
|
15570
|
+
status: h1 ? "ok" : "warn",
|
|
15571
|
+
detail: h1 ? `X-HackerOne-Research: ${h1}` : "H1_USERNAME not set",
|
|
15572
|
+
fix: h1 ? undefined : "export H1_USERNAME=<your-hackerone-username> (adds X-HackerOne-Research to outbound requests)"
|
|
15573
|
+
});
|
|
15558
15574
|
const dataOk = existsSync10(join19(ROOT10, "chains.json")) && existsSync10(join19(ROOT10, "tools-catalog.json"));
|
|
15559
15575
|
issues.push({
|
|
15560
15576
|
name: "Data layers (chains + tools-catalog)",
|
|
@@ -31115,7 +31131,7 @@ async function httpRequest(url, method, data, headers, timeoutMs = DEFAULT_TIMEO
|
|
|
31115
31131
|
const res = await fetch(url, {
|
|
31116
31132
|
method,
|
|
31117
31133
|
body: method === "POST" ? data : undefined,
|
|
31118
|
-
headers: { "User-Agent": "blitzstrike/1.0", ...headers ?? {} },
|
|
31134
|
+
headers: { "User-Agent": "blitzstrike/1.0", ...researchHeaders(), ...headers ?? {} },
|
|
31119
31135
|
signal: controller.signal,
|
|
31120
31136
|
redirect: "follow"
|
|
31121
31137
|
});
|
|
@@ -31940,7 +31956,6 @@ async function executeChainSteps(f, steps) {
|
|
|
31940
31956
|
}
|
|
31941
31957
|
return results;
|
|
31942
31958
|
}
|
|
31943
|
-
|
|
31944
31959
|
// src/orchestration.ts
|
|
31945
31960
|
var STATE_MACHINE = [
|
|
31946
31961
|
{ state: "scope_check", description: "Validate target against scope + risk policy.", recommended_tools: ["scope_check"], required_evidence: ["scope_decision"], next: ["plan"], confidence: "high" },
|
|
@@ -33161,7 +33176,7 @@ async function httpGet(url, opts = {}) {
|
|
|
33161
33176
|
const controller = new AbortController;
|
|
33162
33177
|
const t = setTimeout(() => controller.abort(), HTTP_TIMEOUT_MS);
|
|
33163
33178
|
try {
|
|
33164
|
-
const res = await fetch(url, { signal: controller.signal, redirect: opts.redirect ?? "follow" });
|
|
33179
|
+
const res = await fetch(url, { signal: controller.signal, redirect: opts.redirect ?? "follow", headers: researchHeaders() });
|
|
33165
33180
|
const body = await res.text();
|
|
33166
33181
|
const headers = {};
|
|
33167
33182
|
res.headers.forEach((v, k) => {
|
|
@@ -33937,7 +33952,7 @@ var PATH_PARAMS = ["file", "path", "page", "template", "lang", "include", "doc",
|
|
|
33937
33952
|
var EVIL_HOST = "evil-blitz.example.com";
|
|
33938
33953
|
async function httpGet2(u, extraHeaders) {
|
|
33939
33954
|
try {
|
|
33940
|
-
const res = await fetch(u, { redirect: "manual", headers: extraHeaders, signal: AbortSignal.timeout(1e4) });
|
|
33955
|
+
const res = await fetch(u, { redirect: "manual", headers: { ...researchHeaders(), ...extraHeaders ?? {} }, signal: AbortSignal.timeout(1e4) });
|
|
33941
33956
|
const body = await res.text();
|
|
33942
33957
|
const h = {};
|
|
33943
33958
|
res.headers.forEach((v, k) => {
|
|
@@ -34068,7 +34083,7 @@ async function checkCrlf(base, p) {
|
|
|
34068
34083
|
async function checkHostHeader(url) {
|
|
34069
34084
|
try {
|
|
34070
34085
|
const u = new URL(url);
|
|
34071
|
-
const res = await fetch(u, { redirect: "manual", headers: { Host: HOST_HEADER_MARKER } });
|
|
34086
|
+
const res = await fetch(u, { redirect: "manual", headers: { ...researchHeaders(), Host: HOST_HEADER_MARKER } });
|
|
34072
34087
|
const body = await res.text();
|
|
34073
34088
|
const loc = res.headers.get("location") ?? "";
|
|
34074
34089
|
const setCookie = res.headers.get("set-cookie") ?? "";
|
|
@@ -34081,7 +34096,7 @@ async function checkHostHeader(url) {
|
|
|
34081
34096
|
async function checkJwt(url) {
|
|
34082
34097
|
const out = [];
|
|
34083
34098
|
try {
|
|
34084
|
-
const res = await fetch(url, { redirect: "follow" });
|
|
34099
|
+
const res = await fetch(url, { redirect: "follow", headers: researchHeaders() });
|
|
34085
34100
|
const body = await res.text();
|
|
34086
34101
|
const setCookie = res.headers.get("set-cookie") ?? "";
|
|
34087
34102
|
const jwtRe = /eyJ[a-zA-Z0-9_-]{5,}\.eyJ[a-zA-Z0-9_-]{5,}\.[a-zA-Z0-9_-]{5,}/g;
|
|
@@ -34092,7 +34107,7 @@ async function checkJwt(url) {
|
|
|
34092
34107
|
const [h, p] = token.split(".");
|
|
34093
34108
|
const header = JSON.parse(Buffer.from(h, "base64url").toString());
|
|
34094
34109
|
const noneToken = `${h}.${p}.`;
|
|
34095
|
-
const noneRes = await fetch(url, { headers: { Authorization: `Bearer ${noneToken}` } });
|
|
34110
|
+
const noneRes = await fetch(url, { headers: { ...researchHeaders(), Authorization: `Bearer ${noneToken}` } });
|
|
34096
34111
|
if (noneRes.status !== 401 && noneRes.status !== 403) {
|
|
34097
34112
|
out.push({ type: `alg:none accepted (${header.alg})`, token: noneToken.slice(0, 40), accepted: true });
|
|
34098
34113
|
} else if (header.alg?.toUpperCase().startsWith("RS") || header.alg?.toUpperCase().startsWith("ES")) {
|
|
@@ -34699,7 +34714,7 @@ async function httpFetch(url, init) {
|
|
|
34699
34714
|
const controller = new AbortController;
|
|
34700
34715
|
const t = setTimeout(() => controller.abort(), TIMEOUT_MS2);
|
|
34701
34716
|
try {
|
|
34702
|
-
const res = await fetch(url, { ...init, signal: controller.signal, redirect: "follow" });
|
|
34717
|
+
const res = await fetch(url, { ...init, signal: controller.signal, redirect: "follow", headers: { ...researchHeaders(), ...init?.headers ?? {} } });
|
|
34703
34718
|
const body = await res.text();
|
|
34704
34719
|
const headers = {};
|
|
34705
34720
|
res.headers.forEach((v, k) => {
|
|
@@ -56300,7 +56315,7 @@ function createServer() {
|
|
|
56300
56315
|
let body = text ?? "";
|
|
56301
56316
|
if (url && !body) {
|
|
56302
56317
|
try {
|
|
56303
|
-
const r = await fetch(url, { redirect: "follow" });
|
|
56318
|
+
const r = await fetch(url, { redirect: "follow", headers: researchHeaders() });
|
|
56304
56319
|
body = await r.text();
|
|
56305
56320
|
} catch {
|
|
56306
56321
|
return { content: [{ type: "text", text: JSON.stringify({ error: "fetch failed", url }) }] };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.5",
|
|
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": {
|