blitzstrike 2.4.95 → 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 CHANGED
@@ -5,6 +5,21 @@ 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
+
8
23
  ## [2.4.95] — 2026-09-17
9
24
 
10
25
  ### Fixed — full interconnection audit (unreachable template + 2 ungrounded classes)
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 execSync5 } from "node:child_process";
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 = execSync5("git describe --tags --abbrev=0", { cwd: ROOT8, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"] }).trim();
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 = execSync5("npm view blitzstrike version", { encoding: "utf8", timeout: 8000, stdio: ["ignore", "pipe", "ignore"] }).trim();
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 });
@@ -61408,7 +61408,8 @@ function parseTestResults(out) {
61408
61408
  }
61409
61409
  async function foundryRun(input) {
61410
61410
  const forge = findForge();
61411
- const name = input.contract_name ?? "Vulnerable";
61411
+ const rawName = input.contract_name ?? "Vulnerable";
61412
+ const name = /^[A-Za-z_][A-Za-z0-9_]{0,63}$/.test(rawName) ? rawName : "Vulnerable";
61412
61413
  if (!forge) {
61413
61414
  return {
61414
61415
  forge: null,
@@ -61459,11 +61460,15 @@ solc = "0.8.20"
61459
61460
  }
61460
61461
 
61461
61462
  // src/hunting-intel.ts
61462
- import { execSync as execSync4 } from "node:child_process";
61463
+ import { execFileSync as execFileSync5 } from "node:child_process";
61463
61464
  import { resolve4 as resolve42, Resolver } from "node:dns/promises";
61464
61465
  import { readFileSync as readFileSync25 } from "node:fs";
61465
61466
  import { join as join24 } from "node:path";
61466
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
+ }
61467
61472
  async function localResolve(host) {
61468
61473
  try {
61469
61474
  return await resolve42(host);
@@ -61484,7 +61489,7 @@ function probeBlockingPage(host, ip, timeoutMs = 8000) {
61484
61489
  for (const port of ["443", "80"]) {
61485
61490
  const scheme = port === "443" ? "https" : "http";
61486
61491
  try {
61487
- const out = execSync4(`curl -sk -m 6 --resolve ${host}:${port}:${ip} ${scheme}://${host}/ 2>/dev/null`, { encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"] });
61492
+ const out = execFileSync5("curl", ["-sk", "-m", "6", "--resolve", `${host}:${port}:${ip}`, `${scheme}://${host}/`], { encoding: "utf8", timeout: timeoutMs, stdio: ["ignore", "pipe", "ignore"] });
61488
61493
  const preview = out.replace(/\s+/g, " ").trim().slice(0, 300);
61489
61494
  if (!preview)
61490
61495
  continue;
@@ -61495,6 +61500,9 @@ function probeBlockingPage(host, ip, timeoutMs = 8000) {
61495
61500
  }
61496
61501
  async function resolveCheck(host) {
61497
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
+ }
61498
61506
  const local = await localResolve(clean);
61499
61507
  const pub = await publicResolve(clean);
61500
61508
  if (local.length === 0 && pub.length === 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "2.4.95",
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": {