blitzstrike 2.4.62 → 2.4.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 +18 -0
- package/dist/index.js +250 -127
- package/intelligence/variants.json +352 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,24 @@ 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.63] — 2026-09-14
|
|
9
|
+
|
|
10
|
+
### Added — Variant corpus + detector coverage sweep (data-driven scaling)
|
|
11
|
+
|
|
12
|
+
- New `intelligence/variants.json` — a corpus of 65 concrete real-world code
|
|
13
|
+
shapes (3-4 per class, multi-language: PHP/JS/Python/Java/Node) that each MUST
|
|
14
|
+
fire its class's detector. This is how the empirical grounding scales: more
|
|
15
|
+
shapes → more coverage, tested reproducibly.
|
|
16
|
+
- New `scan_variants` MCP tool + `blitzstrike verify` now also sweeps the variant
|
|
17
|
+
corpus and reports per-class coverage, surfacing false negatives (a detector
|
|
18
|
+
missing a real shape) as a hard failure.
|
|
19
|
+
- Detector coverage raised from 54% to 100% across the corpus, fixing real gaps:
|
|
20
|
+
multi-language sinks (pickle/yaml/readObject, jinja2/twig, axios/requests,
|
|
21
|
+
DocumentBuilderFactory), `$.extend`/`$twig->render` word-boundary bugs, JS `+`
|
|
22
|
+
concatenation in SQLi, `setcookie` CRLF, `mysqli_query`/`whereRaw` SQLi sinks,
|
|
23
|
+
dynamic field binding + `Model.create` mass-assignment, and a double-paren bug
|
|
24
|
+
in the upload sink regex. The corpus is the proof: every shape now fires.
|
|
25
|
+
|
|
8
26
|
## [2.4.62] — 2026-09-14
|
|
9
27
|
|
|
10
28
|
### Added — Deep empirical corpus (per-class bypass + crown jewels)
|
package/dist/index.js
CHANGED
|
@@ -16054,13 +16054,32 @@ function stripCommentsOnly(code) {
|
|
|
16054
16054
|
return out.join("");
|
|
16055
16055
|
}
|
|
16056
16056
|
function detectDeserialization(code, file, out) {
|
|
16057
|
-
if (!MAGIC_METHODS.test(code))
|
|
16058
|
-
return;
|
|
16059
16057
|
const ls = lines2(code);
|
|
16060
|
-
|
|
16061
|
-
|
|
16062
|
-
|
|
16063
|
-
|
|
16058
|
+
if (MAGIC_METHODS.test(code)) {
|
|
16059
|
+
const re = /\b(unserialize|maybe_unserialize)\s*\(/g;
|
|
16060
|
+
let m;
|
|
16061
|
+
while ((m = re.exec(code)) !== null) {
|
|
16062
|
+
const ln = lineNo2(code, m.index);
|
|
16063
|
+
const l = ls[ln - 1] ?? "";
|
|
16064
|
+
const scope = ls.slice(Math.max(0, ln - 4), ln).join(`
|
|
16065
|
+
`);
|
|
16066
|
+
if (!SOURCE_TOKENS3.test(l) && !SOURCE_TOKENS3.test(scope))
|
|
16067
|
+
continue;
|
|
16068
|
+
out.push({
|
|
16069
|
+
file,
|
|
16070
|
+
line: ln,
|
|
16071
|
+
type: "deserialization",
|
|
16072
|
+
category: "Deserialization → POP gadget chain",
|
|
16073
|
+
severity: "high",
|
|
16074
|
+
evidence: l.trim(),
|
|
16075
|
+
detail: `\`${m[1]}()\` of attacker-controlled data AND a magic method is present — object injection can pivot to RCE via the gadget chain.`
|
|
16076
|
+
});
|
|
16077
|
+
}
|
|
16078
|
+
}
|
|
16079
|
+
const unsafeRe = /\b(pickle\.loads?|yaml\.load|yaml\.unsafe_load|ObjectInputStream|\.readObject)\s*\(/g;
|
|
16080
|
+
let u;
|
|
16081
|
+
while ((u = unsafeRe.exec(code)) !== null) {
|
|
16082
|
+
const ln = lineNo2(code, u.index);
|
|
16064
16083
|
const l = ls[ln - 1] ?? "";
|
|
16065
16084
|
const scope = ls.slice(Math.max(0, ln - 4), ln).join(`
|
|
16066
16085
|
`);
|
|
@@ -16070,10 +16089,10 @@ function detectDeserialization(code, file, out) {
|
|
|
16070
16089
|
file,
|
|
16071
16090
|
line: ln,
|
|
16072
16091
|
type: "deserialization",
|
|
16073
|
-
category: "
|
|
16092
|
+
category: "Unsafe deserialization",
|
|
16074
16093
|
severity: "high",
|
|
16075
16094
|
evidence: l.trim(),
|
|
16076
|
-
detail: `\`${
|
|
16095
|
+
detail: `\`${u[1]}\` of attacker-controlled data — unsafe deserialization can execute arbitrary code directly (pickle/yaml/Java object streams).`
|
|
16077
16096
|
});
|
|
16078
16097
|
}
|
|
16079
16098
|
}
|
|
@@ -16087,7 +16106,7 @@ function detectTypeJuggling(code, file, out) {
|
|
|
16087
16106
|
const scope = ls.slice(Math.max(0, ln - 4), ln).join(`
|
|
16088
16107
|
`);
|
|
16089
16108
|
const hasSource = SOURCE_TOKENS3.test(l) || SOURCE_TOKENS3.test(scope);
|
|
16090
|
-
const hasHash = /\b(md5|sha1|sha256|hash|crc32|crypt|password_verify|strcmp|hash_equals|secret|token|nonce)\s*\(?/i.test(l) ||
|
|
16109
|
+
const hasHash = /\b(md5|sha1|sha256|hash|crc32|crypt|password_verify|strcmp|hash_equals|secret|token|nonce)\s*\(?/i.test(l) || /(secret|token|nonce|hash)\b/i.test(l);
|
|
16091
16110
|
if (!hasSource || !hasHash)
|
|
16092
16111
|
continue;
|
|
16093
16112
|
out.push({
|
|
@@ -16138,6 +16157,34 @@ function detectMassAssignment(code, file, out) {
|
|
|
16138
16157
|
detail: "`parse_str()` of user input into the symbol table — variable injection."
|
|
16139
16158
|
});
|
|
16140
16159
|
}
|
|
16160
|
+
const bindRe = /foreach\s*\(\s*\$_(POST|GET|REQUEST|COOKIE)\b[^)]*\)\s*\{[^}]*\$this->\$[A-Za-z_]\w*\s*=/g;
|
|
16161
|
+
while ((m = bindRe.exec(code)) !== null) {
|
|
16162
|
+
const ln = lineNo2(code, m.index);
|
|
16163
|
+
const l = ls[ln - 1] ?? "";
|
|
16164
|
+
out.push({
|
|
16165
|
+
file,
|
|
16166
|
+
line: ln,
|
|
16167
|
+
type: "mass_assignment",
|
|
16168
|
+
category: "Mass assignment (dynamic field binding)",
|
|
16169
|
+
severity: "high",
|
|
16170
|
+
evidence: l.trim(),
|
|
16171
|
+
detail: "Request keys are bound to object properties without an allowlist — attacker can set privileged fields (role, is_admin)."
|
|
16172
|
+
});
|
|
16173
|
+
}
|
|
16174
|
+
const createRe = /\.create\s*\(\s*(req\.body|req\.query|request\.body|request\.data|\$_(POST|REQUEST))\b/g;
|
|
16175
|
+
while ((m = createRe.exec(code)) !== null) {
|
|
16176
|
+
const ln = lineNo2(code, m.index);
|
|
16177
|
+
const l = ls[ln - 1] ?? "";
|
|
16178
|
+
out.push({
|
|
16179
|
+
file,
|
|
16180
|
+
line: ln,
|
|
16181
|
+
type: "mass_assignment",
|
|
16182
|
+
category: "Mass assignment (unrestricted model create)",
|
|
16183
|
+
severity: "high",
|
|
16184
|
+
evidence: l.trim(),
|
|
16185
|
+
detail: "A model is created directly from request data without an allowlist — attacker can set privileged fields."
|
|
16186
|
+
});
|
|
16187
|
+
}
|
|
16141
16188
|
}
|
|
16142
16189
|
function detectComplexBugs(code, file) {
|
|
16143
16190
|
const out = [];
|
|
@@ -16171,7 +16218,7 @@ function detectComplexBugs(code, file) {
|
|
|
16171
16218
|
}
|
|
16172
16219
|
function detectPrototypePollution(code, file, out) {
|
|
16173
16220
|
const ls = lines2(code);
|
|
16174
|
-
const mergeRe =
|
|
16221
|
+
const mergeRe = /(Object\.assign|_\.merge|_\.defaultsDeep|\.extend|\.merge|\.defaultsDeep|mergeDeep|deepMerge|_\.set)\s*\(/g;
|
|
16175
16222
|
let m;
|
|
16176
16223
|
while ((m = mergeRe.exec(code)) !== null) {
|
|
16177
16224
|
const ln = lineNo2(code, m.index);
|
|
@@ -16193,7 +16240,7 @@ function detectPrototypePollution(code, file, out) {
|
|
|
16193
16240
|
}
|
|
16194
16241
|
function detectCrlfInjection(code, file, out) {
|
|
16195
16242
|
const ls = lines2(code);
|
|
16196
|
-
const headerRe = /\b(header|setHeader|addHeader|writeHead|appendHeader)\s*\(/g;
|
|
16243
|
+
const headerRe = /\b(header|setHeader|addHeader|writeHead|appendHeader|setcookie|setCookie|set_cookie)\s*\(/g;
|
|
16197
16244
|
let m;
|
|
16198
16245
|
while ((m = headerRe.exec(code)) !== null) {
|
|
16199
16246
|
const ln = lineNo2(code, m.index);
|
|
@@ -16246,7 +16293,7 @@ function detectPathConfusion(code, file, out) {
|
|
|
16246
16293
|
}
|
|
16247
16294
|
function detectSsrF(code, file, out) {
|
|
16248
16295
|
const ls = lines2(code);
|
|
16249
|
-
const re = /\b(file_get_contents|fopen|file|readfile|curl_setopt|curl_init|fetch|axios|requests\.(get|post|put)|urllib\.request|http_get|wp_remote_(get|post)|new\s+URL\s*\(|got\(|http\.(get|request))\s*\(/g;
|
|
16296
|
+
const re = /\b(file_get_contents|fopen|file|readfile|curl_setopt|curl_init|fetch|axios\.(get|post|put|request|delete|head)|requests\.(get|post|put)|urllib\.request|http_get|wp_remote_(get|post)|new\s+URL\s*\(|got\(|http\.(get|request))\s*\(/g;
|
|
16250
16297
|
let m;
|
|
16251
16298
|
while ((m = re.exec(code)) !== null) {
|
|
16252
16299
|
const ln = lineNo2(code, m.index);
|
|
@@ -16274,7 +16321,7 @@ function detectSsrF(code, file, out) {
|
|
|
16274
16321
|
}
|
|
16275
16322
|
function detectXxe(code, file, out) {
|
|
16276
16323
|
const ls = lines2(code);
|
|
16277
|
-
const re = /\b(simplexml_load_string|simplexml_load_file|new\s+SimpleXMLElement|->loadXML\s*\(|->load\s*\(|XMLReader\s*->\s*XML\s*\(|libxml_parse)\s*\(?/g;
|
|
16324
|
+
const re = /\b(simplexml_load_string|simplexml_load_file|new\s+SimpleXMLElement|->loadXML\s*\(|->load\s*\(|XMLReader\s*->\s*XML\s*\(|libxml_parse|DocumentBuilderFactory|SAXParserFactory|SAXReader)\s*\(?/g;
|
|
16278
16325
|
let m;
|
|
16279
16326
|
while ((m = re.exec(code)) !== null) {
|
|
16280
16327
|
const ln = lineNo2(code, m.index);
|
|
@@ -16299,7 +16346,7 @@ function detectXxe(code, file, out) {
|
|
|
16299
16346
|
}
|
|
16300
16347
|
function detectSsti(code, file, out) {
|
|
16301
16348
|
const ls = lines2(code);
|
|
16302
|
-
const re =
|
|
16349
|
+
const re = /(render_template_string|Template\s*\(|res\.render\s*\(|ejs\.(render|compile)\s*\(|_\.template\s*\(|render_string|twig->render|createTemplate\s*\(|Jinja2\(|jinja2\.Template|jinja2\.from_string|jinja2\.Environment)\s*\(?/g;
|
|
16303
16350
|
let m;
|
|
16304
16351
|
while ((m = re.exec(code)) !== null) {
|
|
16305
16352
|
const ln = lineNo2(code, m.index);
|
|
@@ -16322,14 +16369,14 @@ function detectSsti(code, file, out) {
|
|
|
16322
16369
|
function detectFileUploadRce(code, file, out) {
|
|
16323
16370
|
code = stripCommentsOnly(code);
|
|
16324
16371
|
const ls = lines2(code);
|
|
16325
|
-
const re = /\b(move_uploaded_file|wp_upload_bits|->move
|
|
16372
|
+
const re = /\b(move_uploaded_file|wp_upload_bits|->move|storeAs|->storeAs|putFile|\.save|uploadFile)\s*\(/g;
|
|
16326
16373
|
let m;
|
|
16327
16374
|
while ((m = re.exec(code)) !== null) {
|
|
16328
16375
|
const ln = lineNo2(code, m.index);
|
|
16329
16376
|
const l = ls[ln - 1] ?? "";
|
|
16330
16377
|
const scope = ls.slice(Math.max(0, ln - 30), ln + 3).join(`
|
|
16331
16378
|
`);
|
|
16332
|
-
if (!/\$_FILES\b|tmp_name|getUploadedFile|uploadedFile|\['name'\]/i.test(scope))
|
|
16379
|
+
if (!/\$_FILES\b|tmp_name|getUploadedFile|uploadedFile|req\.files|\['name'\]|\.name/i.test(scope))
|
|
16333
16380
|
continue;
|
|
16334
16381
|
if (/(wp_check_filetype|PATHINFO_EXTENSION|wp_get_mime_types|allowed_?(types|extensions|exts)|mime_?(types|extensions)|in_array\s*\([^)]*\$(?:allowed|whitelist|ext)|wp_handle_upload\s*\(\s*[^)]*test_form\s*=>\s*true)/i.test(scope))
|
|
16335
16382
|
continue;
|
|
@@ -16367,7 +16414,7 @@ function detectUnrestrictedUpload(code, file, out) {
|
|
|
16367
16414
|
}
|
|
16368
16415
|
function detectSqlInjection(code, file, out) {
|
|
16369
16416
|
const ls = lines2(code);
|
|
16370
|
-
const sinkRe = /\$wpdb->(get_results|get_col|get_var|get_row|query)\s*\(|->query\s*\(|DB::unprepared\s*\(
|
|
16417
|
+
const sinkRe = /\$wpdb->(get_results|get_col|get_var|get_row|query)\s*\(|->query\s*\(|->execute\s*\(|mysqli_query\s*\(|pg_query\s*\(|DB::unprepared\s*\(|[->.]whereRaw\s*\(|[->.]selectRaw\s*\(/g;
|
|
16371
16418
|
let m;
|
|
16372
16419
|
while ((m = sinkRe.exec(code)) !== null) {
|
|
16373
16420
|
const ln = lineNo2(code, m.index);
|
|
@@ -16378,7 +16425,7 @@ function detectSqlInjection(code, file, out) {
|
|
|
16378
16425
|
continue;
|
|
16379
16426
|
if (SOURCE_TOKENS3.test(scope))
|
|
16380
16427
|
continue;
|
|
16381
|
-
const inlineConcat =
|
|
16428
|
+
const inlineConcat = /(\.\s*\$[A-Za-z_]\w*|\+\s*[A-Za-z_$]\w*)/.test(l);
|
|
16382
16429
|
const assignConcat = /(?:^|\n)\s*\$[A-Za-z_]\w*\s*(?:\.=|=)\s*[^;\n]*["'][^"']*["']\s*\.\s*\$[A-Za-z_]\w*/.test(scope);
|
|
16383
16430
|
if (!inlineConcat && !assignConcat)
|
|
16384
16431
|
continue;
|
|
@@ -16547,7 +16594,7 @@ function detectOauthMisconfig(code, file, out) {
|
|
|
16547
16594
|
function detectGrpcReflection(code, file, out) {
|
|
16548
16595
|
const ls = lines2(code);
|
|
16549
16596
|
const clean = stripCommentsOnly(code);
|
|
16550
|
-
const re =
|
|
16597
|
+
const re = /grpc_reflection|enable_server_reflection|RegisterReflectionService|reflection\.enable|ServerReflection|ReflectionServicer|reflection\.Register/gi;
|
|
16551
16598
|
let m;
|
|
16552
16599
|
while ((m = re.exec(clean)) !== null) {
|
|
16553
16600
|
const ln = lineNo2(code, m.index);
|
|
@@ -16567,7 +16614,7 @@ var init_complex_bugs = __esm(() => {
|
|
|
16567
16614
|
init_security_state();
|
|
16568
16615
|
init_framework_security();
|
|
16569
16616
|
init_secrets();
|
|
16570
|
-
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)/;
|
|
16617
|
+
SOURCE_TOKENS3 = /\$_(GET|POST|REQUEST|COOKIE|FILES|SERVER)\b|\$request->|\$req->|request\.(get|input|query|post|data|body|headers|cookies|params|args|form|values|json)|getParameter\(|req\.(query|body|params|headers|cookies|args|form)|php:\/\/input/;
|
|
16571
16618
|
MAGIC_METHODS = /\b(__destruct|__wakeup|__toString|__invoke|__call|__get|__set|__isset|__unset|__sleep)\s*\(/;
|
|
16572
16619
|
SECRET_FIELD_RE = new RegExp(`\\b(${SECRET_FIELDS})\\s*(?:::|=>|=|:)\\s*`, "gi");
|
|
16573
16620
|
SECRET_PLACEHOLDER_RE = /^(null|true|false|undefined|your_|xxx+|changeme|example|placeholder|test|secret|password|token|key|api|smtp|localhost|root|admin|pass|change|me|none|na|foo|bar|dummy)$/i;
|
|
@@ -16747,11 +16794,75 @@ $r = openai.chat.completions.create(['messages' => [['role' => 'user', 'content'
|
|
|
16747
16794
|
];
|
|
16748
16795
|
});
|
|
16749
16796
|
|
|
16797
|
+
// src/variants.ts
|
|
16798
|
+
import { readFileSync as readFileSync19 } from "node:fs";
|
|
16799
|
+
import { join as join18, dirname as dirname4 } from "node:path";
|
|
16800
|
+
import { fileURLToPath as fileURLToPath10 } from "node:url";
|
|
16801
|
+
function loadVariants() {
|
|
16802
|
+
try {
|
|
16803
|
+
const doc = JSON.parse(readFileSync19(join18(__dirname4, "..", "intelligence", "variants.json"), "utf8"));
|
|
16804
|
+
return doc.variants ?? [];
|
|
16805
|
+
} catch {
|
|
16806
|
+
return [];
|
|
16807
|
+
}
|
|
16808
|
+
}
|
|
16809
|
+
function scanVariants() {
|
|
16810
|
+
const variants = loadVariants().filter((v) => !CROSS_FILE_ONLY.has(v.class));
|
|
16811
|
+
const results = variants.map((v, i) => {
|
|
16812
|
+
const findings = detectComplexBugs(v.code, `variant-${i}.php`).filter((f) => f.type === v.class);
|
|
16813
|
+
return { id: `variant-${i}`, class: v.class, fired: findings.length > 0, note: v.note };
|
|
16814
|
+
});
|
|
16815
|
+
const per_class = {};
|
|
16816
|
+
for (const r of results) {
|
|
16817
|
+
const c = per_class[r.class] ??= { total: 0, fired: 0, missed: 0, coverage: 0 };
|
|
16818
|
+
c.total++;
|
|
16819
|
+
if (r.fired)
|
|
16820
|
+
c.fired++;
|
|
16821
|
+
else
|
|
16822
|
+
c.missed++;
|
|
16823
|
+
c.coverage = c.fired / c.total;
|
|
16824
|
+
}
|
|
16825
|
+
const fired = results.filter((r) => r.fired).length;
|
|
16826
|
+
return {
|
|
16827
|
+
total: results.length,
|
|
16828
|
+
fired,
|
|
16829
|
+
missed: results.length - fired,
|
|
16830
|
+
coverage: results.length > 0 ? fired / results.length : 0,
|
|
16831
|
+
per_class,
|
|
16832
|
+
results
|
|
16833
|
+
};
|
|
16834
|
+
}
|
|
16835
|
+
function variantCoverageText() {
|
|
16836
|
+
const r = scanVariants();
|
|
16837
|
+
const lines = [];
|
|
16838
|
+
lines.push(`Blitz Strike variant corpus`);
|
|
16839
|
+
lines.push(` ${r.fired}/${r.total} shapes detected (${(r.coverage * 100).toFixed(0)}%) · ${r.missed} missed`);
|
|
16840
|
+
lines.push(``);
|
|
16841
|
+
for (const [cls, c] of Object.entries(r.per_class)) {
|
|
16842
|
+
const mark = c.missed === 0 ? "OK " : "GAP";
|
|
16843
|
+
lines.push(` [${mark}] ${cls.padEnd(20)} ${c.fired}/${c.total}`);
|
|
16844
|
+
}
|
|
16845
|
+
if (r.missed > 0) {
|
|
16846
|
+
lines.push(``);
|
|
16847
|
+
for (const res of r.results.filter((x) => !x.fired)) {
|
|
16848
|
+
lines.push(` missed: ${res.class} — ${res.note}`);
|
|
16849
|
+
}
|
|
16850
|
+
}
|
|
16851
|
+
return lines.join(`
|
|
16852
|
+
`);
|
|
16853
|
+
}
|
|
16854
|
+
var __dirname4, CROSS_FILE_ONLY;
|
|
16855
|
+
var init_variants = __esm(() => {
|
|
16856
|
+
init_complex_bugs();
|
|
16857
|
+
__dirname4 = dirname4(fileURLToPath10(import.meta.url));
|
|
16858
|
+
CROSS_FILE_ONLY = new Set(["priv_esc"]);
|
|
16859
|
+
});
|
|
16860
|
+
|
|
16750
16861
|
// src/sync.ts
|
|
16751
|
-
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, readFileSync as
|
|
16752
|
-
import { join as
|
|
16862
|
+
import { mkdirSync as mkdirSync9, writeFileSync as writeFileSync8, readFileSync as readFileSync25, readdirSync as readdirSync7, statSync as statSync4, rmSync as rmSync4 } from "node:fs";
|
|
16863
|
+
import { join as join22 } from "node:path";
|
|
16753
16864
|
import { homedir as homedir10 } from "node:os";
|
|
16754
|
-
import { fileURLToPath as
|
|
16865
|
+
import { fileURLToPath as fileURLToPath12 } from "node:url";
|
|
16755
16866
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
16756
16867
|
function dirExists(p) {
|
|
16757
16868
|
try {
|
|
@@ -16766,7 +16877,7 @@ function countFiles(dir) {
|
|
|
16766
16877
|
const walk = (d) => {
|
|
16767
16878
|
for (const e of readdirSync7(d, { withFileTypes: true })) {
|
|
16768
16879
|
if (e.isDirectory())
|
|
16769
|
-
walk(
|
|
16880
|
+
walk(join22(d, e.name));
|
|
16770
16881
|
else
|
|
16771
16882
|
n++;
|
|
16772
16883
|
}
|
|
@@ -16780,8 +16891,8 @@ function countFiles(dir) {
|
|
|
16780
16891
|
function syncData() {
|
|
16781
16892
|
if (!dirExists(DATA_ROOT2))
|
|
16782
16893
|
mkdirSync9(DATA_ROOT2, { recursive: true });
|
|
16783
|
-
const payloadsPath =
|
|
16784
|
-
const templatesPath =
|
|
16894
|
+
const payloadsPath = join22(DATA_ROOT2, "payloads");
|
|
16895
|
+
const templatesPath = join22(DATA_ROOT2, "templates");
|
|
16785
16896
|
const hasPayloads = dirExists(payloadsPath) && countFiles(payloadsPath) > 0;
|
|
16786
16897
|
const hasTemplates = dirExists(templatesPath) && countFiles(templatesPath) > 0;
|
|
16787
16898
|
if (hasPayloads && hasTemplates) {
|
|
@@ -16791,8 +16902,8 @@ function syncData() {
|
|
|
16791
16902
|
data_root: DATA_ROOT2
|
|
16792
16903
|
};
|
|
16793
16904
|
}
|
|
16794
|
-
const localPayloads =
|
|
16795
|
-
const localTemplates =
|
|
16905
|
+
const localPayloads = join22(ROOT7, "payloads");
|
|
16906
|
+
const localTemplates = join22(ROOT7, "templates");
|
|
16796
16907
|
let payloadsSynced = false;
|
|
16797
16908
|
let templatesSynced = false;
|
|
16798
16909
|
if (!hasPayloads && dirExists(localPayloads)) {
|
|
@@ -16805,7 +16916,7 @@ function syncData() {
|
|
|
16805
16916
|
}
|
|
16806
16917
|
const gitOk = spawnSync4("git", ["--version"]).status === 0;
|
|
16807
16918
|
if (gitOk && (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0 || !dirExists(templatesPath) || countFiles(templatesPath) === 0)) {
|
|
16808
|
-
const tmp =
|
|
16919
|
+
const tmp = join22(DATA_ROOT2, ".tmp-clone");
|
|
16809
16920
|
if (dirExists(tmp))
|
|
16810
16921
|
rmSync4(tmp, { recursive: true, force: true });
|
|
16811
16922
|
const r = spawnSync4("git", ["clone", "--depth", "1", "--filter=blob:none", "--sparse", GITHUB_REPO, tmp], { timeout: 120000 });
|
|
@@ -16813,11 +16924,11 @@ function syncData() {
|
|
|
16813
16924
|
const sparse = spawnSync4("git", ["-C", tmp, "sparse-checkout", "set", "payloads", "templates"], { timeout: 120000 });
|
|
16814
16925
|
if (sparse.status === 0) {
|
|
16815
16926
|
if (!dirExists(payloadsPath) || countFiles(payloadsPath) === 0) {
|
|
16816
|
-
copyDir(
|
|
16927
|
+
copyDir(join22(tmp, "payloads"), payloadsPath);
|
|
16817
16928
|
payloadsSynced = true;
|
|
16818
16929
|
}
|
|
16819
16930
|
if (!dirExists(templatesPath) || countFiles(templatesPath) === 0) {
|
|
16820
|
-
copyDir(
|
|
16931
|
+
copyDir(join22(tmp, "templates"), templatesPath);
|
|
16821
16932
|
templatesSynced = true;
|
|
16822
16933
|
}
|
|
16823
16934
|
}
|
|
@@ -16839,18 +16950,18 @@ function copyDir(src, dst) {
|
|
|
16839
16950
|
function manualCopy(src, dst) {
|
|
16840
16951
|
mkdirSync9(dst, { recursive: true });
|
|
16841
16952
|
for (const e of readdirSync7(src, { withFileTypes: true })) {
|
|
16842
|
-
const s =
|
|
16843
|
-
const d =
|
|
16953
|
+
const s = join22(src, e.name);
|
|
16954
|
+
const d = join22(dst, e.name);
|
|
16844
16955
|
if (e.isDirectory())
|
|
16845
16956
|
manualCopy(s, d);
|
|
16846
16957
|
else
|
|
16847
|
-
writeFileSync8(d,
|
|
16958
|
+
writeFileSync8(d, readFileSync25(s));
|
|
16848
16959
|
}
|
|
16849
16960
|
}
|
|
16850
16961
|
var ROOT7, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
|
|
16851
16962
|
var init_sync = __esm(() => {
|
|
16852
|
-
ROOT7 =
|
|
16853
|
-
DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ??
|
|
16963
|
+
ROOT7 = join22(fileURLToPath12(new URL(".", import.meta.url)), "..");
|
|
16964
|
+
DATA_ROOT2 = process.env.BLITZSTRIKE_DATA ?? join22(homedir10(), ".blitzstrike", "data");
|
|
16854
16965
|
});
|
|
16855
16966
|
|
|
16856
16967
|
// src/update.ts
|
|
@@ -16895,15 +17006,15 @@ var init_update = __esm(() => {
|
|
|
16895
17006
|
});
|
|
16896
17007
|
|
|
16897
17008
|
// src/release.ts
|
|
16898
|
-
import { readFileSync as
|
|
16899
|
-
import { join as
|
|
16900
|
-
import { fileURLToPath as
|
|
17009
|
+
import { readFileSync as readFileSync27, existsSync as existsSync12 } from "node:fs";
|
|
17010
|
+
import { join as join23 } from "node:path";
|
|
17011
|
+
import { fileURLToPath as fileURLToPath13 } from "node:url";
|
|
16901
17012
|
import { execSync as execSync2 } from "node:child_process";
|
|
16902
17013
|
function validateRelease() {
|
|
16903
17014
|
const checks = [];
|
|
16904
17015
|
checks.push({ name: "package.json version", ok: true, detail: VERSION });
|
|
16905
17016
|
try {
|
|
16906
|
-
const changelog =
|
|
17017
|
+
const changelog = readFileSync27(join23(ROOT8, "CHANGELOG.md"), "utf8");
|
|
16907
17018
|
const m = changelog.match(/^##\s+\[([0-9]+\.[0-9]+\.[0-9]+)\]/m);
|
|
16908
17019
|
const changelogVersion = m?.[1] ?? null;
|
|
16909
17020
|
checks.push({
|
|
@@ -16935,22 +17046,22 @@ function validateRelease() {
|
|
|
16935
17046
|
return { version: VERSION, checks, consistent: checks.filter((c) => !c.soft).every((c) => c.ok) };
|
|
16936
17047
|
}
|
|
16937
17048
|
function lockfilePresent() {
|
|
16938
|
-
return existsSync12(
|
|
17049
|
+
return existsSync12(join23(ROOT8, "package-lock.json")) || existsSync12(join23(ROOT8, "bun.lock")) || existsSync12(join23(ROOT8, "bun.lockb"));
|
|
16939
17050
|
}
|
|
16940
17051
|
var ROOT8;
|
|
16941
17052
|
var init_release = __esm(() => {
|
|
16942
17053
|
init_version();
|
|
16943
|
-
ROOT8 =
|
|
17054
|
+
ROOT8 = join23(fileURLToPath13(new URL(".", import.meta.url)), "..");
|
|
16944
17055
|
});
|
|
16945
17056
|
|
|
16946
17057
|
// src/dependency.ts
|
|
16947
|
-
import { readFileSync as
|
|
16948
|
-
import { join as
|
|
16949
|
-
import { fileURLToPath as
|
|
17058
|
+
import { readFileSync as readFileSync28 } from "node:fs";
|
|
17059
|
+
import { join as join24 } from "node:path";
|
|
17060
|
+
import { fileURLToPath as fileURLToPath14 } from "node:url";
|
|
16950
17061
|
function checkDependencies() {
|
|
16951
17062
|
let pkg = {};
|
|
16952
17063
|
try {
|
|
16953
|
-
pkg = JSON.parse(
|
|
17064
|
+
pkg = JSON.parse(readFileSync28(join24(ROOT9, "package.json"), "utf8"));
|
|
16954
17065
|
} catch {
|
|
16955
17066
|
return { install_scripts: [], git_dependencies: [], total_dependencies: 0, safe: false };
|
|
16956
17067
|
}
|
|
@@ -16973,14 +17084,14 @@ function checkDependencies() {
|
|
|
16973
17084
|
}
|
|
16974
17085
|
var ROOT9;
|
|
16975
17086
|
var init_dependency = __esm(() => {
|
|
16976
|
-
ROOT9 =
|
|
17087
|
+
ROOT9 = join24(fileURLToPath14(new URL(".", import.meta.url)), "..");
|
|
16977
17088
|
});
|
|
16978
17089
|
|
|
16979
17090
|
// src/cli.ts
|
|
16980
|
-
import { readFileSync as
|
|
16981
|
-
import { join as
|
|
17091
|
+
import { readFileSync as readFileSync29, existsSync as existsSync13, writeFileSync as writeFileSync9, mkdirSync as mkdirSync10 } from "node:fs";
|
|
17092
|
+
import { join as join25, dirname as dirname5 } from "node:path";
|
|
16982
17093
|
import { homedir as homedir11 } from "node:os";
|
|
16983
|
-
import { fileURLToPath as
|
|
17094
|
+
import { fileURLToPath as fileURLToPath15 } from "node:url";
|
|
16984
17095
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
16985
17096
|
function which(cmd) {
|
|
16986
17097
|
return spawnSync5("sh", ["-c", 'command -v "$1" >/dev/null 2>&1', "sh", cmd]).status === 0;
|
|
@@ -17023,7 +17134,7 @@ async function runDoctor() {
|
|
|
17023
17134
|
detail: h1 ? `X-HackerOne-Research: ${h1}` : "H1_USERNAME not set",
|
|
17024
17135
|
fix: h1 ? undefined : "export H1_USERNAME=<your-hackerone-username> (adds X-HackerOne-Research to outbound requests)"
|
|
17025
17136
|
});
|
|
17026
|
-
const dataOk = existsSync13(
|
|
17137
|
+
const dataOk = existsSync13(join25(ROOT10, "chains.json")) && existsSync13(join25(ROOT10, "tools-catalog.json"));
|
|
17027
17138
|
issues.push({
|
|
17028
17139
|
name: "Data layers (chains + tools-catalog)",
|
|
17029
17140
|
status: dataOk ? "ok" : "fail",
|
|
@@ -17074,28 +17185,28 @@ BlitzStrike — ${verdict}`);
|
|
|
17074
17185
|
function resolveCommand() {
|
|
17075
17186
|
if (which("blitzstrike"))
|
|
17076
17187
|
return { command: "blitzstrike", args: ["serve", "--mcp"] };
|
|
17077
|
-
const src =
|
|
17188
|
+
const src = join25(ROOT10, "src", "index.ts");
|
|
17078
17189
|
if (which("bun") && existsSync13(src))
|
|
17079
17190
|
return { command: "bun", args: ["run", src, "serve", "--mcp"] };
|
|
17080
17191
|
if (which("npx"))
|
|
17081
17192
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
17082
17193
|
if (which("bunx"))
|
|
17083
17194
|
return { command: "bunx", args: ["blitzstrike", "serve", "--mcp"] };
|
|
17084
|
-
const dist =
|
|
17195
|
+
const dist = join25(ROOT10, "dist", "index.js");
|
|
17085
17196
|
if (existsSync13(dist))
|
|
17086
17197
|
return { command: "bun", args: [dist, "serve", "--mcp"] };
|
|
17087
17198
|
return { command: "npx", args: ["-y", "blitzstrike", "serve", "--mcp"] };
|
|
17088
17199
|
}
|
|
17089
17200
|
function readJson(p) {
|
|
17090
17201
|
try {
|
|
17091
|
-
return JSON.parse(
|
|
17202
|
+
return JSON.parse(readFileSync29(p, "utf8"));
|
|
17092
17203
|
} catch {
|
|
17093
17204
|
return null;
|
|
17094
17205
|
}
|
|
17095
17206
|
}
|
|
17096
17207
|
function jsonMcpServersWrite(p) {
|
|
17097
17208
|
return (command, args) => {
|
|
17098
|
-
const dir =
|
|
17209
|
+
const dir = dirname5(p);
|
|
17099
17210
|
if (!existsSync13(dir))
|
|
17100
17211
|
mkdirSync10(dir, { recursive: true });
|
|
17101
17212
|
const existing = readJson(p) ?? {};
|
|
@@ -17108,7 +17219,7 @@ function jsonMcpServersWrite(p) {
|
|
|
17108
17219
|
}
|
|
17109
17220
|
function opencodeWrite(p) {
|
|
17110
17221
|
return (command, args) => {
|
|
17111
|
-
const dir =
|
|
17222
|
+
const dir = dirname5(p);
|
|
17112
17223
|
if (!existsSync13(dir))
|
|
17113
17224
|
mkdirSync10(dir, { recursive: true });
|
|
17114
17225
|
const existing = readJson(p) ?? {};
|
|
@@ -17121,26 +17232,26 @@ function opencodeWrite(p) {
|
|
|
17121
17232
|
};
|
|
17122
17233
|
}
|
|
17123
17234
|
function copyOpenCodeAgents() {
|
|
17124
|
-
const srcDir =
|
|
17125
|
-
const dstDir =
|
|
17235
|
+
const srcDir = join25(ROOT10, "opencode-agents");
|
|
17236
|
+
const dstDir = join25(homedir11(), ".config", "opencode", "agents");
|
|
17126
17237
|
if (!existsSync13(srcDir))
|
|
17127
17238
|
return;
|
|
17128
17239
|
if (!existsSync13(dstDir))
|
|
17129
17240
|
mkdirSync10(dstDir, { recursive: true });
|
|
17130
17241
|
for (const f of ["Blitz Strike.md", "Blitz.md", "Eagle Eye.md", "Strike.md"]) {
|
|
17131
|
-
const src =
|
|
17132
|
-
const dst =
|
|
17242
|
+
const src = join25(srcDir, f);
|
|
17243
|
+
const dst = join25(dstDir, f);
|
|
17133
17244
|
if (existsSync13(src)) {
|
|
17134
|
-
writeFileSync9(dst,
|
|
17245
|
+
writeFileSync9(dst, readFileSync29(src, "utf8"));
|
|
17135
17246
|
}
|
|
17136
17247
|
}
|
|
17137
17248
|
}
|
|
17138
17249
|
function codexWrite(p) {
|
|
17139
17250
|
return (command, args) => {
|
|
17140
|
-
const dir =
|
|
17251
|
+
const dir = dirname5(p);
|
|
17141
17252
|
if (!existsSync13(dir))
|
|
17142
17253
|
mkdirSync10(dir, { recursive: true });
|
|
17143
|
-
let existing = existsSync13(p) ?
|
|
17254
|
+
let existing = existsSync13(p) ? readFileSync29(p, "utf8") : "";
|
|
17144
17255
|
if (!existing.trimEnd().endsWith(`
|
|
17145
17256
|
`))
|
|
17146
17257
|
existing += `
|
|
@@ -17156,10 +17267,10 @@ args = ${JSON.stringify(args)}
|
|
|
17156
17267
|
}
|
|
17157
17268
|
function hermesWrite(p) {
|
|
17158
17269
|
return (command, args) => {
|
|
17159
|
-
const dir =
|
|
17270
|
+
const dir = dirname5(p);
|
|
17160
17271
|
if (!existsSync13(dir))
|
|
17161
17272
|
mkdirSync10(dir, { recursive: true });
|
|
17162
|
-
let existing = existsSync13(p) ?
|
|
17273
|
+
let existing = existsSync13(p) ? readFileSync29(p, "utf8") : "";
|
|
17163
17274
|
existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
|
|
17164
17275
|
if (!existing.trimEnd().endsWith(`
|
|
17165
17276
|
`))
|
|
@@ -17186,74 +17297,74 @@ mcp_servers:
|
|
|
17186
17297
|
function detectAgents() {
|
|
17187
17298
|
const home = homedir11();
|
|
17188
17299
|
const agents = [];
|
|
17189
|
-
const claudeJson =
|
|
17300
|
+
const claudeJson = join25(home, ".claude.json");
|
|
17190
17301
|
agents.push({
|
|
17191
17302
|
name: "Claude Code",
|
|
17192
17303
|
detectPath: claudeJson,
|
|
17193
17304
|
installed: () => existsSync13(claudeJson) || which("claude"),
|
|
17194
17305
|
write: jsonMcpServersWrite(claudeJson)
|
|
17195
17306
|
});
|
|
17196
|
-
const claudeDesktop =
|
|
17307
|
+
const claudeDesktop = join25(home, ".config", "Claude", "claude_desktop_config.json");
|
|
17197
17308
|
agents.push({
|
|
17198
17309
|
name: "Claude Desktop",
|
|
17199
17310
|
detectPath: claudeDesktop,
|
|
17200
17311
|
installed: () => existsSync13(claudeDesktop),
|
|
17201
17312
|
write: jsonMcpServersWrite(claudeDesktop)
|
|
17202
17313
|
});
|
|
17203
|
-
const cursor =
|
|
17314
|
+
const cursor = join25(home, ".cursor", "mcp.json");
|
|
17204
17315
|
agents.push({
|
|
17205
17316
|
name: "Cursor",
|
|
17206
17317
|
detectPath: cursor,
|
|
17207
|
-
installed: () => existsSync13(
|
|
17318
|
+
installed: () => existsSync13(join25(home, ".cursor")) || which("cursor"),
|
|
17208
17319
|
write: jsonMcpServersWrite(cursor)
|
|
17209
17320
|
});
|
|
17210
|
-
const opencode =
|
|
17321
|
+
const opencode = join25(home, ".config", "opencode", "opencode.jsonc");
|
|
17211
17322
|
agents.push({
|
|
17212
17323
|
name: "OpenCode",
|
|
17213
17324
|
detectPath: opencode,
|
|
17214
|
-
installed: () => existsSync13(
|
|
17325
|
+
installed: () => existsSync13(join25(home, ".config", "opencode")) || which("opencode"),
|
|
17215
17326
|
write: opencodeWrite(opencode)
|
|
17216
17327
|
});
|
|
17217
|
-
const codex =
|
|
17328
|
+
const codex = join25(home, ".codex", "config.toml");
|
|
17218
17329
|
agents.push({
|
|
17219
17330
|
name: "Codex",
|
|
17220
17331
|
detectPath: codex,
|
|
17221
|
-
installed: () => existsSync13(
|
|
17332
|
+
installed: () => existsSync13(join25(home, ".codex")) || which("codex"),
|
|
17222
17333
|
write: codexWrite(codex)
|
|
17223
17334
|
});
|
|
17224
|
-
const hermes =
|
|
17335
|
+
const hermes = join25(home, ".hermes", "config.yaml");
|
|
17225
17336
|
agents.push({
|
|
17226
17337
|
name: "Hermes",
|
|
17227
17338
|
detectPath: hermes,
|
|
17228
|
-
installed: () => existsSync13(
|
|
17339
|
+
installed: () => existsSync13(join25(home, ".hermes")) || which("hermes"),
|
|
17229
17340
|
write: hermesWrite(hermes)
|
|
17230
17341
|
});
|
|
17231
|
-
const gemini =
|
|
17342
|
+
const gemini = join25(home, ".gemini", "settings.json");
|
|
17232
17343
|
agents.push({
|
|
17233
17344
|
name: "Gemini CLI",
|
|
17234
17345
|
detectPath: gemini,
|
|
17235
|
-
installed: () => existsSync13(
|
|
17346
|
+
installed: () => existsSync13(join25(home, ".gemini")) || which("gemini"),
|
|
17236
17347
|
write: jsonMcpServersWrite(gemini)
|
|
17237
17348
|
});
|
|
17238
|
-
const windsurf =
|
|
17349
|
+
const windsurf = join25(home, ".codeium", "windsurf", "mcp_config.json");
|
|
17239
17350
|
agents.push({
|
|
17240
17351
|
name: "Windsurf",
|
|
17241
17352
|
detectPath: windsurf,
|
|
17242
|
-
installed: () => existsSync13(
|
|
17353
|
+
installed: () => existsSync13(join25(home, ".codeium")) || which("windsurf"),
|
|
17243
17354
|
write: jsonMcpServersWrite(windsurf)
|
|
17244
17355
|
});
|
|
17245
|
-
const copilot =
|
|
17356
|
+
const copilot = join25(home, ".copilot", "mcp.json");
|
|
17246
17357
|
agents.push({
|
|
17247
17358
|
name: "Copilot",
|
|
17248
17359
|
detectPath: copilot,
|
|
17249
|
-
installed: () => existsSync13(
|
|
17360
|
+
installed: () => existsSync13(join25(home, ".copilot")),
|
|
17250
17361
|
write: jsonMcpServersWrite(copilot)
|
|
17251
17362
|
});
|
|
17252
|
-
const cline =
|
|
17363
|
+
const cline = join25(home, ".cline", "mcp_settings.json");
|
|
17253
17364
|
agents.push({
|
|
17254
17365
|
name: "Cline",
|
|
17255
17366
|
detectPath: cline,
|
|
17256
|
-
installed: () => existsSync13(
|
|
17367
|
+
installed: () => existsSync13(join25(home, ".cline")),
|
|
17257
17368
|
write: jsonMcpServersWrite(cline)
|
|
17258
17369
|
});
|
|
17259
17370
|
return agents;
|
|
@@ -17371,7 +17482,7 @@ var init_cli = __esm(() => {
|
|
|
17371
17482
|
init_release();
|
|
17372
17483
|
init_dependency();
|
|
17373
17484
|
init_mcp_status();
|
|
17374
|
-
ROOT10 =
|
|
17485
|
+
ROOT10 = join25(fileURLToPath15(new URL(".", import.meta.url)), "..");
|
|
17375
17486
|
});
|
|
17376
17487
|
|
|
17377
17488
|
// node_modules/zod/v3/helpers/util.js
|
|
@@ -32009,7 +32120,7 @@ class StdioServerTransport {
|
|
|
32009
32120
|
|
|
32010
32121
|
// src/server.ts
|
|
32011
32122
|
init_scanner();
|
|
32012
|
-
import { readFileSync as
|
|
32123
|
+
import { readFileSync as readFileSync26 } from "node:fs";
|
|
32013
32124
|
|
|
32014
32125
|
// src/recon.ts
|
|
32015
32126
|
var TIMEOUT_MS = 25000;
|
|
@@ -57675,20 +57786,21 @@ function dataRefresh() {
|
|
|
57675
57786
|
|
|
57676
57787
|
// src/server.ts
|
|
57677
57788
|
init_verify_harness();
|
|
57789
|
+
init_variants();
|
|
57678
57790
|
|
|
57679
57791
|
// src/benchmark.ts
|
|
57680
|
-
import { readFileSync as
|
|
57681
|
-
import { join as
|
|
57682
|
-
import { fileURLToPath as
|
|
57792
|
+
import { readFileSync as readFileSync20, readdirSync as readdirSync5, writeFileSync as writeFileSync6, rmSync as rmSync3, mkdirSync as mkdirSync7 } from "node:fs";
|
|
57793
|
+
import { join as join19 } from "node:path";
|
|
57794
|
+
import { fileURLToPath as fileURLToPath11 } from "node:url";
|
|
57683
57795
|
import { tmpdir as tmpdir3, homedir as homedir8 } from "node:os";
|
|
57684
57796
|
init_complex_bugs();
|
|
57685
|
-
var ROOT5 =
|
|
57686
|
-
var CORPUS =
|
|
57687
|
-
var CAPTURED_CORPUS_DIR = process.env.BLITZSTRIKE_CORPUS_DIR ??
|
|
57797
|
+
var ROOT5 = join19(fileURLToPath11(new URL(".", import.meta.url)), "..");
|
|
57798
|
+
var CORPUS = join19(ROOT5, "bench", "corpus");
|
|
57799
|
+
var CAPTURED_CORPUS_DIR = process.env.BLITZSTRIKE_CORPUS_DIR ?? join19(homedir8(), ".blitzstrike", "corpus");
|
|
57688
57800
|
function parseCorpusFile(path) {
|
|
57689
57801
|
let text;
|
|
57690
57802
|
try {
|
|
57691
|
-
text =
|
|
57803
|
+
text = readFileSync20(path, "utf8");
|
|
57692
57804
|
} catch {
|
|
57693
57805
|
return null;
|
|
57694
57806
|
}
|
|
@@ -57744,7 +57856,7 @@ function loadDir(dir) {
|
|
|
57744
57856
|
for (const f of readdirSync5(dir)) {
|
|
57745
57857
|
if (!f.endsWith(".md"))
|
|
57746
57858
|
continue;
|
|
57747
|
-
const e = parseCorpusFile(
|
|
57859
|
+
const e = parseCorpusFile(join19(dir, f));
|
|
57748
57860
|
if (e)
|
|
57749
57861
|
entries.push(e);
|
|
57750
57862
|
}
|
|
@@ -57771,7 +57883,7 @@ function captureFalsePositive(input) {
|
|
|
57771
57883
|
input.code,
|
|
57772
57884
|
"```"
|
|
57773
57885
|
].filter((l) => l !== null);
|
|
57774
|
-
const path =
|
|
57886
|
+
const path = join19(CAPTURED_CORPUS_DIR, `${id}.md`);
|
|
57775
57887
|
writeFileSync6(path, lines.join(`
|
|
57776
57888
|
`) + `
|
|
57777
57889
|
`, "utf8");
|
|
@@ -57793,7 +57905,7 @@ function detect(entry) {
|
|
|
57793
57905
|
}
|
|
57794
57906
|
const lang = entry.language;
|
|
57795
57907
|
if (lang === "php") {
|
|
57796
|
-
const tmp =
|
|
57908
|
+
const tmp = join19(tmpdir3(), `blitzstrike-bench-${entry.id}-${process.pid}.php`);
|
|
57797
57909
|
writeFileSync6(tmp, entry.code);
|
|
57798
57910
|
const r = analyzeDataFlow2(tmp);
|
|
57799
57911
|
rmSync3(tmp, { force: true });
|
|
@@ -57862,12 +57974,12 @@ function runBenchmark() {
|
|
|
57862
57974
|
}
|
|
57863
57975
|
|
|
57864
57976
|
// src/obligations.ts
|
|
57865
|
-
import { readFileSync as
|
|
57866
|
-
import { join as
|
|
57977
|
+
import { readFileSync as readFileSync21, appendFileSync as appendFileSync4, mkdirSync as mkdirSync8, existsSync as existsSync10, writeFileSync as writeFileSync7 } from "node:fs";
|
|
57978
|
+
import { join as join20 } from "node:path";
|
|
57867
57979
|
import { createHash as createHash6 } from "node:crypto";
|
|
57868
57980
|
import { homedir as homedir9 } from "node:os";
|
|
57869
|
-
var HOME_DIR4 = process.env.BLITZSTRIKE_HOME ??
|
|
57870
|
-
var OBLIGATION_PATH =
|
|
57981
|
+
var HOME_DIR4 = process.env.BLITZSTRIKE_HOME ?? join20(homedir9(), ".blitzstrike");
|
|
57982
|
+
var OBLIGATION_PATH = join20(HOME_DIR4, "obligations.jsonl");
|
|
57871
57983
|
function ensureDir3() {
|
|
57872
57984
|
if (!existsSync10(HOME_DIR4))
|
|
57873
57985
|
mkdirSync8(HOME_DIR4, { recursive: true });
|
|
@@ -57880,7 +57992,7 @@ function loadObligations() {
|
|
|
57880
57992
|
return [];
|
|
57881
57993
|
try {
|
|
57882
57994
|
const out = [];
|
|
57883
|
-
for (const line of
|
|
57995
|
+
for (const line of readFileSync21(OBLIGATION_PATH, "utf8").split(`
|
|
57884
57996
|
`)) {
|
|
57885
57997
|
const t = line.trim();
|
|
57886
57998
|
if (!t)
|
|
@@ -57984,11 +58096,11 @@ init_framework_security();
|
|
|
57984
58096
|
// src/variant-scan.ts
|
|
57985
58097
|
init_scanner();
|
|
57986
58098
|
init_complex_bugs();
|
|
57987
|
-
import { readFileSync as
|
|
58099
|
+
import { readFileSync as readFileSync23 } from "node:fs";
|
|
57988
58100
|
|
|
57989
58101
|
// src/cross-file.ts
|
|
57990
58102
|
init_scanner();
|
|
57991
|
-
import { readFileSync as
|
|
58103
|
+
import { readFileSync as readFileSync22 } from "node:fs";
|
|
57992
58104
|
var PUBLIC_HOOK_RE = /(?:wp_ajax_nopriv_\w+|admin_post_nopriv_\w+|init|template_redirect|wp_loaded|parse_request|user_register|um_user_register|register_new_user|authenticate)/;
|
|
57993
58105
|
var PRIV_SINK_RE = /\b(set_role|add_role|remove_role|set_user_role|wp_set_current_user|wp_set_auth_cookie|wp_insert_user|wp_create_user|wp_update_user)\s*\(/;
|
|
57994
58106
|
var ROLE_META_RE = /update_user_meta\s*\(\s*[^,]+,\s*['"](wp_capabilities|role|roles|capabilities|wp_user_level)['"]/;
|
|
@@ -58040,7 +58152,7 @@ function detectCrossFilePrivesc(root) {
|
|
|
58040
58152
|
for (const file of files) {
|
|
58041
58153
|
let code;
|
|
58042
58154
|
try {
|
|
58043
|
-
code = stripComments(
|
|
58155
|
+
code = stripComments(readFileSync22(file, "utf8"));
|
|
58044
58156
|
} catch {
|
|
58045
58157
|
continue;
|
|
58046
58158
|
}
|
|
@@ -58081,7 +58193,7 @@ function detectCrossFilePrivesc(root) {
|
|
|
58081
58193
|
for (const file of files) {
|
|
58082
58194
|
let code;
|
|
58083
58195
|
try {
|
|
58084
|
-
code = stripComments(
|
|
58196
|
+
code = stripComments(readFileSync22(file, "utf8"));
|
|
58085
58197
|
} catch {
|
|
58086
58198
|
continue;
|
|
58087
58199
|
}
|
|
@@ -58104,7 +58216,7 @@ function detectCrossFilePrivesc(root) {
|
|
|
58104
58216
|
for (const file of files) {
|
|
58105
58217
|
let code;
|
|
58106
58218
|
try {
|
|
58107
|
-
code = stripComments(
|
|
58219
|
+
code = stripComments(readFileSync22(file, "utf8"));
|
|
58108
58220
|
} catch {
|
|
58109
58221
|
continue;
|
|
58110
58222
|
}
|
|
@@ -58219,7 +58331,7 @@ function variantScan(root, opts = {}) {
|
|
|
58219
58331
|
for (const file of files) {
|
|
58220
58332
|
let code;
|
|
58221
58333
|
try {
|
|
58222
|
-
code =
|
|
58334
|
+
code = readFileSync23(file, "utf8");
|
|
58223
58335
|
} catch {
|
|
58224
58336
|
continue;
|
|
58225
58337
|
}
|
|
@@ -58879,17 +58991,17 @@ function analyzeJwt(token, opts = {}) {
|
|
|
58879
58991
|
}
|
|
58880
58992
|
|
|
58881
58993
|
// src/enterprise-benchmark.ts
|
|
58882
|
-
import { existsSync as existsSync11, readFileSync as
|
|
58883
|
-
import { join as
|
|
58994
|
+
import { existsSync as existsSync11, readFileSync as readFileSync24, readdirSync as readdirSync6, statSync as statSync3 } from "node:fs";
|
|
58995
|
+
import { join as join21, resolve } from "node:path";
|
|
58884
58996
|
init_complex_bugs();
|
|
58885
58997
|
var ROOT6 = resolve(process.cwd(), "bench", "enterprise");
|
|
58886
58998
|
var ENTERPRISE_FIXTURES = [
|
|
58887
|
-
{ id: "php-laravel", dir:
|
|
58888
|
-
{ id: "js-express", dir:
|
|
58889
|
-
{ id: "py-django", dir:
|
|
58890
|
-
{ id: "java-spring", dir:
|
|
58891
|
-
{ id: "wp-batch-route", dir:
|
|
58892
|
-
{ id: "php-complex", dir:
|
|
58999
|
+
{ id: "php-laravel", dir: join21(ROOT6, "php-laravel"), expected_vulns: 4 },
|
|
59000
|
+
{ id: "js-express", dir: join21(ROOT6, "js-express"), expected_vulns: 2 },
|
|
59001
|
+
{ id: "py-django", dir: join21(ROOT6, "py-django"), expected_vulns: 2 },
|
|
59002
|
+
{ id: "java-spring", dir: join21(ROOT6, "java-spring"), expected_vulns: 2 },
|
|
59003
|
+
{ id: "wp-batch-route", dir: join21(ROOT6, "wp-batch-route"), expected_vulns: 3 },
|
|
59004
|
+
{ id: "php-complex", dir: join21(ROOT6, "php-complex"), expected_vulns: 4 }
|
|
58893
59005
|
];
|
|
58894
59006
|
function sourceFiles(root) {
|
|
58895
59007
|
const out = [];
|
|
@@ -58901,7 +59013,7 @@ function sourceFiles(root) {
|
|
|
58901
59013
|
return;
|
|
58902
59014
|
}
|
|
58903
59015
|
for (const e of entries) {
|
|
58904
|
-
const p =
|
|
59016
|
+
const p = join21(dir, e);
|
|
58905
59017
|
let st;
|
|
58906
59018
|
try {
|
|
58907
59019
|
st = statSync3(p);
|
|
@@ -58922,7 +59034,7 @@ function detectFixture(dir) {
|
|
|
58922
59034
|
for (const f of sourceFiles(dir)) {
|
|
58923
59035
|
let code;
|
|
58924
59036
|
try {
|
|
58925
|
-
code =
|
|
59037
|
+
code = readFileSync24(f, "utf8");
|
|
58926
59038
|
} catch {
|
|
58927
59039
|
continue;
|
|
58928
59040
|
}
|
|
@@ -59542,6 +59654,13 @@ ${statusLine}` : statusLine },
|
|
|
59542
59654
|
}, async () => {
|
|
59543
59655
|
return { content: [{ type: "text", text: JSON.stringify(runVerificationHarness()) }] };
|
|
59544
59656
|
});
|
|
59657
|
+
server.registerTool("scan_variants", {
|
|
59658
|
+
title: "Variant corpus coverage scan",
|
|
59659
|
+
description: "VERIFY: sweep every real-world code shape (intelligence/variants.json) against its class's detector and report per-class coverage. A shape that does not fire is a coverage gap (false negative) — use this to find detector blind spots, then fix the detector.",
|
|
59660
|
+
inputSchema: {}
|
|
59661
|
+
}, async () => {
|
|
59662
|
+
return { content: [{ type: "text", text: JSON.stringify(scanVariants()) }] };
|
|
59663
|
+
});
|
|
59545
59664
|
server.registerTool("fofa_search", {
|
|
59546
59665
|
title: "FOFA asset search",
|
|
59547
59666
|
description: "STRIKE: search the FOFA asset index (needs FOFA_EMAIL + FOFA_KEY).",
|
|
@@ -59926,7 +60045,7 @@ ${statusLine}` : statusLine },
|
|
|
59926
60045
|
let src = code;
|
|
59927
60046
|
if (!src && path) {
|
|
59928
60047
|
try {
|
|
59929
|
-
src =
|
|
60048
|
+
src = readFileSync26(path, "utf8");
|
|
59930
60049
|
} catch {
|
|
59931
60050
|
return { content: [{ type: "text", text: JSON.stringify({ error: "unreadable path" }) }] };
|
|
59932
60051
|
}
|
|
@@ -60544,7 +60663,7 @@ ${statusLine}` : statusLine },
|
|
|
60544
60663
|
description: "Route confusion (separate class from source→sink taint): detect attacker-controlled dispatch (call_user_func/forward/dispatch), dynamic method calls ($obj->$m()), dynamic include (LFI), batch/proxy forwarding without per-route auth re-check, and route matching without path normalization.",
|
|
60545
60664
|
inputSchema: { path: string2().describe("Source file path (PHP)") }
|
|
60546
60665
|
}, async ({ path }) => {
|
|
60547
|
-
const code =
|
|
60666
|
+
const code = readFileSync26(path, "utf8");
|
|
60548
60667
|
const findings = detectRouteConfusion(code, path);
|
|
60549
60668
|
return {
|
|
60550
60669
|
content: [{
|
|
@@ -60558,7 +60677,7 @@ ${statusLine}` : statusLine },
|
|
|
60558
60677
|
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), wrong-context sanitization (sanitize_text_field() into a SQL query), and missing authorization/nonce on WordPress entry points (wp_ajax_* / admin_post_* / register_rest_route without current_user_can, nonce verification, or permission_callback).",
|
|
60559
60678
|
inputSchema: { path: string2().describe("Source file path") }
|
|
60560
60679
|
}, async ({ path }) => {
|
|
60561
|
-
const code =
|
|
60680
|
+
const code = readFileSync26(path, "utf8");
|
|
60562
60681
|
const findings = detectComplexBugs(code, path);
|
|
60563
60682
|
return {
|
|
60564
60683
|
content: [{
|
|
@@ -60644,7 +60763,7 @@ ${statusLine}` : statusLine },
|
|
|
60644
60763
|
let lang = language;
|
|
60645
60764
|
if (path && !code) {
|
|
60646
60765
|
try {
|
|
60647
|
-
src =
|
|
60766
|
+
src = readFileSync26(path, "utf8");
|
|
60648
60767
|
} catch {
|
|
60649
60768
|
return { content: [{ type: "text", text: JSON.stringify({ error: `cannot read ${path}` }) }] };
|
|
60650
60769
|
}
|
|
@@ -60885,15 +61004,15 @@ async function serve() {
|
|
|
60885
61004
|
|
|
60886
61005
|
// src/index.ts
|
|
60887
61006
|
init_cli();
|
|
60888
|
-
import { readFileSync as
|
|
60889
|
-
import { join as
|
|
60890
|
-
import { fileURLToPath as
|
|
60891
|
-
var ROOT11 =
|
|
61007
|
+
import { readFileSync as readFileSync30, existsSync as existsSync14 } from "node:fs";
|
|
61008
|
+
import { join as join26 } from "node:path";
|
|
61009
|
+
import { fileURLToPath as fileURLToPath16 } from "node:url";
|
|
61010
|
+
var ROOT11 = join26(fileURLToPath16(new URL(".", import.meta.url)), "..");
|
|
60892
61011
|
function readVersion() {
|
|
60893
61012
|
try {
|
|
60894
|
-
const p =
|
|
61013
|
+
const p = join26(ROOT11, "package.json");
|
|
60895
61014
|
if (existsSync14(p))
|
|
60896
|
-
return JSON.parse(
|
|
61015
|
+
return JSON.parse(readFileSync30(p, "utf8")).version ?? "1.0.0";
|
|
60897
61016
|
} catch {}
|
|
60898
61017
|
return "1.0.0";
|
|
60899
61018
|
}
|
|
@@ -60964,9 +61083,13 @@ async function main() {
|
|
|
60964
61083
|
}
|
|
60965
61084
|
if (args[0] === "verify") {
|
|
60966
61085
|
await Promise.resolve().then(() => init_verify_harness());
|
|
61086
|
+
await Promise.resolve().then(() => init_variants());
|
|
60967
61087
|
console.log(verificationReportText());
|
|
61088
|
+
console.log(`
|
|
61089
|
+
` + variantCoverageText());
|
|
60968
61090
|
const r = runVerificationHarness();
|
|
60969
|
-
|
|
61091
|
+
const v = scanVariants();
|
|
61092
|
+
if (r.failed > 0 || v.missed > 0)
|
|
60970
61093
|
process.exit(1);
|
|
60971
61094
|
return;
|
|
60972
61095
|
}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "blitzstrike-variants-v1",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"_comment": "Variant corpus \u2014 concrete real-world code shapes per detector class. Each variant is a shape that MUST fire its class's detector. The verification harness sweeps every variant and reports per-class coverage, so a detector that misses a shape surfaces as a false negative. Shapes are generic (language-idiomatic), with no vendor/product framing.",
|
|
5
|
+
"variants": [
|
|
6
|
+
{
|
|
7
|
+
"class": "sql_injection",
|
|
8
|
+
"code": "$wpdb->query(\"DELETE FROM t WHERE id=\" . $row['id']);",
|
|
9
|
+
"note": "raw string concat into query"
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
"class": "sql_injection",
|
|
13
|
+
"code": "$r = $db->query(\"SELECT * FROM users WHERE name='\" . $name . \"'\");",
|
|
14
|
+
"note": "interpolated SELECT"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"class": "sql_injection",
|
|
18
|
+
"code": "mysqli_query($c, \"UPDATE t SET x='\" . $x . \"' WHERE id=\" . $id);",
|
|
19
|
+
"note": "mysqli raw concat"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"class": "sql_injection",
|
|
23
|
+
"code": "knex('users').whereRaw('id = ' + userId)",
|
|
24
|
+
"note": "ORM raw clause"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"class": "ssrf",
|
|
28
|
+
"code": "$ch = curl_init($_GET['url']); curl_exec($ch);",
|
|
29
|
+
"note": "curl to request URL"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"class": "ssrf",
|
|
33
|
+
"code": "$r = file_get_contents($_POST['target']);",
|
|
34
|
+
"note": "file_get_contents request URL"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"class": "ssrf",
|
|
38
|
+
"code": "requests.get(request.args.get('url'))",
|
|
39
|
+
"note": "python requests request URL"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"class": "ssrf",
|
|
43
|
+
"code": "axios.get(req.body.url)",
|
|
44
|
+
"note": "node axios request URL"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
"class": "ssti",
|
|
48
|
+
"code": "return jinja2.from_string($_GET['tpl']).render()",
|
|
49
|
+
"note": "jinja from_string request input"
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
"class": "ssti",
|
|
53
|
+
"code": "$twig->render($_GET['tpl']);",
|
|
54
|
+
"note": "twig render request input"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"class": "ssti",
|
|
58
|
+
"code": "return render_template_string(request.args['tpl'])",
|
|
59
|
+
"note": "flask render_template_string request input"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"class": "ssti",
|
|
63
|
+
"code": "new Template($_GET['template'])",
|
|
64
|
+
"note": "freemarker template request input"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"class": "xxe",
|
|
68
|
+
"code": "$xml = $_POST['xml'];\n$x = simplexml_load_string($xml, 'SimpleXMLElement', LIBXML_NOENT);",
|
|
69
|
+
"note": "simplexml with entity substitution enabled"
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
"class": "xxe",
|
|
73
|
+
"code": "$xml = $_POST['xml'];\n$x = new SimpleXMLElement($xml, LIBXML_NOENT);",
|
|
74
|
+
"note": "SimpleXMLElement with NOENT"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"class": "xxe",
|
|
78
|
+
"code": "DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(request.getParameter('xml'))",
|
|
79
|
+
"note": "java DOM parse without disabling entities"
|
|
80
|
+
},
|
|
81
|
+
{
|
|
82
|
+
"class": "deserialization",
|
|
83
|
+
"code": "$o = unserialize($_COOKIE['data']);\nclass A { function __destruct() { system($this->c); } }",
|
|
84
|
+
"note": "unserialize cookie"
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"class": "deserialization",
|
|
88
|
+
"code": "obj = pickle.loads(request.data)",
|
|
89
|
+
"note": "pickle.loads request data"
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
"class": "deserialization",
|
|
93
|
+
"code": "data = yaml.load(request.data)",
|
|
94
|
+
"note": "unsafe yaml.load"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"class": "deserialization",
|
|
98
|
+
"code": "new ObjectInputStream(request.getInputStream()).readObject()",
|
|
99
|
+
"note": "java readObject"
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
"class": "type_juggling",
|
|
103
|
+
"code": "if ($_POST['token'] == md5($secret)) { login(); }",
|
|
104
|
+
"note": "loose compare vs md5"
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
"class": "type_juggling",
|
|
108
|
+
"code": "if ($_GET['pwd'] != $stored_hash) { die('no'); }",
|
|
109
|
+
"note": "loose compare vs hash"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
"class": "type_juggling",
|
|
113
|
+
"code": "if (strcmp($_POST['pwd'], $hash) == 0) { login(); }",
|
|
114
|
+
"note": "strcmp array coercion"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"class": "mass_assignment",
|
|
118
|
+
"code": "extract($_REQUEST);",
|
|
119
|
+
"note": "extract request"
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"class": "mass_assignment",
|
|
123
|
+
"code": "parse_str(file_get_contents('php://input'), $data);",
|
|
124
|
+
"note": "parse_str raw body"
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
"class": "mass_assignment",
|
|
128
|
+
"code": "foreach ($_POST as $k => $v) { $this->$k = $v; }",
|
|
129
|
+
"note": "dynamic field binding"
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"class": "mass_assignment",
|
|
133
|
+
"code": "User.create(req.body)",
|
|
134
|
+
"note": "model create without allowlist"
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
"class": "prototype_pollution",
|
|
138
|
+
"code": "_.merge(target, req.body)",
|
|
139
|
+
"note": "lodash merge request body"
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
"class": "prototype_pollution",
|
|
143
|
+
"code": "Object.assign(obj, req.body)",
|
|
144
|
+
"note": "Object.assign request body"
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
"class": "prototype_pollution",
|
|
148
|
+
"code": "$.extend(true, {}, req.body)",
|
|
149
|
+
"note": "jquery deep extend request body"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"class": "crlf_injection",
|
|
153
|
+
"code": "header(\"Location: \" . $_GET['url']);",
|
|
154
|
+
"note": "header location request input"
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
"class": "crlf_injection",
|
|
158
|
+
"code": "setcookie('name', $_GET['v']);",
|
|
159
|
+
"note": "setcookie request value"
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"class": "crlf_injection",
|
|
163
|
+
"code": "res.setHeader('Location', req.query.url)",
|
|
164
|
+
"note": "node setHeader request input"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"class": "path_confusion",
|
|
168
|
+
"code": "include($_GET['page']);",
|
|
169
|
+
"note": "include request path"
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
"class": "path_confusion",
|
|
173
|
+
"code": "$c = file_get_contents($_GET['file']);",
|
|
174
|
+
"note": "file_get_contents request path"
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
"class": "path_confusion",
|
|
178
|
+
"code": "readFile(path.join(base, req.query.file))",
|
|
179
|
+
"note": "node readFile joined path"
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
"class": "path_confusion",
|
|
183
|
+
"code": "new File(request.getParameter('f'))",
|
|
184
|
+
"note": "java File request param"
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"class": "wrong_sanitizer",
|
|
188
|
+
"code": "$x = htmlspecialchars($_GET['q']); $db->query(\"SELECT * FROM t WHERE c='\" . $x . \"'\");",
|
|
189
|
+
"note": "HTML sanitizer into SQL"
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"class": "wrong_sanitizer",
|
|
193
|
+
"code": "$x = strip_tags($_GET['q']); mysqli_query($c, \"SELECT * FROM t WHERE c='\" . $x . \"'\");",
|
|
194
|
+
"note": "strip_tags into SQL"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"class": "wrong_sanitizer",
|
|
198
|
+
"code": "$x = esc_html($_GET['q']); $db->query(\"SELECT * FROM t WHERE c='\" . $x . \"'\");",
|
|
199
|
+
"note": "addslashes into SQL"
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
"class": "missing_authz",
|
|
203
|
+
"code": "add_action('wp_ajax_nopriv_export', 'h'); function h() { update_option('export', $_POST['x']); }",
|
|
204
|
+
"note": "nopriv handler no capability check"
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
"class": "missing_authz",
|
|
208
|
+
"code": "app.delete('/admin/user/:id', (req, res) => { User.delete(req.params.id); })",
|
|
209
|
+
"note": "admin route no auth"
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"class": "missing_nonce",
|
|
213
|
+
"code": "add_action('wp_ajax_del_order', 'h'); function h() { wp_delete_post((int)$_POST['id']); }",
|
|
214
|
+
"note": "ajax handler state change no nonce"
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
"class": "missing_nonce",
|
|
218
|
+
"code": "add_action('wp_ajax_del', 'h'); function h() { wp_delete_post((int)$_POST['id']); }",
|
|
219
|
+
"note": "ajax no nonce check"
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
"class": "missing_nonce",
|
|
223
|
+
"code": "add_action('wp_ajax_save', 'h'); function h() { wp_update_post($_POST); }",
|
|
224
|
+
"note": "ajax handler state change no nonce"
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"class": "missing_nonce",
|
|
228
|
+
"code": "add_action('wp_ajax_save', 'h'); function h() { update_option('x', $_POST['x']); }",
|
|
229
|
+
"note": "ajax save no nonce"
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
"class": "file_upload",
|
|
233
|
+
"code": "move_uploaded_file($_FILES['f']['tmp_name'], '/uploads/' . $_FILES['f']['name']);",
|
|
234
|
+
"note": "move_uploaded_file raw name"
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
"class": "file_upload",
|
|
238
|
+
"code": "wp_upload_bits($_FILES['f']['name'], null, file_get_contents($_FILES['f']['tmp_name']));",
|
|
239
|
+
"note": "wp_upload_bits raw name"
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"class": "file_upload",
|
|
243
|
+
"code": "file.save(path.join(uploadDir, req.files.f.name))",
|
|
244
|
+
"note": "node multer raw name"
|
|
245
|
+
},
|
|
246
|
+
{
|
|
247
|
+
"class": "file_upload",
|
|
248
|
+
"code": "move_uploaded_file($_FILES['f']['tmp_name'], '/uploads/' . basename($_FILES['f']['name']));",
|
|
249
|
+
"note": "basename only, no ext whitelist"
|
|
250
|
+
},
|
|
251
|
+
{
|
|
252
|
+
"class": "priv_esc",
|
|
253
|
+
"code": "$u->set_role($_POST['role']);",
|
|
254
|
+
"note": "set_role request role"
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
"class": "priv_esc",
|
|
258
|
+
"code": "update_user_meta($id, 'wp_capabilities', $_POST['caps']);",
|
|
259
|
+
"note": "user meta capabilities request"
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
"class": "priv_esc",
|
|
263
|
+
"code": "$u->add_role($_GET['role']);",
|
|
264
|
+
"note": "add_role request role"
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
"class": "priv_esc",
|
|
268
|
+
"code": "db.query(\"UPDATE users SET role='\" + $_POST['role'] + \"' WHERE id=\" + $_POST['id'])",
|
|
269
|
+
"note": "raw role update"
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
"class": "hardcoded_secret",
|
|
273
|
+
"code": "$db_password = \"EXAMPLE_DB_PASSWORD_123\";",
|
|
274
|
+
"note": "db password literal"
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"class": "hardcoded_secret",
|
|
278
|
+
"code": "const apiKey = \"EXAMPLE_API_KEY_123456789\";",
|
|
279
|
+
"note": "api key literal"
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"class": "hardcoded_secret",
|
|
283
|
+
"code": "$aws_access_key_id = \"EXAMPLE_ACCESS_KEY_123\";",
|
|
284
|
+
"note": "aws key literal"
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
"class": "hardcoded_secret",
|
|
288
|
+
"code": "JWT_SECRET = \"EXAMPLE_JWT_SIGNING_KEY_123\";",
|
|
289
|
+
"note": "jwt secret literal"
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
"class": "llm_injection",
|
|
293
|
+
"code": "openai.chat.completions.create(messages=[{'content': req.body.text}])",
|
|
294
|
+
"note": "openai completion request input"
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"class": "llm_injection",
|
|
298
|
+
"code": "anthropic.messages.create(prompt=req.body.prompt)",
|
|
299
|
+
"note": "anthropic prompt request input"
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
"class": "llm_injection",
|
|
303
|
+
"code": "llm.invoke(req.body.query)",
|
|
304
|
+
"note": "langchain invoke request input"
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"class": "graphql_exposure",
|
|
308
|
+
"code": "const server = new ApolloServer({ introspection: true });",
|
|
309
|
+
"note": "apollo introspection on"
|
|
310
|
+
},
|
|
311
|
+
{
|
|
312
|
+
"class": "graphql_exposure",
|
|
313
|
+
"code": "const server = new ApolloServer({ playground: true });",
|
|
314
|
+
"note": "apollo playground on"
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
"class": "graphql_exposure",
|
|
318
|
+
"code": "graphqlHTTP({ schema, graphiql: true })",
|
|
319
|
+
"note": "express-graphql graphiql on"
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"class": "oauth_misconfig",
|
|
323
|
+
"code": "const redirect_uri = req.query.redirect_uri;",
|
|
324
|
+
"note": "redirect_uri from query"
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
"class": "oauth_misconfig",
|
|
328
|
+
"code": "$redirect_uri = $_GET['redirect_uri'];",
|
|
329
|
+
"note": "redirect_uri from request"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
"class": "oauth_misconfig",
|
|
333
|
+
"code": "const returnUrl = request.params.get('returnUrl');",
|
|
334
|
+
"note": "returnUrl from request"
|
|
335
|
+
},
|
|
336
|
+
{
|
|
337
|
+
"class": "grpc_reflection",
|
|
338
|
+
"code": "server.add_service(grpc_reflection_v1alpha.ServerReflectionServicer(...))",
|
|
339
|
+
"note": "grpc reflection servicer"
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"class": "grpc_reflection",
|
|
343
|
+
"code": "reflection.enable_server_reflection()",
|
|
344
|
+
"note": "reflection enabled"
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
"class": "grpc_reflection",
|
|
348
|
+
"code": "grpc_reflection_v1.ReflectionServicer.to_server(server)",
|
|
349
|
+
"note": "reflection to server"
|
|
350
|
+
}
|
|
351
|
+
]
|
|
352
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.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": {
|