blitzstrike 1.0.64 → 1.0.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +15 -0
- package/dist/index.js +75 -13
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ All notable changes to Blitz Strike are documented here.
|
|
|
5
5
|
The format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
7
7
|
|
|
8
|
+
## [1.0.65] — 2026-09-13
|
|
9
|
+
|
|
10
|
+
### Fixed — breadth-scanner false positives (live MCP verification)
|
|
11
|
+
|
|
12
|
+
Found via an LLM-driven verification run (agent → `blitz_scan`/`eagle_eye2`):
|
|
13
|
+
|
|
14
|
+
- **Sink tokens in comments/strings no longer reported.** `scanFile`/
|
|
15
|
+
`traceFunction`/`grepInFunctions` now match against a comment- and
|
|
16
|
+
string-literal-stripped view of the source (positions + line numbers
|
|
17
|
+
preserved). A `// use extract($_REQUEST)` comment or a
|
|
18
|
+
`"https://…/system("` string is not a sink.
|
|
19
|
+
- Rebuilt `dist/` so the fix ships in the published bundle (a long-running MCP
|
|
20
|
+
server must be restarted to pick up new `dist/` code — skills load fresh from
|
|
21
|
+
`skills/`, but compiled engine code is loaded once at startup).
|
|
22
|
+
|
|
8
23
|
## [1.0.64] — 2026-09-13
|
|
9
24
|
|
|
10
25
|
### Added — agent-native skills (LLM-driven orchestration doctrine)
|
package/dist/index.js
CHANGED
|
@@ -6944,6 +6944,65 @@ function lineno(text, pos) {
|
|
|
6944
6944
|
}
|
|
6945
6945
|
return n;
|
|
6946
6946
|
}
|
|
6947
|
+
function stripComments(text) {
|
|
6948
|
+
const out = text.split("");
|
|
6949
|
+
const n = text.length;
|
|
6950
|
+
let i = 0;
|
|
6951
|
+
while (i < n) {
|
|
6952
|
+
const c = text[i];
|
|
6953
|
+
const nc = text[i + 1];
|
|
6954
|
+
if (c === "'" || c === '"') {
|
|
6955
|
+
const q = c;
|
|
6956
|
+
out[i] = " ";
|
|
6957
|
+
i++;
|
|
6958
|
+
while (i < n && text[i] !== q) {
|
|
6959
|
+
if (text[i] === "\\") {
|
|
6960
|
+
out[i] = " ";
|
|
6961
|
+
i++;
|
|
6962
|
+
if (i < n)
|
|
6963
|
+
out[i] = " ";
|
|
6964
|
+
i++;
|
|
6965
|
+
continue;
|
|
6966
|
+
}
|
|
6967
|
+
if (text[i] !== `
|
|
6968
|
+
`)
|
|
6969
|
+
out[i] = " ";
|
|
6970
|
+
i++;
|
|
6971
|
+
}
|
|
6972
|
+
if (i < n)
|
|
6973
|
+
out[i] = " ";
|
|
6974
|
+
i++;
|
|
6975
|
+
continue;
|
|
6976
|
+
}
|
|
6977
|
+
if (c === "/" && nc === "/" || c === "#") {
|
|
6978
|
+
while (i < n && text[i] !== `
|
|
6979
|
+
`) {
|
|
6980
|
+
out[i] = " ";
|
|
6981
|
+
i++;
|
|
6982
|
+
}
|
|
6983
|
+
continue;
|
|
6984
|
+
}
|
|
6985
|
+
if (c === "/" && nc === "*") {
|
|
6986
|
+
out[i] = " ";
|
|
6987
|
+
out[i + 1] = " ";
|
|
6988
|
+
i += 2;
|
|
6989
|
+
while (i < n && !(text[i] === "*" && text[i + 1] === "/")) {
|
|
6990
|
+
if (text[i] !== `
|
|
6991
|
+
`)
|
|
6992
|
+
out[i] = " ";
|
|
6993
|
+
i++;
|
|
6994
|
+
}
|
|
6995
|
+
if (i < n) {
|
|
6996
|
+
out[i] = " ";
|
|
6997
|
+
out[i + 1] = " ";
|
|
6998
|
+
i += 2;
|
|
6999
|
+
}
|
|
7000
|
+
continue;
|
|
7001
|
+
}
|
|
7002
|
+
i++;
|
|
7003
|
+
}
|
|
7004
|
+
return out.join("");
|
|
7005
|
+
}
|
|
6947
7006
|
function iterSourceFiles(root, maxFiles = 5000) {
|
|
6948
7007
|
const out = [];
|
|
6949
7008
|
const walk = (dir) => {
|
|
@@ -7005,23 +7064,24 @@ function scanFile(path) {
|
|
|
7005
7064
|
} catch {
|
|
7006
7065
|
return { file: path, endpoints: [], sinks: [], auth_gates_present: [], error: "unreadable" };
|
|
7007
7066
|
}
|
|
7067
|
+
const code = stripComments(text);
|
|
7008
7068
|
const endpoints = [];
|
|
7009
7069
|
for (const hook of NOPRIV_HOOKS) {
|
|
7010
7070
|
const re = new RegExp(escapeRegExp(hook), "g");
|
|
7011
7071
|
let m;
|
|
7012
|
-
while ((m = re.exec(
|
|
7013
|
-
endpoints.push({ hook, line: lineno(
|
|
7072
|
+
while ((m = re.exec(code)) !== null) {
|
|
7073
|
+
endpoints.push({ hook, line: lineno(code, m.index) });
|
|
7014
7074
|
}
|
|
7015
7075
|
}
|
|
7016
7076
|
const sinks = [];
|
|
7017
7077
|
for (const [sink, bugclass] of Object.entries(SINKS)) {
|
|
7018
7078
|
const re = new RegExp(escapeRegExp(sink), "g");
|
|
7019
7079
|
let m;
|
|
7020
|
-
while ((m = re.exec(
|
|
7021
|
-
sinks.push({ sink, class: bugclass, line: lineno(
|
|
7080
|
+
while ((m = re.exec(code)) !== null) {
|
|
7081
|
+
sinks.push({ sink, class: bugclass, line: lineno(code, m.index) });
|
|
7022
7082
|
}
|
|
7023
7083
|
}
|
|
7024
|
-
const gates = AUTH_GATES.filter((g) =>
|
|
7084
|
+
const gates = AUTH_GATES.filter((g) => code.includes(g));
|
|
7025
7085
|
const lines = text.split(`
|
|
7026
7086
|
`).length;
|
|
7027
7087
|
return { file: path, lines, endpoints, sinks, auth_gates_present: gates };
|
|
@@ -7042,22 +7102,23 @@ function traceFunction(path, symbol) {
|
|
|
7042
7102
|
continue;
|
|
7043
7103
|
const [start, end] = bounds;
|
|
7044
7104
|
const body = text.slice(start, end);
|
|
7105
|
+
const bodyCode = stripComments(body);
|
|
7045
7106
|
const bodyStartLine = lineno(text, start);
|
|
7046
7107
|
const bodyEndLine = lineno(text, end);
|
|
7047
7108
|
const innerSinks = [];
|
|
7048
7109
|
for (const [sink, bugclass] of Object.entries(SINKS)) {
|
|
7049
7110
|
const re = new RegExp(escapeRegExp(sink), "g");
|
|
7050
7111
|
let sm;
|
|
7051
|
-
while ((sm = re.exec(
|
|
7112
|
+
while ((sm = re.exec(bodyCode)) !== null) {
|
|
7052
7113
|
innerSinks.push({
|
|
7053
7114
|
sink,
|
|
7054
7115
|
class: bugclass,
|
|
7055
|
-
line: bodyStartLine +
|
|
7116
|
+
line: bodyStartLine + bodyCode.slice(0, sm.index).split(`
|
|
7056
7117
|
`).length - 1
|
|
7057
7118
|
});
|
|
7058
7119
|
}
|
|
7059
7120
|
}
|
|
7060
|
-
const innerGates = AUTH_GATES.filter((g) =>
|
|
7121
|
+
const innerGates = AUTH_GATES.filter((g) => bodyCode.includes(g));
|
|
7061
7122
|
results.push({
|
|
7062
7123
|
line: bodyStartLine,
|
|
7063
7124
|
line_end: bodyEndLine,
|
|
@@ -7085,11 +7146,12 @@ function grepInFunctions(root, sink, maxHits = 50) {
|
|
|
7085
7146
|
} catch {
|
|
7086
7147
|
continue;
|
|
7087
7148
|
}
|
|
7149
|
+
const code = stripComments(text);
|
|
7088
7150
|
const re = new RegExp(escapeRegExp(sink), "g");
|
|
7089
7151
|
let m;
|
|
7090
|
-
while ((m = re.exec(
|
|
7091
|
-
const line = lineno(
|
|
7092
|
-
const head =
|
|
7152
|
+
while ((m = re.exec(code)) !== null) {
|
|
7153
|
+
const line = lineno(code, m.index);
|
|
7154
|
+
const head = code.slice(0, m.index);
|
|
7093
7155
|
const funcRe = /function\s+&?\w+\s*\(/g;
|
|
7094
7156
|
const funcs = [];
|
|
7095
7157
|
let fm;
|
|
@@ -7098,10 +7160,10 @@ function grepInFunctions(root, sink, maxHits = 50) {
|
|
|
7098
7160
|
if (funcs.length === 0)
|
|
7099
7161
|
continue;
|
|
7100
7162
|
const fstart = funcs[funcs.length - 1].index;
|
|
7101
|
-
const bounds = findFunctionBounds(
|
|
7163
|
+
const bounds = findFunctionBounds(code, fstart);
|
|
7102
7164
|
if (!bounds || !(bounds[0] <= m.index && m.index < bounds[1]))
|
|
7103
7165
|
continue;
|
|
7104
|
-
const body =
|
|
7166
|
+
const body = code.slice(bounds[0], bounds[1]);
|
|
7105
7167
|
const gates = AUTH_GATES.filter((g) => body.includes(g));
|
|
7106
7168
|
hits.push({
|
|
7107
7169
|
file: p,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "blitzstrike",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.65",
|
|
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": {
|