blitzstrike 1.0.13 → 1.0.15

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.
Files changed (2) hide show
  1. package/dist/index.js +390 -23
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env node
2
- import { createRequire } from "node:module";
3
2
  var __create = Object.create;
4
3
  var __getProtoOf = Object.getPrototypeOf;
5
4
  var __defProp = Object.defineProperty;
@@ -44,7 +43,6 @@ var __esm = (fn, res, err) => () => {
44
43
  throw err[0];
45
44
  return res;
46
45
  };
47
- var __require = /* @__PURE__ */ createRequire(import.meta.url);
48
46
 
49
47
  // node_modules/ajv/dist/compile/codegen/code.js
50
48
  var require_code = __commonJS(function(exports) {
@@ -7156,7 +7154,12 @@ var init_scanner = __esm(() => {
7156
7154
  "call_user_func(": "Dynamic dispatch (attacker-controlled callback)",
7157
7155
  "call_user_func_array(": "Dynamic dispatch",
7158
7156
  "create_function(": "RCE (deprecated eval wrapper)",
7159
- "preg_replace(": "RCE (if /e modifier or code in pattern)"
7157
+ "preg_replace(": "RCE (if /e modifier or code in pattern)",
7158
+ "echo ": "XSS (reflected output)",
7159
+ "print ": "XSS (reflected output)",
7160
+ "printf(": "XSS (reflected output)",
7161
+ "header(": "Open redirect / header injection",
7162
+ "wp_redirect(": "Open redirect"
7160
7163
  };
7161
7164
  AUTH_GATES = [
7162
7165
  "check_ajax_referer",
@@ -7189,7 +7192,7 @@ var init_scanner = __esm(() => {
7189
7192
  });
7190
7193
 
7191
7194
  // src/sync.ts
7192
- import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readFileSync as readFileSync8, readdirSync as readdirSync4, statSync as statSync3, rmSync } from "node:fs";
7195
+ import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readFileSync as readFileSync10, readdirSync as readdirSync4, statSync as statSync3, rmSync } from "node:fs";
7193
7196
  import { join as join8 } from "node:path";
7194
7197
  import { homedir as homedir4 } from "node:os";
7195
7198
  import { fileURLToPath as fileURLToPath6 } from "node:url";
@@ -7285,7 +7288,7 @@ function manualCopy(src, dst) {
7285
7288
  if (e.isDirectory())
7286
7289
  manualCopy(s, d);
7287
7290
  else
7288
- writeFileSync2(d, readFileSync8(s));
7291
+ writeFileSync2(d, readFileSync10(s));
7289
7292
  }
7290
7293
  }
7291
7294
  var ROOT5, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
@@ -23157,6 +23160,7 @@ async function activeScan(target, scope = "", mode = "bug-bounty", allowed = und
23157
23160
 
23158
23161
  // src/dataflow.ts
23159
23162
  init_scanner();
23163
+ import { readFileSync as readFileSync7 } from "node:fs";
23160
23164
  var SOURCES = {
23161
23165
  $_GET: { id: "http_get", label: "HTTP GET parameter", attacker_controlled: true },
23162
23166
  $_POST: { id: "http_post", label: "HTTP POST body", attacker_controlled: true },
@@ -23238,23 +23242,37 @@ function traceDataFlow(path) {
23238
23242
  return result;
23239
23243
  let text;
23240
23244
  try {
23241
- const { readFileSync } = __require("node:fs");
23242
- text = readFileSync(path, "utf8");
23245
+ text = readFileSync7(path, "utf8");
23243
23246
  } catch {
23244
23247
  return result;
23245
23248
  }
23246
23249
  const lines = text.split(`
23247
23250
  `);
23248
- for (const s of scan.sinks) {
23251
+ const seen = new Set;
23252
+ const sinks = scan.sinks.filter((s) => {
23253
+ const cls = classifySink2(s.sink);
23254
+ if (!cls)
23255
+ return false;
23256
+ const key = `${cls.id}|${s.line}`;
23257
+ if (seen.has(key))
23258
+ return false;
23259
+ seen.add(key);
23260
+ return true;
23261
+ });
23262
+ for (const s of sinks) {
23249
23263
  const sinkClass = classifySink2(s.sink);
23250
- if (!sinkClass)
23251
- continue;
23252
- const start = Math.max(0, s.line - 15);
23253
- const window = lines.slice(start, s.line).join(`
23264
+ const wideStart = Math.max(0, s.line - 15);
23265
+ const wideWindow = lines.slice(wideStart, s.line).join(`
23266
+ `);
23267
+ const sanitizerWindow = lines.slice(Math.max(0, s.line - 3), s.line + 1).join(`
23254
23268
  `);
23255
- const source = classifySource(window);
23256
- const sanitizers = findSanitizers(window);
23257
- const authGates = AUTH_GATES.filter((g) => window.includes(g));
23269
+ const sinkLine = lines[s.line - 1] ?? "";
23270
+ const prevLine = lines[s.line - 2] ?? "";
23271
+ const authContext = sinkLine + `
23272
+ ` + prevLine;
23273
+ const source = classifySource(wideWindow);
23274
+ const sanitizers = findSanitizers(sanitizerWindow);
23275
+ const authGates = AUTH_GATES.filter((g) => authContext.includes(g));
23258
23276
  const edge = {
23259
23277
  source,
23260
23278
  sanitizers,
@@ -23267,7 +23285,7 @@ function traceDataFlow(path) {
23267
23285
  result.authorized.push(edge);
23268
23286
  continue;
23269
23287
  }
23270
- if (isSanitized(sanitizers, sinkClass.category)) {
23288
+ if (isSanitized(sanitizers, sinkClass.id)) {
23271
23289
  result.sanitized.push(edge);
23272
23290
  continue;
23273
23291
  }
@@ -23300,6 +23318,326 @@ function groupVariants(results) {
23300
23318
  return [...groups.values()].sort((a, b) => b.occurrences - a.occurrences);
23301
23319
  }
23302
23320
 
23321
+ // src/taint.ts
23322
+ init_scanner();
23323
+ import { readFileSync as readFileSync8 } from "node:fs";
23324
+ function lineIndexOf(text, pos) {
23325
+ let n = 1;
23326
+ for (let i = 0;i < pos; i++)
23327
+ if (text.charCodeAt(i) === 10)
23328
+ n++;
23329
+ return n;
23330
+ }
23331
+ function extractVars(s) {
23332
+ const out = [];
23333
+ const re = /\$([A-Za-z_][A-Za-z0-9_]*)/g;
23334
+ let m;
23335
+ while ((m = re.exec(s)) !== null) {
23336
+ if (!out.includes(m[1]))
23337
+ out.push(m[1]);
23338
+ }
23339
+ return out;
23340
+ }
23341
+ function parseFunctions(text, lines) {
23342
+ const scopes = [];
23343
+ const re = /function\s+&?([A-Za-z_][A-Za-z0-9_]*)\s*\(([^)]*)\)/g;
23344
+ let m;
23345
+ while ((m = re.exec(text)) !== null) {
23346
+ const name = m[1];
23347
+ const params = extractVars(m[2]);
23348
+ const brace = text.indexOf("{", m.index + m[0].length);
23349
+ if (brace === -1)
23350
+ continue;
23351
+ let depth = 0;
23352
+ let end = -1;
23353
+ for (let i = brace;i < text.length; i++) {
23354
+ if (text[i] === "{")
23355
+ depth++;
23356
+ else if (text[i] === "}") {
23357
+ depth--;
23358
+ if (depth === 0) {
23359
+ end = i;
23360
+ break;
23361
+ }
23362
+ }
23363
+ }
23364
+ if (end === -1)
23365
+ continue;
23366
+ const startLine = lineIndexOf(text, m.index) - 1;
23367
+ const endLine = lineIndexOf(text, end);
23368
+ scopes.push({ name, start: startLine, end: endLine, params, isGlobal: false });
23369
+ }
23370
+ return scopes;
23371
+ }
23372
+ function isSourceExpr(expr) {
23373
+ for (const token of Object.keys({ $_GET: 1, $_POST: 1, $_REQUEST: 1, $_COOKIE: 1, $_FILES: 1, $_SERVER: 1 })) {
23374
+ if (expr.includes(token))
23375
+ return token;
23376
+ }
23377
+ if (expr.includes("php://input") || expr.includes("file_get_contents('php://input')"))
23378
+ return "php://input";
23379
+ if (expr.includes("getallheaders"))
23380
+ return "getallheaders";
23381
+ return null;
23382
+ }
23383
+ function analyzeScope(text, lines, scope) {
23384
+ const vars = new Map;
23385
+ for (const p of scope.params)
23386
+ vars.set(p, { tainted: false, sanitizers: [] });
23387
+ const endLine = Math.min(scope.end, lines.length);
23388
+ for (let li = scope.start;li < endLine; li++) {
23389
+ const line = lines[li];
23390
+ const assignRe = /\$([A-Za-z_][A-Za-z0-9_]*)\s*(?:\.?=)\s*(.+)/;
23391
+ const am = assignRe.exec(line);
23392
+ if (!am)
23393
+ continue;
23394
+ const varName = am[1];
23395
+ const expr = am[2];
23396
+ const src = isSourceExpr(expr);
23397
+ if (src) {
23398
+ vars.set(varName, { tainted: true, sanitizers: [], source: src, sourceLine: li + 1 });
23399
+ continue;
23400
+ }
23401
+ const sanitizers = findSanitizers(expr);
23402
+ const innerVars = extractVars(expr);
23403
+ if (sanitizers.length > 0 && innerVars.length > 0) {
23404
+ const inner = vars.get(innerVars[0]);
23405
+ vars.set(varName, {
23406
+ tainted: inner?.tainted ?? false,
23407
+ sanitizers: [...inner?.sanitizers ?? [], ...sanitizers],
23408
+ source: inner?.source,
23409
+ sourceLine: inner?.sourceLine
23410
+ });
23411
+ continue;
23412
+ }
23413
+ let tainted = false;
23414
+ let mergedSanitizers = [];
23415
+ let source;
23416
+ let sourceLine;
23417
+ for (const v of innerVars) {
23418
+ const st = vars.get(v);
23419
+ if (st?.tainted) {
23420
+ tainted = true;
23421
+ mergedSanitizers = st.sanitizers;
23422
+ source = st.source;
23423
+ sourceLine = st.sourceLine;
23424
+ break;
23425
+ }
23426
+ }
23427
+ vars.set(varName, { tainted, sanitizers: mergedSanitizers, source, sourceLine });
23428
+ }
23429
+ return vars;
23430
+ }
23431
+ function buildSummaries(text, lines, scopes) {
23432
+ const summaries = new Map;
23433
+ for (const scope of scopes) {
23434
+ if (scope.isGlobal)
23435
+ continue;
23436
+ const sinkParams = new Map;
23437
+ const sanitizedParams = new Map;
23438
+ const vars = analyzeScope(text, lines, scope);
23439
+ const endLine = Math.min(scope.end, lines.length);
23440
+ for (let li = scope.start;li < endLine; li++) {
23441
+ const line = lines[li];
23442
+ const inlineSan = findSanitizers(line);
23443
+ if (inlineSan.length > 0) {
23444
+ for (const p of scope.params) {
23445
+ if (line.includes("$" + p)) {
23446
+ const existing = sanitizedParams.get(p) ?? [];
23447
+ sanitizedParams.set(p, [...existing, ...inlineSan]);
23448
+ }
23449
+ }
23450
+ }
23451
+ for (const sinkToken of Object.keys({
23452
+ "->query(": 1,
23453
+ "$wpdb->query": 1,
23454
+ "$wpdb->get_var": 1,
23455
+ "$wpdb->get_results": 1,
23456
+ "system(": 1,
23457
+ "exec(": 1,
23458
+ "shell_exec(": 1,
23459
+ "eval(": 1,
23460
+ "unserialize(": 1,
23461
+ "move_uploaded_file(": 1,
23462
+ "file_put_contents(": 1,
23463
+ include: 1,
23464
+ require: 1,
23465
+ "echo ": 1,
23466
+ "print ": 1,
23467
+ "header(": 1,
23468
+ "wp_redirect(": 1,
23469
+ "file_get_contents(": 1
23470
+ })) {
23471
+ if (!line.includes(sinkToken))
23472
+ continue;
23473
+ const varsInLine = extractVars(line);
23474
+ for (const v of varsInLine) {
23475
+ const st = vars.get(v);
23476
+ if (st?.tainted) {
23477
+ sinkParams.set(v, { sink: sinkToken, line: li + 1 });
23478
+ }
23479
+ }
23480
+ for (const p of scope.params) {
23481
+ if (line.includes("$" + p))
23482
+ sinkParams.set(p, { sink: sinkToken, line: li + 1 });
23483
+ }
23484
+ }
23485
+ }
23486
+ summaries.set(scope.name, { name: scope.name, sinkParams, sanitizedParams });
23487
+ }
23488
+ return summaries;
23489
+ }
23490
+ function analyzeTaint(path) {
23491
+ const result = { file: path, findings: [], suppressed: 0 };
23492
+ let text;
23493
+ try {
23494
+ text = readFileSync8(path, "utf8");
23495
+ } catch {
23496
+ return result;
23497
+ }
23498
+ const lines = text.split(`
23499
+ `);
23500
+ const scopes = parseFunctions(text, lines);
23501
+ const summaries = buildSummaries(text, lines, scopes);
23502
+ const globalScope = { name: "<global>", start: 0, end: lines.length, params: [], isGlobal: true };
23503
+ const allScopes = [...scopes, globalScope];
23504
+ for (const scope of allScopes) {
23505
+ const vars = analyzeScope(text, lines, scope);
23506
+ const endLine = Math.min(scope.end, lines.length);
23507
+ for (let li = scope.start;li < endLine; li++) {
23508
+ const line = lines[li];
23509
+ const calledFn = /([A-Za-z_][A-Za-z0-9_]*)\s*\(/.exec(line);
23510
+ if (calledFn && summaries.has(calledFn[1])) {
23511
+ const sum = summaries.get(calledFn[1]);
23512
+ const callVars = extractVars(line);
23513
+ const passedTainted = callVars.filter((v) => vars.get(v)?.tainted);
23514
+ if (passedTainted.length > 0 && sum.sinkParams.size > 0) {
23515
+ const firstSink = [...sum.sinkParams.values()][0];
23516
+ const cls = classifySink2(firstSink.sink);
23517
+ if (cls) {
23518
+ const st = vars.get(passedTainted[0]);
23519
+ const inline = findSanitizers(line);
23520
+ const helperSan = [...sum.sanitizedParams.values()].flat();
23521
+ const san = [...st.sanitizers, ...inline, ...helperSan];
23522
+ const authGated = AUTH_GATES.some((g) => line.includes(g) || (lines[li - 1] ?? "").includes(g));
23523
+ if (!isSanitized(san, cls.id) && !authGated) {
23524
+ result.findings.push({
23525
+ sink: firstSink.sink,
23526
+ sink_line: firstSink.line,
23527
+ category: cls.category,
23528
+ cwe: cls.cwe,
23529
+ variables: passedTainted.map((v) => vars.get(v).source ?? "var"),
23530
+ source: st.source,
23531
+ source_line: st.sourceLine,
23532
+ sanitized: false,
23533
+ sanitizers: san.map((s) => s.id),
23534
+ auth_gated: authGated,
23535
+ interprocedural: true
23536
+ });
23537
+ } else {
23538
+ result.suppressed += 1;
23539
+ }
23540
+ continue;
23541
+ }
23542
+ }
23543
+ }
23544
+ let sinkToken = null;
23545
+ let sinkClass = null;
23546
+ for (const tok of Object.keys({
23547
+ "->query(": 1,
23548
+ "$wpdb->query": 1,
23549
+ "$wpdb->get_var": 1,
23550
+ "$wpdb->get_results": 1,
23551
+ "->whereRaw": 1,
23552
+ "system(": 1,
23553
+ "exec(": 1,
23554
+ "shell_exec(": 1,
23555
+ "passthru(": 1,
23556
+ "popen(": 1,
23557
+ "proc_open(": 1,
23558
+ "eval(": 1,
23559
+ "assert(": 1,
23560
+ "unserialize(": 1,
23561
+ "maybe_unserialize(": 1,
23562
+ "move_uploaded_file(": 1,
23563
+ "file_put_contents(": 1,
23564
+ "fwrite(": 1,
23565
+ include: 1,
23566
+ require: 1,
23567
+ "echo ": 1,
23568
+ "print ": 1,
23569
+ "printf(": 1,
23570
+ "header(": 1,
23571
+ "wp_redirect(": 1,
23572
+ "file_get_contents(": 1,
23573
+ "wp_remote_get(": 1,
23574
+ "wp_remote_post(": 1
23575
+ })) {
23576
+ if (line.includes(tok)) {
23577
+ const cls = classifySink2(tok);
23578
+ if (cls) {
23579
+ sinkToken = tok;
23580
+ sinkClass = cls;
23581
+ break;
23582
+ }
23583
+ }
23584
+ }
23585
+ if (!sinkToken || !sinkClass)
23586
+ continue;
23587
+ const varsInLine = extractVars(line);
23588
+ const taintedVars = [];
23589
+ const allSanitizers = [];
23590
+ let source;
23591
+ let sourceLine;
23592
+ for (const v of varsInLine) {
23593
+ const st = vars.get(v);
23594
+ if (st?.tainted) {
23595
+ taintedVars.push(st);
23596
+ allSanitizers.push(...st.sanitizers);
23597
+ if (!source) {
23598
+ source = st.source;
23599
+ sourceLine = st.sourceLine;
23600
+ }
23601
+ }
23602
+ }
23603
+ const inlineSource = isSourceExpr(line);
23604
+ if (inlineSource && taintedVars.length === 0) {
23605
+ taintedVars.push({ tainted: true, sanitizers: [], source: inlineSource, sourceLine: li + 1 });
23606
+ source = inlineSource;
23607
+ sourceLine = li + 1;
23608
+ }
23609
+ if (taintedVars.length === 0)
23610
+ continue;
23611
+ const inlineSanitizers = findSanitizers(line);
23612
+ const sanitizers = [...allSanitizers, ...inlineSanitizers].filter((s, i, arr) => arr.indexOf(s) === i);
23613
+ const authGated = AUTH_GATES.some((g) => line.includes(g) || (lines[li - 1] ?? "").includes(g));
23614
+ const sanitized = isSanitized(sanitizers, sinkClass.id);
23615
+ if (sanitized || authGated) {
23616
+ result.suppressed += 1;
23617
+ continue;
23618
+ }
23619
+ result.findings.push({
23620
+ sink: sinkToken,
23621
+ sink_line: li + 1,
23622
+ category: sinkClass.category,
23623
+ cwe: sinkClass.cwe,
23624
+ variables: taintedVars.map((t) => t.source ?? "var").filter((v, i, a) => a.indexOf(v) === i),
23625
+ source,
23626
+ source_line: sourceLine,
23627
+ sanitized,
23628
+ sanitizers: sanitizers.map((s) => s.id),
23629
+ auth_gated: authGated,
23630
+ interprocedural: false
23631
+ });
23632
+ }
23633
+ }
23634
+ return result;
23635
+ }
23636
+ function taintTree(root, maxFiles = 2000) {
23637
+ const files = iterSourceFiles(root, maxFiles);
23638
+ return files.map((f) => analyzeTaint(f));
23639
+ }
23640
+
23303
23641
  // src/server.ts
23304
23642
  function createServer() {
23305
23643
  const server = new McpServer({
@@ -23848,6 +24186,35 @@ function createServer() {
23848
24186
  }]
23849
24187
  };
23850
24188
  });
24189
+ server.registerTool("taint_scan", {
24190
+ title: "Inter-procedural taint analysis",
24191
+ description: "EAGLE-EYE: real taint tracking — variable assignment, cross-function flow, and sanitizer awareness. Finds tainted data reaching sinks even across function boundaries.",
24192
+ inputSchema: { path: string2().describe("Source file path") }
24193
+ }, async ({ path }) => {
24194
+ const r = analyzeTaint(path);
24195
+ return { content: [{ type: "text", text: JSON.stringify(r) }] };
24196
+ });
24197
+ server.registerTool("taint_tree", {
24198
+ title: "Whole-tree taint scan",
24199
+ description: "EAGLE-EYE: run inter-procedural taint analysis across an entire source tree.",
24200
+ inputSchema: {
24201
+ path: string2().describe("Source directory path"),
24202
+ max_files: number2().int().optional().describe("Max files (default 2000)")
24203
+ }
24204
+ }, async ({ path, max_files }) => {
24205
+ const results = taintTree(path, max_files ?? 2000);
24206
+ return {
24207
+ content: [{
24208
+ type: "text",
24209
+ text: JSON.stringify({
24210
+ files_analyzed: results.length,
24211
+ total_findings: results.reduce((n, r) => n + r.findings.length, 0),
24212
+ total_suppressed: results.reduce((n, r) => n + r.suppressed, 0),
24213
+ findings: results.filter((r) => r.findings.length > 0).map((r) => ({ file: r.file, findings: r.findings }))
24214
+ })
24215
+ }]
24216
+ };
24217
+ });
23851
24218
  return server;
23852
24219
  }
23853
24220
  async function serve() {
@@ -23857,7 +24224,7 @@ async function serve() {
23857
24224
  }
23858
24225
 
23859
24226
  // src/cli.ts
23860
- import { readFileSync as readFileSync7, existsSync as existsSync6, writeFileSync, mkdirSync as mkdirSync2 } from "node:fs";
24227
+ import { readFileSync as readFileSync9, existsSync as existsSync6, writeFileSync, mkdirSync as mkdirSync2 } from "node:fs";
23861
24228
  import { join as join7, dirname } from "node:path";
23862
24229
  import { homedir as homedir3 } from "node:os";
23863
24230
  import { fileURLToPath as fileURLToPath5 } from "node:url";
@@ -23939,7 +24306,7 @@ function resolveCommand() {
23939
24306
  }
23940
24307
  function readJson(p) {
23941
24308
  try {
23942
- return JSON.parse(readFileSync7(p, "utf8"));
24309
+ return JSON.parse(readFileSync9(p, "utf8"));
23943
24310
  } catch {
23944
24311
  return null;
23945
24312
  }
@@ -23982,7 +24349,7 @@ function copyOpenCodeAgents() {
23982
24349
  const src = join7(srcDir, f);
23983
24350
  const dst = join7(dstDir, f);
23984
24351
  if (existsSync6(src)) {
23985
- writeFileSync(dst, readFileSync7(src, "utf8"));
24352
+ writeFileSync(dst, readFileSync9(src, "utf8"));
23986
24353
  }
23987
24354
  }
23988
24355
  }
@@ -23991,7 +24358,7 @@ function codexWrite(p) {
23991
24358
  const dir = dirname(p);
23992
24359
  if (!existsSync6(dir))
23993
24360
  mkdirSync2(dir, { recursive: true });
23994
- let existing = existsSync6(p) ? readFileSync7(p, "utf8") : "";
24361
+ let existing = existsSync6(p) ? readFileSync9(p, "utf8") : "";
23995
24362
  if (!existing.trimEnd().endsWith(`
23996
24363
  `))
23997
24364
  existing += `
@@ -24010,7 +24377,7 @@ function hermesWrite(p) {
24010
24377
  const dir = dirname(p);
24011
24378
  if (!existsSync6(dir))
24012
24379
  mkdirSync2(dir, { recursive: true });
24013
- let existing = existsSync6(p) ? readFileSync7(p, "utf8") : "";
24380
+ let existing = existsSync6(p) ? readFileSync9(p, "utf8") : "";
24014
24381
  existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
24015
24382
  if (!existing.trimEnd().endsWith(`
24016
24383
  `))
@@ -24148,7 +24515,7 @@ Registered with ${ok}/${installed.length} agent(s).`);
24148
24515
  }
24149
24516
 
24150
24517
  // src/index.ts
24151
- import { readFileSync as readFileSync9, existsSync as existsSync8 } from "node:fs";
24518
+ import { readFileSync as readFileSync11, existsSync as existsSync8 } from "node:fs";
24152
24519
  import { join as join9 } from "node:path";
24153
24520
  import { fileURLToPath as fileURLToPath7 } from "node:url";
24154
24521
  var ROOT6 = join9(fileURLToPath7(new URL(".", import.meta.url)), "..");
@@ -24156,7 +24523,7 @@ function readVersion() {
24156
24523
  try {
24157
24524
  const p = join9(ROOT6, "package.json");
24158
24525
  if (existsSync8(p))
24159
- return JSON.parse(readFileSync9(p, "utf8")).version ?? "1.0.0";
24526
+ return JSON.parse(readFileSync11(p, "utf8")).version ?? "1.0.0";
24160
24527
  } catch {}
24161
24528
  return "1.0.0";
24162
24529
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blitzstrike",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
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": {