blitzstrike 1.0.62 → 1.0.63

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,23 @@ 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
+ ## [1.0.63] — 2026-09-13
9
+
10
+ ### Added — complex-bug detection (`src/complex-bugs.ts`)
11
+
12
+ Three "hard" vulnerability classes beyond source→sink taint:
13
+
14
+ - **Deserialization → POP gadget chain** — `unserialize()` of attacker-controlled
15
+ data when a magic method (`__destruct`/`__wakeup`/`__toString`/`__invoke`/`__call`)
16
+ is present in the file (object injection → RCE pivot).
17
+ - **Type juggling** — loose comparison (`==`/`!=`) of user input against a
18
+ hash/secret (PHP `0e…` collision auth bypass); strict `===` is ignored.
19
+ - **Mass assignment** — `extract()` of request data without `EXTR_SKIP`, or
20
+ `parse_str()` of user input (variable injection).
21
+
22
+ Wired into the `@source` agent as `vulnerability` graph nodes. Added a
23
+ `php-complex` fixture to the enterprise benchmark.
24
+
8
25
  ## [1.0.62] — 2026-09-13
9
26
 
10
27
  ### Added — route-confusion & dispatch-abuse detection
@@ -0,0 +1,34 @@
1
+ <?php
2
+ /**
3
+ * Plugin helper (simplified) with multiple complex-bug patterns.
4
+ */
5
+
6
+ // 1. Deserialization → object injection (magic method present)
7
+ function restore_session($payload) {
8
+ $data = $_POST['data'];
9
+ $obj = unserialize($data);
10
+ return $obj;
11
+ }
12
+
13
+ class CacheManager {
14
+ public function __destruct() {
15
+ // Magic method — a POP gadget can pivot object injection to RCE here.
16
+ system("rm -rf /tmp/cache");
17
+ }
18
+ }
19
+
20
+ // 2. Type juggling — loose comparison of user input against a hash
21
+ function verify_signature() {
22
+ $sig = $_GET['sig'];
23
+ $secret = get_option('signing_secret');
24
+ if ($sig == $secret) { // <-- loose == , 0e... collision bypass
25
+ return true;
26
+ }
27
+ return false;
28
+ }
29
+
30
+ // 3. Mass assignment — extract() of request data without EXTR_SKIP
31
+ function bootstrap_options() {
32
+ extract($_REQUEST); // <-- variable injection
33
+ return $admin; // $admin is attacker-controlled
34
+ }
package/dist/index.js CHANGED
@@ -37186,7 +37186,7 @@ function detectRouteConfusion(code, file) {
37186
37186
  }
37187
37187
  var SOURCE_TOKENS, DYNAMIC_DISPATCH_RE, DYNAMIC_METHOD_RE, BATCH_LOOP_RE, ROUTE_MATCH_RE, NORMALIZERS;
37188
37188
  var init_route_confusion = __esm(() => {
37189
- SOURCE_TOKENS = /\$_(GET|POST|REQUEST|COOKIE|FILES)\b|\$request->|\$req->|request\.(get|input|query|post)|getParameter\(|req\.(query|body|params)/;
37189
+ SOURCE_TOKENS = /\$_(GET|POST|REQUEST|COOKIE|FILES|SERVER)\b|\$request->|\$req->|request\.(get|input|query|post)|getParameter\(|req\.(query|body|params)/;
37190
37190
  DYNAMIC_DISPATCH_RE = /\b(call_user_func|call_user_func_array|forward|dispatch|resolve_route|invoke)\s*\(\s*(\$|\[)/gi;
37191
37191
  DYNAMIC_METHOD_RE = /(->|::)\s*\$[A-Za-z_]/g;
37192
37192
  BATCH_LOOP_RE = /\bforeach\s*\([^)]*(requests|batch|sub_requests|operations)\b/gi;
@@ -37194,6 +37194,124 @@ var init_route_confusion = __esm(() => {
37194
37194
  NORMALIZERS = /\b(strtolower|strtoupper|rtrim|urldecode|rawurldecode|parse_url|normalize|canonical)\b/i;
37195
37195
  });
37196
37196
 
37197
+ // src/complex-bugs.ts
37198
+ function lines2(code) {
37199
+ return code.split(`
37200
+ `);
37201
+ }
37202
+ function lineNo2(code, pos) {
37203
+ let n = 1;
37204
+ for (let i = 0;i < pos; i++)
37205
+ if (code.charCodeAt(i) === 10)
37206
+ n++;
37207
+ return n;
37208
+ }
37209
+ function detectDeserialization(code, file, out) {
37210
+ if (!MAGIC_METHODS.test(code))
37211
+ return;
37212
+ const ls = lines2(code);
37213
+ const re = /\b(unserialize|maybe_unserialize)\s*\(/g;
37214
+ let m;
37215
+ while ((m = re.exec(code)) !== null) {
37216
+ const ln = lineNo2(code, m.index);
37217
+ const l = ls[ln - 1] ?? "";
37218
+ const scope = ls.slice(Math.max(0, ln - 4), ln).join(`
37219
+ `);
37220
+ if (!SOURCE_TOKENS2.test(l) && !SOURCE_TOKENS2.test(scope))
37221
+ continue;
37222
+ out.push({
37223
+ file,
37224
+ line: ln,
37225
+ type: "deserialization",
37226
+ category: "Deserialization → POP gadget chain",
37227
+ severity: "high",
37228
+ evidence: l.trim(),
37229
+ detail: `\`${m[1]}()\` of attacker-controlled data AND a magic method is present — object injection can pivot to RCE via the gadget chain.`
37230
+ });
37231
+ }
37232
+ }
37233
+ function detectTypeJuggling(code, file, out) {
37234
+ const ls = lines2(code);
37235
+ const re = /(?<![=!<>])(==|!=)(?!=)/g;
37236
+ let m;
37237
+ while ((m = re.exec(code)) !== null) {
37238
+ const ln = lineNo2(code, m.index);
37239
+ const l = ls[ln - 1] ?? "";
37240
+ const scope = ls.slice(Math.max(0, ln - 4), ln).join(`
37241
+ `);
37242
+ const hasSource = SOURCE_TOKENS2.test(l) || SOURCE_TOKENS2.test(scope);
37243
+ const hasHash = /\b(md5|sha1|sha256|hash|crc32|crypt|password_verify|strcmp|hash_equals|secret|token|nonce)\s*\(?/i.test(l) || /\b(secret|token|nonce|hash)\b/i.test(l);
37244
+ if (!hasSource || !hasHash)
37245
+ continue;
37246
+ out.push({
37247
+ file,
37248
+ line: ln,
37249
+ type: "type_juggling",
37250
+ category: "Type juggling (loose comparison)",
37251
+ severity: "medium",
37252
+ evidence: l.trim(),
37253
+ detail: 'Loose comparison of user input against a hash/secret — PHP type juggling (`0e…`, `"str" == 0`) may bypass the check. Use strict `===`.'
37254
+ });
37255
+ }
37256
+ }
37257
+ function detectMassAssignment(code, file, out) {
37258
+ const ls = lines2(code);
37259
+ const extractRe = /\bextract\s*\(\s*[^)]*(REQUEST|POST|GET|COOKIE|FILES|\$_(REQUEST|POST|GET|COOKIE|FILES))/g;
37260
+ let m;
37261
+ while ((m = extractRe.exec(code)) !== null) {
37262
+ const ln = lineNo2(code, m.index);
37263
+ const l = ls[ln - 1] ?? "";
37264
+ if (/EXTR_SKIP|EXTR_PREFIX_ALL/.test(l))
37265
+ continue;
37266
+ out.push({
37267
+ file,
37268
+ line: ln,
37269
+ type: "mass_assignment",
37270
+ category: "Mass assignment (variable injection)",
37271
+ severity: "high",
37272
+ evidence: l.trim(),
37273
+ detail: "`extract()` of request data without EXTR_SKIP — attacker can overwrite arbitrary variables."
37274
+ });
37275
+ }
37276
+ const parseRe = /\bparse_str\s*\(/g;
37277
+ while ((m = parseRe.exec(code)) !== null) {
37278
+ const ln = lineNo2(code, m.index);
37279
+ const l = ls[ln - 1] ?? "";
37280
+ const scope = ls.slice(Math.max(0, ln - 3), ln).join(`
37281
+ `);
37282
+ if (!SOURCE_TOKENS2.test(l) && !SOURCE_TOKENS2.test(scope))
37283
+ continue;
37284
+ out.push({
37285
+ file,
37286
+ line: ln,
37287
+ type: "mass_assignment",
37288
+ category: "Mass assignment (variable injection)",
37289
+ severity: "medium",
37290
+ evidence: l.trim(),
37291
+ detail: "`parse_str()` of user input into the symbol table — variable injection."
37292
+ });
37293
+ }
37294
+ }
37295
+ function detectComplexBugs(code, file) {
37296
+ const out = [];
37297
+ detectDeserialization(code, file, out);
37298
+ detectTypeJuggling(code, file, out);
37299
+ detectMassAssignment(code, file, out);
37300
+ const seen = new Set;
37301
+ return out.filter((f) => {
37302
+ const key = `${f.type}:${f.line}`;
37303
+ if (seen.has(key))
37304
+ return false;
37305
+ seen.add(key);
37306
+ return true;
37307
+ });
37308
+ }
37309
+ var SOURCE_TOKENS2, MAGIC_METHODS;
37310
+ var init_complex_bugs = __esm(() => {
37311
+ SOURCE_TOKENS2 = /\$_(GET|POST|REQUEST|COOKIE|FILES|SERVER)\b|\$request->|\$req->|request\.(get|input|query|post|data)|getParameter\(|req\.(query|body|params)/;
37312
+ MAGIC_METHODS = /\b(__destruct|__wakeup|__toString|__invoke|__call|__get|__set|__isset|__unset|__sleep)\s*\(/;
37313
+ });
37314
+
37197
37315
  // src/agents.ts
37198
37316
  import { readFileSync as readFileSync18 } from "node:fs";
37199
37317
  function emptyGraph() {
@@ -37218,6 +37336,7 @@ var init_agents = __esm(() => {
37218
37336
  init_eagle2();
37219
37337
  init_universal_taint();
37220
37338
  init_route_confusion();
37339
+ init_complex_bugs();
37221
37340
  init_adapters();
37222
37341
  init_active();
37223
37342
  init_strike();
@@ -37324,6 +37443,21 @@ var init_agents = __esm(() => {
37324
37443
  graph = upsertNode(graph, nodeId, "route", { file: f, line: rc.line, type: rc.type, hypothesis: hyp.id });
37325
37444
  graph = addEdge2(graph, tid, nodeId, "routes_to");
37326
37445
  }
37446
+ for (const cb of detectComplexBugs(code, f).slice(0, 50)) {
37447
+ const hyp = makeHypothesis({
37448
+ type: cb.category,
37449
+ target: `${f}:${cb.line}`,
37450
+ evidence: cb.detail,
37451
+ confidence: cb.severity === "high" ? 0.7 : 0.55,
37452
+ required_validation: "confirm the payload reaches the sink and is exploitable",
37453
+ potential_impact: cb.type
37454
+ });
37455
+ hypotheses.push(hyp);
37456
+ findings += 1;
37457
+ const nodeId = `complex:${f}:${cb.line}:${cb.type}`;
37458
+ graph = upsertNode(graph, nodeId, "vulnerability", { file: f, line: cb.line, type: cb.type, hypothesis: hyp.id });
37459
+ graph = addEdge2(graph, tid, nodeId, "vulnerable_to");
37460
+ }
37327
37461
  }
37328
37462
  return { ok: true, output: { files: files.length, findings }, hypotheses, graph };
37329
37463
  }
@@ -38267,6 +38401,13 @@ var init_enterprise_benchmark = __esm(() => {
38267
38401
  expected_vulns: 1,
38268
38402
  expected_sinks: ["Route confusion"],
38269
38403
  safe_tokens: []
38404
+ },
38405
+ {
38406
+ id: "php-complex",
38407
+ dir: join17(ROOT10, "php-complex"),
38408
+ expected_vulns: 4,
38409
+ expected_sinks: ["Deserialization", "Type juggling", "Mass assignment"],
38410
+ safe_tokens: []
38270
38411
  }
38271
38412
  ];
38272
38413
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "1.0.62",
3
+ "version": "1.0.63",
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": {