blitzstrike 1.0.12 → 1.0.14
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/dist/index.js +463 -262
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6930,8 +6930,269 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6930
6930
|
exports.default = formatsPlugin;
|
|
6931
6931
|
});
|
|
6932
6932
|
|
|
6933
|
+
// src/scanner.ts
|
|
6934
|
+
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
6935
|
+
import { join } from "node:path";
|
|
6936
|
+
function escapeRegExp(s) {
|
|
6937
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
6938
|
+
}
|
|
6939
|
+
function lineno(text, pos) {
|
|
6940
|
+
let n = 1;
|
|
6941
|
+
for (let i = 0;i < pos; i++) {
|
|
6942
|
+
if (text.charCodeAt(i) === 10)
|
|
6943
|
+
n++;
|
|
6944
|
+
}
|
|
6945
|
+
return n;
|
|
6946
|
+
}
|
|
6947
|
+
function iterSourceFiles(root, maxFiles = 5000) {
|
|
6948
|
+
const out = [];
|
|
6949
|
+
const walk = (dir) => {
|
|
6950
|
+
if (out.length >= maxFiles)
|
|
6951
|
+
return;
|
|
6952
|
+
let entries;
|
|
6953
|
+
try {
|
|
6954
|
+
entries = readdirSync(dir);
|
|
6955
|
+
} catch {
|
|
6956
|
+
return;
|
|
6957
|
+
}
|
|
6958
|
+
for (const name of entries) {
|
|
6959
|
+
if (out.length >= maxFiles)
|
|
6960
|
+
return;
|
|
6961
|
+
const full = join(dir, name);
|
|
6962
|
+
let st;
|
|
6963
|
+
try {
|
|
6964
|
+
st = statSync(full);
|
|
6965
|
+
} catch {
|
|
6966
|
+
continue;
|
|
6967
|
+
}
|
|
6968
|
+
if (st.isDirectory()) {
|
|
6969
|
+
if (SKIP_PARTS.has(name) || LIB_PARTS.has(name))
|
|
6970
|
+
continue;
|
|
6971
|
+
walk(full);
|
|
6972
|
+
} else if (st.isFile()) {
|
|
6973
|
+
const dot = name.lastIndexOf(".");
|
|
6974
|
+
const ext = dot >= 0 ? name.slice(dot).toLowerCase() : "";
|
|
6975
|
+
if (!TARGET_EXTS.has(ext))
|
|
6976
|
+
continue;
|
|
6977
|
+
out.push(full);
|
|
6978
|
+
}
|
|
6979
|
+
}
|
|
6980
|
+
};
|
|
6981
|
+
walk(root);
|
|
6982
|
+
return out;
|
|
6983
|
+
}
|
|
6984
|
+
function findFunctionBounds(text, funcStart) {
|
|
6985
|
+
const brace = text.indexOf("{", funcStart);
|
|
6986
|
+
if (brace === -1)
|
|
6987
|
+
return null;
|
|
6988
|
+
let depth = 0;
|
|
6989
|
+
for (let i = brace;i < text.length; i++) {
|
|
6990
|
+
const c = text[i];
|
|
6991
|
+
if (c === "{")
|
|
6992
|
+
depth++;
|
|
6993
|
+
else if (c === "}") {
|
|
6994
|
+
depth--;
|
|
6995
|
+
if (depth === 0)
|
|
6996
|
+
return [funcStart, i + 1];
|
|
6997
|
+
}
|
|
6998
|
+
}
|
|
6999
|
+
return null;
|
|
7000
|
+
}
|
|
7001
|
+
function scanFile(path) {
|
|
7002
|
+
let text;
|
|
7003
|
+
try {
|
|
7004
|
+
text = readFileSync(path, "utf8");
|
|
7005
|
+
} catch {
|
|
7006
|
+
return { file: path, endpoints: [], sinks: [], auth_gates_present: [], error: "unreadable" };
|
|
7007
|
+
}
|
|
7008
|
+
const endpoints = [];
|
|
7009
|
+
for (const hook of NOPRIV_HOOKS) {
|
|
7010
|
+
const re = new RegExp(escapeRegExp(hook), "g");
|
|
7011
|
+
let m;
|
|
7012
|
+
while ((m = re.exec(text)) !== null) {
|
|
7013
|
+
endpoints.push({ hook, line: lineno(text, m.index) });
|
|
7014
|
+
}
|
|
7015
|
+
}
|
|
7016
|
+
const sinks = [];
|
|
7017
|
+
for (const [sink, bugclass] of Object.entries(SINKS)) {
|
|
7018
|
+
const re = new RegExp(escapeRegExp(sink), "g");
|
|
7019
|
+
let m;
|
|
7020
|
+
while ((m = re.exec(text)) !== null) {
|
|
7021
|
+
sinks.push({ sink, class: bugclass, line: lineno(text, m.index) });
|
|
7022
|
+
}
|
|
7023
|
+
}
|
|
7024
|
+
const gates = AUTH_GATES.filter((g) => text.includes(g));
|
|
7025
|
+
const lines = text.split(`
|
|
7026
|
+
`).length;
|
|
7027
|
+
return { file: path, lines, endpoints, sinks, auth_gates_present: gates };
|
|
7028
|
+
}
|
|
7029
|
+
function traceFunction(path, symbol) {
|
|
7030
|
+
let text;
|
|
7031
|
+
try {
|
|
7032
|
+
text = readFileSync(path, "utf8");
|
|
7033
|
+
} catch {
|
|
7034
|
+
return { file: path, symbol, definitions: [], definition_count: 0, error: "unreadable" };
|
|
7035
|
+
}
|
|
7036
|
+
const pattern = new RegExp(`function\\s+&?${escapeRegExp(symbol)}\\s*\\(`, "g");
|
|
7037
|
+
const results = [];
|
|
7038
|
+
let m;
|
|
7039
|
+
while ((m = pattern.exec(text)) !== null) {
|
|
7040
|
+
const bounds = findFunctionBounds(text, m.index);
|
|
7041
|
+
if (!bounds)
|
|
7042
|
+
continue;
|
|
7043
|
+
const [start, end] = bounds;
|
|
7044
|
+
const body = text.slice(start, end);
|
|
7045
|
+
const bodyStartLine = lineno(text, start);
|
|
7046
|
+
const bodyEndLine = lineno(text, end);
|
|
7047
|
+
const innerSinks = [];
|
|
7048
|
+
for (const [sink, bugclass] of Object.entries(SINKS)) {
|
|
7049
|
+
const re = new RegExp(escapeRegExp(sink), "g");
|
|
7050
|
+
let sm;
|
|
7051
|
+
while ((sm = re.exec(body)) !== null) {
|
|
7052
|
+
innerSinks.push({
|
|
7053
|
+
sink,
|
|
7054
|
+
class: bugclass,
|
|
7055
|
+
line: bodyStartLine + body.slice(0, sm.index).split(`
|
|
7056
|
+
`).length - 1
|
|
7057
|
+
});
|
|
7058
|
+
}
|
|
7059
|
+
}
|
|
7060
|
+
const innerGates = AUTH_GATES.filter((g) => body.includes(g));
|
|
7061
|
+
results.push({
|
|
7062
|
+
line: bodyStartLine,
|
|
7063
|
+
line_end: bodyEndLine,
|
|
7064
|
+
length: end - start,
|
|
7065
|
+
sinks_in_scope: innerSinks,
|
|
7066
|
+
auth_gates_in_scope: innerGates,
|
|
7067
|
+
body_preview: body.slice(0, 4000)
|
|
7068
|
+
});
|
|
7069
|
+
}
|
|
7070
|
+
return {
|
|
7071
|
+
file: path,
|
|
7072
|
+
symbol,
|
|
7073
|
+
definitions: results,
|
|
7074
|
+
definition_count: results.length
|
|
7075
|
+
};
|
|
7076
|
+
}
|
|
7077
|
+
function grepInFunctions(root, sink, maxHits = 50) {
|
|
7078
|
+
const hits = [];
|
|
7079
|
+
for (const p of iterSourceFiles(root)) {
|
|
7080
|
+
if (hits.length >= maxHits)
|
|
7081
|
+
break;
|
|
7082
|
+
let text;
|
|
7083
|
+
try {
|
|
7084
|
+
text = readFileSync(p, "utf8");
|
|
7085
|
+
} catch {
|
|
7086
|
+
continue;
|
|
7087
|
+
}
|
|
7088
|
+
const re = new RegExp(escapeRegExp(sink), "g");
|
|
7089
|
+
let m;
|
|
7090
|
+
while ((m = re.exec(text)) !== null) {
|
|
7091
|
+
const line = lineno(text, m.index);
|
|
7092
|
+
const head = text.slice(0, m.index);
|
|
7093
|
+
const funcRe = /function\s+&?\w+\s*\(/g;
|
|
7094
|
+
const funcs = [];
|
|
7095
|
+
let fm;
|
|
7096
|
+
while ((fm = funcRe.exec(head)) !== null)
|
|
7097
|
+
funcs.push(fm);
|
|
7098
|
+
if (funcs.length === 0)
|
|
7099
|
+
continue;
|
|
7100
|
+
const fstart = funcs[funcs.length - 1].index;
|
|
7101
|
+
const bounds = findFunctionBounds(text, fstart);
|
|
7102
|
+
if (!bounds || !(bounds[0] <= m.index && m.index < bounds[1]))
|
|
7103
|
+
continue;
|
|
7104
|
+
const body = text.slice(bounds[0], bounds[1]);
|
|
7105
|
+
const gates = AUTH_GATES.filter((g) => body.includes(g));
|
|
7106
|
+
hits.push({
|
|
7107
|
+
file: p,
|
|
7108
|
+
line,
|
|
7109
|
+
sink,
|
|
7110
|
+
auth_gates_in_scope: gates,
|
|
7111
|
+
guarded: gates.length > 0
|
|
7112
|
+
});
|
|
7113
|
+
if (hits.length >= maxHits)
|
|
7114
|
+
break;
|
|
7115
|
+
}
|
|
7116
|
+
}
|
|
7117
|
+
return hits;
|
|
7118
|
+
}
|
|
7119
|
+
var NOPRIV_HOOKS, SINKS, AUTH_GATES, TARGET_EXTS, SKIP_PARTS, LIB_PARTS;
|
|
7120
|
+
var init_scanner = __esm(() => {
|
|
7121
|
+
NOPRIV_HOOKS = [
|
|
7122
|
+
"wp_ajax_nopriv_",
|
|
7123
|
+
"admin_post_nopriv_",
|
|
7124
|
+
"register_rest_route",
|
|
7125
|
+
"wp_ajax_"
|
|
7126
|
+
];
|
|
7127
|
+
SINKS = {
|
|
7128
|
+
"eval(": "RCE (code execution)",
|
|
7129
|
+
"assert(": "RCE (code execution, PHP <8)",
|
|
7130
|
+
"system(": "RCE (command execution)",
|
|
7131
|
+
"exec(": "RCE (command execution)",
|
|
7132
|
+
"shell_exec(": "RCE (command execution)",
|
|
7133
|
+
"passthru(": "RCE (command execution)",
|
|
7134
|
+
"proc_open(": "RCE (command execution)",
|
|
7135
|
+
"popen(": "RCE (command execution)",
|
|
7136
|
+
"move_uploaded_file(": "File upload (-> RCE if .php lands in webroot)",
|
|
7137
|
+
"file_put_contents(": "Arbitrary file write (-> RCE via CF-003)",
|
|
7138
|
+
"fwrite(": "Arbitrary file write",
|
|
7139
|
+
"unserialize(": "PHP object injection (POP gadget chain)",
|
|
7140
|
+
"maybe_unserialize(": "PHP object injection (weak)",
|
|
7141
|
+
"include(": "Local file inclusion",
|
|
7142
|
+
"require(": "Local file inclusion",
|
|
7143
|
+
"include_once(": "Local file inclusion",
|
|
7144
|
+
"require_once(": "Local file inclusion",
|
|
7145
|
+
"$wpdb->query(": "SQL injection (unprepared query)",
|
|
7146
|
+
"$wpdb->get_var(": "SQL injection (unprepared query)",
|
|
7147
|
+
"$wpdb->get_results(": "SQL injection (unprepared query)",
|
|
7148
|
+
"->query(": "SQL injection (query builder)",
|
|
7149
|
+
"->whereRaw(": "SQL injection (raw where)",
|
|
7150
|
+
"wp_remote_get(": "SSRF (unvalidated URL fetch)",
|
|
7151
|
+
"wp_remote_post(": "SSRF (unvalidated URL fetch)",
|
|
7152
|
+
"file_get_contents(": "SSRF / file read",
|
|
7153
|
+
"extract(": "Variable injection (-> LFI/RCE without EXTR_SKIP)",
|
|
7154
|
+
"call_user_func(": "Dynamic dispatch (attacker-controlled callback)",
|
|
7155
|
+
"call_user_func_array(": "Dynamic dispatch",
|
|
7156
|
+
"create_function(": "RCE (deprecated eval wrapper)",
|
|
7157
|
+
"preg_replace(": "RCE (if /e modifier or code in pattern)",
|
|
7158
|
+
"echo ": "XSS (reflected output)",
|
|
7159
|
+
"print ": "XSS (reflected output)",
|
|
7160
|
+
"printf(": "XSS (reflected output)",
|
|
7161
|
+
"header(": "Open redirect / header injection",
|
|
7162
|
+
"wp_redirect(": "Open redirect"
|
|
7163
|
+
};
|
|
7164
|
+
AUTH_GATES = [
|
|
7165
|
+
"check_ajax_referer",
|
|
7166
|
+
"check_admin_referer",
|
|
7167
|
+
"wp_verify_nonce",
|
|
7168
|
+
"current_user_can",
|
|
7169
|
+
"is_user_logged_in",
|
|
7170
|
+
"JSession::checkToken",
|
|
7171
|
+
"->authorise(",
|
|
7172
|
+
"->authorize(",
|
|
7173
|
+
"permission_callback"
|
|
7174
|
+
];
|
|
7175
|
+
TARGET_EXTS = new Set([
|
|
7176
|
+
".php",
|
|
7177
|
+
".phtml",
|
|
7178
|
+
".php5",
|
|
7179
|
+
".php7",
|
|
7180
|
+
".inc",
|
|
7181
|
+
".module",
|
|
7182
|
+
".install"
|
|
7183
|
+
]);
|
|
7184
|
+
SKIP_PARTS = new Set(["vendor", "node_modules", ".git", "tests", "test"]);
|
|
7185
|
+
LIB_PARTS = new Set([
|
|
7186
|
+
"lib",
|
|
7187
|
+
"libraries",
|
|
7188
|
+
"third-party",
|
|
7189
|
+
"third_party",
|
|
7190
|
+
"libs"
|
|
7191
|
+
]);
|
|
7192
|
+
});
|
|
7193
|
+
|
|
6933
7194
|
// src/sync.ts
|
|
6934
|
-
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readFileSync as
|
|
7195
|
+
import { mkdirSync as mkdirSync3, writeFileSync as writeFileSync2, readFileSync as readFileSync9, readdirSync as readdirSync4, statSync as statSync3, rmSync } from "node:fs";
|
|
6935
7196
|
import { join as join8 } from "node:path";
|
|
6936
7197
|
import { homedir as homedir4 } from "node:os";
|
|
6937
7198
|
import { fileURLToPath as fileURLToPath6 } from "node:url";
|
|
@@ -7027,7 +7288,7 @@ function manualCopy(src, dst) {
|
|
|
7027
7288
|
if (e.isDirectory())
|
|
7028
7289
|
manualCopy(s, d);
|
|
7029
7290
|
else
|
|
7030
|
-
writeFileSync2(d,
|
|
7291
|
+
writeFileSync2(d, readFileSync9(s));
|
|
7031
7292
|
}
|
|
7032
7293
|
}
|
|
7033
7294
|
var ROOT5, DATA_ROOT2, GITHUB_REPO = "https://github.com/shinthink/blitzstrike.git";
|
|
@@ -21669,258 +21930,8 @@ class StdioServerTransport {
|
|
|
21669
21930
|
}
|
|
21670
21931
|
}
|
|
21671
21932
|
|
|
21672
|
-
// src/
|
|
21673
|
-
|
|
21674
|
-
import { join } from "node:path";
|
|
21675
|
-
var NOPRIV_HOOKS = [
|
|
21676
|
-
"wp_ajax_nopriv_",
|
|
21677
|
-
"admin_post_nopriv_",
|
|
21678
|
-
"register_rest_route",
|
|
21679
|
-
"wp_ajax_"
|
|
21680
|
-
];
|
|
21681
|
-
var SINKS = {
|
|
21682
|
-
"eval(": "RCE (code execution)",
|
|
21683
|
-
"assert(": "RCE (code execution, PHP <8)",
|
|
21684
|
-
"system(": "RCE (command execution)",
|
|
21685
|
-
"exec(": "RCE (command execution)",
|
|
21686
|
-
"shell_exec(": "RCE (command execution)",
|
|
21687
|
-
"passthru(": "RCE (command execution)",
|
|
21688
|
-
"proc_open(": "RCE (command execution)",
|
|
21689
|
-
"popen(": "RCE (command execution)",
|
|
21690
|
-
"move_uploaded_file(": "File upload (-> RCE if .php lands in webroot)",
|
|
21691
|
-
"file_put_contents(": "Arbitrary file write (-> RCE via CF-003)",
|
|
21692
|
-
"fwrite(": "Arbitrary file write",
|
|
21693
|
-
"unserialize(": "PHP object injection (POP gadget chain)",
|
|
21694
|
-
"maybe_unserialize(": "PHP object injection (weak)",
|
|
21695
|
-
"include(": "Local file inclusion",
|
|
21696
|
-
"require(": "Local file inclusion",
|
|
21697
|
-
"include_once(": "Local file inclusion",
|
|
21698
|
-
"require_once(": "Local file inclusion",
|
|
21699
|
-
"$wpdb->query(": "SQL injection (unprepared query)",
|
|
21700
|
-
"$wpdb->get_var(": "SQL injection (unprepared query)",
|
|
21701
|
-
"$wpdb->get_results(": "SQL injection (unprepared query)",
|
|
21702
|
-
"->query(": "SQL injection (query builder)",
|
|
21703
|
-
"->whereRaw(": "SQL injection (raw where)",
|
|
21704
|
-
"wp_remote_get(": "SSRF (unvalidated URL fetch)",
|
|
21705
|
-
"wp_remote_post(": "SSRF (unvalidated URL fetch)",
|
|
21706
|
-
"file_get_contents(": "SSRF / file read",
|
|
21707
|
-
"extract(": "Variable injection (-> LFI/RCE without EXTR_SKIP)",
|
|
21708
|
-
"call_user_func(": "Dynamic dispatch (attacker-controlled callback)",
|
|
21709
|
-
"call_user_func_array(": "Dynamic dispatch",
|
|
21710
|
-
"create_function(": "RCE (deprecated eval wrapper)",
|
|
21711
|
-
"preg_replace(": "RCE (if /e modifier or code in pattern)"
|
|
21712
|
-
};
|
|
21713
|
-
var AUTH_GATES = [
|
|
21714
|
-
"check_ajax_referer",
|
|
21715
|
-
"check_admin_referer",
|
|
21716
|
-
"wp_verify_nonce",
|
|
21717
|
-
"current_user_can",
|
|
21718
|
-
"is_user_logged_in",
|
|
21719
|
-
"JSession::checkToken",
|
|
21720
|
-
"->authorise(",
|
|
21721
|
-
"->authorize(",
|
|
21722
|
-
"permission_callback"
|
|
21723
|
-
];
|
|
21724
|
-
var TARGET_EXTS = new Set([
|
|
21725
|
-
".php",
|
|
21726
|
-
".phtml",
|
|
21727
|
-
".php5",
|
|
21728
|
-
".php7",
|
|
21729
|
-
".inc",
|
|
21730
|
-
".module",
|
|
21731
|
-
".install"
|
|
21732
|
-
]);
|
|
21733
|
-
var SKIP_PARTS = new Set(["vendor", "node_modules", ".git", "tests", "test"]);
|
|
21734
|
-
var LIB_PARTS = new Set([
|
|
21735
|
-
"lib",
|
|
21736
|
-
"libraries",
|
|
21737
|
-
"third-party",
|
|
21738
|
-
"third_party",
|
|
21739
|
-
"libs"
|
|
21740
|
-
]);
|
|
21741
|
-
function escapeRegExp(s) {
|
|
21742
|
-
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
21743
|
-
}
|
|
21744
|
-
function lineno(text, pos) {
|
|
21745
|
-
let n = 1;
|
|
21746
|
-
for (let i = 0;i < pos; i++) {
|
|
21747
|
-
if (text.charCodeAt(i) === 10)
|
|
21748
|
-
n++;
|
|
21749
|
-
}
|
|
21750
|
-
return n;
|
|
21751
|
-
}
|
|
21752
|
-
function iterSourceFiles(root, maxFiles = 5000) {
|
|
21753
|
-
const out = [];
|
|
21754
|
-
const walk = (dir) => {
|
|
21755
|
-
if (out.length >= maxFiles)
|
|
21756
|
-
return;
|
|
21757
|
-
let entries;
|
|
21758
|
-
try {
|
|
21759
|
-
entries = readdirSync(dir);
|
|
21760
|
-
} catch {
|
|
21761
|
-
return;
|
|
21762
|
-
}
|
|
21763
|
-
for (const name of entries) {
|
|
21764
|
-
if (out.length >= maxFiles)
|
|
21765
|
-
return;
|
|
21766
|
-
const full = join(dir, name);
|
|
21767
|
-
let st;
|
|
21768
|
-
try {
|
|
21769
|
-
st = statSync(full);
|
|
21770
|
-
} catch {
|
|
21771
|
-
continue;
|
|
21772
|
-
}
|
|
21773
|
-
if (st.isDirectory()) {
|
|
21774
|
-
if (SKIP_PARTS.has(name) || LIB_PARTS.has(name))
|
|
21775
|
-
continue;
|
|
21776
|
-
walk(full);
|
|
21777
|
-
} else if (st.isFile()) {
|
|
21778
|
-
const dot = name.lastIndexOf(".");
|
|
21779
|
-
const ext = dot >= 0 ? name.slice(dot).toLowerCase() : "";
|
|
21780
|
-
if (!TARGET_EXTS.has(ext))
|
|
21781
|
-
continue;
|
|
21782
|
-
out.push(full);
|
|
21783
|
-
}
|
|
21784
|
-
}
|
|
21785
|
-
};
|
|
21786
|
-
walk(root);
|
|
21787
|
-
return out;
|
|
21788
|
-
}
|
|
21789
|
-
function findFunctionBounds(text, funcStart) {
|
|
21790
|
-
const brace = text.indexOf("{", funcStart);
|
|
21791
|
-
if (brace === -1)
|
|
21792
|
-
return null;
|
|
21793
|
-
let depth = 0;
|
|
21794
|
-
for (let i = brace;i < text.length; i++) {
|
|
21795
|
-
const c = text[i];
|
|
21796
|
-
if (c === "{")
|
|
21797
|
-
depth++;
|
|
21798
|
-
else if (c === "}") {
|
|
21799
|
-
depth--;
|
|
21800
|
-
if (depth === 0)
|
|
21801
|
-
return [funcStart, i + 1];
|
|
21802
|
-
}
|
|
21803
|
-
}
|
|
21804
|
-
return null;
|
|
21805
|
-
}
|
|
21806
|
-
function scanFile(path) {
|
|
21807
|
-
let text;
|
|
21808
|
-
try {
|
|
21809
|
-
text = readFileSync(path, "utf8");
|
|
21810
|
-
} catch {
|
|
21811
|
-
return { file: path, endpoints: [], sinks: [], auth_gates_present: [], error: "unreadable" };
|
|
21812
|
-
}
|
|
21813
|
-
const endpoints = [];
|
|
21814
|
-
for (const hook of NOPRIV_HOOKS) {
|
|
21815
|
-
const re = new RegExp(escapeRegExp(hook), "g");
|
|
21816
|
-
let m;
|
|
21817
|
-
while ((m = re.exec(text)) !== null) {
|
|
21818
|
-
endpoints.push({ hook, line: lineno(text, m.index) });
|
|
21819
|
-
}
|
|
21820
|
-
}
|
|
21821
|
-
const sinks = [];
|
|
21822
|
-
for (const [sink, bugclass] of Object.entries(SINKS)) {
|
|
21823
|
-
const re = new RegExp(escapeRegExp(sink), "g");
|
|
21824
|
-
let m;
|
|
21825
|
-
while ((m = re.exec(text)) !== null) {
|
|
21826
|
-
sinks.push({ sink, class: bugclass, line: lineno(text, m.index) });
|
|
21827
|
-
}
|
|
21828
|
-
}
|
|
21829
|
-
const gates = AUTH_GATES.filter((g) => text.includes(g));
|
|
21830
|
-
const lines = text.split(`
|
|
21831
|
-
`).length;
|
|
21832
|
-
return { file: path, lines, endpoints, sinks, auth_gates_present: gates };
|
|
21833
|
-
}
|
|
21834
|
-
function traceFunction(path, symbol) {
|
|
21835
|
-
let text;
|
|
21836
|
-
try {
|
|
21837
|
-
text = readFileSync(path, "utf8");
|
|
21838
|
-
} catch {
|
|
21839
|
-
return { file: path, symbol, definitions: [], definition_count: 0, error: "unreadable" };
|
|
21840
|
-
}
|
|
21841
|
-
const pattern = new RegExp(`function\\s+&?${escapeRegExp(symbol)}\\s*\\(`, "g");
|
|
21842
|
-
const results = [];
|
|
21843
|
-
let m;
|
|
21844
|
-
while ((m = pattern.exec(text)) !== null) {
|
|
21845
|
-
const bounds = findFunctionBounds(text, m.index);
|
|
21846
|
-
if (!bounds)
|
|
21847
|
-
continue;
|
|
21848
|
-
const [start, end] = bounds;
|
|
21849
|
-
const body = text.slice(start, end);
|
|
21850
|
-
const bodyStartLine = lineno(text, start);
|
|
21851
|
-
const bodyEndLine = lineno(text, end);
|
|
21852
|
-
const innerSinks = [];
|
|
21853
|
-
for (const [sink, bugclass] of Object.entries(SINKS)) {
|
|
21854
|
-
const re = new RegExp(escapeRegExp(sink), "g");
|
|
21855
|
-
let sm;
|
|
21856
|
-
while ((sm = re.exec(body)) !== null) {
|
|
21857
|
-
innerSinks.push({
|
|
21858
|
-
sink,
|
|
21859
|
-
class: bugclass,
|
|
21860
|
-
line: bodyStartLine + body.slice(0, sm.index).split(`
|
|
21861
|
-
`).length - 1
|
|
21862
|
-
});
|
|
21863
|
-
}
|
|
21864
|
-
}
|
|
21865
|
-
const innerGates = AUTH_GATES.filter((g) => body.includes(g));
|
|
21866
|
-
results.push({
|
|
21867
|
-
line: bodyStartLine,
|
|
21868
|
-
line_end: bodyEndLine,
|
|
21869
|
-
length: end - start,
|
|
21870
|
-
sinks_in_scope: innerSinks,
|
|
21871
|
-
auth_gates_in_scope: innerGates,
|
|
21872
|
-
body_preview: body.slice(0, 4000)
|
|
21873
|
-
});
|
|
21874
|
-
}
|
|
21875
|
-
return {
|
|
21876
|
-
file: path,
|
|
21877
|
-
symbol,
|
|
21878
|
-
definitions: results,
|
|
21879
|
-
definition_count: results.length
|
|
21880
|
-
};
|
|
21881
|
-
}
|
|
21882
|
-
function grepInFunctions(root, sink, maxHits = 50) {
|
|
21883
|
-
const hits = [];
|
|
21884
|
-
for (const p of iterSourceFiles(root)) {
|
|
21885
|
-
if (hits.length >= maxHits)
|
|
21886
|
-
break;
|
|
21887
|
-
let text;
|
|
21888
|
-
try {
|
|
21889
|
-
text = readFileSync(p, "utf8");
|
|
21890
|
-
} catch {
|
|
21891
|
-
continue;
|
|
21892
|
-
}
|
|
21893
|
-
const re = new RegExp(escapeRegExp(sink), "g");
|
|
21894
|
-
let m;
|
|
21895
|
-
while ((m = re.exec(text)) !== null) {
|
|
21896
|
-
const line = lineno(text, m.index);
|
|
21897
|
-
const head = text.slice(0, m.index);
|
|
21898
|
-
const funcRe = /function\s+&?\w+\s*\(/g;
|
|
21899
|
-
const funcs = [];
|
|
21900
|
-
let fm;
|
|
21901
|
-
while ((fm = funcRe.exec(head)) !== null)
|
|
21902
|
-
funcs.push(fm);
|
|
21903
|
-
if (funcs.length === 0)
|
|
21904
|
-
continue;
|
|
21905
|
-
const fstart = funcs[funcs.length - 1].index;
|
|
21906
|
-
const bounds = findFunctionBounds(text, fstart);
|
|
21907
|
-
if (!bounds || !(bounds[0] <= m.index && m.index < bounds[1]))
|
|
21908
|
-
continue;
|
|
21909
|
-
const body = text.slice(bounds[0], bounds[1]);
|
|
21910
|
-
const gates = AUTH_GATES.filter((g) => body.includes(g));
|
|
21911
|
-
hits.push({
|
|
21912
|
-
file: p,
|
|
21913
|
-
line,
|
|
21914
|
-
sink,
|
|
21915
|
-
auth_gates_in_scope: gates,
|
|
21916
|
-
guarded: gates.length > 0
|
|
21917
|
-
});
|
|
21918
|
-
if (hits.length >= maxHits)
|
|
21919
|
-
break;
|
|
21920
|
-
}
|
|
21921
|
-
}
|
|
21922
|
-
return hits;
|
|
21923
|
-
}
|
|
21933
|
+
// src/server.ts
|
|
21934
|
+
init_scanner();
|
|
21924
21935
|
|
|
21925
21936
|
// src/recon.ts
|
|
21926
21937
|
var TIMEOUT_MS = 25000;
|
|
@@ -22002,6 +22013,7 @@ async function nvdLookup(cveId) {
|
|
|
22002
22013
|
}
|
|
22003
22014
|
|
|
22004
22015
|
// src/orchestrator.ts
|
|
22016
|
+
init_scanner();
|
|
22005
22017
|
import { readFileSync as readFileSync4, existsSync as existsSync3, statSync as statSync2 } from "node:fs";
|
|
22006
22018
|
import { join as join4 } from "node:path";
|
|
22007
22019
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
@@ -22294,7 +22306,7 @@ var LIFECYCLE_TRANSITIONS = {
|
|
|
22294
22306
|
detected: ["triaged", "rejected"],
|
|
22295
22307
|
triaged: ["hypothesis", "rejected", "out_of_scope"],
|
|
22296
22308
|
hypothesis: ["validating", "false_positive", "rejected", "blocked"],
|
|
22297
|
-
validating: ["confirmed", "
|
|
22309
|
+
validating: ["confirmed", "false_positive", "blocked", "out_of_scope"],
|
|
22298
22310
|
confirmed: [],
|
|
22299
22311
|
false_positive: [],
|
|
22300
22312
|
rejected: [],
|
|
@@ -23146,6 +23158,166 @@ async function activeScan(target, scope = "", mode = "bug-bounty", allowed = und
|
|
|
23146
23158
|
return result;
|
|
23147
23159
|
}
|
|
23148
23160
|
|
|
23161
|
+
// src/dataflow.ts
|
|
23162
|
+
init_scanner();
|
|
23163
|
+
import { readFileSync as readFileSync7 } from "node:fs";
|
|
23164
|
+
var SOURCES = {
|
|
23165
|
+
$_GET: { id: "http_get", label: "HTTP GET parameter", attacker_controlled: true },
|
|
23166
|
+
$_POST: { id: "http_post", label: "HTTP POST body", attacker_controlled: true },
|
|
23167
|
+
$_REQUEST: { id: "http_request", label: "HTTP request (merged)", attacker_controlled: true },
|
|
23168
|
+
$_COOKIE: { id: "http_cookie", label: "HTTP cookie", attacker_controlled: true },
|
|
23169
|
+
$_FILES: { id: "uploaded_file", label: "Uploaded file", attacker_controlled: true },
|
|
23170
|
+
$_SERVER: { id: "http_header", label: "HTTP header / server env", attacker_controlled: true },
|
|
23171
|
+
"file_get_contents('php://input')": { id: "raw_body", label: "Raw request body", attacker_controlled: true },
|
|
23172
|
+
"php://input": { id: "raw_body", label: "Raw request body", attacker_controlled: true },
|
|
23173
|
+
json_decode: { id: "json_input", label: "JSON input", attacker_controlled: true },
|
|
23174
|
+
getallheaders: { id: "http_header", label: "HTTP header", attacker_controlled: true },
|
|
23175
|
+
$argv: { id: "cli_argument", label: "CLI argument", attacker_controlled: false },
|
|
23176
|
+
getenv: { id: "environment_variable", label: "Environment variable", attacker_controlled: false },
|
|
23177
|
+
"$wpdb->get_results": { id: "database_value", label: "Database value", attacker_controlled: false },
|
|
23178
|
+
wp_remote_get: { id: "external_api", label: "External API data", attacker_controlled: false }
|
|
23179
|
+
};
|
|
23180
|
+
function classifySource(line) {
|
|
23181
|
+
for (const [token, cls] of Object.entries(SOURCES)) {
|
|
23182
|
+
if (line.includes(token))
|
|
23183
|
+
return cls;
|
|
23184
|
+
}
|
|
23185
|
+
return null;
|
|
23186
|
+
}
|
|
23187
|
+
var SINK_CLASS_MAP = [
|
|
23188
|
+
[/->query\(|\$wpdb->query|\$wpdb->get_var|\$wpdb->get_results|->whereRaw|->selectRaw/, { id: "sql_execution", category: "SQL execution", cwe: "CWE-89" }],
|
|
23189
|
+
[/eval\(|assert\(|create_function\(|call_user_func\(|preg_replace\(/, { id: "code_execution", category: "Dynamic evaluation / code execution", cwe: "CWE-94" }],
|
|
23190
|
+
[/system\(|exec\(|shell_exec\(|passthru\(|proc_open\(|popen\(/, { id: "command_execution", category: "Command execution", cwe: "CWE-78" }],
|
|
23191
|
+
[/move_uploaded_file\(|file_put_contents\(|fwrite\(|fopen\(|unlink\(/, { id: "file_operations", category: "File operations", cwe: "CWE-434" }],
|
|
23192
|
+
[/include\(|require\(|include_once\(|require_once\(/, { id: "file_inclusion", category: "File inclusion", cwe: "CWE-98" }],
|
|
23193
|
+
[/unserialize\(|maybe_unserialize\(/, { id: "deserialization", category: "Deserialization", cwe: "CWE-502" }],
|
|
23194
|
+
[/wp_remote_get\(|wp_remote_post\(|file_get_contents\(|curl_exec\(/, { id: "http_request", category: "HTTP request (SSRF)", cwe: "CWE-918" }],
|
|
23195
|
+
[/header\(|wp_redirect\(|wp_safe_redirect\(/, { id: "redirect", category: "Redirect handling", cwe: "CWE-601" }],
|
|
23196
|
+
[/echo\s|print\s|printf\(/, { id: "html_render", category: "HTML rendering (XSS)", cwe: "CWE-79" }],
|
|
23197
|
+
[/simplexml_load_string\(|new SimpleXMLElement|DOMDocument/, { id: "xml_processing", category: "XML processing (XXE)", cwe: "CWE-611" }],
|
|
23198
|
+
[/ZipArchive|PharData|->extractTo\(/, { id: "archive_extraction", category: "Archive extraction (zip slip)", cwe: "CWE-22" }]
|
|
23199
|
+
];
|
|
23200
|
+
function classifySink2(sink) {
|
|
23201
|
+
for (const [re, cls] of SINK_CLASS_MAP) {
|
|
23202
|
+
if (re.test(sink))
|
|
23203
|
+
return cls;
|
|
23204
|
+
}
|
|
23205
|
+
return null;
|
|
23206
|
+
}
|
|
23207
|
+
var SANITIZERS = {
|
|
23208
|
+
htmlspecialchars: { id: "html_escape", label: "HTML entity encoding", neutralizes: ["html_render"] },
|
|
23209
|
+
esc_html: { id: "html_escape", label: "HTML escape", neutralizes: ["html_render"] },
|
|
23210
|
+
esc_attr: { id: "html_escape", label: "HTML attribute escape", neutralizes: ["html_render"] },
|
|
23211
|
+
htmlentities: { id: "html_escape", label: "HTML entity encoding", neutralizes: ["html_render"] },
|
|
23212
|
+
esc_sql: { id: "sql_escape", label: "SQL escape", neutralizes: ["sql_execution"] },
|
|
23213
|
+
"$wpdb->prepare": { id: "sql_prepare", label: "Prepared SQL statement", neutralizes: ["sql_execution"] },
|
|
23214
|
+
"->prepare(": { id: "sql_prepare", label: "Prepared statement", neutralizes: ["sql_execution"] },
|
|
23215
|
+
filter_var: { id: "filter_var", label: "Filter input", neutralizes: ["html_render", "sql_execution", "command_execution"] },
|
|
23216
|
+
filter_input: { id: "filter_var", label: "Filter input", neutralizes: ["html_render", "sql_execution", "command_execution"] },
|
|
23217
|
+
sanitize_text_field: { id: "wp_sanitize", label: "WordPress sanitize", neutralizes: ["html_render"] },
|
|
23218
|
+
sanitize_file_name: { id: "wp_sanitize_file", label: "File-name sanitize", neutralizes: ["file_operations", "file_inclusion"] },
|
|
23219
|
+
wp_verify_nonce: { id: "nonce_check", label: "Nonce verification", neutralizes: ["authorization"] },
|
|
23220
|
+
check_ajax_referer: { id: "nonce_check", label: "Nonce verification", neutralizes: ["authorization"] },
|
|
23221
|
+
escapeshellarg: { id: "shell_escape", label: "Shell argument escape", neutralizes: ["command_execution"] },
|
|
23222
|
+
escapeshellcmd: { id: "shell_escape", label: "Shell command escape", neutralizes: ["command_execution"] },
|
|
23223
|
+
intval: { id: "int_cast", label: "Integer cast", neutralizes: ["sql_execution", "command_execution", "file_inclusion"] },
|
|
23224
|
+
absint: { id: "int_cast", label: "Absolute integer cast", neutralizes: ["sql_execution", "command_execution", "file_inclusion"] },
|
|
23225
|
+
"preg_replace.*FILTER": { id: "regex_filter", label: "Regex filter", neutralizes: ["html_render"] }
|
|
23226
|
+
};
|
|
23227
|
+
function findSanitizers(line) {
|
|
23228
|
+
const out = [];
|
|
23229
|
+
for (const [token, san] of Object.entries(SANITIZERS)) {
|
|
23230
|
+
if (line.includes(token))
|
|
23231
|
+
out.push(san);
|
|
23232
|
+
}
|
|
23233
|
+
return out;
|
|
23234
|
+
}
|
|
23235
|
+
function isSanitized(sanitizers, sinkCategory) {
|
|
23236
|
+
return sanitizers.some((s) => s.neutralizes.includes(sinkCategory) || s.neutralizes.includes("authorization"));
|
|
23237
|
+
}
|
|
23238
|
+
function traceDataFlow(path) {
|
|
23239
|
+
const scan = scanFile(path);
|
|
23240
|
+
const result = { file: path, candidates: [], sanitized: [], authorized: [] };
|
|
23241
|
+
if (scan.error)
|
|
23242
|
+
return result;
|
|
23243
|
+
let text;
|
|
23244
|
+
try {
|
|
23245
|
+
text = readFileSync7(path, "utf8");
|
|
23246
|
+
} catch {
|
|
23247
|
+
return result;
|
|
23248
|
+
}
|
|
23249
|
+
const lines = text.split(`
|
|
23250
|
+
`);
|
|
23251
|
+
const seen = new Set;
|
|
23252
|
+
const sinks = scan.sinks.filter((s) => {
|
|
23253
|
+
const cls = classifySink2(s.sink);
|
|
23254
|
+
if (!cls)
|
|
23255
|
+
return false;
|
|
23256
|
+
const key = `${cls.id}|${s.line}`;
|
|
23257
|
+
if (seen.has(key))
|
|
23258
|
+
return false;
|
|
23259
|
+
seen.add(key);
|
|
23260
|
+
return true;
|
|
23261
|
+
});
|
|
23262
|
+
for (const s of sinks) {
|
|
23263
|
+
const sinkClass = classifySink2(s.sink);
|
|
23264
|
+
const wideStart = Math.max(0, s.line - 15);
|
|
23265
|
+
const wideWindow = lines.slice(wideStart, s.line).join(`
|
|
23266
|
+
`);
|
|
23267
|
+
const sanitizerWindow = lines.slice(Math.max(0, s.line - 3), s.line + 1).join(`
|
|
23268
|
+
`);
|
|
23269
|
+
const sinkLine = lines[s.line - 1] ?? "";
|
|
23270
|
+
const prevLine = lines[s.line - 2] ?? "";
|
|
23271
|
+
const authContext = sinkLine + `
|
|
23272
|
+
` + prevLine;
|
|
23273
|
+
const source = classifySource(wideWindow);
|
|
23274
|
+
const sanitizers = findSanitizers(sanitizerWindow);
|
|
23275
|
+
const authGates = AUTH_GATES.filter((g) => authContext.includes(g));
|
|
23276
|
+
const edge = {
|
|
23277
|
+
source,
|
|
23278
|
+
sanitizers,
|
|
23279
|
+
auth_gates: authGates,
|
|
23280
|
+
sink: sinkClass,
|
|
23281
|
+
sink_line: s.line,
|
|
23282
|
+
sink_token: s.sink
|
|
23283
|
+
};
|
|
23284
|
+
if (authGates.length > 0) {
|
|
23285
|
+
result.authorized.push(edge);
|
|
23286
|
+
continue;
|
|
23287
|
+
}
|
|
23288
|
+
if (isSanitized(sanitizers, sinkClass.id)) {
|
|
23289
|
+
result.sanitized.push(edge);
|
|
23290
|
+
continue;
|
|
23291
|
+
}
|
|
23292
|
+
result.candidates.push(edge);
|
|
23293
|
+
}
|
|
23294
|
+
return result;
|
|
23295
|
+
}
|
|
23296
|
+
function groupVariants(results) {
|
|
23297
|
+
const groups = new Map;
|
|
23298
|
+
for (const r of results) {
|
|
23299
|
+
for (const c of r.candidates) {
|
|
23300
|
+
const sig = `${c.sink?.category ?? "?"}|${c.source?.id ?? "unknown"}`;
|
|
23301
|
+
const g = groups.get(sig);
|
|
23302
|
+
if (g) {
|
|
23303
|
+
g.occurrences += 1;
|
|
23304
|
+
if (!g.files.includes(r.file))
|
|
23305
|
+
g.files.push(r.file);
|
|
23306
|
+
} else {
|
|
23307
|
+
groups.set(sig, {
|
|
23308
|
+
signature: sig,
|
|
23309
|
+
root_cause: `unsanitized ${c.source?.label ?? "unknown"} reaching ${c.sink?.category ?? "sink"}`,
|
|
23310
|
+
sink_category: c.sink?.category ?? "?",
|
|
23311
|
+
cwe: c.sink?.cwe,
|
|
23312
|
+
occurrences: 1,
|
|
23313
|
+
files: [r.file]
|
|
23314
|
+
});
|
|
23315
|
+
}
|
|
23316
|
+
}
|
|
23317
|
+
}
|
|
23318
|
+
return [...groups.values()].sort((a, b) => b.occurrences - a.occurrences);
|
|
23319
|
+
}
|
|
23320
|
+
|
|
23149
23321
|
// src/server.ts
|
|
23150
23322
|
function createServer() {
|
|
23151
23323
|
const server = new McpServer({
|
|
@@ -23665,6 +23837,35 @@ function createServer() {
|
|
|
23665
23837
|
}, async ({ text }) => {
|
|
23666
23838
|
return { content: [{ type: "text", text: JSON.stringify({ redacted: redactSecrets(text) }) }] };
|
|
23667
23839
|
});
|
|
23840
|
+
server.registerTool("trace_data_flow", {
|
|
23841
|
+
title: "Trace source-to-sink data flow",
|
|
23842
|
+
description: "EAGLE-EYE: trace data flow in a file — classify sources, sinks, sanitizers, and authorization gates. Returns candidates (reachable+unsanitized), sanitized (not findings), and authorized (lower priority).",
|
|
23843
|
+
inputSchema: { path: string2().describe("Source file path") }
|
|
23844
|
+
}, async ({ path }) => {
|
|
23845
|
+
const r = traceDataFlow(path);
|
|
23846
|
+
return { content: [{ type: "text", text: JSON.stringify(r) }] };
|
|
23847
|
+
});
|
|
23848
|
+
server.registerTool("variant_analysis", {
|
|
23849
|
+
title: "Group findings by root cause",
|
|
23850
|
+
description: "EAGLE-EYE: variant analysis — group data-flow candidates by root cause (sink category + source), deduplicating identical patterns across files.",
|
|
23851
|
+
inputSchema: { path: string2().describe("Source directory path") }
|
|
23852
|
+
}, async ({ path }) => {
|
|
23853
|
+
await Promise.resolve().then(() => init_scanner());
|
|
23854
|
+
const files = iterSourceFiles(path, 5000);
|
|
23855
|
+
const results = files.map((f) => traceDataFlow(f));
|
|
23856
|
+
const groups = groupVariants(results);
|
|
23857
|
+
return {
|
|
23858
|
+
content: [{
|
|
23859
|
+
type: "text",
|
|
23860
|
+
text: JSON.stringify({
|
|
23861
|
+
files_analyzed: files.length,
|
|
23862
|
+
total_candidates: results.reduce((n, r) => n + r.candidates.length, 0),
|
|
23863
|
+
variant_groups: groups.length,
|
|
23864
|
+
groups
|
|
23865
|
+
})
|
|
23866
|
+
}]
|
|
23867
|
+
};
|
|
23868
|
+
});
|
|
23668
23869
|
return server;
|
|
23669
23870
|
}
|
|
23670
23871
|
async function serve() {
|
|
@@ -23674,7 +23875,7 @@ async function serve() {
|
|
|
23674
23875
|
}
|
|
23675
23876
|
|
|
23676
23877
|
// src/cli.ts
|
|
23677
|
-
import { readFileSync as
|
|
23878
|
+
import { readFileSync as readFileSync8, existsSync as existsSync6, writeFileSync, mkdirSync as mkdirSync2 } from "node:fs";
|
|
23678
23879
|
import { join as join7, dirname } from "node:path";
|
|
23679
23880
|
import { homedir as homedir3 } from "node:os";
|
|
23680
23881
|
import { fileURLToPath as fileURLToPath5 } from "node:url";
|
|
@@ -23756,7 +23957,7 @@ function resolveCommand() {
|
|
|
23756
23957
|
}
|
|
23757
23958
|
function readJson(p) {
|
|
23758
23959
|
try {
|
|
23759
|
-
return JSON.parse(
|
|
23960
|
+
return JSON.parse(readFileSync8(p, "utf8"));
|
|
23760
23961
|
} catch {
|
|
23761
23962
|
return null;
|
|
23762
23963
|
}
|
|
@@ -23799,7 +24000,7 @@ function copyOpenCodeAgents() {
|
|
|
23799
24000
|
const src = join7(srcDir, f);
|
|
23800
24001
|
const dst = join7(dstDir, f);
|
|
23801
24002
|
if (existsSync6(src)) {
|
|
23802
|
-
writeFileSync(dst,
|
|
24003
|
+
writeFileSync(dst, readFileSync8(src, "utf8"));
|
|
23803
24004
|
}
|
|
23804
24005
|
}
|
|
23805
24006
|
}
|
|
@@ -23808,7 +24009,7 @@ function codexWrite(p) {
|
|
|
23808
24009
|
const dir = dirname(p);
|
|
23809
24010
|
if (!existsSync6(dir))
|
|
23810
24011
|
mkdirSync2(dir, { recursive: true });
|
|
23811
|
-
let existing = existsSync6(p) ?
|
|
24012
|
+
let existing = existsSync6(p) ? readFileSync8(p, "utf8") : "";
|
|
23812
24013
|
if (!existing.trimEnd().endsWith(`
|
|
23813
24014
|
`))
|
|
23814
24015
|
existing += `
|
|
@@ -23827,7 +24028,7 @@ function hermesWrite(p) {
|
|
|
23827
24028
|
const dir = dirname(p);
|
|
23828
24029
|
if (!existsSync6(dir))
|
|
23829
24030
|
mkdirSync2(dir, { recursive: true });
|
|
23830
|
-
let existing = existsSync6(p) ?
|
|
24031
|
+
let existing = existsSync6(p) ? readFileSync8(p, "utf8") : "";
|
|
23831
24032
|
existing = existing.replace(/^ blitzstrike:\n(?: .*\n?)*/m, "");
|
|
23832
24033
|
if (!existing.trimEnd().endsWith(`
|
|
23833
24034
|
`))
|
|
@@ -23965,7 +24166,7 @@ Registered with ${ok}/${installed.length} agent(s).`);
|
|
|
23965
24166
|
}
|
|
23966
24167
|
|
|
23967
24168
|
// src/index.ts
|
|
23968
|
-
import { readFileSync as
|
|
24169
|
+
import { readFileSync as readFileSync10, existsSync as existsSync8 } from "node:fs";
|
|
23969
24170
|
import { join as join9 } from "node:path";
|
|
23970
24171
|
import { fileURLToPath as fileURLToPath7 } from "node:url";
|
|
23971
24172
|
var ROOT6 = join9(fileURLToPath7(new URL(".", import.meta.url)), "..");
|
|
@@ -23973,7 +24174,7 @@ function readVersion() {
|
|
|
23973
24174
|
try {
|
|
23974
24175
|
const p = join9(ROOT6, "package.json");
|
|
23975
24176
|
if (existsSync8(p))
|
|
23976
|
-
return JSON.parse(
|
|
24177
|
+
return JSON.parse(readFileSync10(p, "utf8")).version ?? "1.0.0";
|
|
23977
24178
|
} catch {}
|
|
23978
24179
|
return "1.0.0";
|
|
23979
24180
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.14",
|
|
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": {
|