blitzstrike 2.4.74 → 2.4.75

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,34 @@ 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.75] — 2026-09-17
9
+
10
+ ### Fixed — detection FP/FN remediation (Hackbot Arena benchmark-driven)
11
+
12
+ Four fixes driven by the 30-lab detection benchmark (TP/FP/FN):
13
+
14
+ 1. **`detectRouteAuthz` decorator/middleware auth + public-route skip.**
15
+ Recognizes `require_auth` / `auth_required` / `jwt_required` / `token_required` /
16
+ `verify_token` / `current_user` / `before_request` as auth enforcement; skips
17
+ public-by-design routes (register/login/health/status/static/docs/...). Also
18
+ fixed a cross-framework FP: the express detector was matching Flask
19
+ `@app.get/post` decorators (no `@` exclusion) — added `(?<!@)` and expanded the
20
+ flask entry to `@app.(route|get|post|put|delete|patch)`. `missing_authz` FP
21
+ dropped ~23 → 19 labs.
22
+ 2. **Node/Python sinks.** Added `subprocess.run/call/Popen/check_output`,
23
+ `os.system/popen`, `child_process.exec/spawn/execSync`, `sqlx::query(&format!`,
24
+ `sequelize.query`, `knex.raw`, `requests.get/post/put`, `urllib.request.urlopen`,
25
+ `httpx.get`, `render_template_string`/`Template` to the sink scanner — so Python
26
+ command injection (labs24), SSRF (labs07/10/15), and Node SQLi now register.
27
+ 3. **Two new detectors.** `jwt_alg_confusion` (verifier accepting RS256 + HS256
28
+ with public-key HMAC — CWE-347, caught labs03) and `cache_deception`
29
+ (cookie-less static cache key over a session-authenticated backend — CWE-525,
30
+ caught labs01). Also enhanced `ssti` to catch Liquid `Template(var)` from stored
31
+ input (labs23). Both wired through cwe_map/patterns/severity/synonyms/harness.
32
+ 4. **`hardcoded_secret` flag suppression.** Suppresses `FLAG{...}` literals, the
33
+ bare `FLAG` env marker, and quoted-prefix concatenations (`'usr_' + random`) so
34
+ seeded CTF flags and runtime-built values aren't reported as CWE-798.
35
+
8
36
  ## [2.4.74] — 2026-09-17
9
37
 
10
38
  ### Fixed — live_recon tech fingerprint false-positive (benchmark-driven)
package/dist/index.js CHANGED
@@ -7228,7 +7228,30 @@ var init_scanner = __esm(() => {
7228
7228
  "print ": "XSS (reflected output)",
7229
7229
  "printf(": "XSS (reflected output)",
7230
7230
  "header(": "Open redirect / header injection",
7231
- "wp_redirect(": "Open redirect"
7231
+ "wp_redirect(": "Open redirect",
7232
+ "child_process.exec(": "RCE (command execution)",
7233
+ "child_process.spawn(": "RCE (command execution)",
7234
+ "child_process.execSync(": "RCE (command execution)",
7235
+ "child_process.execFile(": "RCE (command execution)",
7236
+ "sqlx::query(&format!": "SQL injection (runtime string-built query)",
7237
+ "sqlx::query_as(&format!": "SQL injection (runtime string-built query)",
7238
+ "sequelize.query(": "SQL injection (raw query)",
7239
+ "knex.raw(": "SQL injection (raw query)",
7240
+ "subprocess.run(": "RCE (command execution)",
7241
+ "subprocess.call(": "RCE (command execution)",
7242
+ "subprocess.Popen(": "RCE (command execution)",
7243
+ "subprocess.check_output(": "RCE (command execution)",
7244
+ "os.system(": "RCE (command execution)",
7245
+ "os.popen(": "RCE (command execution)",
7246
+ "requests.get(": "SSRF (unvalidated URL fetch)",
7247
+ "requests.post(": "SSRF (unvalidated URL fetch)",
7248
+ "requests.put(": "SSRF (unvalidated URL fetch)",
7249
+ "urllib.request.urlopen(": "SSRF (unvalidated URL fetch)",
7250
+ "urllib.urlopen(": "SSRF (unvalidated URL fetch)",
7251
+ "httpx.get(": "SSRF (unvalidated URL fetch)",
7252
+ "render_template_string(": "SSTI (server-side template injection)",
7253
+ "jinja2.Template(": "SSTI (server-side template injection)",
7254
+ "Template(": "SSTI (server-side template injection)"
7232
7255
  };
7233
7256
  AUTH_GATES = [
7234
7257
  "check_ajax_referer",
@@ -15934,6 +15957,8 @@ function detectRouteAuthz(code, file, out) {
15934
15957
  const ln = lineNoOf(code, m.index);
15935
15958
  const window2 = ls.slice(Math.max(0, ln - d.window), Math.min(ls.length, ln + d.window + 1)).join(`
15936
15959
  `);
15960
+ if (PUBLIC_ROUTE_RE.test(m[0] + " " + window2))
15961
+ continue;
15937
15962
  if (d.auth.test(window2))
15938
15963
  continue;
15939
15964
  out.push({
@@ -15961,7 +15986,7 @@ function detectMissingAuthz(code, file) {
15961
15986
  return true;
15962
15987
  });
15963
15988
  }
15964
- var FRAMEWORKS, INDIRECT_AUTH, AUTH_RE, NONCE_RE, DANGEROUS_OP_RE, ROUTE_DETECTORS;
15989
+ var FRAMEWORKS, INDIRECT_AUTH, AUTH_RE, NONCE_RE, DANGEROUS_OP_RE, ROUTE_DETECTORS, PUBLIC_ROUTE_RE;
15965
15990
  var init_framework_security = __esm(() => {
15966
15991
  FRAMEWORKS = {
15967
15992
  wordpress: {
@@ -16042,13 +16067,14 @@ var init_framework_security = __esm(() => {
16042
16067
  ROUTE_DETECTORS = [
16043
16068
  { 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 },
16044
16069
  { 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 },
16045
- { framework: "flask", entry: /@(?:app|bp|blueprint)\.route\s*\(/g, auth: /login_required|current_user\.is_authenticated|permission_required/, window: 3 },
16046
- { framework: "express", entry: /(?:app|router)\.(?:get|post|put|delete|patch|use)\s*\(\s*['"][^'"]+['"]/g, auth: /requireAuth|authenticate|passport\.authenticate|req\.user|isAuthenticated|ensureAuthenticated|requireLogin|\.authorize|authMiddleware/, window: 2 },
16070
+ { 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 },
16071
+ { 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 },
16047
16072
  { framework: "spring", entry: /@(?:Get|Post|Put|Delete|Patch|Request)Mapping\s*\(/g, auth: /@PreAuthorize|@Secured|@RolesAllowed|hasRole|hasAuthority|isAuthenticated|SecurityContextHolder/, window: 2 },
16048
16073
  { framework: "symfony", entry: /#\[(?:Route|Get|Post|Put|Delete|Patch)\b|@Route\s*\(/g, auth: /is_granted|denyAccessUnlessGranted|->isGranted\s*\(|IsGranted|Voter/, window: 2 },
16049
16074
  { framework: "aspnet", entry: /\[Http(?:Get|Post|Put|Delete|Patch)\b/g, auth: /\[Authorize|User\.Identity|IsInRole|AuthorizeAsync|\[AllowAnonymous|IAuthorizationService/, window: 2 },
16050
16075
  { 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 }
16051
16076
  ];
16077
+ 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)[/'"?]|['"`]\/?['"`]$/i;
16052
16078
  });
16053
16079
 
16054
16080
  // src/complex-bugs.ts
@@ -16267,6 +16293,8 @@ function detectComplexBugs(code, file) {
16267
16293
  detectMlSupplyChain(code, file, out);
16268
16294
  detectRustUnsafe(code, file, out);
16269
16295
  detectRustFormatString(code, file, out);
16296
+ detectJwtAlgConfusion(code, file, out);
16297
+ detectCacheDeception(code, file, out);
16270
16298
  out.push(...detectWrongSanitizer(code, file));
16271
16299
  out.push(...detectMissingAuthz(code, file));
16272
16300
  const seen = new Set;
@@ -16427,6 +16455,21 @@ function detectSsti(code, file, out) {
16427
16455
  detail: "User input reaches a template render with no sandbox — SSTI (`{{7*7}}`, `${7*7}`, `#{7*7}`) → RCE."
16428
16456
  });
16429
16457
  }
16458
+ if (/from\s+liquid\s+import\s+Template|liquid\.Template|import\s+liquid/.test(code)) {
16459
+ const tRe = /\bTemplate\s*\(\s*([a-z_][a-z0-9_]*)\s*\)/g;
16460
+ while ((m = tRe.exec(code)) !== null) {
16461
+ const ln = lineNo2(code, m.index);
16462
+ out.push({
16463
+ file,
16464
+ line: ln,
16465
+ type: "ssti",
16466
+ category: "SSTI (Liquid template source from stored input)",
16467
+ severity: "high",
16468
+ evidence: (ls[ln - 1] ?? "").trim(),
16469
+ detail: "A Liquid template is compiled from a variable (`Template(var)`) with no template-syntax screening — profile/stored input becomes template source, so `{{ ... }}` tags execute server-side (SSTI → data exfiltration / RCE). Render with an explicit, static template and pass user values only as variables."
16470
+ });
16471
+ }
16472
+ }
16430
16473
  }
16431
16474
  function detectFileUploadRce(code, file, out) {
16432
16475
  code = stripCommentsOnly(code);
@@ -16525,6 +16568,10 @@ function detectHardcodedSecret(code, file, out) {
16525
16568
  continue;
16526
16569
  if (/\$[A-Za-z_]|[{}\[\]]|process\.env|getenv|os\.environ|config\s*\(|env\s*\(/.test(valueRaw))
16527
16570
  continue;
16571
+ if (/^FLAG\{[a-z0-9_\-]+\}$/i.test(value) || /^FLAG$/i.test(value))
16572
+ continue;
16573
+ if (qm && /^["'][^"']+["']\s*\+/.test(valueRaw))
16574
+ continue;
16528
16575
  const ln = lineNo2(code, m.index);
16529
16576
  flaggedLines.add(ln);
16530
16577
  let sev = "high";
@@ -16795,6 +16842,48 @@ function detectRustFormatString(code, file, out) {
16795
16842
  });
16796
16843
  }
16797
16844
  }
16845
+ function detectJwtAlgConfusion(code, file, out) {
16846
+ const clean = stripCommentsOnly(code);
16847
+ const hasRs256 = /\bRS256\b/.test(clean);
16848
+ const hasHs256 = /\bHS256\b/.test(clean);
16849
+ if (!hasRs256 || !hasHs256)
16850
+ return;
16851
+ const hmacPublicKey = /createHmac\s*\(\s*['"]sha256['"]\s*,\s*(?:[A-Za-z_][\w.]*\.(?:publicKey|public_key|PUBLIC_KEY|verifying_key|verifyingKey)|publicKey|public_key|PUBLIC_KEY)/.test(clean) || /algorithms\s*=\s*\[[^\]]*['"]HS256['"]\s*,\s*['"]RS256['"]/.test(clean) || /algorithms\s*=\s*\[[^\]]*['"]RS256['"]\s*,\s*['"]HS256['"]/.test(clean);
16852
+ if (!hmacPublicKey)
16853
+ return;
16854
+ const ls = lines2(code);
16855
+ const ln = lineNo2(code, clean.search(/\bHS256\b/));
16856
+ out.push({
16857
+ file,
16858
+ line: ln,
16859
+ type: "jwt_alg_confusion",
16860
+ category: "JWT algorithm confusion (RS256 -> HS256)",
16861
+ severity: "critical",
16862
+ evidence: (ls[ln - 1] ?? "").trim(),
16863
+ detail: "The JWT verifier dispatches on the token's alg header and accepts BOTH RS256 and HS256, with the HS256 HMAC secret derived from public/shared key material. An attacker sets alg=HS256 and signs with the public key to forge a valid token (CVE-2015-9235 family). Pin the algorithm server-side (allow only RS256) and never reuse asymmetric public keys as symmetric HMAC secrets."
16864
+ });
16865
+ }
16866
+ function detectCacheDeception(code, file, out) {
16867
+ const clean = stripCommentsOnly(code);
16868
+ const staticCache = /proxy_cache\b|proxy_cache_path\b|proxy_cache_key\b/.test(clean);
16869
+ if (!staticCache)
16870
+ return;
16871
+ const cacheKeyNoCookie = /proxy_cache_key\s+[^;]*\$scheme[^;]*\$request_uri[^;]*;/.test(clean) && !/proxy_cache_key\s+[^;]*\$cookie/.test(clean);
16872
+ const staticExt = /location\s+~?\*?\s+\\?\.[a-z|()]*(css|js|png|jpg|jpeg|gif|ico|woff|svg|map)/.test(clean) || /location\s+~[*\s]*\\\./.test(clean);
16873
+ if (!cacheKeyNoCookie || !staticExt)
16874
+ return;
16875
+ const ls = lines2(code);
16876
+ const ln = lineNo2(code, clean.search(/proxy_cache_key\b/));
16877
+ out.push({
16878
+ file,
16879
+ line: ln,
16880
+ type: "cache_deception",
16881
+ category: "Web cache deception (cookie-less static cache key)",
16882
+ severity: "high",
16883
+ evidence: (ls[ln - 1] ?? "").trim(),
16884
+ detail: "The reverse proxy caches static-extension URLs using a cache key that excludes the session cookie, while a backend route (often a greedy regex like /account(/.*)?) returns per-session private data for such URLs. An attacker can poison the cache with a victim's session response and re-fetch it anonymously. Include the cookie in the cache key, or don't cache cookie-authenticated responses."
16885
+ });
16886
+ }
16798
16887
  var SOURCE_TOKENS3, MAGIC_METHODS, SECRET_FIELD_RE, SECRET_PLACEHOLDER_RE, ENTROPY_EXCLUDE_VAR;
16799
16888
  var init_complex_bugs = __esm(() => {
16800
16889
  init_security_state();
@@ -17002,8 +17091,29 @@ $r = openai.chat.completions.create(['messages' => [['role' => 'user', 'content'
17002
17091
  id: "rust-format-string",
17003
17092
  detector: "rust_format_string",
17004
17093
  file: "main.rs",
17005
- positive: `fn f() { println!(user_input); }\\n`,
17006
- negative: `fn f() { println!("{}", user_input); }\\n`
17094
+ positive: `fn f() { println!(user_input); }
17095
+ `,
17096
+ negative: `fn f() { println!("{}", user_input); }
17097
+ `
17098
+ },
17099
+ {
17100
+ id: "jwt-alg-confusion",
17101
+ detector: "jwt_alg_confusion",
17102
+ file: "auth.js",
17103
+ positive: `function verify(t){ if(t.alg==='RS256'){ verifier.verify(KEYS.publicKey); } else if(t.alg==='HS256'){ crypto.createHmac('sha256', KEYS.publicKey).update(d).digest(); } }
17104
+ `,
17105
+ negative: `function verify(t){ if(t.alg==='RS256'){ verifier.verify(KEYS.publicKey); } else { throw new Error('unsupported alg'); } }
17106
+ `
17107
+ },
17108
+ {
17109
+ id: "cache-deception",
17110
+ detector: "cache_deception",
17111
+ file: "nginx.conf",
17112
+ positive: `location ~* \\.(css|js|png|jpg|gif|ico|svg)$ { proxy_cache ctf_cache; proxy_cache_valid 200 5m; }
17113
+ proxy_cache_key "$scheme$request_method$request_uri";
17114
+ `,
17115
+ negative: `location / { proxy_pass http://backend; }
17116
+ `
17007
17117
  }
17008
17118
  ];
17009
17119
  });
@@ -58313,7 +58423,15 @@ var ALIASES = {
58313
58423
  transmute: "rust_unsafe",
58314
58424
  "rust format string": "rust_format_string",
58315
58425
  "format string": "rust_format_string",
58316
- "format string injection": "rust_format_string"
58426
+ "format string injection": "rust_format_string",
58427
+ "jwt algorithm confusion": "jwt_alg_confusion",
58428
+ "jwt alg confusion": "jwt_alg_confusion",
58429
+ "alg confusion": "jwt_alg_confusion",
58430
+ "rs256 hs256": "jwt_alg_confusion",
58431
+ "web cache deception": "cache_deception",
58432
+ "cache deception": "cache_deception",
58433
+ "cache poisoning": "cache_deception",
58434
+ "cache key injection": "cache_deception"
58317
58435
  };
58318
58436
  function normalize(term) {
58319
58437
  return term.trim().toLowerCase().replace(/[_-]+/g, " ").replace(/\s+/g, " ").trim();
@@ -58703,7 +58821,9 @@ var VRT_MAP = {
58703
58821
  dependency_confusion: { vrt: "insecure_design > dependency_confusion", priority: "P2", severity: "high", rationale: "An internal package resolves from a squattable public registry → attacker-controlled code in the build." },
58704
58822
  ml_supply_chain: { vrt: "injection > insecure_deserialization", priority: "P1", severity: "critical", rationale: "Untrusted model loading via pickle (torch/joblib/numpy) → arbitrary code execution on load." },
58705
58823
  rust_unsafe: { vrt: "insecure_design > unsafe_memory_operations", priority: "P3", severity: "medium", rationale: "transmute/from_raw_parts/assume_init bypass the borrow checker — a hint that needs bounds verification; memory corruption if attacker-controlled." },
58706
- rust_format_string: { vrt: "injection > format_string_injection", priority: "P2", severity: "high", rationale: "An attacker-controlled format string can read or corrupt memory via format specifiers." }
58824
+ rust_format_string: { vrt: "injection > format_string_injection", priority: "P2", severity: "high", rationale: "An attacker-controlled format string can read or corrupt memory via format specifiers." },
58825
+ jwt_alg_confusion: { vrt: "authentication > jwt_algorithm_confusion", priority: "P1", severity: "critical", rationale: "A verifier accepting HS256 with public-key HMAC lets an attacker forge admin tokens (CVE-2015-9235)." },
58826
+ cache_deception: { vrt: "cryptographic_failure > web_cache_deception", priority: "P2", severity: "high", rationale: "Cookie-less static cache keys leak per-session private responses to anonymous callers." }
58707
58827
  };
58708
58828
  function severityCalibrate(input) {
58709
58829
  const type = input.type?.toLowerCase() ?? "";
@@ -330,6 +330,18 @@
330
330
  "id": "rust_format_string",
331
331
  "cwe": "CWE-134",
332
332
  "name": "Use of Externally-Controlled Format String"
333
+ },
334
+ {
335
+ "id": "jwt_alg_confusion",
336
+ "cwe": "CWE-347",
337
+ "name": "Improper Verification of Cryptographic Signature",
338
+ "category": "authentication"
339
+ },
340
+ {
341
+ "id": "cache_deception",
342
+ "cwe": "CWE-525",
343
+ "name": "Use of Web Browser Cache Containing Sensitive Information",
344
+ "category": "cryptographic_failure"
333
345
  }
334
346
  ]
335
347
  }
@@ -691,6 +691,74 @@
691
691
  "any panic path that formats attacker input",
692
692
  "logging/telemetry that echoes request data"
693
693
  ]
694
+ },
695
+ {
696
+ "id": "jwt_alg_confusion",
697
+ "name": "JWT Algorithm Confusion (RS256 -> HS256)",
698
+ "cwe": "CWE-347",
699
+ "severity": "critical",
700
+ "signature": "alg-dispatch verifier accepting RS256 AND HS256 with public/shared HMAC key",
701
+ "chains": [
702
+ "jwt"
703
+ ],
704
+ "references": [
705
+ "CVE-2015-9235",
706
+ "OWASP ASVS V3.5.2"
707
+ ],
708
+ "real_world": "A hand-written JWT verifier dispatches on the token's alg header and accepts BOTH RS256 (asymmetric) and HS256 (symmetric HMAC), with the HS256 secret derived from the RSA public key. Setting alg=HS256 and signing with the public key forges a valid token.",
709
+ "detection_signatures": [
710
+ "alg-dispatch verifier accepting RS256 AND HS256",
711
+ "createHmac('sha256', <publicKey>) alongside createVerify('RSA-SHA256')",
712
+ "pyjwt decode(..., algorithms=['HS256','RS256'])"
713
+ ],
714
+ "chain_templates": [
715
+ "jwt_alg_confusion_forgery",
716
+ "jwt_algorithm_confusion"
717
+ ],
718
+ "bypass_techniques": [
719
+ "public-key-to-HMAC confusion",
720
+ "kid/jku header injection to point the verifier at attacker keys",
721
+ "alg:none / empty signature when the verifier also accepts it"
722
+ ],
723
+ "crown_jewels": [
724
+ "the RSA public key (published at /.well-known/jwks.json)",
725
+ "admin role claims overridden by token claims",
726
+ "treasury/admin endpoints gated only by a role claim"
727
+ ]
728
+ },
729
+ {
730
+ "id": "cache_deception",
731
+ "name": "Web Cache Deception",
732
+ "cwe": "CWE-525",
733
+ "severity": "high",
734
+ "signature": "reverse-proxy caches static-extension URLs with a cookie-less cache key over a session-authenticated backend",
735
+ "chains": [
736
+ "cache_deception"
737
+ ],
738
+ "references": [
739
+ "CWE-525",
740
+ "Omer Gil — Web Cache Deception Attack",
741
+ "OWASP WSTG-ATHN-03"
742
+ ],
743
+ "real_world": "A reverse proxy caches static-extension URLs (.css/.js/.png) with a cache key that omits the session cookie, while a backend route (often a greedy regex like /account(/.*)?) serves per-session private data for those URLs. An attacker submits a victim-visited URL, the bot's private response is cached, then re-fetched anonymously.",
744
+ "detection_signatures": [
745
+ "proxy_cache_key without $cookie over a session-authenticated backend",
746
+ "location ~* \\.(css|js|png|...) with proxy_cache",
747
+ "greedy backend route regex + static-extension passthrough"
748
+ ],
749
+ "chain_templates": [
750
+ "cache_poisoning_xss"
751
+ ],
752
+ "bypass_techniques": [
753
+ "append a static extension to an authenticated path",
754
+ "path delimiters (; / ..) that still match the route regex",
755
+ "cache-key normalization differences (trailing slash, percent-encoding)"
756
+ ],
757
+ "crown_jewels": [
758
+ "per-session private profile/account JSON",
759
+ "api_key / session tokens in the cached body",
760
+ "any victim-session data cached under a static-extension URL"
761
+ ]
694
762
  }
695
763
  ]
696
764
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "2.4.74",
3
+ "version": "2.4.75",
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": {