@vibecheckai/cli 3.7.0 → 3.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 +135 -63
- package/bin/_deprecations.js +447 -19
- package/bin/_router.js +1 -1
- package/bin/registry.js +347 -280
- package/bin/runners/context/generators/cursor-enhanced.js +2439 -0
- package/bin/runners/lib/agent-firewall/enforcement/gateway.js +1059 -0
- package/bin/runners/lib/agent-firewall/enforcement/index.js +98 -0
- package/bin/runners/lib/agent-firewall/enforcement/mode.js +318 -0
- package/bin/runners/lib/agent-firewall/enforcement/orchestrator.js +484 -0
- package/bin/runners/lib/agent-firewall/enforcement/proof-artifact.js +418 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/change-event.schema.json +173 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/intent.schema.json +181 -0
- package/bin/runners/lib/agent-firewall/enforcement/schemas/verdict.schema.json +222 -0
- package/bin/runners/lib/agent-firewall/enforcement/verdict-v2.js +333 -0
- package/bin/runners/lib/agent-firewall/index.js +200 -0
- package/bin/runners/lib/agent-firewall/integration/index.js +20 -0
- package/bin/runners/lib/agent-firewall/integration/ship-gate.js +437 -0
- package/bin/runners/lib/agent-firewall/intent/alignment-engine.js +622 -0
- package/bin/runners/lib/agent-firewall/intent/auto-detect.js +426 -0
- package/bin/runners/lib/agent-firewall/intent/index.js +102 -0
- package/bin/runners/lib/agent-firewall/intent/schema.js +352 -0
- package/bin/runners/lib/agent-firewall/intent/store.js +283 -0
- package/bin/runners/lib/agent-firewall/interception/fs-interceptor.js +502 -0
- package/bin/runners/lib/agent-firewall/interception/index.js +23 -0
- package/bin/runners/lib/agent-firewall/session/collector.js +451 -0
- package/bin/runners/lib/agent-firewall/session/index.js +26 -0
- package/bin/runners/lib/artifact-envelope.js +540 -0
- package/bin/runners/lib/auth-shared.js +977 -0
- package/bin/runners/lib/checkpoint.js +941 -0
- package/bin/runners/lib/cleanup/engine.js +571 -0
- package/bin/runners/lib/cleanup/index.js +53 -0
- package/bin/runners/lib/cleanup/output.js +375 -0
- package/bin/runners/lib/cleanup/rules.js +1060 -0
- package/bin/runners/lib/doctor/diagnosis-receipt.js +454 -0
- package/bin/runners/lib/doctor/failure-signatures.js +526 -0
- package/bin/runners/lib/doctor/fix-script.js +336 -0
- package/bin/runners/lib/doctor/modules/build-tools.js +453 -0
- package/bin/runners/lib/doctor/modules/index.js +62 -3
- package/bin/runners/lib/doctor/modules/os-quirks.js +706 -0
- package/bin/runners/lib/doctor/modules/repo-integrity.js +485 -0
- package/bin/runners/lib/doctor/safe-repair.js +384 -0
- package/bin/runners/lib/engines/attack-detector.js +1192 -0
- package/bin/runners/lib/entitlements-v2.js +2 -2
- package/bin/runners/lib/missions/briefing.js +427 -0
- package/bin/runners/lib/missions/checkpoint.js +753 -0
- package/bin/runners/lib/missions/hardening.js +851 -0
- package/bin/runners/lib/missions/plan.js +421 -32
- package/bin/runners/lib/missions/safety-gates.js +645 -0
- package/bin/runners/lib/missions/schema.js +478 -0
- package/bin/runners/lib/packs/bundle.js +675 -0
- package/bin/runners/lib/packs/evidence-pack.js +671 -0
- package/bin/runners/lib/packs/pack-factory.js +837 -0
- package/bin/runners/lib/packs/permissions-pack.js +686 -0
- package/bin/runners/lib/packs/proof-graph-pack.js +779 -0
- package/bin/runners/lib/safelist/index.js +96 -0
- package/bin/runners/lib/safelist/integration.js +334 -0
- package/bin/runners/lib/safelist/matcher.js +696 -0
- package/bin/runners/lib/safelist/schema.js +948 -0
- package/bin/runners/lib/safelist/store.js +438 -0
- package/bin/runners/lib/schemas/ship-manifest.schema.json +251 -0
- package/bin/runners/lib/ship-gate.js +832 -0
- package/bin/runners/lib/ship-manifest.js +1153 -0
- package/bin/runners/lib/ship-output.js +1 -1
- package/bin/runners/lib/unified-cli-output.js +710 -383
- package/bin/runners/lib/upsell.js +3 -3
- package/bin/runners/lib/why-tree.js +650 -0
- package/bin/runners/runAllowlist.js +33 -4
- package/bin/runners/runApprove.js +240 -1122
- package/bin/runners/runAudit.js +692 -0
- package/bin/runners/runAuth.js +325 -29
- package/bin/runners/runCheckpoint.js +442 -494
- package/bin/runners/runCleanup.js +343 -0
- package/bin/runners/runDoctor.js +269 -19
- package/bin/runners/runFix.js +411 -32
- package/bin/runners/runForge.js +411 -0
- package/bin/runners/runIntent.js +906 -0
- package/bin/runners/runKickoff.js +878 -0
- package/bin/runners/runLaunch.js +2000 -0
- package/bin/runners/runLink.js +785 -0
- package/bin/runners/runMcp.js +1741 -837
- package/bin/runners/runPacks.js +2089 -0
- package/bin/runners/runPolish.js +41 -0
- package/bin/runners/runSafelist.js +1190 -0
- package/bin/runners/runScan.js +21 -9
- package/bin/runners/runShield.js +1282 -0
- package/bin/runners/runShip.js +395 -16
- package/bin/vibecheck.js +34 -6
- package/mcp-server/README.md +117 -158
- package/mcp-server/handlers/tool-handler.ts +3 -3
- package/mcp-server/index.js +16 -0
- package/mcp-server/intent-firewall-interceptor.js +529 -0
- package/mcp-server/manifest.json +473 -0
- package/mcp-server/package.json +1 -1
- package/mcp-server/registry/tool-registry.js +315 -523
- package/mcp-server/registry/tools.json +442 -428
- package/mcp-server/tier-auth.js +68 -11
- package/mcp-server/tools-v3.js +70 -16
- package/package.json +1 -1
- package/bin/runners/runProof.zip +0 -0
|
@@ -0,0 +1,502 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Real-Time File System Interceptor
|
|
3
|
+
*
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
5
|
+
* AGENT FIREWALL™ - FS INTERCEPTION LAYER
|
|
6
|
+
* ═══════════════════════════════════════════════════════════════════════════════
|
|
7
|
+
*
|
|
8
|
+
* Intercepts file system writes in REAL-TIME and blocks them if they violate
|
|
9
|
+
* the declared intent. This is the "write-time blocking" capability.
|
|
10
|
+
*
|
|
11
|
+
* HOW IT WORKS:
|
|
12
|
+
* 1. Monkey-patches fs.writeFile, fs.writeFileSync, etc.
|
|
13
|
+
* 2. Before any write, checks the content against the enforcement gateway
|
|
14
|
+
* 3. If BLOCKED, throws an error BEFORE the file is written
|
|
15
|
+
* 4. If PASSED, allows the write to proceed
|
|
16
|
+
*
|
|
17
|
+
* IMPORTANT: This should only be activated in IDE mode or when explicitly enabled.
|
|
18
|
+
* In CI/CLI, use the gateway directly instead of patching fs.
|
|
19
|
+
*
|
|
20
|
+
* @module interception/fs-interceptor
|
|
21
|
+
* @version 1.0.0
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
"use strict";
|
|
25
|
+
|
|
26
|
+
const fs = require("fs");
|
|
27
|
+
const path = require("path");
|
|
28
|
+
|
|
29
|
+
// Store original functions
|
|
30
|
+
const originalWriteFile = fs.writeFile;
|
|
31
|
+
const originalWriteFileSync = fs.writeFileSync;
|
|
32
|
+
const originalAppendFile = fs.appendFile;
|
|
33
|
+
const originalAppendFileSync = fs.appendFileSync;
|
|
34
|
+
const originalCopyFile = fs.copyFile;
|
|
35
|
+
const originalCopyFileSync = fs.copyFileSync;
|
|
36
|
+
const originalRename = fs.rename;
|
|
37
|
+
const originalRenameSync = fs.renameSync;
|
|
38
|
+
const originalUnlink = fs.unlink;
|
|
39
|
+
const originalUnlinkSync = fs.unlinkSync;
|
|
40
|
+
|
|
41
|
+
// Interceptor state
|
|
42
|
+
let isActive = false;
|
|
43
|
+
let projectRoot = process.cwd();
|
|
44
|
+
let gateway = null;
|
|
45
|
+
let agentId = "unknown";
|
|
46
|
+
let onBlock = null; // Callback when write is blocked
|
|
47
|
+
|
|
48
|
+
// File patterns to always allow (system files, temp files, etc.)
|
|
49
|
+
const ALWAYS_ALLOW_PATTERNS = [
|
|
50
|
+
/node_modules/,
|
|
51
|
+
/\.vibecheck\//,
|
|
52
|
+
/\.git\//,
|
|
53
|
+
/\.DS_Store/,
|
|
54
|
+
/Thumbs\.db/,
|
|
55
|
+
/\.swp$/,
|
|
56
|
+
/\.swo$/,
|
|
57
|
+
/~$/,
|
|
58
|
+
/\.tmp$/,
|
|
59
|
+
/\.temp$/,
|
|
60
|
+
/\.cache/,
|
|
61
|
+
/\.log$/,
|
|
62
|
+
/package-lock\.json$/,
|
|
63
|
+
/pnpm-lock\.yaml$/,
|
|
64
|
+
/yarn\.lock$/,
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
// File patterns that should trigger blocking (source code)
|
|
68
|
+
const SOURCE_FILE_PATTERNS = [
|
|
69
|
+
/\.[jt]sx?$/, // JavaScript/TypeScript
|
|
70
|
+
/\.vue$/, // Vue
|
|
71
|
+
/\.svelte$/, // Svelte
|
|
72
|
+
/\.py$/, // Python
|
|
73
|
+
/\.go$/, // Go
|
|
74
|
+
/\.rs$/, // Rust
|
|
75
|
+
/\.rb$/, // Ruby
|
|
76
|
+
/\.php$/, // PHP
|
|
77
|
+
/\.java$/, // Java
|
|
78
|
+
/\.cs$/, // C#
|
|
79
|
+
/\.sql$/, // SQL
|
|
80
|
+
/\.prisma$/, // Prisma
|
|
81
|
+
/\.graphql$/, // GraphQL
|
|
82
|
+
/\.json$/, // JSON (configs)
|
|
83
|
+
/\.ya?ml$/, // YAML
|
|
84
|
+
/\.env/, // Env files
|
|
85
|
+
/\.md$/, // Markdown (docs)
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Check if a file path should be intercepted
|
|
90
|
+
*/
|
|
91
|
+
function shouldIntercept(filePath) {
|
|
92
|
+
// Normalize path
|
|
93
|
+
const normalizedPath = path.resolve(filePath);
|
|
94
|
+
|
|
95
|
+
// Must be within project root
|
|
96
|
+
if (!normalizedPath.startsWith(projectRoot)) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Check always-allow patterns
|
|
101
|
+
for (const pattern of ALWAYS_ALLOW_PATTERNS) {
|
|
102
|
+
if (pattern.test(normalizedPath)) {
|
|
103
|
+
return false;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// Check if it's a source file
|
|
108
|
+
for (const pattern of SOURCE_FILE_PATTERNS) {
|
|
109
|
+
if (pattern.test(normalizedPath)) {
|
|
110
|
+
return true;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Create a BlockedWriteError
|
|
119
|
+
*/
|
|
120
|
+
class BlockedWriteError extends Error {
|
|
121
|
+
constructor(verdict) {
|
|
122
|
+
super(`BLOCKED_BY_AGENT_FIREWALL: ${verdict.summary}`);
|
|
123
|
+
this.name = "BlockedWriteError";
|
|
124
|
+
this.code = "EBLOCKED";
|
|
125
|
+
this.verdict = verdict;
|
|
126
|
+
this.violations = verdict.violations;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Intercept a file write operation
|
|
132
|
+
*/
|
|
133
|
+
async function interceptWrite(filePath, content, operation = "write") {
|
|
134
|
+
if (!gateway) {
|
|
135
|
+
console.warn("[FS Interceptor] No gateway configured - allowing write");
|
|
136
|
+
return { allowed: true };
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Build change event
|
|
140
|
+
const changeType = operation === "append" ? "file_write" :
|
|
141
|
+
operation === "delete" ? "file_delete" :
|
|
142
|
+
operation === "rename" ? "file_rename" : "file_write";
|
|
143
|
+
|
|
144
|
+
// Get old content if file exists
|
|
145
|
+
let oldContent = null;
|
|
146
|
+
try {
|
|
147
|
+
if (fs.existsSync(filePath)) {
|
|
148
|
+
oldContent = originalReadFileSync(filePath, "utf-8");
|
|
149
|
+
}
|
|
150
|
+
} catch {
|
|
151
|
+
// Ignore read errors
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const rawChange = {
|
|
155
|
+
type: changeType,
|
|
156
|
+
path: path.relative(projectRoot, filePath),
|
|
157
|
+
content: typeof content === "string" ? content : content?.toString(),
|
|
158
|
+
diff: oldContent ? {
|
|
159
|
+
before: oldContent,
|
|
160
|
+
after: content?.toString() || "",
|
|
161
|
+
} : null,
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
const verdict = await gateway.intercept(rawChange, {
|
|
166
|
+
agentId,
|
|
167
|
+
interceptionPoint: "fs_hook",
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
if (verdict.decision === "BLOCK") {
|
|
171
|
+
// Call block callback if provided
|
|
172
|
+
if (onBlock) {
|
|
173
|
+
onBlock(filePath, verdict);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return { allowed: false, verdict };
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { allowed: true, verdict };
|
|
180
|
+
} catch (err) {
|
|
181
|
+
// On error, default to allow (fail-open for fs operations)
|
|
182
|
+
console.warn(`[FS Interceptor] Error during interception: ${err.message}`);
|
|
183
|
+
return { allowed: true, error: err };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Synchronous intercept (blocking)
|
|
189
|
+
*/
|
|
190
|
+
function interceptWriteSync(filePath, content, operation = "write") {
|
|
191
|
+
if (!gateway) {
|
|
192
|
+
return { allowed: true };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// For sync operations, we do a quick check instead of full intercept
|
|
196
|
+
const rawChange = {
|
|
197
|
+
type: operation === "delete" ? "file_delete" : "file_write",
|
|
198
|
+
path: path.relative(projectRoot, filePath),
|
|
199
|
+
content: typeof content === "string" ? content : content?.toString(),
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
try {
|
|
203
|
+
const result = gateway.quickCheck(rawChange);
|
|
204
|
+
|
|
205
|
+
if (!result.allowed) {
|
|
206
|
+
if (onBlock) {
|
|
207
|
+
onBlock(filePath, { violations: result.violations });
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
return { allowed: false, violations: result.violations };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return { allowed: true };
|
|
214
|
+
} catch (err) {
|
|
215
|
+
// Fail-open
|
|
216
|
+
return { allowed: true, error: err };
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Store original readFileSync for internal use
|
|
221
|
+
const originalReadFileSync = fs.readFileSync;
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Create patched writeFile
|
|
225
|
+
*/
|
|
226
|
+
function patchedWriteFile(filePath, data, options, callback) {
|
|
227
|
+
// Handle overloads
|
|
228
|
+
if (typeof options === "function") {
|
|
229
|
+
callback = options;
|
|
230
|
+
options = {};
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const resolvedPath = path.resolve(filePath);
|
|
234
|
+
|
|
235
|
+
if (!shouldIntercept(resolvedPath)) {
|
|
236
|
+
return originalWriteFile.call(fs, filePath, data, options, callback);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Intercept asynchronously
|
|
240
|
+
interceptWrite(resolvedPath, data, "write")
|
|
241
|
+
.then(result => {
|
|
242
|
+
if (!result.allowed) {
|
|
243
|
+
const error = new BlockedWriteError(result.verdict);
|
|
244
|
+
if (callback) callback(error);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
originalWriteFile.call(fs, filePath, data, options, callback);
|
|
249
|
+
})
|
|
250
|
+
.catch(err => {
|
|
251
|
+
// On error, proceed with write (fail-open)
|
|
252
|
+
originalWriteFile.call(fs, filePath, data, options, callback);
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* Create patched writeFileSync
|
|
258
|
+
*/
|
|
259
|
+
function patchedWriteFileSync(filePath, data, options) {
|
|
260
|
+
const resolvedPath = path.resolve(filePath);
|
|
261
|
+
|
|
262
|
+
if (!shouldIntercept(resolvedPath)) {
|
|
263
|
+
return originalWriteFileSync.call(fs, filePath, data, options);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
const result = interceptWriteSync(resolvedPath, data, "write");
|
|
267
|
+
|
|
268
|
+
if (!result.allowed) {
|
|
269
|
+
throw new BlockedWriteError({
|
|
270
|
+
summary: "Write blocked by Agent Firewall",
|
|
271
|
+
violations: result.violations
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return originalWriteFileSync.call(fs, filePath, data, options);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Create patched appendFile
|
|
280
|
+
*/
|
|
281
|
+
function patchedAppendFile(filePath, data, options, callback) {
|
|
282
|
+
if (typeof options === "function") {
|
|
283
|
+
callback = options;
|
|
284
|
+
options = {};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
const resolvedPath = path.resolve(filePath);
|
|
288
|
+
|
|
289
|
+
if (!shouldIntercept(resolvedPath)) {
|
|
290
|
+
return originalAppendFile.call(fs, filePath, data, options, callback);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
interceptWrite(resolvedPath, data, "append")
|
|
294
|
+
.then(result => {
|
|
295
|
+
if (!result.allowed) {
|
|
296
|
+
const error = new BlockedWriteError(result.verdict);
|
|
297
|
+
if (callback) callback(error);
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
originalAppendFile.call(fs, filePath, data, options, callback);
|
|
302
|
+
})
|
|
303
|
+
.catch(() => {
|
|
304
|
+
originalAppendFile.call(fs, filePath, data, options, callback);
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Create patched appendFileSync
|
|
310
|
+
*/
|
|
311
|
+
function patchedAppendFileSync(filePath, data, options) {
|
|
312
|
+
const resolvedPath = path.resolve(filePath);
|
|
313
|
+
|
|
314
|
+
if (!shouldIntercept(resolvedPath)) {
|
|
315
|
+
return originalAppendFileSync.call(fs, filePath, data, options);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const result = interceptWriteSync(resolvedPath, data, "append");
|
|
319
|
+
|
|
320
|
+
if (!result.allowed) {
|
|
321
|
+
throw new BlockedWriteError({
|
|
322
|
+
summary: "Append blocked by Agent Firewall",
|
|
323
|
+
violations: result.violations
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return originalAppendFileSync.call(fs, filePath, data, options);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Create patched unlink (delete)
|
|
332
|
+
*/
|
|
333
|
+
function patchedUnlink(filePath, callback) {
|
|
334
|
+
const resolvedPath = path.resolve(filePath);
|
|
335
|
+
|
|
336
|
+
if (!shouldIntercept(resolvedPath)) {
|
|
337
|
+
return originalUnlink.call(fs, filePath, callback);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
interceptWrite(resolvedPath, null, "delete")
|
|
341
|
+
.then(result => {
|
|
342
|
+
if (!result.allowed) {
|
|
343
|
+
const error = new BlockedWriteError(result.verdict);
|
|
344
|
+
if (callback) callback(error);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
originalUnlink.call(fs, filePath, callback);
|
|
349
|
+
})
|
|
350
|
+
.catch(() => {
|
|
351
|
+
originalUnlink.call(fs, filePath, callback);
|
|
352
|
+
});
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Create patched unlinkSync
|
|
357
|
+
*/
|
|
358
|
+
function patchedUnlinkSync(filePath) {
|
|
359
|
+
const resolvedPath = path.resolve(filePath);
|
|
360
|
+
|
|
361
|
+
if (!shouldIntercept(resolvedPath)) {
|
|
362
|
+
return originalUnlinkSync.call(fs, filePath);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
const result = interceptWriteSync(resolvedPath, null, "delete");
|
|
366
|
+
|
|
367
|
+
if (!result.allowed) {
|
|
368
|
+
throw new BlockedWriteError({
|
|
369
|
+
summary: "Delete blocked by Agent Firewall",
|
|
370
|
+
violations: result.violations
|
|
371
|
+
});
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
return originalUnlinkSync.call(fs, filePath);
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
378
|
+
// PUBLIC API
|
|
379
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Activate file system interception
|
|
383
|
+
*
|
|
384
|
+
* @param {Object} options
|
|
385
|
+
* @param {string} options.projectRoot - Project root directory
|
|
386
|
+
* @param {Object} options.gateway - EnforcementGateway instance
|
|
387
|
+
* @param {string} options.agentId - Agent identifier
|
|
388
|
+
* @param {Function} options.onBlock - Callback when write is blocked
|
|
389
|
+
*/
|
|
390
|
+
function activate(options = {}) {
|
|
391
|
+
if (isActive) {
|
|
392
|
+
console.warn("[FS Interceptor] Already active");
|
|
393
|
+
return;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
projectRoot = options.projectRoot || process.cwd();
|
|
397
|
+
gateway = options.gateway || null;
|
|
398
|
+
agentId = options.agentId || "unknown";
|
|
399
|
+
onBlock = options.onBlock || null;
|
|
400
|
+
|
|
401
|
+
// Patch fs module
|
|
402
|
+
fs.writeFile = patchedWriteFile;
|
|
403
|
+
fs.writeFileSync = patchedWriteFileSync;
|
|
404
|
+
fs.appendFile = patchedAppendFile;
|
|
405
|
+
fs.appendFileSync = patchedAppendFileSync;
|
|
406
|
+
fs.unlink = patchedUnlink;
|
|
407
|
+
fs.unlinkSync = patchedUnlinkSync;
|
|
408
|
+
|
|
409
|
+
isActive = true;
|
|
410
|
+
|
|
411
|
+
console.log(`[FS Interceptor] Activated for ${projectRoot}`);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Deactivate file system interception
|
|
416
|
+
*/
|
|
417
|
+
function deactivate() {
|
|
418
|
+
if (!isActive) return;
|
|
419
|
+
|
|
420
|
+
// Restore original functions
|
|
421
|
+
fs.writeFile = originalWriteFile;
|
|
422
|
+
fs.writeFileSync = originalWriteFileSync;
|
|
423
|
+
fs.appendFile = originalAppendFile;
|
|
424
|
+
fs.appendFileSync = originalAppendFileSync;
|
|
425
|
+
fs.unlink = originalUnlink;
|
|
426
|
+
fs.unlinkSync = originalUnlinkSync;
|
|
427
|
+
|
|
428
|
+
isActive = false;
|
|
429
|
+
gateway = null;
|
|
430
|
+
|
|
431
|
+
console.log("[FS Interceptor] Deactivated");
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Check if interceptor is active
|
|
436
|
+
*/
|
|
437
|
+
function isActivated() {
|
|
438
|
+
return isActive;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Update gateway instance
|
|
443
|
+
*/
|
|
444
|
+
function setGateway(newGateway) {
|
|
445
|
+
gateway = newGateway;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Update agent ID
|
|
450
|
+
*/
|
|
451
|
+
function setAgentId(newAgentId) {
|
|
452
|
+
agentId = newAgentId;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Add pattern to always-allow list
|
|
457
|
+
*/
|
|
458
|
+
function addAllowPattern(pattern) {
|
|
459
|
+
if (pattern instanceof RegExp) {
|
|
460
|
+
ALWAYS_ALLOW_PATTERNS.push(pattern);
|
|
461
|
+
} else {
|
|
462
|
+
ALWAYS_ALLOW_PATTERNS.push(new RegExp(pattern));
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* Get interception stats
|
|
468
|
+
*/
|
|
469
|
+
function getStats() {
|
|
470
|
+
return {
|
|
471
|
+
isActive,
|
|
472
|
+
projectRoot,
|
|
473
|
+
agentId,
|
|
474
|
+
allowPatterns: ALWAYS_ALLOW_PATTERNS.length,
|
|
475
|
+
sourcePatterns: SOURCE_FILE_PATTERNS.length,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
480
|
+
// EXPORTS
|
|
481
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
482
|
+
|
|
483
|
+
module.exports = {
|
|
484
|
+
activate,
|
|
485
|
+
deactivate,
|
|
486
|
+
isActivated,
|
|
487
|
+
setGateway,
|
|
488
|
+
setAgentId,
|
|
489
|
+
addAllowPattern,
|
|
490
|
+
getStats,
|
|
491
|
+
BlockedWriteError,
|
|
492
|
+
shouldIntercept,
|
|
493
|
+
// Expose originals for testing
|
|
494
|
+
_originals: {
|
|
495
|
+
writeFile: originalWriteFile,
|
|
496
|
+
writeFileSync: originalWriteFileSync,
|
|
497
|
+
appendFile: originalAppendFile,
|
|
498
|
+
appendFileSync: originalAppendFileSync,
|
|
499
|
+
unlink: originalUnlink,
|
|
500
|
+
unlinkSync: originalUnlinkSync,
|
|
501
|
+
},
|
|
502
|
+
};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Interception Layer Index
|
|
3
|
+
*
|
|
4
|
+
* Exports all interception mechanisms for the Agent Firewall.
|
|
5
|
+
*
|
|
6
|
+
* @module interception
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
"use strict";
|
|
10
|
+
|
|
11
|
+
const fsInterceptor = require("./fs-interceptor");
|
|
12
|
+
|
|
13
|
+
module.exports = {
|
|
14
|
+
// FS Interceptor
|
|
15
|
+
activateFsInterceptor: fsInterceptor.activate,
|
|
16
|
+
deactivateFsInterceptor: fsInterceptor.deactivate,
|
|
17
|
+
isFsInterceptorActive: fsInterceptor.isActivated,
|
|
18
|
+
setFsGateway: fsInterceptor.setGateway,
|
|
19
|
+
BlockedWriteError: fsInterceptor.BlockedWriteError,
|
|
20
|
+
|
|
21
|
+
// Re-export full module for advanced use
|
|
22
|
+
fsInterceptor,
|
|
23
|
+
};
|