blitzstrike 2.4.77 → 2.4.79

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,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.79] — 2026-09-17
9
+
10
+ ### Changed — precision pass (FP reduction, benchmark-driven)
11
+
12
+ - Removed the bare `exec(` sink (sqlite `db.exec(...)` / `stmt.exec(...)` was
13
+ flagged as command execution in Node apps). Command injection is still covered
14
+ by `child_process.exec/spawn`, `system`, `shell_exec`, `subprocess.*`,
15
+ `os.system`, `popen`.
16
+ - `detectRouteAuthz`: added `requireAdmin`/`requireRole`/`requirePermission`/
17
+ `isAdmin`/`checkRole`/`bearer`/`apiAuth`/`getApiKeyFromRequest`/`verifyApiKey`
18
+ to the express auth model + widened the window to ±3, and added a public-path
19
+ SEGMENT matcher for multi-segment public routes (`/api/system/version`,
20
+ `/.well-known/jwks.json`, `/v1/metrics`).
21
+ - `detectHardcodedSecret`: skip values that are function calls
22
+ (`getApiKeyFromRequest(req)`, `fs.readFileSync(...)`, `make_token(...)`) or DOM
23
+ reads (`document.getElementById(...).value`) — those are computed at runtime.
24
+
25
+ FP labs on the 30-lab benchmark dropped 16 → 8 (TP 12 unchanged).
26
+
27
+ ## [2.4.78] — 2026-09-17
28
+
29
+ ### Fixed — taint-engine sanitizer name collision (whole-class FN)
30
+
31
+ - The Python taint adapter neutralized every identifier inside a sanitizer call
32
+ (e.g. `int(os.environ.get("PORT", "5000"))` neutralized the method name `get`
33
+ for sql_execution/command_execution). Because taint state is keyed by name, that
34
+ polluted the `get` in `request.args.get(...)` elsewhere and wrongly suppressed
35
+ real findings (labs20 f-string SQLi went undetected). Sanitizers now only
36
+ neutralize VALUE arguments, skipping dotted-chain receiver/method names and
37
+ string literals. Python f-string SQLi (`request.args.get -> f"{var}" -> execute`)
38
+ is now caught.
39
+
8
40
  ## [2.4.77] — 2026-09-17
9
41
 
10
42
  ### Changed — SQLi Node/Python coverage + missing_authz root-path precision
package/dist/index.js CHANGED
@@ -7190,7 +7190,6 @@ var init_scanner = __esm(() => {
7190
7190
  "eval(": "RCE (code execution)",
7191
7191
  "assert(": "RCE (code execution, PHP <8)",
7192
7192
  "system(": "RCE (command execution)",
7193
- "exec(": "RCE (command execution)",
7194
7193
  "shell_exec(": "RCE (command execution)",
7195
7194
  "passthru(": "RCE (command execution)",
7196
7195
  "proc_open(": "RCE (command execution)",
@@ -15981,7 +15980,7 @@ function detectRouteAuthz(code, file, out) {
15981
15980
  const ln = lineNoOf(code, m.index);
15982
15981
  const window2 = ls.slice(Math.max(0, ln - d.window), Math.min(ls.length, ln + d.window + 1)).join(`
15983
15982
  `);
15984
- if (PUBLIC_ROUTE_RE.test(m[0] + " " + window2))
15983
+ if (PUBLIC_ROUTE_RE.test(m[0] + " " + window2) || PUBLIC_SEGMENT_RE.test(m[0] + " " + window2))
15985
15984
  continue;
15986
15985
  if (d.auth.test(window2))
15987
15986
  continue;
@@ -16010,7 +16009,7 @@ function detectMissingAuthz(code, file) {
16010
16009
  return true;
16011
16010
  });
16012
16011
  }
16013
- var FRAMEWORKS, INDIRECT_AUTH, AUTH_RE, NONCE_RE, DANGEROUS_OP_RE, ROUTE_DETECTORS, PUBLIC_ROUTE_RE;
16012
+ var FRAMEWORKS, INDIRECT_AUTH, AUTH_RE, NONCE_RE, DANGEROUS_OP_RE, ROUTE_DETECTORS, PUBLIC_ROUTE_RE, PUBLIC_SEGMENT_RE;
16014
16013
  var init_framework_security = __esm(() => {
16015
16014
  FRAMEWORKS = {
16016
16015
  wordpress: {
@@ -16092,13 +16091,14 @@ var init_framework_security = __esm(() => {
16092
16091
  { framework: "laravel", entry: /Route::(?:get|post|put|patch|delete|any|match|resource)\s*\(\s*['"][^'"]+['"]/g, auth: /->middleware\s*\(\s*['"]auth|middleware\s*\(\s*\[[^\]]*['"]auth|->can\s*\(|\$this->authorize|Gate::|authorize\s*\(|@can\b/, window: 2 },
16093
16092
  { framework: "django", entry: /@api_view\s*\(|def\s+\w+\s*\(\s*request\s*[,)]/g, auth: /login_required|permission_required|user_passes_test|LoginRequiredMixin|PermissionRequiredMixin|permission_classes|IsAuthenticated|has_permission/, window: 3 },
16094
16093
  { framework: "flask", entry: /@(?:app|bp|blueprint)\.(?:route|get|post|put|delete|patch|before_request)\s*\(/g, auth: /login_required|require_auth|auth_required|jwt_required|token_required|current_user|is_authenticated|before_request|verify_token|permission_required/, window: 3 },
16095
- { framework: "express", entry: /(?<!@)(?:app|router)\.(?:get|post|put|delete|patch|use)\s*\(\s*['"][^'"]+['"]/g, auth: /requireAuth|require_auth|authenticate|passport\.authenticate|req\.user|isAuthenticated|ensureAuthenticated|requireLogin|\.authorize|authMiddleware|verifyToken|verify_token|jwt\.verify/, window: 2 },
16094
+ { framework: "express", entry: /(?<!@)(?:app|router)\.(?:get|post|put|delete|patch|use)\s*\(\s*['"][^'"]+['"]/g, auth: /requireAuth|require_auth|requireAdmin|requireRole|requirePermission|isAdmin|checkRole|getApiKeyFromRequest|verifyApiKey|checkApiKey|verifyInternalToken|authenticate|passport\.authenticate|req\.user|isAuthenticated|ensureAuthenticated|requireLogin|\.authorize|authMiddleware|verifyToken|verify_token|jwt\.verify|\bbearer\b|apiAuth/, window: 3 },
16096
16095
  { framework: "spring", entry: /@(?:Get|Post|Put|Delete|Patch|Request)Mapping\s*\(/g, auth: /@PreAuthorize|@Secured|@RolesAllowed|hasRole|hasAuthority|isAuthenticated|SecurityContextHolder/, window: 2 },
16097
16096
  { framework: "symfony", entry: /#\[(?:Route|Get|Post|Put|Delete|Patch)\b|@Route\s*\(/g, auth: /is_granted|denyAccessUnlessGranted|->isGranted\s*\(|IsGranted|Voter/, window: 2 },
16098
16097
  { framework: "aspnet", entry: /\[Http(?:Get|Post|Put|Delete|Patch)\b/g, auth: /\[Authorize|User\.Identity|IsInRole|AuthorizeAsync|\[AllowAnonymous|IAuthorizationService/, window: 2 },
16099
16098
  { framework: "rails", entry: /class\s+\w+\s*<\s*ApplicationController/g, auth: /before_action\s+:authenticate|before_action\s+:authorize|authenticate_user!|authorize\s+|current_user|authenticate/, window: 6 }
16100
16099
  ];
16101
- PUBLIC_ROUTE_RE = /['"`]\/?(?:(?:api|v1|v2|o|auth|adminapi)\/)?(?:register|login|logout|signup|signin|forgot-password|reset-password|health|healthz|ready|status|version|ping|static|public|assets|css|js|img|favicon|echo|config|docs|openapi|swagger|robots|sitemap|graphql|graphiql|query|upload|callback|webhook|home|index|root)[/'"?]|['"`]\/?['"`](?!["'`])/i;
16100
+ PUBLIC_ROUTE_RE = /['"`]\/?(?:(?:api|v1|v2|o|auth|adminapi|system|internal)\/)?(?:register|login|logout|signup|signin|forgot-password|reset-password|health|healthz|ready|status|version|ping|static|public|assets|css|js|img|favicon|echo|config|docs|openapi|swagger|robots|sitemap|graphql|graphiql|query|upload|callback|webhook|home|index|root|jwks|well-known|openid-configuration|traces|logs|metrics)[/'"?]|['"`]\/?['"`](?!["'`])/i;
16101
+ PUBLIC_SEGMENT_RE = /['"`][^'"`]*\/(?:version|health|healthz|ready|status|ping|jwks|openid-configuration|well-known|metrics|traces|logs|favicon|robots|sitemap)(?:[.\/'"?]|$)/i;
16102
16102
  });
16103
16103
 
16104
16104
  // src/complex-bugs.ts
@@ -16596,6 +16596,10 @@ function detectHardcodedSecret(code, file, out) {
16596
16596
  continue;
16597
16597
  if (qm && /^["'][^"']+["']\s*\+/.test(valueRaw))
16598
16598
  continue;
16599
+ if (!qm && new RegExp(`${value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*\\(`).test(valueRaw))
16600
+ continue;
16601
+ if (!qm && /document\.|window\.|localStorage\.|sessionStorage\./.test(valueRaw))
16602
+ continue;
16599
16603
  const ln = lineNo2(code, m.index);
16600
16604
  flaggedLines.add(ln);
16601
16605
  let sev = "high";
@@ -56908,9 +56912,13 @@ var pythonAdapter = {
56908
56912
  sinks.push({ variable: v, kind, line: lineOf(n), cwe });
56909
56913
  }
56910
56914
  for (const [re, kinds] of PY_SANITIZERS) {
56911
- if (re.test(full))
56912
- for (const v of argVars)
56915
+ if (re.test(full)) {
56916
+ for (const v of argVars) {
56917
+ if (new RegExp(`\\b${v}\\s*[.(]`).test(full))
56918
+ continue;
56913
56919
  sanitizers.push({ variable: v, neutralizes: kinds, line: lineOf(n) });
56920
+ }
56921
+ }
56914
56922
  }
56915
56923
  calls.push({ name: callee, args: argVars, line: lineOf(n) });
56916
56924
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "2.4.77",
3
+ "version": "2.4.79",
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": {