blitzstrike 2.4.32 → 2.4.33
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 +15 -0
- package/bench/corpus/cb-wrongsanitizer-safe.md +10 -0
- package/bench/corpus/cb-wrongsanitizer-vuln.md +10 -0
- package/dist/index.js +210 -12
- package/package.json +1 -1
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.33] — 2026-09-14
|
|
9
|
+
|
|
10
|
+
### Added — Security-State Lattice (beyond binary taint)
|
|
11
|
+
|
|
12
|
+
- New `src/security-state.ts`: models each value's SECURITY STATE
|
|
13
|
+
(clean < whitelisted < validated < sanitized(context) < tainted) instead of
|
|
14
|
+
binary tainted/not. A sink declares the minimum context it accepts.
|
|
15
|
+
- `detectWrongSanitizer` (wired into `complex_scan`/`blitz_file`) finds the
|
|
16
|
+
complex bug class binary taint misses: a value sanitized for context X
|
|
17
|
+
reaching a sink that needs Y (e.g. `sanitize_text_field()` into a SQL query),
|
|
18
|
+
and pseudo-sanitizers (`base64_encode()`/`trim()` misused as escaping).
|
|
19
|
+
- New `security_state` tool: deterministic verdict for a sanitizer flow.
|
|
20
|
+
- Data-driven tables (sanitizers, pseudo-sanitizers, sink requirements) —
|
|
21
|
+
the lattice is data, not guesses.
|
|
22
|
+
|
|
8
23
|
## [2.4.32] — 2026-09-14
|
|
9
24
|
|
|
10
25
|
### Removed
|
package/dist/index.js
CHANGED
|
@@ -56078,8 +56078,189 @@ function detectRouteConfusion(code, file) {
|
|
|
56078
56078
|
return out;
|
|
56079
56079
|
}
|
|
56080
56080
|
|
|
56081
|
+
// src/security-state.ts
|
|
56082
|
+
var SANITIZERS2 = {
|
|
56083
|
+
esc_sql: { context: "sql", level: "sanitized" },
|
|
56084
|
+
addslashes: { context: "sql", level: "sanitized" },
|
|
56085
|
+
mysqli_real_escape_string: { context: "sql", level: "sanitized" },
|
|
56086
|
+
mysql_real_escape_string: { context: "sql", level: "sanitized" },
|
|
56087
|
+
pg_escape_string: { context: "sql", level: "sanitized" },
|
|
56088
|
+
sanitize_text_field: { context: "html", level: "sanitized" },
|
|
56089
|
+
sanitize_textarea_field: { context: "html", level: "sanitized" },
|
|
56090
|
+
esc_html: { context: "html", level: "sanitized" },
|
|
56091
|
+
esc_html__: { context: "html", level: "sanitized" },
|
|
56092
|
+
htmlspecialchars: { context: "html", level: "sanitized" },
|
|
56093
|
+
htmlentities: { context: "html", level: "sanitized" },
|
|
56094
|
+
wp_kses: { context: "html", level: "sanitized" },
|
|
56095
|
+
wp_kses_post: { context: "html", level: "sanitized" },
|
|
56096
|
+
wp_kses_data: { context: "html", level: "sanitized" },
|
|
56097
|
+
strip_tags: { context: "html", level: "sanitized" },
|
|
56098
|
+
esc_attr: { context: "html_attr", level: "sanitized" },
|
|
56099
|
+
esc_url: { context: "url", level: "sanitized" },
|
|
56100
|
+
esc_url_raw: { context: "url", level: "sanitized" },
|
|
56101
|
+
urlencode: { context: "url", level: "sanitized" },
|
|
56102
|
+
rawurlencode: { context: "url", level: "sanitized" },
|
|
56103
|
+
wp_json_encode: { context: "json", level: "sanitized" },
|
|
56104
|
+
json_encode: { context: "json", level: "sanitized" },
|
|
56105
|
+
absint: { context: "int", level: "validated" },
|
|
56106
|
+
intval: { context: "int", level: "validated" },
|
|
56107
|
+
sanitize_key: { context: "key", level: "validated" },
|
|
56108
|
+
sanitize_email: { context: "email", level: "validated" },
|
|
56109
|
+
sanitize_file_name: { context: "filename", level: "validated" },
|
|
56110
|
+
filter_var: { context: "validated", level: "validated" }
|
|
56111
|
+
};
|
|
56112
|
+
var PSEUDO_SANITIZERS = new Set([
|
|
56113
|
+
"trim",
|
|
56114
|
+
"ltrim",
|
|
56115
|
+
"rtrim",
|
|
56116
|
+
"strtolower",
|
|
56117
|
+
"strtoupper",
|
|
56118
|
+
"ucfirst",
|
|
56119
|
+
"ucwords",
|
|
56120
|
+
"base64_encode",
|
|
56121
|
+
"base64_decode",
|
|
56122
|
+
"urldecode",
|
|
56123
|
+
"rawurldecode",
|
|
56124
|
+
"stripslashes",
|
|
56125
|
+
"html_entity_decode",
|
|
56126
|
+
"htmlspecialchars_decode",
|
|
56127
|
+
"json_decode",
|
|
56128
|
+
"unserialize",
|
|
56129
|
+
"serialize",
|
|
56130
|
+
"substr",
|
|
56131
|
+
"mb_substr",
|
|
56132
|
+
"explode",
|
|
56133
|
+
"implode",
|
|
56134
|
+
"str_replace",
|
|
56135
|
+
"preg_replace",
|
|
56136
|
+
"nl2br",
|
|
56137
|
+
"strval",
|
|
56138
|
+
"number_format",
|
|
56139
|
+
"sprintf",
|
|
56140
|
+
"md5",
|
|
56141
|
+
"sha1",
|
|
56142
|
+
"hash",
|
|
56143
|
+
"crypt",
|
|
56144
|
+
"chr",
|
|
56145
|
+
"ord",
|
|
56146
|
+
"strrev",
|
|
56147
|
+
"str_repeat",
|
|
56148
|
+
"str_pad"
|
|
56149
|
+
]);
|
|
56150
|
+
var SINK_REQUIREMENTS = {
|
|
56151
|
+
sql_execution: ["sql", "sql_param", "int"],
|
|
56152
|
+
code_execution: ["code"],
|
|
56153
|
+
command_execution: ["code"],
|
|
56154
|
+
deserialization: ["code"],
|
|
56155
|
+
file_operations: ["filename"],
|
|
56156
|
+
redirect: ["url"],
|
|
56157
|
+
html_render: ["html"]
|
|
56158
|
+
};
|
|
56159
|
+
var SOURCE_TOKENS2 = /\$_(GET|POST|REQUEST|COOKIE|FILES|SERVER)\b|->(input|query|request)\(|\breq\.(query|body|params|headers|cookies)|\bgetParameter\(/;
|
|
56160
|
+
function analyzeSecurityState(input) {
|
|
56161
|
+
let state = "tainted";
|
|
56162
|
+
let context = null;
|
|
56163
|
+
for (const fn of input.sanitizers) {
|
|
56164
|
+
const s = SANITIZERS2[fn];
|
|
56165
|
+
if (!s)
|
|
56166
|
+
continue;
|
|
56167
|
+
if (s.level === "validated") {
|
|
56168
|
+
if (state === "tainted" || state === "sanitized") {
|
|
56169
|
+
state = "validated";
|
|
56170
|
+
context = s.context;
|
|
56171
|
+
}
|
|
56172
|
+
} else if (state === "tainted") {
|
|
56173
|
+
state = "sanitized";
|
|
56174
|
+
context = s.context;
|
|
56175
|
+
}
|
|
56176
|
+
}
|
|
56177
|
+
const required = SINK_REQUIREMENTS[input.sinkType] ?? [];
|
|
56178
|
+
if (required.length === 0) {
|
|
56179
|
+
return { verdict: "unknown", state, context, required, reason: `sink type '${input.sinkType}' not in the requirements table` };
|
|
56180
|
+
}
|
|
56181
|
+
if (state === "tainted") {
|
|
56182
|
+
return { verdict: "vulnerable", state, context, required, reason: `raw tainted input reaches a ${input.sinkType} sink with no sanitization` };
|
|
56183
|
+
}
|
|
56184
|
+
if (state === "validated") {
|
|
56185
|
+
return { verdict: "safe", state, context, required, reason: `input validated (${context}) — safe for any sink` };
|
|
56186
|
+
}
|
|
56187
|
+
if (state === "sanitized") {
|
|
56188
|
+
if (context && required.includes(context)) {
|
|
56189
|
+
return { verdict: "safe", state, context, required, reason: `sanitized for '${context}' — matches sink requirement ${required.join("/")}` };
|
|
56190
|
+
}
|
|
56191
|
+
return { verdict: "vulnerable", state, context, required, reason: `sanitized for '${context}' but this sink requires ${required.join("/")} — WRONG sanitizer (context mismatch)` };
|
|
56192
|
+
}
|
|
56193
|
+
return { verdict: "unknown", state, context, required, reason: "unhandled state" };
|
|
56194
|
+
}
|
|
56195
|
+
function sinkTypeOf(line) {
|
|
56196
|
+
if (/\$wpdb->(query|get_var|get_row|get_results|get_col)|mysqli_query\s*\(|mysql_query\s*\(|pg_query\s*\(|->query\s*\(/.test(line) && !/->prepare\s*\(/.test(line))
|
|
56197
|
+
return "sql_execution";
|
|
56198
|
+
if (/\b(eval|assert)\s*\(/.test(line))
|
|
56199
|
+
return "code_execution";
|
|
56200
|
+
if (/\b(system|exec|shell_exec|passthru|proc_open|popen)\s*\(/.test(line))
|
|
56201
|
+
return "command_execution";
|
|
56202
|
+
if (/\b(unserialize|maybe_unserialize)\s*\(/.test(line))
|
|
56203
|
+
return "deserialization";
|
|
56204
|
+
if (/\b(file_get_contents|file_put_contents|fopen|readfile|include|require|include_once|require_once)\s*\(/.test(line))
|
|
56205
|
+
return "file_operations";
|
|
56206
|
+
if (/header\s*\(\s*['"]Location/i.test(line))
|
|
56207
|
+
return "redirect";
|
|
56208
|
+
return null;
|
|
56209
|
+
}
|
|
56210
|
+
function detectWrongSanitizer(code, file) {
|
|
56211
|
+
const out = [];
|
|
56212
|
+
const ls = code.split(`
|
|
56213
|
+
`);
|
|
56214
|
+
const varSanitizer = new Map;
|
|
56215
|
+
for (const line of ls) {
|
|
56216
|
+
const m = line.match(/\$(\w+)\s*=\s*([a-zA-Z0-9_]+)\s*\(\s*([^)]*)\s*\)\s*;?/);
|
|
56217
|
+
if (!m)
|
|
56218
|
+
continue;
|
|
56219
|
+
const [, varName, fn, arg] = m;
|
|
56220
|
+
if (!SOURCE_TOKENS2.test(arg))
|
|
56221
|
+
continue;
|
|
56222
|
+
if (SANITIZERS2[fn])
|
|
56223
|
+
varSanitizer.set(varName, fn);
|
|
56224
|
+
else if (PSEUDO_SANITIZERS.has(fn))
|
|
56225
|
+
varSanitizer.set(varName, fn);
|
|
56226
|
+
}
|
|
56227
|
+
for (let i = 0;i < ls.length; i++) {
|
|
56228
|
+
const line = ls[i];
|
|
56229
|
+
const sinkType = sinkTypeOf(line);
|
|
56230
|
+
if (!sinkType)
|
|
56231
|
+
continue;
|
|
56232
|
+
const required = SINK_REQUIREMENTS[sinkType] ?? [];
|
|
56233
|
+
for (const [varName, fn] of varSanitizer) {
|
|
56234
|
+
if (!new RegExp(`\\$${varName}\\b`).test(line))
|
|
56235
|
+
continue;
|
|
56236
|
+
const sanitizer = SANITIZERS2[fn];
|
|
56237
|
+
const context = sanitizer?.context ?? null;
|
|
56238
|
+
if (sanitizer && (sanitizer.level === "validated" || context && required.includes(context)))
|
|
56239
|
+
continue;
|
|
56240
|
+
const detail = sanitizer ? `$${varName} was sanitized for '${context}' (${fn}()) but this ${sinkType} sink requires ${required.join("/")} — WRONG sanitizer (context mismatch).` : `$${varName} passed through ${fn}() — a NON-sanitizing transformation — and still reaches this ${sinkType} sink as raw tainted input. ${fn}() does not neutralize ${required.join("/")} context.`;
|
|
56241
|
+
out.push({
|
|
56242
|
+
file,
|
|
56243
|
+
line: i + 1,
|
|
56244
|
+
type: "wrong_sanitizer",
|
|
56245
|
+
category: "Wrong-context sanitization / pseudo-sanitizer",
|
|
56246
|
+
severity: "high",
|
|
56247
|
+
evidence: line.trim(),
|
|
56248
|
+
detail
|
|
56249
|
+
});
|
|
56250
|
+
}
|
|
56251
|
+
}
|
|
56252
|
+
const seen = new Set;
|
|
56253
|
+
return out.filter((f) => {
|
|
56254
|
+
const key = `${f.type}:${f.line}`;
|
|
56255
|
+
if (seen.has(key))
|
|
56256
|
+
return false;
|
|
56257
|
+
seen.add(key);
|
|
56258
|
+
return true;
|
|
56259
|
+
});
|
|
56260
|
+
}
|
|
56261
|
+
|
|
56081
56262
|
// src/complex-bugs.ts
|
|
56082
|
-
var
|
|
56263
|
+
var SOURCE_TOKENS3 = /\$_(GET|POST|REQUEST|COOKIE|FILES|SERVER)\b|\$request->|\$req->|request\.(get|input|query|post|data|body|headers|cookies|params)|getParameter\(|req\.(query|body|params|headers|cookies)/;
|
|
56083
56264
|
function lines2(code) {
|
|
56084
56265
|
return code.split(`
|
|
56085
56266
|
`);
|
|
@@ -56103,7 +56284,7 @@ function detectDeserialization(code, file, out) {
|
|
|
56103
56284
|
const l = ls[ln - 1] ?? "";
|
|
56104
56285
|
const scope = ls.slice(Math.max(0, ln - 4), ln).join(`
|
|
56105
56286
|
`);
|
|
56106
|
-
if (!
|
|
56287
|
+
if (!SOURCE_TOKENS3.test(l) && !SOURCE_TOKENS3.test(scope))
|
|
56107
56288
|
continue;
|
|
56108
56289
|
out.push({
|
|
56109
56290
|
file,
|
|
@@ -56125,7 +56306,7 @@ function detectTypeJuggling(code, file, out) {
|
|
|
56125
56306
|
const l = ls[ln - 1] ?? "";
|
|
56126
56307
|
const scope = ls.slice(Math.max(0, ln - 4), ln).join(`
|
|
56127
56308
|
`);
|
|
56128
|
-
const hasSource =
|
|
56309
|
+
const hasSource = SOURCE_TOKENS3.test(l) || SOURCE_TOKENS3.test(scope);
|
|
56129
56310
|
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);
|
|
56130
56311
|
if (!hasSource || !hasHash)
|
|
56131
56312
|
continue;
|
|
@@ -56165,7 +56346,7 @@ function detectMassAssignment(code, file, out) {
|
|
|
56165
56346
|
const l = ls[ln - 1] ?? "";
|
|
56166
56347
|
const scope = ls.slice(Math.max(0, ln - 3), ln).join(`
|
|
56167
56348
|
`);
|
|
56168
|
-
if (!
|
|
56349
|
+
if (!SOURCE_TOKENS3.test(l) && !SOURCE_TOKENS3.test(scope))
|
|
56169
56350
|
continue;
|
|
56170
56351
|
out.push({
|
|
56171
56352
|
file,
|
|
@@ -56189,6 +56370,7 @@ function detectComplexBugs(code, file) {
|
|
|
56189
56370
|
detectSsrF(code, file, out);
|
|
56190
56371
|
detectXxe(code, file, out);
|
|
56191
56372
|
detectSsti(code, file, out);
|
|
56373
|
+
out.push(...detectWrongSanitizer(code, file));
|
|
56192
56374
|
const seen = new Set;
|
|
56193
56375
|
return out.filter((f) => {
|
|
56194
56376
|
const key = `${f.type}:${f.line}`;
|
|
@@ -56207,7 +56389,7 @@ function detectPrototypePollution(code, file, out) {
|
|
|
56207
56389
|
const l = ls[ln - 1] ?? "";
|
|
56208
56390
|
const scope = ls.slice(Math.max(0, ln - 4), ln).join(`
|
|
56209
56391
|
`);
|
|
56210
|
-
if (!
|
|
56392
|
+
if (!SOURCE_TOKENS3.test(l) && !SOURCE_TOKENS3.test(scope))
|
|
56211
56393
|
continue;
|
|
56212
56394
|
out.push({
|
|
56213
56395
|
file,
|
|
@@ -56229,7 +56411,7 @@ function detectCrlfInjection(code, file, out) {
|
|
|
56229
56411
|
const l = ls[ln - 1] ?? "";
|
|
56230
56412
|
const scope = ls.slice(Math.max(0, ln - 3), ln).join(`
|
|
56231
56413
|
`);
|
|
56232
|
-
if (!
|
|
56414
|
+
if (!SOURCE_TOKENS3.test(l) && !SOURCE_TOKENS3.test(scope))
|
|
56233
56415
|
continue;
|
|
56234
56416
|
out.push({
|
|
56235
56417
|
file,
|
|
@@ -56251,7 +56433,7 @@ function detectPathConfusion(code, file, out) {
|
|
|
56251
56433
|
const l = ls[ln - 1] ?? "";
|
|
56252
56434
|
const scope = ls.slice(Math.max(0, ln - 3), ln).join(`
|
|
56253
56435
|
`);
|
|
56254
|
-
if (!
|
|
56436
|
+
if (!SOURCE_TOKENS3.test(l) && !SOURCE_TOKENS3.test(scope))
|
|
56255
56437
|
continue;
|
|
56256
56438
|
if (/\b(basename|realpath|canonicalPath|getCanonicalPath|normalize|resolve|toRealPath)\s*\(/i.test(scope + `
|
|
56257
56439
|
` + l))
|
|
@@ -56282,7 +56464,7 @@ function detectSsrF(code, file, out) {
|
|
|
56282
56464
|
const l = ls[ln - 1] ?? "";
|
|
56283
56465
|
const scope = ls.slice(Math.max(0, ln - 3), ln).join(`
|
|
56284
56466
|
`);
|
|
56285
|
-
if (!
|
|
56467
|
+
if (!SOURCE_TOKENS3.test(l) && !SOURCE_TOKENS3.test(scope))
|
|
56286
56468
|
continue;
|
|
56287
56469
|
if (/(allowlist|whitelist|in_array|preg_match|parse_url|startsWith\(["']https?|endsWith\(["']|basename|realpath|normalize)/i.test(scope + `
|
|
56288
56470
|
` + l))
|
|
@@ -56310,7 +56492,7 @@ function detectXxe(code, file, out) {
|
|
|
56310
56492
|
const l = ls[ln - 1] ?? "";
|
|
56311
56493
|
const scope = ls.slice(Math.max(0, ln - 4), ln).join(`
|
|
56312
56494
|
`);
|
|
56313
|
-
if (!
|
|
56495
|
+
if (!SOURCE_TOKENS3.test(l) && !SOURCE_TOKENS3.test(scope))
|
|
56314
56496
|
continue;
|
|
56315
56497
|
if (/libxml_disable_entity_loader\s*\(\s*true|LIBXML_NONET/.test(scope + `
|
|
56316
56498
|
` + l))
|
|
@@ -56335,7 +56517,7 @@ function detectSsti(code, file, out) {
|
|
|
56335
56517
|
const l = ls[ln - 1] ?? "";
|
|
56336
56518
|
const scope = ls.slice(Math.max(0, ln - 3), ln).join(`
|
|
56337
56519
|
`);
|
|
56338
|
-
if (!
|
|
56520
|
+
if (!SOURCE_TOKENS3.test(l) && !SOURCE_TOKENS3.test(scope))
|
|
56339
56521
|
continue;
|
|
56340
56522
|
out.push({
|
|
56341
56523
|
file,
|
|
@@ -57628,6 +57810,22 @@ ${statusLine}` : statusLine },
|
|
|
57628
57810
|
}
|
|
57629
57811
|
return { content: [{ type: "text", text: JSON.stringify(m) }] };
|
|
57630
57812
|
});
|
|
57813
|
+
server.registerTool("security_state", {
|
|
57814
|
+
title: "Analyze a sanitizer flow (security-state lattice)",
|
|
57815
|
+
description: "SECURITY-STATE: given a value's sanitizer/validator functions + the sink type it reaches, return the deterministic verdict — vulnerable (wrong-context sanitization / pseudo-sanitizer / raw taint) or safe (correct context / validated). This is the step beyond binary taint: it catches 'sanitized for X but used in a Y sink'. The lattice is data-driven, no guesses.",
|
|
57816
|
+
inputSchema: {
|
|
57817
|
+
sanitizers: string2().optional().describe('JSON array of sanitizer/validator function names applied to the value (e.g. ["sanitize_text_field"])'),
|
|
57818
|
+
sink_type: string2().describe("Sink type: sql_execution, code_execution, command_execution, deserialization, file_operations, redirect, html_render")
|
|
57819
|
+
}
|
|
57820
|
+
}, async ({ sanitizers, sink_type }) => {
|
|
57821
|
+
let arr = [];
|
|
57822
|
+
try {
|
|
57823
|
+
arr = sanitizers ? JSON.parse(sanitizers) : [];
|
|
57824
|
+
} catch {
|
|
57825
|
+
return { content: [{ type: "text", text: JSON.stringify({ error: "invalid sanitizers JSON" }) }] };
|
|
57826
|
+
}
|
|
57827
|
+
return { content: [{ type: "text", text: JSON.stringify(analyzeSecurityState({ sanitizers: arr, sinkType: sink_type })) }] };
|
|
57828
|
+
});
|
|
57631
57829
|
server.registerTool("payload_lookup", {
|
|
57632
57830
|
title: "Look up exploit payloads",
|
|
57633
57831
|
description: "INTEL: find a payload collection (66 categories from PayloadsAllTheThings).",
|
|
@@ -58176,8 +58374,8 @@ ${statusLine}` : statusLine },
|
|
|
58176
58374
|
};
|
|
58177
58375
|
});
|
|
58178
58376
|
server.registerTool("complex_scan", {
|
|
58179
|
-
title: "Complex-bug scan (deserialization / type juggling / mass assignment / prototype pollution / CRLF / path confusion / SSRF / XXE / SSTI)",
|
|
58180
|
-
description: "Complex-bug detection beyond taint: deserialization→POP gadget chain (unserialize + magic method), type juggling (loose ==/!= vs hash/secret), mass assignment (extract/parse_str without a safe flag), prototype pollution (unsafe merge of request data), CRLF/header injection (user input into a header), path confusion (user input into a file path without canonicalization), SSRF (user-controlled URL with no host allowlist), XXE (user XML parsed without disabling entities),
|
|
58377
|
+
title: "Complex-bug scan (deserialization / type juggling / mass assignment / prototype pollution / CRLF / path confusion / SSRF / XXE / SSTI / wrong-context sanitization)",
|
|
58378
|
+
description: "Complex-bug detection beyond taint: deserialization→POP gadget chain (unserialize + magic method), type juggling (loose ==/!= vs hash/secret), mass assignment (extract/parse_str without a safe flag), prototype pollution (unsafe merge of request data), CRLF/header injection (user input into a header), path confusion (user input into a file path without canonicalization), SSRF (user-controlled URL with no host allowlist), XXE (user XML parsed without disabling entities), SSTI (user input into a template render with no sandbox), and wrong-context sanitization (a value sanitized for context X reaching a sink that needs Y — e.g. sanitize_text_field() into a SQL query).",
|
|
58181
58379
|
inputSchema: { path: string2().describe("Source file path") }
|
|
58182
58380
|
}, async ({ path }) => {
|
|
58183
58381
|
const code = readFileSync21(path, "utf8");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.33",
|
|
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": {
|