blitzstrike 2.4.94 → 2.4.96
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 +32 -0
- package/dist/index.js +28 -9
- package/intelligence/patterns.json +63 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,38 @@ 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.96] — 2026-09-17
|
|
9
|
+
|
|
10
|
+
### Security — self-audit (command injection + path traversal fixed)
|
|
11
|
+
|
|
12
|
+
- `resolve_check(host)` interpolated the user-controlled host into a `curl` shell
|
|
13
|
+
string (`execSync`), allowing command injection (`host = "x.com; id"`). Now
|
|
14
|
+
validates the host against a strict hostname regex BEFORE any subprocess/DNS and
|
|
15
|
+
calls curl via `execFileSync` (array argv, no shell) — defense in depth.
|
|
16
|
+
- `foundry_run(contract_name=...)` used the name as a filename under the temp
|
|
17
|
+
project without validation, allowing path traversal (`"../../evil"`). Now
|
|
18
|
+
sanitizes to a valid Solidity identifier (`[A-Za-z_][A-Za-z0-9_]*`), defaulting
|
|
19
|
+
to `Vulnerable` otherwise.
|
|
20
|
+
- Added regression checks: `isValidHostname` rejects shell metacharacters
|
|
21
|
+
(`;`, backticks, `$()`, `|`, `../`, newlines); the identifier regex is shared.
|
|
22
|
+
|
|
23
|
+
## [2.4.95] — 2026-09-17
|
|
24
|
+
|
|
25
|
+
### Fixed — full interconnection audit (unreachable template + 2 ungrounded classes)
|
|
26
|
+
|
|
27
|
+
- `auto_poc` `open_redirect` template was UNREACHABLE: `canonicalId("open_redirect")`
|
|
28
|
+
resolves to `oauth_misconfig`, but the template key was `open_redirect`, so the
|
|
29
|
+
lookup fell back to the `missing_authz` recipe. Renamed the key to the canonical
|
|
30
|
+
`oauth_misconfig` + added an interconn invariant that every auto-poc key IS its
|
|
31
|
+
own canonical id.
|
|
32
|
+
- `nosql_injection` and `host_header` were referenced across intel/orchestrator/
|
|
33
|
+
writeup/technique-lookup but had no pattern, severity, or synonym (ungrounded).
|
|
34
|
+
Added full patterns (CWE-943 / CWE-644), severity VRT entries (P1 critical /
|
|
35
|
+
P3 medium), and synonyms (nosql/mongo injection; host-header poisoning /
|
|
36
|
+
password-reset poisoning). cwe_map already carried both CWEs.
|
|
37
|
+
- `SINK_TO_VECTOR` mapped the `code_execution` sink to a non-pattern vector name;
|
|
38
|
+
now maps to `command_injection` (consistent with the "code execution" synonym).
|
|
39
|
+
|
|
8
40
|
## [2.4.94] — 2026-09-17
|
|
9
41
|
|
|
10
42
|
### Added — Hunting intel (DNS sinkhole detection + CWE/CVE watchlist)
|
package/dist/index.js
CHANGED
|
@@ -17499,7 +17499,7 @@ var init_update = __esm(() => {
|
|
|
17499
17499
|
import { readFileSync as readFileSync29, existsSync as existsSync16 } from "node:fs";
|
|
17500
17500
|
import { join as join27 } from "node:path";
|
|
17501
17501
|
import { fileURLToPath as fileURLToPath14 } from "node:url";
|
|
17502
|
-
import { execSync as
|
|
17502
|
+
import { execSync as execSync4 } from "node:child_process";
|
|
17503
17503
|
function validateRelease() {
|
|
17504
17504
|
const checks = [];
|
|
17505
17505
|
checks.push({ name: "package.json version", ok: true, detail: VERSION });
|
|
@@ -17516,13 +17516,13 @@ function validateRelease() {
|
|
|
17516
17516
|
checks.push({ name: "CHANGELOG version", ok: false, detail: "CHANGELOG.md not found" });
|
|
17517
17517
|
}
|
|
17518
17518
|
try {
|
|
17519
|
-
const tag =
|
|
17519
|
+
const tag = execSync4("git describe --tags --abbrev=0", { cwd: ROOT8, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
17520
17520
|
checks.push({ name: "Git tag", ok: tag === `v${VERSION}`, detail: tag, soft: true });
|
|
17521
17521
|
} catch {
|
|
17522
17522
|
checks.push({ name: "Git tag", ok: true, detail: "unavailable (no git/tags)", soft: true });
|
|
17523
17523
|
}
|
|
17524
17524
|
try {
|
|
17525
|
-
const published =
|
|
17525
|
+
const published = execSync4("npm view blitzstrike version", { encoding: "utf8", timeout: 8000, stdio: ["ignore", "pipe", "ignore"] }).trim();
|
|
17526
17526
|
checks.push({ name: "npm registry version", ok: published === VERSION, detail: published, soft: true });
|
|
17527
17527
|
} catch {
|
|
17528
17528
|
checks.push({ name: "npm registry version", ok: true, detail: "unavailable (offline)", soft: true });
|
|
@@ -34262,7 +34262,7 @@ var SINK_TO_VECTOR = {
|
|
|
34262
34262
|
sql_execution: "sql_injection",
|
|
34263
34263
|
command_execution: "command_injection",
|
|
34264
34264
|
html_render: "xss",
|
|
34265
|
-
code_execution: "
|
|
34265
|
+
code_execution: "command_injection",
|
|
34266
34266
|
http_request: "ssrf",
|
|
34267
34267
|
deserialization: "deserialization",
|
|
34268
34268
|
file_operations: "path_traversal",
|
|
@@ -34545,7 +34545,16 @@ var ALIASES = {
|
|
|
34545
34545
|
"subdomain takeover": "subdomain_takeover",
|
|
34546
34546
|
takeover: "subdomain_takeover",
|
|
34547
34547
|
"dangling dns": "subdomain_takeover",
|
|
34548
|
-
"dangling cname": "subdomain_takeover"
|
|
34548
|
+
"dangling cname": "subdomain_takeover",
|
|
34549
|
+
"nosql injection": "nosql_injection",
|
|
34550
|
+
nosql: "nosql_injection",
|
|
34551
|
+
nosqli: "nosql_injection",
|
|
34552
|
+
"mongo injection": "nosql_injection",
|
|
34553
|
+
"mongodb injection": "nosql_injection",
|
|
34554
|
+
"host header": "host_header",
|
|
34555
|
+
"host header injection": "host_header",
|
|
34556
|
+
"host header poisoning": "host_header",
|
|
34557
|
+
"password reset poisoning": "host_header"
|
|
34549
34558
|
};
|
|
34550
34559
|
function normalize(term) {
|
|
34551
34560
|
return term.trim().toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
|
|
@@ -59037,6 +59046,8 @@ var VRT_MAP = {
|
|
|
59037
59046
|
sql_injection: { vrt: "injection > sql_injection", priority: "P1", severity: "critical", rationale: "SQL injection reaches sensitive data or enables admin takeover." },
|
|
59038
59047
|
ssti: { vrt: "injection > server_side_template_injection", priority: "P1", severity: "critical", rationale: "SSTI evaluates expressions server-side → code execution." },
|
|
59039
59048
|
command_injection: { vrt: "injection > os_command_injection", priority: "P1", severity: "critical", rationale: "User input reaches a shell (system/exec/popen) → arbitrary command execution, full host takeover." },
|
|
59049
|
+
nosql_injection: { vrt: "injection > nosql_injection", priority: "P1", severity: "critical", rationale: "NoSQL operator injection reaches a database query → auth bypass or data exfiltration." },
|
|
59050
|
+
host_header: { vrt: "injection > host_header_injection", priority: "P3", severity: "medium", rationale: "A reflected/trusted Host header can poison password-reset links or enable cache poisoning → account takeover in the right flow." },
|
|
59040
59051
|
deserialization: { vrt: "injection > insecure_deserialization", priority: "P1", severity: "critical", rationale: "Deserialization escalated via a gadget chain → code execution." },
|
|
59041
59052
|
xxe: { vrt: "injection > xml_external_entity", priority: "P1", severity: "critical", rationale: "XXE reads local files or reaches internal services." },
|
|
59042
59053
|
file_upload: { vrt: "insecure_design > unrestricted_file_upload", priority: "P1", severity: "critical", rationale: "A web-reachable uploaded file of an executable type → code execution." },
|
|
@@ -61257,7 +61268,7 @@ var POC_TEMPLATES = {
|
|
|
61257
61268
|
expected: `The response contains an injected \`X-Injected: ${MK}\` header.`,
|
|
61258
61269
|
fix: "Strip CR/LF from any value placed into a response header."
|
|
61259
61270
|
},
|
|
61260
|
-
|
|
61271
|
+
oauth_misconfig: {
|
|
61261
61272
|
severity: "medium",
|
|
61262
61273
|
marker: "https://attacker.example",
|
|
61263
61274
|
request: (i) => qs(i, encodeURIComponent("https://attacker.example")),
|
|
@@ -61397,7 +61408,8 @@ function parseTestResults(out) {
|
|
|
61397
61408
|
}
|
|
61398
61409
|
async function foundryRun(input) {
|
|
61399
61410
|
const forge = findForge();
|
|
61400
|
-
const
|
|
61411
|
+
const rawName = input.contract_name ?? "Vulnerable";
|
|
61412
|
+
const name = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/.test(rawName) ? rawName : "Vulnerable";
|
|
61401
61413
|
if (!forge) {
|
|
61402
61414
|
return {
|
|
61403
61415
|
forge: null,
|
|
@@ -61448,11 +61460,15 @@ solc = "0.8.20"
|
|
|
61448
61460
|
}
|
|
61449
61461
|
|
|
61450
61462
|
// src/hunting-intel.ts
|
|
61451
|
-
import {
|
|
61463
|
+
import { execFileSync as execFileSync5 } from "node:child_process";
|
|
61452
61464
|
import { resolve4 as resolve42, Resolver } from "node:dns/promises";
|
|
61453
61465
|
import { readFileSync as readFileSync25 } from "node:fs";
|
|
61454
61466
|
import { join as join24 } from "node:path";
|
|
61455
61467
|
var BLOCKING_KEYWORDS = /internet positif|kominfo|\.go\.id\b|pemblokiran|ditutup|dilarang|akses dibatasi|blocked|restricted|illegal|advertencia|coljuegos|regulat|forbidden|this (site|website) has been (blocked|banned)/i;
|
|
61468
|
+
var HOSTNAME_RE = /^[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?(\.[a-z0-9]([a-z0-9-]{0,61}[a-z0-9])?)*$/i;
|
|
61469
|
+
function isValidHostname(host) {
|
|
61470
|
+
return host.length <= 253 && HOSTNAME_RE.test(host);
|
|
61471
|
+
}
|
|
61456
61472
|
async function localResolve(host) {
|
|
61457
61473
|
try {
|
|
61458
61474
|
return await resolve42(host);
|
|
@@ -61473,7 +61489,7 @@ function probeBlockingPage(host, ip, timeoutMs = 8000) {
|
|
|
61473
61489
|
for (const port of ["443", "80"]) {
|
|
61474
61490
|
const scheme = port === "443" ? "https" : "http";
|
|
61475
61491
|
try {
|
|
61476
|
-
const out =
|
|
61492
|
+
const out = execFileSync5("curl", ["-sk", "-m", "6", "--resolve", `${host}:${port}:${ip}`, `${scheme}://${host}/`], { encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"] });
|
|
61477
61493
|
const preview = out.replace(/\s+/g, " ").trim().slice(0, 300);
|
|
61478
61494
|
if (!preview)
|
|
61479
61495
|
continue;
|
|
@@ -61484,6 +61500,9 @@ function probeBlockingPage(host, ip, timeoutMs = 8000) {
|
|
|
61484
61500
|
}
|
|
61485
61501
|
async function resolveCheck(host) {
|
|
61486
61502
|
const clean = host.replace(/^https?:\/\//, "").split("/")[0];
|
|
61503
|
+
if (!isValidHostname(clean)) {
|
|
61504
|
+
return { host: clean, local_ips: [], public_ips: [], sinkhole: false, verdict: "invalid", evidence: "not a valid hostname — rejected before DNS/subprocess" };
|
|
61505
|
+
}
|
|
61487
61506
|
const local = await localResolve(clean);
|
|
61488
61507
|
const pub = await publicResolve(clean);
|
|
61489
61508
|
if (local.length === 0 && pub.length === 0) {
|
|
@@ -988,6 +988,69 @@
|
|
|
988
988
|
"payout_min": 1000,
|
|
989
989
|
"payout_max": 15000,
|
|
990
990
|
"typical_payout": "$1K\u2013$15K"
|
|
991
|
+
},
|
|
992
|
+
{
|
|
993
|
+
"id": "nosql_injection",
|
|
994
|
+
"name": "NoSQL Injection",
|
|
995
|
+
"cwe": "CWE-943",
|
|
996
|
+
"real_world": "User input reaches a NoSQL query (MongoDB $where/$gt/$ne, an unsanitized find/aggregate). Attackers inject operators to bypass authentication or dump collections.",
|
|
997
|
+
"detection_signatures": [
|
|
998
|
+
"unsanitized input into a NoSQL find/where/aggregate",
|
|
999
|
+
"operator injection ($ne/$gt/$where) from request input"
|
|
1000
|
+
],
|
|
1001
|
+
"chain_templates": [
|
|
1002
|
+
"nosql_injection_to_data_theft"
|
|
1003
|
+
],
|
|
1004
|
+
"references": [
|
|
1005
|
+
"CWE-943",
|
|
1006
|
+
"OWASP A03:2021 Injection"
|
|
1007
|
+
],
|
|
1008
|
+
"bypass_techniques": [
|
|
1009
|
+
"operator injection ($ne/$gt/$where)",
|
|
1010
|
+
"JSON-body operator injection",
|
|
1011
|
+
"array/object type juggling to slip past input filters",
|
|
1012
|
+
"blind extraction via regex/timing"
|
|
1013
|
+
],
|
|
1014
|
+
"crown_jewels": [
|
|
1015
|
+
"user/account collections",
|
|
1016
|
+
"password hashes",
|
|
1017
|
+
"admin documents"
|
|
1018
|
+
],
|
|
1019
|
+
"severity": "critical",
|
|
1020
|
+
"payout_min": 1000,
|
|
1021
|
+
"payout_max": 15000,
|
|
1022
|
+
"typical_payout": "$1K\u2013$15K"
|
|
1023
|
+
},
|
|
1024
|
+
{
|
|
1025
|
+
"id": "host_header",
|
|
1026
|
+
"name": "Host Header Injection",
|
|
1027
|
+
"cwe": "CWE-644",
|
|
1028
|
+
"real_world": "The app trusts the Host header for link generation, password-reset URLs, or routing. An attacker injects a malicious host to poison password-reset links or bypass access controls.",
|
|
1029
|
+
"detection_signatures": [
|
|
1030
|
+
"Host header reflected into a generated URL / Location / Set-Cookie",
|
|
1031
|
+
"Host header used in password-reset link generation"
|
|
1032
|
+
],
|
|
1033
|
+
"chain_templates": [
|
|
1034
|
+
"http_smuggling_to_auth_bypass"
|
|
1035
|
+
],
|
|
1036
|
+
"references": [
|
|
1037
|
+
"CWE-644"
|
|
1038
|
+
],
|
|
1039
|
+
"bypass_techniques": [
|
|
1040
|
+
"Host header override via X-Forwarded-Host",
|
|
1041
|
+
"absolute-URL host injection",
|
|
1042
|
+
"password-reset link poisoning",
|
|
1043
|
+
"web cache poisoning via Host"
|
|
1044
|
+
],
|
|
1045
|
+
"crown_jewels": [
|
|
1046
|
+
"password-reset tokens",
|
|
1047
|
+
"account takeover via poisoned links",
|
|
1048
|
+
"cache poisoning"
|
|
1049
|
+
],
|
|
1050
|
+
"severity": "medium",
|
|
1051
|
+
"payout_min": 500,
|
|
1052
|
+
"payout_max": 5000,
|
|
1053
|
+
"typical_payout": "$500\u2013$5K"
|
|
991
1054
|
}
|
|
992
1055
|
]
|
|
993
1056
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.96",
|
|
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": {
|