@vaultcompass/vault-guard-mcp 1.4.7 → 1.6.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/dist/index.js +49 -14
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -59,9 +59,11 @@ var BINARY_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
59
59
|
function isBinaryFile(filePath) {
|
|
60
60
|
return BINARY_EXTENSIONS.has(import_path.default.extname(filePath).toLowerCase());
|
|
61
61
|
}
|
|
62
|
-
|
|
62
|
+
var MCP_SCAN_BUDGET_MS = 5e3;
|
|
63
|
+
async function scanWorkspaceDirectory(root, scanner, concurrency = 10, configIgnorePatterns2 = [], scanBudgetMs = MCP_SCAN_BUDGET_MS) {
|
|
63
64
|
const files = await (0, import_vault_guard_core.getFilesToScanAsync)(root, false, void 0, configIgnorePatterns2);
|
|
64
65
|
const results = [];
|
|
66
|
+
const overBudget = [];
|
|
65
67
|
let filesScanned = 0;
|
|
66
68
|
let bytesScanned = 0;
|
|
67
69
|
const scanOne = async (file) => {
|
|
@@ -71,10 +73,15 @@ async function scanWorkspaceDirectory(root, scanner, concurrency = 10, configIgn
|
|
|
71
73
|
if (!st.isFile()) return;
|
|
72
74
|
filesScanned += 1;
|
|
73
75
|
bytesScanned += st.size;
|
|
76
|
+
const t0 = Date.now();
|
|
74
77
|
const matches = await (0, import_vault_guard_core.scanTextFileAsync)(scanner, file, { maxFileBytes: MAX_FILE_SIZE });
|
|
78
|
+
const elapsed = Date.now() - t0;
|
|
75
79
|
if (matches.length > 0) {
|
|
76
80
|
results.push({ file, matches });
|
|
77
81
|
}
|
|
82
|
+
if (elapsed > scanBudgetMs) {
|
|
83
|
+
overBudget.push({ file, elapsed_ms: elapsed, budget_ms: scanBudgetMs });
|
|
84
|
+
}
|
|
78
85
|
} catch {
|
|
79
86
|
}
|
|
80
87
|
};
|
|
@@ -82,11 +89,11 @@ async function scanWorkspaceDirectory(root, scanner, concurrency = 10, configIgn
|
|
|
82
89
|
const batch = files.slice(i, i + concurrency);
|
|
83
90
|
await Promise.all(batch.map(scanOne));
|
|
84
91
|
}
|
|
85
|
-
return { results, filesScanned, bytesScanned };
|
|
92
|
+
return { results, filesScanned, bytesScanned, overBudget };
|
|
86
93
|
}
|
|
87
94
|
|
|
88
95
|
// src/server.ts
|
|
89
|
-
var SERVER_VERSION = true ? "1.
|
|
96
|
+
var SERVER_VERSION = true ? "1.6.0" : "0.0.0-dev";
|
|
90
97
|
function loadMcpConfig(workspaceRoot) {
|
|
91
98
|
let cfg;
|
|
92
99
|
try {
|
|
@@ -107,6 +114,7 @@ function loadMcpConfig(workspaceRoot) {
|
|
|
107
114
|
function makeScanner(config) {
|
|
108
115
|
return new import_vault_guard_core2.SecretScanner(config);
|
|
109
116
|
}
|
|
117
|
+
var MAX_SCAN_BYTES = 10 * 1024 * 1024;
|
|
110
118
|
function toolPayload(obj) {
|
|
111
119
|
return {
|
|
112
120
|
content: [{ type: "text", text: JSON.stringify(obj, null, 2) }]
|
|
@@ -204,7 +212,7 @@ function createMcpServer(options = {}) {
|
|
|
204
212
|
}
|
|
205
213
|
const dir = resolved.path;
|
|
206
214
|
const t0 = Date.now();
|
|
207
|
-
const { results, filesScanned, bytesScanned } = await scanWorkspaceDirectory(
|
|
215
|
+
const { results, filesScanned, bytesScanned, overBudget } = await scanWorkspaceDirectory(
|
|
208
216
|
dir,
|
|
209
217
|
scanner,
|
|
210
218
|
10,
|
|
@@ -219,8 +227,13 @@ function createMcpServer(options = {}) {
|
|
|
219
227
|
return toolPayload({
|
|
220
228
|
summary: {
|
|
221
229
|
files_with_secrets: results.length,
|
|
222
|
-
total_matches: results.reduce((n, r) => n + r.matches.length, 0)
|
|
230
|
+
total_matches: results.reduce((n, r) => n + r.matches.length, 0),
|
|
231
|
+
// Post-hoc: these files WERE scanned, but took long enough that the
|
|
232
|
+
// result is not trusted. Surfaced so an agent is not handed a silent
|
|
233
|
+
// "clean" for a file whose scan behaved pathologically.
|
|
234
|
+
...overBudget.length > 0 ? { files_over_scan_budget: overBudget.length } : {}
|
|
223
235
|
},
|
|
236
|
+
...overBudget.length > 0 ? { over_budget: overBudget } : {},
|
|
224
237
|
json: JSON.parse((0, import_vault_guard_core2.formatJson)(results, { cwd: dir, run })),
|
|
225
238
|
sarif: (0, import_vault_guard_core2.formatSarif)(results, { cwd: dir, run }),
|
|
226
239
|
results
|
|
@@ -247,18 +260,28 @@ function createMcpServer(options = {}) {
|
|
|
247
260
|
if (!import_fs2.default.existsSync(fp) || !import_fs2.default.statSync(fp).isFile()) {
|
|
248
261
|
return toolPayload({ error: "not_a_file", path: fp });
|
|
249
262
|
}
|
|
263
|
+
const st = import_fs2.default.statSync(fp);
|
|
264
|
+
if (st.size > MAX_SCAN_BYTES) {
|
|
265
|
+
return toolPayload({ error: "file_too_large", path: fp, bytes: st.size, max_bytes: MAX_SCAN_BYTES });
|
|
266
|
+
}
|
|
250
267
|
const t0 = Date.now();
|
|
251
268
|
const matches = scanner.scan(fp);
|
|
252
|
-
const
|
|
269
|
+
const elapsed = Date.now() - t0;
|
|
253
270
|
const run = {
|
|
254
|
-
duration_ms:
|
|
271
|
+
duration_ms: elapsed,
|
|
255
272
|
files_scanned: 1,
|
|
256
273
|
bytes_scanned: st.size,
|
|
257
274
|
patterns_active: scanner.getActivePatternCount()
|
|
258
275
|
};
|
|
259
276
|
const results = matches.length ? [{ file: fp, matches }] : [];
|
|
277
|
+
const overBudget = elapsed > MCP_SCAN_BUDGET_MS;
|
|
260
278
|
return toolPayload({
|
|
261
|
-
summary: {
|
|
279
|
+
summary: {
|
|
280
|
+
files_with_secrets: results.length,
|
|
281
|
+
total_matches: matches.length,
|
|
282
|
+
...overBudget ? { over_scan_budget: true } : {}
|
|
283
|
+
},
|
|
284
|
+
...overBudget ? { over_budget: [{ file: fp, elapsed_ms: elapsed, budget_ms: MCP_SCAN_BUDGET_MS }] } : {},
|
|
262
285
|
json: JSON.parse((0, import_vault_guard_core2.formatJson)(results, { cwd: workspaceRoot, run })),
|
|
263
286
|
sarif: (0, import_vault_guard_core2.formatSarif)(results, { cwd: workspaceRoot, run })
|
|
264
287
|
});
|
|
@@ -275,12 +298,15 @@ function createMcpServer(options = {}) {
|
|
|
275
298
|
}
|
|
276
299
|
},
|
|
277
300
|
async ({ text, virtual_path }) => {
|
|
301
|
+
const bytes = Buffer.byteLength(text, "utf8");
|
|
302
|
+
if (bytes > MAX_SCAN_BYTES) {
|
|
303
|
+
return toolPayload({ error: "text_too_large", bytes, max_bytes: MAX_SCAN_BYTES });
|
|
304
|
+
}
|
|
278
305
|
const scanner = makeScanner(loadMcpConfig(workspaceRoot));
|
|
279
306
|
const t0 = Date.now();
|
|
280
307
|
const matches = scanner.scanContent(text);
|
|
281
308
|
const label = virtual_path ?? "inline://snippet";
|
|
282
309
|
const results = matches.length ? [{ file: label, matches }] : [];
|
|
283
|
-
const bytes = Buffer.byteLength(text, "utf8");
|
|
284
310
|
const run = {
|
|
285
311
|
duration_ms: Date.now() - t0,
|
|
286
312
|
files_scanned: 1,
|
|
@@ -308,16 +334,20 @@ function createMcpServer(options = {}) {
|
|
|
308
334
|
const targets = paths && paths.length > 0 ? paths : ["."];
|
|
309
335
|
let total = 0;
|
|
310
336
|
const breakdown = {};
|
|
337
|
+
const visited = /* @__PURE__ */ new Set();
|
|
311
338
|
const walkFile = (file) => {
|
|
312
339
|
try {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
if (
|
|
340
|
+
const lst = import_fs2.default.lstatSync(file);
|
|
341
|
+
if (lst.isSymbolicLink()) return;
|
|
342
|
+
if (lst.isFile()) {
|
|
316
343
|
const n = tokenCounter.countTokensInFile(file);
|
|
317
344
|
total += n;
|
|
318
345
|
const ext = import_path2.default.extname(file) || "(no-ext)";
|
|
319
346
|
breakdown[ext] = (breakdown[ext] ?? 0) + n;
|
|
320
|
-
} else if (
|
|
347
|
+
} else if (lst.isDirectory()) {
|
|
348
|
+
const real = import_fs2.default.realpathSync(file);
|
|
349
|
+
if (visited.has(real)) return;
|
|
350
|
+
visited.add(real);
|
|
321
351
|
const entries = import_fs2.default.readdirSync(file, { withFileTypes: true });
|
|
322
352
|
for (const e of entries) {
|
|
323
353
|
if (e.name === "node_modules" || e.name === ".git") continue;
|
|
@@ -332,7 +362,12 @@ function createMcpServer(options = {}) {
|
|
|
332
362
|
if (!resolved.ok) {
|
|
333
363
|
return toolPayload({ error: resolved.error, path: resolved.path });
|
|
334
364
|
}
|
|
335
|
-
|
|
365
|
+
let start = resolved.path;
|
|
366
|
+
try {
|
|
367
|
+
start = import_fs2.default.realpathSync(resolved.path);
|
|
368
|
+
} catch {
|
|
369
|
+
}
|
|
370
|
+
walkFile(start);
|
|
336
371
|
}
|
|
337
372
|
const estCostAnthropic = tokenCounter.calculateCost("anthropic", total, 0);
|
|
338
373
|
return toolPayload({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vaultcompass/vault-guard-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"description": "Let Cursor and Claude scan AI-proposed edits for secrets before applying. Local MCP server.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -26,8 +26,8 @@
|
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.30.0",
|
|
28
28
|
"zod": "^4.4.3",
|
|
29
|
-
"@vaultcompass/vault-guard-core": "1.
|
|
30
|
-
"@vaultcompass/vault-guard-telemetry": "1.
|
|
29
|
+
"@vaultcompass/vault-guard-core": "1.6.0",
|
|
30
|
+
"@vaultcompass/vault-guard-telemetry": "1.6.0"
|
|
31
31
|
},
|
|
32
32
|
"devDependencies": {
|
|
33
33
|
"@types/jest": "^30.0.0",
|