blitzstrike 2.4.61 → 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 +29 -0
- package/dist/index.js +251 -128
- package/intelligence/patterns.json +62 -22
- package/intelligence/variants.json +352 -0
- package/package.json +1 -1
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
|
}
|
|
@@ -59117,7 +59229,7 @@ function createServer() {
|
|
|
59117
59229
|
` + `- A hard block (Cloudflare/WAF/IP-block/captcha) is NOT the end: mark the finding blocked + note the required vantage (residential / authorized / correct-SNI / --resolve direct-to-origin), then CONTINUE to the next lead (origin bypass, non-prod cluster, mobile APK, subdomain).
|
|
59118
59230
|
` + `- Keep trying every lead AND every chain step until each is verified/executed or blocked, THEN generate_report. Never deliver a bare surface map, and never stop at 'found X' when X enables more.
|
|
59119
59231
|
|
|
59120
|
-
` + "TWO PATHS:\\n" + "- Source path on disk -> blitz_scan / taint_file / eagle_eye2 (static).\\n" + "- Live URL -> live_recon / active_scan / strike_verify (web-hunting).\\n\\n" + "DEEPEN A FINDING: attack_plan(target, tech, params) to get the prioritized vector plan (decide what to test next with data); list_chains (escalation), chain_links (compose), technique_lookup(cls), payload_lookup, bypass_lookup (blocked). Ground any detector hit empirically with pattern_lookup(id) — it returns the class's CWE, real-world signature, escalation chains,
|
|
59232
|
+
` + "TWO PATHS:\\n" + "- Source path on disk -> blitz_scan / taint_file / eagle_eye2 (static).\\n" + "- Live URL -> live_recon / active_scan / strike_verify (web-hunting).\\n\\n" + "DEEPEN A FINDING: attack_plan(target, tech, params) to get the prioritized vector plan (decide what to test next with data); list_chains (escalation), chain_links (compose), technique_lookup(cls), payload_lookup, bypass_lookup (blocked). Ground any detector hit empirically with pattern_lookup(id) — it returns the class's CWE, real-world signature, escalation chains, citable references, per-class bypass techniques, and the high-value assets (crown jewels) that class hits, so a raw hit becomes a class-aware finding. BEFORE generating a report, run submission_gate(...) and only ship a finding that passes all 7 questions (reproducible, in-scope, real-impact, no-privileged-assumption, verified-live, evidence-redacted, severity-derived); one false = kill it. Calibrate severity with severity_calibrate(type, cvss) to get a P1–P5 tier + VRT-style category. Start each engagement with engagement_scaffold(target, scope) for the folder + scope.md structure, and check data_refresh() to confirm the data layers are consistent. Phase doctrine: read_skill(bs-orchestrate-engagement | bs-web-hunting | bs-source-audit | bs-verify-finding). Campaign plan: read_playbook(target-type). On any failure call retry_guidance(signal) / model_fallback(signal) before giving up.\n" + "KNOWLEDGE BASE (discover, don't guess): skillLookup(topic) / listSkills for the 40 attack-path + hunting skills (ap-*, hp-*, bs-*); list_attack_vectors / attack_vectors(cat) for the 34-category vector taxonomy; taxonomy(kind) for OWASP/CWE/ASVS/API-top-10; read_playbook(type) for the 17 campaign playbooks; list_manuals / read_tool_manual for 317 tool references; list_tools / run_catalog_tool for the 141-tool catalog. Every tool, skill, and data layer is reachable from these. CROSS-TARGET INTELLIGENCE: strike_resolve auto-records confirmed/false_positive verdicts into the empirical ledger; query it with intelligence(action=hit_rates/top_vectors) so a new target on a known tech inherits historical hit-rates (attack_plan already blends them into success_probability). LIVE PENTEST + TARGETING (black-box, WAF-safe): cache_gap(base_url, endpoint) fuzzes path-normalization + static-extension variants for cache deception/poisoning; blind_oracle(base_url, param, value) detects which SINK a param reaches (sql/ssti/xss) via benign arithmetic (id=1+0, test${7*7}) WITHOUT a malicious payload; param_precedence(base_url, param, A, B) maps duplicate-parameter precedence (first/last-wins) for WAF-bypass. SOURCE TARGETING: action_surface(code) maps every wp_ajax_* action to its vuln class + DERIVED priority (what to look for WHERE); security_state(sanitizers, sink) gives a wrong-context sanitization verdict; framework_knowledge(framework) returns auth/nonce primitives for 9 frameworks; variant_scan(root, signature) mass-sweeps an install base for ONE bug family; patch_analyze(old, new) REVERSES a security patch into the 0-day, and its mine_signature feeds variant_scan to sweep unpatched copies. All of these are DETECTION HINTS — verify each live (strike_verify / browser_validate) before it enters the report. NEWEST DETECTORS — understand AND try them: variant_scan also emits priv_esc (privilege escalation — a role/capability/user change fed by request input, traced cross-file via reverse call-graph, e.g. set_role($id, sanitize_key($_POST['um-role'])) or update_user_meta(...,'wp_capabilities', $_POST[...])) and flags uploadAllow=>array('all') unrestricted-upload configs as file_upload (the file-manager/connector shape). TO TRY a priv_esc hit: submit the role field with 'administrator' (or send wp_capabilities meta) and confirm the account actually gains admin. TO TRY an unrestricted-upload hit: POST a .php/.phtml and confirm it lands web-reachable + executes. variant_scan also emits hardcoded_secret (CWE-798) in three tiers: a secret field (api_key/db_password/access_token/aws_*) assigned a literal (HIGH), a known secret FORMAT (JWT eyJ…/AWS AKIA…/GitHub ghp_…/Slack xoxb-…/…) regardless of name (HIGH), or a high-entropy suspect token (MEDIUM — verify before reporting). Verify a frontend/JS secret live via drive_devtools (inspect the JS bundle / network response and confirm the key is actually exposed), and for a backend secret confirm it is reachable/valid. A static hit is a HYPOTHESIS until verified live — never report unverified.\n" + `PRECISION ANALYSIS — chrome-devtools MCP is MANDATORY, not optional:
|
|
59121
59233
|
` + `- When a lead needs DOM/JS precision — DOM-XSS sink execution, AJAX endpoint + payload interception, JS runtime errors, redirect chains, SSO/token flow, or anything a static HTML glance cannot prove — call drive_devtools(target). It spawns chrome-devtools-mcp and drives a REAL headless browser (navigate -> list_network_requests -> list_console_messages -> evaluate_script), giving reproducible evidence instead of a guess.
|
|
59122
59234
|
` + `- Before calling it, check_mcp() to confirm the browser MCP is available; if not, blitzstrike install-tools or npm i -g chrome-devtools-mcp. Never substitute eyeballing minified JS or a static sink guess when a live browser can PROVE it. This is the precision + consistency layer.
|
|
59123
59235
|
` + "EXTERNAL MCP SERVERS (verified, connect them — NOT one-shot CLIs): chrome-devtools-mcp (29 tools: list_network_requests/get_network_request for raw HTTP interception, list_console_messages for JS errors + DOM-XSS, evaluate_script to run JS, take_screenshot) -> stdio chrome-devtools-mcp --headless --chromeArg=--no-sandbox --chromeArg=--disable-gpu (as root/container this is MANDATORY or the browser crashes; add --proxyServer http://127.0.0.1:8080 to route through Burp, --acceptInsecureCerts for bad TLS). burp-suite-mcp (proxy history + active scan + repeater) -> NOT npm: build the JAR (git clone https://github.com/PortSwigger/mcp-server.git && cd mcp-server && ./gradlew embedProxyJar), load it in Burp Extensions, then connect the client to http://127.0.0.1:9876 (SSE) or the stdio proxy java -jar mcp-proxy-all.jar --sse-url http://127.0.0.1:9876."
|
|
@@ -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
|
}
|