@cybedefend/vibedefend 1.6.0 → 1.8.0
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/README.md +24 -13
- package/dist/clients/antigravity.js +327 -0
- package/dist/clients/antigravity.js.map +1 -0
- package/dist/clients/registry.js +2 -0
- package/dist/clients/registry.js.map +1 -1
- package/dist/commands/mcp/proxy.js +67 -11
- package/dist/commands/mcp/proxy.js.map +1 -1
- package/dist/hook-runner.js +902 -227
- package/dist/hooks/install.js +1 -1
- package/dist/hooks/install.js.map +1 -1
- package/dist/hooks/runtime/antigravity-session.js +400 -0
- package/dist/hooks/runtime/antigravity-session.js.map +1 -0
- package/dist/hooks/runtime/emit.js +206 -0
- package/dist/hooks/runtime/emit.js.map +1 -1
- package/dist/hooks/runtime/exit-deadlock-guard.js +46 -0
- package/dist/hooks/runtime/exit-deadlock-guard.js.map +1 -0
- package/dist/hooks/runtime/fetch-rules.js +39 -3
- package/dist/hooks/runtime/fetch-rules.js.map +1 -1
- package/dist/hooks/runtime/pre-invocation.js +45 -0
- package/dist/hooks/runtime/pre-invocation.js.map +1 -0
- package/dist/hooks/runtime/runner-args.js +12 -0
- package/dist/hooks/runtime/runner-args.js.map +1 -0
- package/dist/hooks/runtime/session-activation.js +10 -4
- package/dist/hooks/runtime/session-activation.js.map +1 -1
- package/dist/hooks/runtime/session-review.js +42 -0
- package/dist/hooks/runtime/session-review.js.map +1 -1
- package/dist/hooks/runtime/session-start.js +21 -1
- package/dist/hooks/runtime/session-start.js.map +1 -1
- package/dist/hooks/runtime/sniff.js +325 -70
- package/dist/hooks/runtime/sniff.js.map +1 -1
- package/dist/hooks/runtime/work-dir.js +3 -0
- package/dist/hooks/runtime/work-dir.js.map +1 -1
- package/dist/hooks/shim.js +102 -5
- package/dist/hooks/shim.js.map +1 -1
- package/dist/json-file.js +6 -3
- package/dist/json-file.js.map +1 -1
- package/dist/mcp/proxy/audit.js.map +1 -1
- package/dist/mcp/proxy/inject.js +20 -5
- package/dist/mcp/proxy/inject.js.map +1 -1
- package/dist/mcp/proxy/intercept.js +26 -0
- package/dist/mcp/proxy/intercept.js.map +1 -1
- package/dist/mcp/proxy/paths.js +4 -0
- package/dist/mcp/proxy/paths.js.map +1 -1
- package/dist/mcp/proxy/proxy.js +12 -2
- package/dist/mcp/proxy/proxy.js.map +1 -1
- package/dist/mcp/proxy/serverCheckTimer.js +75 -0
- package/dist/mcp/proxy/serverCheckTimer.js.map +1 -0
- package/dist/mcp/proxy/sessionLifecycle.js +29 -0
- package/dist/mcp/proxy/sessionLifecycle.js.map +1 -0
- package/dist/mcp/proxy/verifyInterceptor.js +186 -27
- package/dist/mcp/proxy/verifyInterceptor.js.map +1 -1
- package/dist/mcp/report/buffer.js +351 -0
- package/dist/mcp/report/buffer.js.map +1 -0
- package/dist/mcp/report/coalesce.js +165 -0
- package/dist/mcp/report/coalesce.js.map +1 -0
- package/dist/mcp/report/constants.js +51 -0
- package/dist/mcp/report/constants.js.map +1 -0
- package/dist/mcp/report/project.js +83 -0
- package/dist/mcp/report/project.js.map +1 -0
- package/dist/mcp/report/reporter.js +443 -0
- package/dist/mcp/report/reporter.js.map +1 -0
- package/dist/mcp/report/types.js +3 -0
- package/dist/mcp/report/types.js.map +1 -0
- package/dist/mcp/report/validate.js +179 -0
- package/dist/mcp/report/validate.js.map +1 -0
- package/dist/mcp/verify/api.js +39 -0
- package/dist/mcp/verify/api.js.map +1 -1
- package/dist/mcp/verify/cache.js +18 -0
- package/dist/mcp/verify/cache.js.map +1 -1
- package/dist/mcp/verify/identity.js +120 -7
- package/dist/mcp/verify/identity.js.map +1 -1
- package/dist/mcp/verify/policy.js +2 -0
- package/dist/mcp/verify/policy.js.map +1 -1
- package/dist/mcp/verify/verifier.js +189 -32
- package/dist/mcp/verify/verifier.js.map +1 -1
- package/dist/shim.js +53 -3
- package/dist/utils.js +9 -0
- package/dist/utils.js.map +1 -1
- package/package.json +4 -3
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
// ─── The on-disk buffer of call reports ───────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// A report that cannot be delivered now (backend down, VibeDefend off, expired
|
|
4
|
+
// login) waits here, bounded in count and in age, and goes out on a later flush
|
|
5
|
+
// or from a later process. Modelled on the guard violations buffer: JSON lines
|
|
6
|
+
// appended without a lock, one flush at a time under an O_EXCL lock, holds per
|
|
7
|
+
// backend and organisation so a refusing organisation is left alone without
|
|
8
|
+
// stopping the others.
|
|
9
|
+
//
|
|
10
|
+
// Each record names the backend (`apiBase`) it was made for, and a flush posts
|
|
11
|
+
// only its own backend's records: a report written while an EU backend was down
|
|
12
|
+
// never reaches the US backend (or a staging one) through the next proxy, and
|
|
13
|
+
// that other backend's refusal never holds it. A record without a backend
|
|
14
|
+
// predates this rule and is dropped: its destination is not guessed.
|
|
15
|
+
//
|
|
16
|
+
// Written under 0700 / 0600. A record holds what a report holds (C4) plus its
|
|
17
|
+
// backend, which stays on disk: the post is handed the body alone. The token is
|
|
18
|
+
// never written, only its fingerprint (401 holds).
|
|
19
|
+
import { appendFileSync, chmodSync, closeSync, existsSync, mkdirSync, openSync, readFileSync, rmSync, statSync, utimesSync, writeFileSync, writeSync, } from 'node:fs';
|
|
20
|
+
import { join } from 'node:path';
|
|
21
|
+
import { httpStatusOf, retryAfterOf } from '../../hooks/runtime/gateway-http-error.js';
|
|
22
|
+
import { tokenFingerprint, transientRetryMs, unauthorizedBackoffMs, } from '../../hooks/runtime/retry-policy.js';
|
|
23
|
+
import { mcpReportsDir } from '../proxy/paths.js';
|
|
24
|
+
import { REPORT_BUFFER_MAX_AGE_MS, REPORT_BUFFER_MAX_RECORDS, REPORT_HOLD_MS, } from './constants.js';
|
|
25
|
+
/** A flush lock older than this is a crashed flush's (as the violations buffer's). */
|
|
26
|
+
export const FLUSH_LOCK_STALE_MS = 60_000;
|
|
27
|
+
const HELD_STATUSES = new Set([400, 403, 404, 422]);
|
|
28
|
+
/** A URL holds no space: the pair cannot be read back two ways. */
|
|
29
|
+
function holdKey(record) {
|
|
30
|
+
return `${record.apiBase} ${record.organizationId}`;
|
|
31
|
+
}
|
|
32
|
+
export function makeReportBuffer(opts = {}) {
|
|
33
|
+
const dir = opts.dir ?? mcpReportsDir(opts.env ?? process.env);
|
|
34
|
+
const bufferFile = join(dir, 'reports.jsonl');
|
|
35
|
+
const holdsFile = join(dir, 'holds.json');
|
|
36
|
+
const lockFile = join(dir, 'flush.lock');
|
|
37
|
+
const now = opts.now ?? Date.now;
|
|
38
|
+
function ensureDir() {
|
|
39
|
+
if (!existsSync(dir))
|
|
40
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
41
|
+
try {
|
|
42
|
+
chmodSync(dir, 0o700);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Best-effort (not on every platform)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function writeSecret(file, content, flag) {
|
|
49
|
+
ensureDir();
|
|
50
|
+
const fresh = !existsSync(file);
|
|
51
|
+
if (flag === 'a')
|
|
52
|
+
appendFileSync(file, content, { encoding: 'utf8', mode: 0o600 });
|
|
53
|
+
else
|
|
54
|
+
writeFileSync(file, content, { encoding: 'utf8', mode: 0o600 });
|
|
55
|
+
if (fresh) {
|
|
56
|
+
try {
|
|
57
|
+
chmodSync(file, 0o600);
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// Best-effort
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
function append(apiBase, organizationId, body) {
|
|
65
|
+
try {
|
|
66
|
+
const record = {
|
|
67
|
+
apiBase,
|
|
68
|
+
organizationId,
|
|
69
|
+
body,
|
|
70
|
+
bufferedAt: new Date(now()).toISOString(),
|
|
71
|
+
};
|
|
72
|
+
writeSecret(bufferFile, JSON.stringify(record) + '\n', 'a');
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Best-effort: a report is never worth blocking a call.
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
async function flush(apiBase, token, post) {
|
|
79
|
+
if (!existsSync(bufferFile))
|
|
80
|
+
return { ran: true, retryAt: null };
|
|
81
|
+
if (!acquireLock())
|
|
82
|
+
return { ran: false, retryAt: null };
|
|
83
|
+
try {
|
|
84
|
+
return { ran: true, retryAt: await flushLocked(apiBase, token, post) };
|
|
85
|
+
}
|
|
86
|
+
finally {
|
|
87
|
+
releaseLock();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/** Returns when the earliest record kept for `apiBase` is due again, or `null`. */
|
|
91
|
+
async function flushLocked(apiBase, token, post) {
|
|
92
|
+
const snapshot = readBuffer();
|
|
93
|
+
if (!snapshot)
|
|
94
|
+
return null;
|
|
95
|
+
const flushedAt = now();
|
|
96
|
+
const records = capRecords(parseRecords(snapshot.content), flushedAt);
|
|
97
|
+
const fingerprint = tokenFingerprint(token);
|
|
98
|
+
const holds = readHolds();
|
|
99
|
+
const kept = [];
|
|
100
|
+
const blocked = new Set(); // hold keys that stop for this flush
|
|
101
|
+
for (const record of records) {
|
|
102
|
+
// Another backend's record: not ours to post, nor to hold.
|
|
103
|
+
if (record.apiBase !== apiBase) {
|
|
104
|
+
kept.push(record);
|
|
105
|
+
continue;
|
|
106
|
+
}
|
|
107
|
+
const key = holdKey(record);
|
|
108
|
+
if (blocked.has(key) || !isDue(holds[key], flushedAt, fingerprint)) {
|
|
109
|
+
kept.push(record);
|
|
110
|
+
blocked.add(key);
|
|
111
|
+
continue;
|
|
112
|
+
}
|
|
113
|
+
touchLock();
|
|
114
|
+
try {
|
|
115
|
+
await post(record.organizationId, record.body);
|
|
116
|
+
delete holds[key];
|
|
117
|
+
}
|
|
118
|
+
catch (e) {
|
|
119
|
+
kept.push(record);
|
|
120
|
+
blocked.add(key);
|
|
121
|
+
holds[key] = holdAfter(e, holds[key], flushedAt, fingerprint);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// A hold only matters while its backend and organisation still have records.
|
|
125
|
+
const waiting = new Set(kept.map(holdKey));
|
|
126
|
+
for (const key of Object.keys(holds))
|
|
127
|
+
if (!waiting.has(key))
|
|
128
|
+
delete holds[key];
|
|
129
|
+
writeHolds(holds);
|
|
130
|
+
let retryAt = null;
|
|
131
|
+
for (const record of kept) {
|
|
132
|
+
if (record.apiBase !== apiBase)
|
|
133
|
+
continue;
|
|
134
|
+
const due = holds[holdKey(record)]?.retryAt;
|
|
135
|
+
if (due !== undefined && (retryAt === null || due < retryAt))
|
|
136
|
+
retryAt = due;
|
|
137
|
+
}
|
|
138
|
+
// Keep what was appended while we posted (appends only grow the file).
|
|
139
|
+
try {
|
|
140
|
+
const serialised = kept.map((r) => JSON.stringify(r) + '\n').join('');
|
|
141
|
+
writeSecret(bufferFile, serialised + appendedSince(snapshot.bytes), 'w');
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
// Worst case: posted records are posted again; the route upserts by eventId.
|
|
145
|
+
}
|
|
146
|
+
return retryAt;
|
|
147
|
+
}
|
|
148
|
+
function hasDue(apiBase, token) {
|
|
149
|
+
try {
|
|
150
|
+
const snapshot = readBuffer();
|
|
151
|
+
if (!snapshot || !snapshot.content.trim())
|
|
152
|
+
return false;
|
|
153
|
+
if (existsSync(lockFile) && !lockIsStale())
|
|
154
|
+
return false;
|
|
155
|
+
const at = now();
|
|
156
|
+
const parsed = parseRecords(snapshot.content);
|
|
157
|
+
// Records to drop make a flush worth running (it rewrites the file).
|
|
158
|
+
if (parsed.length === 0 || capRecords(parsed, at).length !== parsed.length)
|
|
159
|
+
return true;
|
|
160
|
+
const holds = readHolds();
|
|
161
|
+
const fingerprint = tokenFingerprint(token);
|
|
162
|
+
return parsed.some((r) => r.apiBase === apiBase && isDue(holds[holdKey(r)], at, fingerprint));
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function readBuffer() {
|
|
169
|
+
try {
|
|
170
|
+
const raw = readFileSync(bufferFile);
|
|
171
|
+
return { content: raw.toString('utf8'), bytes: raw.length };
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
function appendedSince(bytes) {
|
|
178
|
+
try {
|
|
179
|
+
const raw = readFileSync(bufferFile);
|
|
180
|
+
return raw.length > bytes ? raw.subarray(bytes).toString('utf8') : '';
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return '';
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function acquireLock() {
|
|
187
|
+
try {
|
|
188
|
+
ensureDir();
|
|
189
|
+
}
|
|
190
|
+
catch {
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
194
|
+
try {
|
|
195
|
+
const fd = openSync(lockFile, 'wx', 0o600);
|
|
196
|
+
try {
|
|
197
|
+
writeSync(fd, String(process.pid));
|
|
198
|
+
}
|
|
199
|
+
finally {
|
|
200
|
+
closeSync(fd);
|
|
201
|
+
}
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
catch (e) {
|
|
205
|
+
if (e.code !== 'EEXIST')
|
|
206
|
+
return false;
|
|
207
|
+
if (!lockIsStale())
|
|
208
|
+
return false;
|
|
209
|
+
try {
|
|
210
|
+
rmSync(lockFile, { force: true });
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
return false;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
/** File mtimes are wall-clock time, hence Date.now() and not `now`. */
|
|
220
|
+
function lockIsStale() {
|
|
221
|
+
try {
|
|
222
|
+
return Date.now() - statSync(lockFile).mtimeMs > FLUSH_LOCK_STALE_MS;
|
|
223
|
+
}
|
|
224
|
+
catch {
|
|
225
|
+
return true;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
function touchLock() {
|
|
229
|
+
try {
|
|
230
|
+
const t = new Date();
|
|
231
|
+
utimesSync(lockFile, t, t);
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
// Best-effort
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
function releaseLock() {
|
|
238
|
+
try {
|
|
239
|
+
rmSync(lockFile, { force: true });
|
|
240
|
+
}
|
|
241
|
+
catch {
|
|
242
|
+
// Best-effort: a lock left behind goes stale after a minute.
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
function readHolds() {
|
|
246
|
+
try {
|
|
247
|
+
if (!existsSync(holdsFile))
|
|
248
|
+
return {};
|
|
249
|
+
const parsed = JSON.parse(readFileSync(holdsFile, 'utf8'));
|
|
250
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
|
|
251
|
+
return {};
|
|
252
|
+
const holds = {};
|
|
253
|
+
for (const [key, raw] of Object.entries(parsed)) {
|
|
254
|
+
const h = raw;
|
|
255
|
+
if (h &&
|
|
256
|
+
(typeof h.status === 'number' || h.status === null) &&
|
|
257
|
+
typeof h.failedAt === 'number' &&
|
|
258
|
+
typeof h.retryAt === 'number') {
|
|
259
|
+
holds[key] = {
|
|
260
|
+
status: h.status,
|
|
261
|
+
failedAt: h.failedAt,
|
|
262
|
+
retryAt: h.retryAt,
|
|
263
|
+
...(typeof h.unauthorized === 'number' ? { unauthorized: h.unauthorized } : {}),
|
|
264
|
+
...(typeof h.token === 'string' ? { token: h.token } : {}),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
return holds;
|
|
269
|
+
}
|
|
270
|
+
catch {
|
|
271
|
+
return {};
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
function writeHolds(holds) {
|
|
275
|
+
try {
|
|
276
|
+
if (Object.keys(holds).length === 0) {
|
|
277
|
+
rmSync(holdsFile, { force: true });
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
writeSecret(holdsFile, JSON.stringify(holds), 'w');
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
// Best-effort: one more request to a refusing organisation at worst.
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return { append, flush, hasDue };
|
|
287
|
+
}
|
|
288
|
+
/** The hold an organisation gets after a failed post (400/403/404/422 | 401 | the rest). */
|
|
289
|
+
function holdAfter(error, previous, at, fingerprint) {
|
|
290
|
+
const status = httpStatusOf(error);
|
|
291
|
+
if (status !== null && HELD_STATUSES.has(status)) {
|
|
292
|
+
return { status, failedAt: at, retryAt: at + REPORT_HOLD_MS };
|
|
293
|
+
}
|
|
294
|
+
if (status === 401) {
|
|
295
|
+
const unauthorized = previous?.status === 401 && previous.token === fingerprint
|
|
296
|
+
? (previous.unauthorized ?? 0) + 1
|
|
297
|
+
: 1;
|
|
298
|
+
return {
|
|
299
|
+
status,
|
|
300
|
+
failedAt: at,
|
|
301
|
+
retryAt: at + unauthorizedBackoffMs(unauthorized),
|
|
302
|
+
unauthorized,
|
|
303
|
+
token: fingerprint,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
return { status, failedAt: at, retryAt: at + transientRetryMs(retryAfterOf(error)) };
|
|
307
|
+
}
|
|
308
|
+
/** Due when not held, when the hold is over, or when a 401 hold was set by another token. */
|
|
309
|
+
function isDue(hold, at, fingerprint) {
|
|
310
|
+
if (!hold || at >= hold.retryAt)
|
|
311
|
+
return true;
|
|
312
|
+
return hold.status === 401 && hold.token !== fingerprint;
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Unreadable lines and records without a backend, an organisation or a body are
|
|
316
|
+
* dropped. A record without a backend was written before records named theirs
|
|
317
|
+
* (1.7.0): which backend it was for is not guessed.
|
|
318
|
+
*/
|
|
319
|
+
function parseRecords(content) {
|
|
320
|
+
const records = [];
|
|
321
|
+
for (const line of content.split('\n')) {
|
|
322
|
+
const trimmed = line.trim();
|
|
323
|
+
if (!trimmed)
|
|
324
|
+
continue;
|
|
325
|
+
try {
|
|
326
|
+
const p = JSON.parse(trimmed);
|
|
327
|
+
if (typeof p.apiBase === 'string' &&
|
|
328
|
+
p.apiBase &&
|
|
329
|
+
typeof p.organizationId === 'string' &&
|
|
330
|
+
p.organizationId &&
|
|
331
|
+
p.body &&
|
|
332
|
+
typeof p.body === 'object' &&
|
|
333
|
+
typeof p.bufferedAt === 'string') {
|
|
334
|
+
records.push(p);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
catch {
|
|
338
|
+
// Corrupt line: drop it
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return records;
|
|
342
|
+
}
|
|
343
|
+
/** None older than the age cap, at most the newest N, oldest first. */
|
|
344
|
+
function capRecords(records, at) {
|
|
345
|
+
const dated = records
|
|
346
|
+
.map((record) => ({ record, t: Date.parse(record.bufferedAt) }))
|
|
347
|
+
.filter(({ t }) => Number.isFinite(t) && at - t <= REPORT_BUFFER_MAX_AGE_MS);
|
|
348
|
+
dated.sort((a, b) => a.t - b.t);
|
|
349
|
+
return dated.slice(-REPORT_BUFFER_MAX_RECORDS).map(({ record }) => record);
|
|
350
|
+
}
|
|
351
|
+
//# sourceMappingURL=buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"buffer.js","sourceRoot":"","sources":["../../../src/mcp/report/buffer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
// ─── Coalescing of call events ────────────────────────────────────────────────
|
|
2
|
+
//
|
|
3
|
+
// An agent looping on a refused tool would otherwise send thousands of events.
|
|
4
|
+
// Identical events (kind, reason, tool) inside COALESCE_WINDOW_MS are ONE event:
|
|
5
|
+
// one eventId whose count rises. The gateway upserts by eventId, so a resend
|
|
6
|
+
// with a higher count replaces the earlier one.
|
|
7
|
+
import { randomUUID } from 'node:crypto';
|
|
8
|
+
import { COALESCE_WINDOW_MS, REPORT_BUFFER_MAX_RECORDS, REPORT_MAX_EVENT_COUNT, REPORT_MAX_EVENTS, } from './constants.js';
|
|
9
|
+
const CATEGORY_PATTERN = /^[a-z0-9_-]{1,64}$/;
|
|
10
|
+
const SEVERITY_PATTERN = /^[a-z]{1,16}$/;
|
|
11
|
+
const RULE_PATTERN = /^[a-z0-9._-]{1,64}$/;
|
|
12
|
+
const DIGEST_PATTERN = /^d1:[0-9a-f]{64}$/;
|
|
13
|
+
const MAX_RULES = 5;
|
|
14
|
+
const MAX_TOOL_NAME = 128;
|
|
15
|
+
export function toReportVerdict(verdict) {
|
|
16
|
+
if (verdict === undefined)
|
|
17
|
+
return undefined;
|
|
18
|
+
if (verdict === 'ok')
|
|
19
|
+
return 'clean';
|
|
20
|
+
if (verdict === 'malicious' || verdict === 'suspicious')
|
|
21
|
+
return verdict;
|
|
22
|
+
return 'unverified';
|
|
23
|
+
}
|
|
24
|
+
/** A tool name as it leaves the machine: the gateway's charset and length. */
|
|
25
|
+
export function reportToolName(name) {
|
|
26
|
+
const cleaned = name.replace(/[^A-Za-z0-9._/-]/g, '_').slice(0, MAX_TOOL_NAME);
|
|
27
|
+
return cleaned === '' ? 'unnamed' : cleaned;
|
|
28
|
+
}
|
|
29
|
+
const SWEEP_EVERY_MS = 1_000;
|
|
30
|
+
export class EventCoalescer {
|
|
31
|
+
now;
|
|
32
|
+
windowMs;
|
|
33
|
+
newId;
|
|
34
|
+
maxGroups;
|
|
35
|
+
groups = new Map();
|
|
36
|
+
lastSweepMs = -Infinity;
|
|
37
|
+
constructor(opts = {}) {
|
|
38
|
+
this.now = opts.now ?? Date.now;
|
|
39
|
+
this.windowMs = opts.windowMs ?? COALESCE_WINDOW_MS;
|
|
40
|
+
this.newId = opts.newId ?? randomUUID;
|
|
41
|
+
// Nothing may be sent for a long time (no organization, offline): the
|
|
42
|
+
// memory a proxy keeps for its reports stays bounded all the same.
|
|
43
|
+
this.maxGroups = Math.max(1, opts.maxGroups ?? REPORT_BUFFER_MAX_RECORDS);
|
|
44
|
+
}
|
|
45
|
+
add(input) {
|
|
46
|
+
const nowMs = this.now();
|
|
47
|
+
// On the hot path: forgetting closed groups is worth one pass a second,
|
|
48
|
+
// not one per call (`pending()` sweeps before reading anyway).
|
|
49
|
+
if (nowMs < this.lastSweepMs || nowMs - this.lastSweepMs >= SWEEP_EVERY_MS)
|
|
50
|
+
this.sweep(nowMs);
|
|
51
|
+
const tool = reportToolName(input.tool);
|
|
52
|
+
const key = groupKey(input.kind, input.reason, tool);
|
|
53
|
+
const iso = new Date(nowMs).toISOString();
|
|
54
|
+
let group = this.groups.get(key);
|
|
55
|
+
if (group !== undefined && nowMs - group.firstMs >= this.windowMs) {
|
|
56
|
+
// The window is over. A group still unsent keeps its own key slot until
|
|
57
|
+
// replaced here, so it is lost only if it was never sent: keep it under
|
|
58
|
+
// its eventId by moving it aside.
|
|
59
|
+
if (group.dirty)
|
|
60
|
+
this.groups.set(`${key}#${group.event.eventId}`, group);
|
|
61
|
+
group = undefined;
|
|
62
|
+
}
|
|
63
|
+
if (group === undefined) {
|
|
64
|
+
group = {
|
|
65
|
+
event: {
|
|
66
|
+
eventId: this.newId(),
|
|
67
|
+
kind: input.kind,
|
|
68
|
+
reason: input.reason,
|
|
69
|
+
tool,
|
|
70
|
+
count: 0,
|
|
71
|
+
firstAt: iso,
|
|
72
|
+
lastAt: iso,
|
|
73
|
+
},
|
|
74
|
+
firstMs: nowMs,
|
|
75
|
+
lastMs: nowMs,
|
|
76
|
+
dirty: true,
|
|
77
|
+
};
|
|
78
|
+
this.groups.set(key, group);
|
|
79
|
+
this.evictOldest();
|
|
80
|
+
}
|
|
81
|
+
const e = group.event;
|
|
82
|
+
// Stops at the gateway's bound: past it, the group's every later send
|
|
83
|
+
// would be refused and the event lost. `lastAt` still moves on below.
|
|
84
|
+
e.count = Math.min(e.count + 1, REPORT_MAX_EVENT_COUNT);
|
|
85
|
+
// `lastAt` only moves forward. A wall clock can step back (NTP, a resume),
|
|
86
|
+
// and an event that ends before it starts is refused by the gateway: one such
|
|
87
|
+
// event would poison its whole report, and the report would keep coming back
|
|
88
|
+
// from the buffer for the 7 days it lives there.
|
|
89
|
+
if (nowMs > group.lastMs) {
|
|
90
|
+
group.lastMs = nowMs;
|
|
91
|
+
e.lastAt = iso;
|
|
92
|
+
}
|
|
93
|
+
if (input.digest !== undefined && DIGEST_PATTERN.test(input.digest))
|
|
94
|
+
e.toolDigest = input.digest;
|
|
95
|
+
const verdict = toReportVerdict(input.verdict);
|
|
96
|
+
if (verdict !== undefined)
|
|
97
|
+
e.verdict = verdict;
|
|
98
|
+
if (input.category !== undefined && CATEGORY_PATTERN.test(input.category))
|
|
99
|
+
e.category = input.category;
|
|
100
|
+
if (input.severity !== undefined && SEVERITY_PATTERN.test(input.severity))
|
|
101
|
+
e.severity = input.severity;
|
|
102
|
+
if (input.rules !== undefined) {
|
|
103
|
+
const merged = new Set(e.rules ?? []);
|
|
104
|
+
for (const r of input.rules) {
|
|
105
|
+
if (RULE_PATTERN.test(r))
|
|
106
|
+
merged.add(r);
|
|
107
|
+
}
|
|
108
|
+
const rules = [...merged].slice(0, MAX_RULES);
|
|
109
|
+
if (rules.length > 0)
|
|
110
|
+
e.rules = rules;
|
|
111
|
+
}
|
|
112
|
+
group.dirty = true;
|
|
113
|
+
}
|
|
114
|
+
/** Dirty groups, oldest first, copied. */
|
|
115
|
+
pending(limit = REPORT_MAX_EVENTS) {
|
|
116
|
+
this.sweep(this.now());
|
|
117
|
+
return [...this.groups.values()]
|
|
118
|
+
.filter((g) => g.dirty)
|
|
119
|
+
.sort((a, b) => a.firstMs - b.firstMs)
|
|
120
|
+
.slice(0, limit)
|
|
121
|
+
.map((g) => ({ ...g.event, ...(g.event.rules ? { rules: [...g.event.rules] } : {}) }));
|
|
122
|
+
}
|
|
123
|
+
markSent(sent) {
|
|
124
|
+
for (const s of sent) {
|
|
125
|
+
for (const g of this.groups.values()) {
|
|
126
|
+
if (g.event.eventId === s.eventId && g.event.count === s.count)
|
|
127
|
+
g.dirty = false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
hasPending() {
|
|
132
|
+
for (const g of this.groups.values())
|
|
133
|
+
if (g.dirty)
|
|
134
|
+
return true;
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
/** Over the bound, the oldest groups go first, sent or not. */
|
|
138
|
+
evictOldest() {
|
|
139
|
+
while (this.groups.size > this.maxGroups) {
|
|
140
|
+
let oldestKey;
|
|
141
|
+
let oldestMs = Infinity;
|
|
142
|
+
for (const [key, g] of this.groups) {
|
|
143
|
+
if (g.firstMs < oldestMs) {
|
|
144
|
+
oldestMs = g.firstMs;
|
|
145
|
+
oldestKey = key;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
if (oldestKey === undefined)
|
|
149
|
+
return;
|
|
150
|
+
this.groups.delete(oldestKey);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/** Closed and clean groups are forgotten. */
|
|
154
|
+
sweep(nowMs) {
|
|
155
|
+
this.lastSweepMs = nowMs;
|
|
156
|
+
for (const [key, g] of this.groups) {
|
|
157
|
+
if (!g.dirty && nowMs - g.firstMs >= this.windowMs)
|
|
158
|
+
this.groups.delete(key);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function groupKey(kind, reason, tool) {
|
|
163
|
+
return `${kind}|${reason}|${tool}`;
|
|
164
|
+
}
|
|
165
|
+
//# sourceMappingURL=coalesce.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"coalesce.js","sourceRoot":"","sources":["../../../src/mcp/report/coalesce.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// ─── Constants of MCP call reporting (contract C6 and the validation of C4) ──
|
|
2
|
+
/** Events of one report, at most. */
|
|
3
|
+
export const REPORT_MAX_EVENTS = 200;
|
|
4
|
+
/** Repeated events of one (server, tool, kind, reason) are one event over this window. */
|
|
5
|
+
export const COALESCE_WINDOW_MS = 600_000;
|
|
6
|
+
/** The first send goes this long after an event. */
|
|
7
|
+
export const FIRST_SEND_DELAY_MS = 5_000;
|
|
8
|
+
/** Then at most one send per this interval. */
|
|
9
|
+
export const MIN_SEND_INTERVAL_MS = 60_000;
|
|
10
|
+
/** The inventory is resent at least this often. */
|
|
11
|
+
export const INVENTORY_REFRESH_MS = 3_600_000;
|
|
12
|
+
/** The report buffer holds this many records, at most... */
|
|
13
|
+
export const REPORT_BUFFER_MAX_RECORDS = 500;
|
|
14
|
+
/** ...and none older than this. */
|
|
15
|
+
export const REPORT_BUFFER_MAX_AGE_MS = 7 * 24 * 3_600_000;
|
|
16
|
+
/** How long an organisation is left alone after a 400, 403, 404 or 422. */
|
|
17
|
+
export const REPORT_HOLD_MS = 3_600_000;
|
|
18
|
+
/** Timeout of one report request. */
|
|
19
|
+
export const REPORT_HTTP_TIMEOUT_MS = 5_000;
|
|
20
|
+
/** `servers/check` runs this often for the whole life of the proxy. */
|
|
21
|
+
export const SERVER_CHECK_INTERVAL_MS = 900_000;
|
|
22
|
+
/** A known organisation block stays applied this long while the backend cannot be reached. */
|
|
23
|
+
export const ORG_BLOCK_STICKY_MAX_MS = 7 * 24 * 3_600_000;
|
|
24
|
+
// What a report may carry, field by field (C4). Anything else is not sent.
|
|
25
|
+
/** A config entry name or a launcher name, as it leaves the machine. */
|
|
26
|
+
export const LABEL_PATTERN = /^[A-Za-z0-9._-]{1,64}$/;
|
|
27
|
+
/**
|
|
28
|
+
* A lowercase hostname, no scheme, port or path: labels of 1 to 63 characters
|
|
29
|
+
* that neither start nor end with a hyphen, 253 characters at most. The
|
|
30
|
+
* gateway's pattern, so an empty label (`mcp..example.com`) or a trailing dot
|
|
31
|
+
* is refused here instead of costing the whole report a 400.
|
|
32
|
+
*/
|
|
33
|
+
export const HOST_PATTERN = /^(?=.{1,253}$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?)*$/;
|
|
34
|
+
/** At most 214 characters in all, scope included: npm's own limit (ruling R30). */
|
|
35
|
+
export const NPM_NAME_PATTERN = /^(?=.{1,214}$)(@[a-z0-9._~-]+\/)?[a-z0-9._~-]{1,214}$/;
|
|
36
|
+
export const PYPI_NAME_PATTERN = /^[A-Za-z0-9._-]{1,128}$/;
|
|
37
|
+
export const PACKAGE_VERSION_PATTERN = /^[A-Za-z0-9.+_-]{1,64}$/;
|
|
38
|
+
export const SERVER_KEY_PATTERN = /^[a-f0-9]{64}$/;
|
|
39
|
+
/** The gateway's pattern for `vibedefendVersion`: semver, at most one pre-release and one build part. */
|
|
40
|
+
export const VIBEDEFEND_VERSION_PATTERN = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
41
|
+
/** `server.toolCount` and `server.flaggedToolCount`, at most (the gateway refuses more). */
|
|
42
|
+
export const REPORT_MAX_TOOL_COUNT = 100_000;
|
|
43
|
+
/** An event's `count`, at most (the gateway refuses more). */
|
|
44
|
+
export const REPORT_MAX_EVENT_COUNT = 100_000;
|
|
45
|
+
/**
|
|
46
|
+
* How far ahead of this machine's clock an organisation block's confirmation
|
|
47
|
+
* may be and still count: a clock set back a little while offline (an NTP
|
|
48
|
+
* correction) must not lift a block that is in force.
|
|
49
|
+
*/
|
|
50
|
+
export const ORG_BLOCK_CLOCK_SKEW_MS = 5 * 60_000;
|
|
51
|
+
//# sourceMappingURL=constants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/mcp/report/constants.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAEhF,qCAAqC;AACrC,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAErC,0FAA0F;AAC1F,MAAM,CAAC,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAC1C,oDAAoD;AACpD,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,CAAC;AACzC,+CAA+C;AAC/C,MAAM,CAAC,MAAM,oBAAoB,GAAG,MAAM,CAAC;AAC3C,mDAAmD;AACnD,MAAM,CAAC,MAAM,oBAAoB,GAAG,SAAS,CAAC;AAE9C,4DAA4D;AAC5D,MAAM,CAAC,MAAM,yBAAyB,GAAG,GAAG,CAAC;AAC7C,mCAAmC;AACnC,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;AAC3D,2EAA2E;AAC3E,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAC;AACxC,qCAAqC;AACrC,MAAM,CAAC,MAAM,sBAAsB,GAAG,KAAK,CAAC;AAE5C,uEAAuE;AACvE,MAAM,CAAC,MAAM,wBAAwB,GAAG,OAAO,CAAC;AAChD,8FAA8F;AAC9F,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;AAE1D,2EAA2E;AAE3E,wEAAwE;AACxE,MAAM,CAAC,MAAM,aAAa,GAAG,wBAAwB,CAAC;AACtD;;;;;GAKG;AACH,MAAM,CAAC,MAAM,YAAY,GACvB,gGAAgG,CAAC;AACnG,mFAAmF;AACnF,MAAM,CAAC,MAAM,gBAAgB,GAAG,uDAAuD,CAAC;AACxF,MAAM,CAAC,MAAM,iBAAiB,GAAG,yBAAyB,CAAC;AAC3D,MAAM,CAAC,MAAM,uBAAuB,GAAG,yBAAyB,CAAC;AACjE,MAAM,CAAC,MAAM,kBAAkB,GAAG,gBAAgB,CAAC;AACnD,yGAAyG;AACzG,MAAM,CAAC,MAAM,0BAA0B,GAAG,0DAA0D,CAAC;AACrG,4FAA4F;AAC5F,MAAM,CAAC,MAAM,qBAAqB,GAAG,OAAO,CAAC;AAC7C,8DAA8D;AAC9D,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAC;AAC9C;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,GAAG,MAAM,CAAC"}
|