@agenshield/interceptor 0.1.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/register.js ADDED
@@ -0,0 +1,1208 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (let key of __getOwnPropNames(from))
11
+ if (!__hasOwnProp.call(to, key) && key !== except)
12
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
13
+ }
14
+ return to;
15
+ };
16
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
17
+ // If the importer is in node compatibility mode or this is not an ESM
18
+ // file that has been converted to a CommonJS file using a Babel-
19
+ // compatible transform (i.e. "__esModule" has not been set), then set
20
+ // "default" to the CommonJS "module.exports" for node compatibility.
21
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
22
+ mod
23
+ ));
24
+
25
+ // libs/shield-interceptor/src/config.ts
26
+ function createConfig(overrides) {
27
+ const env = process.env;
28
+ return {
29
+ socketPath: env["AGENSHIELD_SOCKET"] || "/var/run/agenshield/agenshield.sock",
30
+ httpHost: env["AGENSHIELD_HOST"] || "localhost",
31
+ httpPort: parseInt(env["AGENSHIELD_PORT"] || "5201", 10),
32
+ failOpen: env["AGENSHIELD_FAIL_OPEN"] === "true",
33
+ logLevel: env["AGENSHIELD_LOG_LEVEL"] || "warn",
34
+ interceptFetch: env["AGENSHIELD_INTERCEPT_FETCH"] !== "false",
35
+ interceptHttp: env["AGENSHIELD_INTERCEPT_HTTP"] !== "false",
36
+ interceptWs: env["AGENSHIELD_INTERCEPT_WS"] !== "false",
37
+ interceptFs: env["AGENSHIELD_INTERCEPT_FS"] !== "false",
38
+ interceptExec: env["AGENSHIELD_INTERCEPT_EXEC"] !== "false",
39
+ timeout: parseInt(env["AGENSHIELD_TIMEOUT"] || "30000", 10),
40
+ ...overrides
41
+ };
42
+ }
43
+ var defaultConfig = createConfig();
44
+
45
+ // libs/shield-interceptor/src/errors.ts
46
+ var AgenShieldError = class extends Error {
47
+ code;
48
+ operation;
49
+ target;
50
+ constructor(message, code, options) {
51
+ super(message);
52
+ this.name = "AgenShieldError";
53
+ this.code = code;
54
+ this.operation = options?.operation;
55
+ this.target = options?.target;
56
+ Error.captureStackTrace?.(this, this.constructor);
57
+ }
58
+ };
59
+ var PolicyDeniedError = class extends AgenShieldError {
60
+ policyId;
61
+ constructor(message, options) {
62
+ super(message, "POLICY_DENIED", options);
63
+ this.name = "PolicyDeniedError";
64
+ this.policyId = options?.policyId;
65
+ }
66
+ };
67
+ var BrokerUnavailableError = class extends AgenShieldError {
68
+ constructor(message = "AgenShield broker is unavailable") {
69
+ super(message, "BROKER_UNAVAILABLE");
70
+ this.name = "BrokerUnavailableError";
71
+ }
72
+ };
73
+ var TimeoutError = class extends AgenShieldError {
74
+ constructor(message = "Request timed out") {
75
+ super(message, "TIMEOUT");
76
+ this.name = "TimeoutError";
77
+ }
78
+ };
79
+
80
+ // libs/shield-interceptor/src/interceptors/base.ts
81
+ var BaseInterceptor = class {
82
+ client;
83
+ policyEvaluator;
84
+ eventReporter;
85
+ failOpen;
86
+ installed = false;
87
+ constructor(options) {
88
+ this.client = options.client;
89
+ this.policyEvaluator = options.policyEvaluator;
90
+ this.eventReporter = options.eventReporter;
91
+ this.failOpen = options.failOpen;
92
+ }
93
+ /**
94
+ * Check if the interceptor is installed
95
+ */
96
+ isInstalled() {
97
+ return this.installed;
98
+ }
99
+ /**
100
+ * Check policy and handle the result
101
+ */
102
+ async checkPolicy(operation, target) {
103
+ const startTime = Date.now();
104
+ try {
105
+ this.eventReporter.intercept(operation, target);
106
+ const result = await this.policyEvaluator.check(operation, target);
107
+ if (!result.allowed) {
108
+ this.eventReporter.deny(operation, target, result.policyId, result.reason);
109
+ throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
110
+ operation,
111
+ target,
112
+ policyId: result.policyId
113
+ });
114
+ }
115
+ this.eventReporter.allow(
116
+ operation,
117
+ target,
118
+ result.policyId,
119
+ Date.now() - startTime
120
+ );
121
+ } catch (error) {
122
+ if (error instanceof PolicyDeniedError) {
123
+ throw error;
124
+ }
125
+ if (this.failOpen) {
126
+ this.eventReporter.error(
127
+ operation,
128
+ target,
129
+ `Broker unavailable, failing open: ${error.message}`
130
+ );
131
+ return;
132
+ }
133
+ throw new BrokerUnavailableError(error.message);
134
+ }
135
+ }
136
+ /**
137
+ * Log a debug message
138
+ */
139
+ debug(message) {
140
+ console.debug(`[AgenShield:${this.constructor.name}] ${message}`);
141
+ }
142
+ };
143
+
144
+ // libs/shield-interceptor/src/interceptors/fetch.ts
145
+ var FetchInterceptor = class extends BaseInterceptor {
146
+ originalFetch = null;
147
+ constructor(options) {
148
+ super(options);
149
+ }
150
+ install() {
151
+ if (this.installed) return;
152
+ this.originalFetch = globalThis.fetch;
153
+ globalThis.fetch = this.interceptedFetch.bind(this);
154
+ this.installed = true;
155
+ }
156
+ uninstall() {
157
+ if (!this.installed || !this.originalFetch) return;
158
+ globalThis.fetch = this.originalFetch;
159
+ this.originalFetch = null;
160
+ this.installed = false;
161
+ }
162
+ async interceptedFetch(input, init) {
163
+ if (!this.originalFetch) {
164
+ throw new Error("Original fetch not available");
165
+ }
166
+ let url;
167
+ if (typeof input === "string") {
168
+ url = input;
169
+ } else if (input instanceof URL) {
170
+ url = input.toString();
171
+ } else {
172
+ url = input.url;
173
+ }
174
+ if (this.isBrokerUrl(url)) {
175
+ return this.originalFetch(input, init);
176
+ }
177
+ await this.checkPolicy("http_request", url);
178
+ try {
179
+ const method = init?.method || "GET";
180
+ const headers = {};
181
+ if (init?.headers) {
182
+ if (init.headers instanceof Headers) {
183
+ init.headers.forEach((value, key) => {
184
+ headers[key] = value;
185
+ });
186
+ } else if (Array.isArray(init.headers)) {
187
+ for (const [key, value] of init.headers) {
188
+ headers[key] = value;
189
+ }
190
+ } else {
191
+ Object.assign(headers, init.headers);
192
+ }
193
+ }
194
+ let body;
195
+ if (init?.body) {
196
+ if (typeof init.body === "string") {
197
+ body = init.body;
198
+ } else if (init.body instanceof ArrayBuffer) {
199
+ body = Buffer.from(init.body).toString("base64");
200
+ } else {
201
+ body = String(init.body);
202
+ }
203
+ }
204
+ const result = await this.client.request("http_request", {
205
+ url,
206
+ method,
207
+ headers,
208
+ body
209
+ });
210
+ const responseHeaders = new Headers(result.headers);
211
+ return new Response(result.body, {
212
+ status: result.status,
213
+ statusText: result.statusText,
214
+ headers: responseHeaders
215
+ });
216
+ } catch (error) {
217
+ if (error.name === "PolicyDeniedError") {
218
+ throw error;
219
+ }
220
+ if (this.failOpen) {
221
+ return this.originalFetch(input, init);
222
+ }
223
+ throw error;
224
+ }
225
+ }
226
+ isBrokerUrl(url) {
227
+ try {
228
+ const parsed = new URL(url);
229
+ return (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1") && parsed.port === "5200";
230
+ } catch {
231
+ return false;
232
+ }
233
+ }
234
+ };
235
+
236
+ // libs/shield-interceptor/src/interceptors/http.ts
237
+ var httpModule = require("node:http");
238
+ var httpsModule = require("node:https");
239
+ var HttpInterceptor = class extends BaseInterceptor {
240
+ originalHttpRequest = null;
241
+ originalHttpGet = null;
242
+ originalHttpsRequest = null;
243
+ originalHttpsGet = null;
244
+ constructor(options) {
245
+ super(options);
246
+ }
247
+ install() {
248
+ if (this.installed) return;
249
+ this.originalHttpRequest = httpModule.request;
250
+ this.originalHttpGet = httpModule.get;
251
+ this.originalHttpsRequest = httpsModule.request;
252
+ this.originalHttpsGet = httpsModule.get;
253
+ httpModule.request = this.createInterceptedRequest("http", this.originalHttpRequest);
254
+ httpModule.get = this.createInterceptedGet("http", this.originalHttpGet);
255
+ httpsModule.request = this.createInterceptedRequest("https", this.originalHttpsRequest);
256
+ httpsModule.get = this.createInterceptedGet("https", this.originalHttpsGet);
257
+ this.installed = true;
258
+ }
259
+ uninstall() {
260
+ if (!this.installed) return;
261
+ if (this.originalHttpRequest) {
262
+ httpModule.request = this.originalHttpRequest;
263
+ }
264
+ if (this.originalHttpGet) {
265
+ httpModule.get = this.originalHttpGet;
266
+ }
267
+ if (this.originalHttpsRequest) {
268
+ httpsModule.request = this.originalHttpsRequest;
269
+ }
270
+ if (this.originalHttpsGet) {
271
+ httpsModule.get = this.originalHttpsGet;
272
+ }
273
+ this.originalHttpRequest = null;
274
+ this.originalHttpGet = null;
275
+ this.originalHttpsRequest = null;
276
+ this.originalHttpsGet = null;
277
+ this.installed = false;
278
+ }
279
+ createInterceptedRequest(protocol, original) {
280
+ const self = this;
281
+ return function interceptedRequest(urlOrOptions, optionsOrCallback, callback) {
282
+ let url;
283
+ let options;
284
+ let cb;
285
+ if (typeof urlOrOptions === "string" || urlOrOptions instanceof URL) {
286
+ url = urlOrOptions.toString();
287
+ options = typeof optionsOrCallback === "object" ? optionsOrCallback : {};
288
+ cb = typeof optionsOrCallback === "function" ? optionsOrCallback : callback;
289
+ } else {
290
+ options = urlOrOptions;
291
+ url = `${protocol}://${options.hostname || options.host || "localhost"}:${options.port || (protocol === "https" ? 443 : 80)}${options.path || "/"}`;
292
+ cb = optionsOrCallback;
293
+ }
294
+ if (self.isBrokerUrl(url)) {
295
+ return original.call(
296
+ protocol === "http" ? httpModule : httpsModule,
297
+ urlOrOptions,
298
+ optionsOrCallback,
299
+ callback
300
+ );
301
+ }
302
+ const req = original.call(
303
+ protocol === "http" ? httpModule : httpsModule,
304
+ urlOrOptions,
305
+ optionsOrCallback,
306
+ callback
307
+ );
308
+ self.eventReporter.intercept("http_request", url);
309
+ self.checkPolicy("http_request", url).catch((error) => {
310
+ req.destroy(error);
311
+ });
312
+ return req;
313
+ };
314
+ }
315
+ createInterceptedGet(protocol, original) {
316
+ const interceptedRequest = this.createInterceptedRequest(
317
+ protocol,
318
+ protocol === "http" ? this.originalHttpRequest : this.originalHttpsRequest
319
+ );
320
+ return function interceptedGet(urlOrOptions, optionsOrCallback, callback) {
321
+ const req = interceptedRequest(urlOrOptions, optionsOrCallback, callback);
322
+ req.end();
323
+ return req;
324
+ };
325
+ }
326
+ isBrokerUrl(url) {
327
+ try {
328
+ const parsed = new URL(url);
329
+ return (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1") && parsed.port === "5200";
330
+ } catch {
331
+ return false;
332
+ }
333
+ }
334
+ };
335
+
336
+ // libs/shield-interceptor/src/interceptors/websocket.ts
337
+ var WebSocketInterceptor = class extends BaseInterceptor {
338
+ originalWebSocket = null;
339
+ constructor(options) {
340
+ super(options);
341
+ }
342
+ install() {
343
+ if (this.installed) return;
344
+ if (typeof globalThis.WebSocket === "undefined") {
345
+ this.debug("WebSocket not available in this environment");
346
+ return;
347
+ }
348
+ this.originalWebSocket = globalThis.WebSocket;
349
+ const self = this;
350
+ const OriginalWebSocket = this.originalWebSocket;
351
+ class InterceptedWebSocket extends OriginalWebSocket {
352
+ constructor(url, protocols) {
353
+ const urlString = url.toString();
354
+ if (self.isBrokerUrl(urlString)) {
355
+ super(url, protocols);
356
+ return;
357
+ }
358
+ self.eventReporter.intercept("websocket", urlString);
359
+ super(url, protocols);
360
+ self.checkPolicy("websocket", urlString).catch((error) => {
361
+ this.close(1008, "Policy denied");
362
+ const errorEvent = new Event("error");
363
+ this.dispatchEvent(errorEvent);
364
+ });
365
+ }
366
+ }
367
+ globalThis.WebSocket = InterceptedWebSocket;
368
+ this.installed = true;
369
+ }
370
+ uninstall() {
371
+ if (!this.installed || !this.originalWebSocket) return;
372
+ globalThis.WebSocket = this.originalWebSocket;
373
+ this.originalWebSocket = null;
374
+ this.installed = false;
375
+ }
376
+ isBrokerUrl(url) {
377
+ try {
378
+ const parsed = new URL(url);
379
+ return (parsed.hostname === "localhost" || parsed.hostname === "127.0.0.1") && parsed.port === "5200";
380
+ } catch {
381
+ return false;
382
+ }
383
+ }
384
+ };
385
+
386
+ // libs/shield-interceptor/src/client/sync-client.ts
387
+ var import_node_child_process = require("node:child_process");
388
+ var fs = __toESM(require("node:fs"), 1);
389
+ var import_node_crypto = require("node:crypto");
390
+ var _existsSync = fs.existsSync.bind(fs);
391
+ var _readFileSync = fs.readFileSync.bind(fs);
392
+ var _unlinkSync = fs.unlinkSync.bind(fs);
393
+ var _spawnSync = import_node_child_process.spawnSync;
394
+ var _execSync = import_node_child_process.execSync;
395
+ var SyncClient = class {
396
+ socketPath;
397
+ httpHost;
398
+ httpPort;
399
+ timeout;
400
+ constructor(options) {
401
+ this.socketPath = options.socketPath;
402
+ this.httpHost = options.httpHost;
403
+ this.httpPort = options.httpPort;
404
+ this.timeout = options.timeout;
405
+ }
406
+ /**
407
+ * Send a synchronous request to the broker
408
+ */
409
+ request(method, params) {
410
+ try {
411
+ return this.socketRequestSync(method, params);
412
+ } catch {
413
+ return this.httpRequestSync(method, params);
414
+ }
415
+ }
416
+ /**
417
+ * Synchronous socket request
418
+ *
419
+ * This uses a blocking approach with a temporary file for the response.
420
+ */
421
+ socketRequestSync(method, params) {
422
+ const id = (0, import_node_crypto.randomUUID)();
423
+ const request = JSON.stringify({
424
+ jsonrpc: "2.0",
425
+ id,
426
+ method,
427
+ params
428
+ }) + "\n";
429
+ const tmpFile = `/tmp/agenshield-sync-${id}.json`;
430
+ const script = `
431
+ const net = require('net');
432
+ const fs = require('fs');
433
+
434
+ const socket = net.createConnection('${this.socketPath}');
435
+ let data = '';
436
+
437
+ socket.on('connect', () => {
438
+ socket.write(${JSON.stringify(request)});
439
+ });
440
+
441
+ socket.on('data', (chunk) => {
442
+ data += chunk.toString();
443
+ if (data.includes('\\n')) {
444
+ socket.end();
445
+ fs.writeFileSync('${tmpFile}', data.split('\\n')[0]);
446
+ }
447
+ });
448
+
449
+ socket.on('error', (err) => {
450
+ fs.writeFileSync('${tmpFile}', JSON.stringify({ error: err.message }));
451
+ });
452
+
453
+ setTimeout(() => {
454
+ socket.destroy();
455
+ fs.writeFileSync('${tmpFile}', JSON.stringify({ error: 'timeout' }));
456
+ }, ${this.timeout});
457
+ `;
458
+ try {
459
+ _spawnSync("node", ["-e", script], {
460
+ timeout: this.timeout + 1e3,
461
+ stdio: "ignore"
462
+ });
463
+ if (_existsSync(tmpFile)) {
464
+ const response = JSON.parse(_readFileSync(tmpFile, "utf-8"));
465
+ _unlinkSync(tmpFile);
466
+ if (response.error) {
467
+ throw new Error(response.error);
468
+ }
469
+ return response.result;
470
+ }
471
+ throw new Error("No response from broker");
472
+ } finally {
473
+ try {
474
+ if (_existsSync(tmpFile)) {
475
+ _unlinkSync(tmpFile);
476
+ }
477
+ } catch {
478
+ }
479
+ }
480
+ }
481
+ /**
482
+ * Synchronous HTTP request using curl
483
+ */
484
+ httpRequestSync(method, params) {
485
+ const url = `http://${this.httpHost}:${this.httpPort}/rpc`;
486
+ const id = (0, import_node_crypto.randomUUID)();
487
+ const request = JSON.stringify({
488
+ jsonrpc: "2.0",
489
+ id,
490
+ method,
491
+ params
492
+ });
493
+ try {
494
+ const result = _execSync(
495
+ `curl -s -X POST -H "Content-Type: application/json" -d '${request.replace(/'/g, "\\'")}' "${url}"`,
496
+ {
497
+ timeout: this.timeout,
498
+ encoding: "utf-8"
499
+ }
500
+ );
501
+ const response = JSON.parse(result);
502
+ if (response.error) {
503
+ throw new Error(response.error.message);
504
+ }
505
+ return response.result;
506
+ } catch (error) {
507
+ throw new Error(`Sync request failed: ${error.message}`);
508
+ }
509
+ }
510
+ /**
511
+ * Check if broker is available
512
+ */
513
+ ping() {
514
+ try {
515
+ this.request("ping", {});
516
+ return true;
517
+ } catch {
518
+ return false;
519
+ }
520
+ }
521
+ };
522
+
523
+ // libs/shield-interceptor/src/interceptors/child-process.ts
524
+ var childProcessModule = require("node:child_process");
525
+ var ChildProcessInterceptor = class extends BaseInterceptor {
526
+ syncClient;
527
+ originalExec = null;
528
+ originalExecSync = null;
529
+ originalSpawn = null;
530
+ originalSpawnSync = null;
531
+ originalExecFile = null;
532
+ originalFork = null;
533
+ constructor(options) {
534
+ super(options);
535
+ this.syncClient = new SyncClient({
536
+ socketPath: "/var/run/agenshield/agenshield.sock",
537
+ httpHost: "localhost",
538
+ httpPort: 5201,
539
+ // Broker uses 5201
540
+ timeout: 3e4
541
+ });
542
+ }
543
+ install() {
544
+ if (this.installed) return;
545
+ this.originalExec = childProcessModule.exec;
546
+ this.originalExecSync = childProcessModule.execSync;
547
+ this.originalSpawn = childProcessModule.spawn;
548
+ this.originalSpawnSync = childProcessModule.spawnSync;
549
+ this.originalExecFile = childProcessModule.execFile;
550
+ this.originalFork = childProcessModule.fork;
551
+ childProcessModule.exec = this.createInterceptedExec();
552
+ childProcessModule.execSync = this.createInterceptedExecSync();
553
+ childProcessModule.spawn = this.createInterceptedSpawn();
554
+ childProcessModule.spawnSync = this.createInterceptedSpawnSync();
555
+ childProcessModule.execFile = this.createInterceptedExecFile();
556
+ childProcessModule.fork = this.createInterceptedFork();
557
+ this.installed = true;
558
+ }
559
+ uninstall() {
560
+ if (!this.installed) return;
561
+ if (this.originalExec) childProcessModule.exec = this.originalExec;
562
+ if (this.originalExecSync) childProcessModule.execSync = this.originalExecSync;
563
+ if (this.originalSpawn) childProcessModule.spawn = this.originalSpawn;
564
+ if (this.originalSpawnSync) childProcessModule.spawnSync = this.originalSpawnSync;
565
+ if (this.originalExecFile) childProcessModule.execFile = this.originalExecFile;
566
+ if (this.originalFork) childProcessModule.fork = this.originalFork;
567
+ this.originalExec = null;
568
+ this.originalExecSync = null;
569
+ this.originalSpawn = null;
570
+ this.originalSpawnSync = null;
571
+ this.originalExecFile = null;
572
+ this.originalFork = null;
573
+ this.installed = false;
574
+ }
575
+ createInterceptedExec() {
576
+ const self = this;
577
+ const original = this.originalExec;
578
+ return function interceptedExec(command, ...args) {
579
+ const callback = typeof args[args.length - 1] === "function" ? args.pop() : void 0;
580
+ self.eventReporter.intercept("exec", command);
581
+ self.checkPolicy("exec", command).then(() => {
582
+ original(command, ...args, callback);
583
+ }).catch((error) => {
584
+ if (callback) {
585
+ callback(error, "", "");
586
+ }
587
+ });
588
+ return original('echo ""');
589
+ };
590
+ }
591
+ createInterceptedExecSync() {
592
+ const self = this;
593
+ const original = this.originalExecSync;
594
+ const interceptedExecSync = function(command, options) {
595
+ self.eventReporter.intercept("exec", command);
596
+ try {
597
+ const result = self.syncClient.request(
598
+ "policy_check",
599
+ { operation: "exec", target: command }
600
+ );
601
+ if (!result.allowed) {
602
+ throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
603
+ operation: "exec",
604
+ target: command
605
+ });
606
+ }
607
+ } catch (error) {
608
+ if (error instanceof PolicyDeniedError) {
609
+ throw error;
610
+ }
611
+ if (!self.failOpen) {
612
+ throw error;
613
+ }
614
+ }
615
+ return original(command, options);
616
+ };
617
+ return interceptedExecSync;
618
+ }
619
+ createInterceptedSpawn() {
620
+ const self = this;
621
+ const original = this.originalSpawn;
622
+ const interceptedSpawn = function(command, args, options) {
623
+ const fullCommand = args ? `${command} ${args.join(" ")}` : command;
624
+ self.eventReporter.intercept("exec", fullCommand);
625
+ self.checkPolicy("exec", fullCommand).catch((error) => {
626
+ self.eventReporter.error("exec", fullCommand, error.message);
627
+ });
628
+ return original(command, args, options || {});
629
+ };
630
+ return interceptedSpawn;
631
+ }
632
+ createInterceptedSpawnSync() {
633
+ const self = this;
634
+ const original = this.originalSpawnSync;
635
+ return function interceptedSpawnSync(command, args, options) {
636
+ const fullCommand = args ? `${command} ${args.join(" ")}` : command;
637
+ self.eventReporter.intercept("exec", fullCommand);
638
+ try {
639
+ const result = self.syncClient.request(
640
+ "policy_check",
641
+ { operation: "exec", target: fullCommand }
642
+ );
643
+ if (!result.allowed) {
644
+ return {
645
+ pid: -1,
646
+ output: [],
647
+ stdout: Buffer.alloc(0),
648
+ stderr: Buffer.from(result.reason || "Policy denied"),
649
+ status: 1,
650
+ signal: null,
651
+ error: new PolicyDeniedError(result.reason || "Policy denied")
652
+ };
653
+ }
654
+ } catch (error) {
655
+ if (!self.failOpen) {
656
+ return {
657
+ pid: -1,
658
+ output: [],
659
+ stdout: Buffer.alloc(0),
660
+ stderr: Buffer.from(error.message),
661
+ status: 1,
662
+ signal: null,
663
+ error
664
+ };
665
+ }
666
+ }
667
+ return original(command, args, options);
668
+ };
669
+ }
670
+ createInterceptedExecFile() {
671
+ const self = this;
672
+ const original = this.originalExecFile;
673
+ return function interceptedExecFile(file, ...args) {
674
+ self.eventReporter.intercept("exec", file);
675
+ self.checkPolicy("exec", file).catch((error) => {
676
+ self.eventReporter.error("exec", file, error.message);
677
+ });
678
+ return original(file, ...args);
679
+ };
680
+ }
681
+ createInterceptedFork() {
682
+ const self = this;
683
+ const original = this.originalFork;
684
+ const interceptedFork = function(modulePath, args, options) {
685
+ const pathStr = modulePath.toString();
686
+ self.eventReporter.intercept("exec", `fork:${pathStr}`);
687
+ self.checkPolicy("exec", `fork:${pathStr}`).catch((error) => {
688
+ self.eventReporter.error("exec", pathStr, error.message);
689
+ });
690
+ return original(modulePath, args, options);
691
+ };
692
+ return interceptedFork;
693
+ }
694
+ };
695
+
696
+ // libs/shield-interceptor/src/interceptors/fs.ts
697
+ var fsModule = require("node:fs");
698
+ var fsPromisesModule = require("node:fs/promises");
699
+ function safeOverride(target, prop, value) {
700
+ try {
701
+ target[prop] = value;
702
+ } catch {
703
+ Object.defineProperty(target, prop, {
704
+ value,
705
+ writable: true,
706
+ configurable: true,
707
+ enumerable: true
708
+ });
709
+ }
710
+ }
711
+ var FsInterceptor = class extends BaseInterceptor {
712
+ syncClient;
713
+ originals = /* @__PURE__ */ new Map();
714
+ constructor(options) {
715
+ super(options);
716
+ this.syncClient = new SyncClient({
717
+ socketPath: "/var/run/agenshield/agenshield.sock",
718
+ httpHost: "localhost",
719
+ httpPort: 5201,
720
+ // Broker uses 5201
721
+ timeout: 3e4
722
+ });
723
+ }
724
+ install() {
725
+ if (this.installed) return;
726
+ this.interceptMethod(fsModule, "readFile", "file_read");
727
+ this.interceptMethod(fsModule, "writeFile", "file_write");
728
+ this.interceptMethod(fsModule, "appendFile", "file_write");
729
+ this.interceptMethod(fsModule, "unlink", "file_write");
730
+ this.interceptMethod(fsModule, "mkdir", "file_write");
731
+ this.interceptMethod(fsModule, "rmdir", "file_write");
732
+ this.interceptMethod(fsModule, "rm", "file_write");
733
+ this.interceptMethod(fsModule, "readdir", "file_list");
734
+ this.interceptSyncMethod(fsModule, "readFileSync", "file_read");
735
+ this.interceptSyncMethod(fsModule, "writeFileSync", "file_write");
736
+ this.interceptSyncMethod(fsModule, "appendFileSync", "file_write");
737
+ this.interceptSyncMethod(fsModule, "unlinkSync", "file_write");
738
+ this.interceptSyncMethod(fsModule, "mkdirSync", "file_write");
739
+ this.interceptSyncMethod(fsModule, "rmdirSync", "file_write");
740
+ this.interceptSyncMethod(fsModule, "rmSync", "file_write");
741
+ this.interceptSyncMethod(fsModule, "readdirSync", "file_list");
742
+ this.interceptPromiseMethod(fsPromisesModule, "readFile", "file_read");
743
+ this.interceptPromiseMethod(fsPromisesModule, "writeFile", "file_write");
744
+ this.interceptPromiseMethod(fsPromisesModule, "appendFile", "file_write");
745
+ this.interceptPromiseMethod(fsPromisesModule, "unlink", "file_write");
746
+ this.interceptPromiseMethod(fsPromisesModule, "mkdir", "file_write");
747
+ this.interceptPromiseMethod(fsPromisesModule, "rmdir", "file_write");
748
+ this.interceptPromiseMethod(fsPromisesModule, "rm", "file_write");
749
+ this.interceptPromiseMethod(fsPromisesModule, "readdir", "file_list");
750
+ this.installed = true;
751
+ }
752
+ uninstall() {
753
+ if (!this.installed) return;
754
+ for (const [key, original] of this.originals) {
755
+ const [moduleName, methodName] = key.split(":");
756
+ const module2 = moduleName === "fs" ? fsModule : fsPromisesModule;
757
+ safeOverride(module2, methodName, original);
758
+ }
759
+ this.originals.clear();
760
+ this.installed = false;
761
+ }
762
+ interceptMethod(module2, methodName, operation) {
763
+ const original = module2[methodName];
764
+ if (!original) return;
765
+ const key = `fs:${methodName}`;
766
+ this.originals.set(key, original);
767
+ const self = this;
768
+ safeOverride(module2, methodName, function intercepted(path, ...args) {
769
+ const pathString = path.toString();
770
+ const callback = typeof args[args.length - 1] === "function" ? args.pop() : void 0;
771
+ self.eventReporter.intercept(operation, pathString);
772
+ self.checkPolicy(operation, pathString).then(() => {
773
+ original.call(module2, path, ...args, callback);
774
+ }).catch((error) => {
775
+ if (callback) {
776
+ callback(error);
777
+ }
778
+ });
779
+ });
780
+ }
781
+ interceptSyncMethod(module2, methodName, operation) {
782
+ const original = module2[methodName];
783
+ if (!original) return;
784
+ const key = `fs:${methodName}`;
785
+ this.originals.set(key, original);
786
+ const self = this;
787
+ safeOverride(module2, methodName, function interceptedSync(path, ...args) {
788
+ const pathString = path.toString();
789
+ self.eventReporter.intercept(operation, pathString);
790
+ try {
791
+ const result = self.syncClient.request(
792
+ "policy_check",
793
+ { operation, target: pathString }
794
+ );
795
+ if (!result.allowed) {
796
+ throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
797
+ operation,
798
+ target: pathString
799
+ });
800
+ }
801
+ } catch (error) {
802
+ if (error instanceof PolicyDeniedError) {
803
+ throw error;
804
+ }
805
+ if (!self.failOpen) {
806
+ throw error;
807
+ }
808
+ }
809
+ return original.call(module2, path, ...args);
810
+ });
811
+ }
812
+ interceptPromiseMethod(module2, methodName, operation) {
813
+ const original = module2[methodName];
814
+ if (!original) return;
815
+ const key = `fsPromises:${methodName}`;
816
+ this.originals.set(key, original);
817
+ const self = this;
818
+ safeOverride(module2, methodName, async function interceptedPromise(path, ...args) {
819
+ const pathString = path.toString();
820
+ self.eventReporter.intercept(operation, pathString);
821
+ await self.checkPolicy(operation, pathString);
822
+ return original.call(module2, path, ...args);
823
+ });
824
+ }
825
+ };
826
+
827
+ // libs/shield-interceptor/src/client/http-client.ts
828
+ var net = __toESM(require("node:net"), 1);
829
+ var import_node_crypto2 = require("node:crypto");
830
+ var AsyncClient = class {
831
+ socketPath;
832
+ httpHost;
833
+ httpPort;
834
+ timeout;
835
+ constructor(options) {
836
+ this.socketPath = options.socketPath;
837
+ this.httpHost = options.httpHost;
838
+ this.httpPort = options.httpPort;
839
+ this.timeout = options.timeout;
840
+ }
841
+ /**
842
+ * Send a request to the broker
843
+ */
844
+ async request(method, params) {
845
+ try {
846
+ return await this.socketRequest(method, params);
847
+ } catch (socketError) {
848
+ try {
849
+ return await this.httpRequest(method, params);
850
+ } catch (httpError) {
851
+ throw new BrokerUnavailableError(
852
+ `Failed to connect to broker: ${socketError.message}`
853
+ );
854
+ }
855
+ }
856
+ }
857
+ /**
858
+ * Send request via Unix socket
859
+ */
860
+ async socketRequest(method, params) {
861
+ return new Promise((resolve, reject) => {
862
+ const socket = net.createConnection(this.socketPath);
863
+ const id = (0, import_node_crypto2.randomUUID)();
864
+ let responseData = "";
865
+ let timeoutId;
866
+ socket.on("connect", () => {
867
+ const request = {
868
+ jsonrpc: "2.0",
869
+ id,
870
+ method,
871
+ params
872
+ };
873
+ socket.write(JSON.stringify(request) + "\n");
874
+ timeoutId = setTimeout(() => {
875
+ socket.destroy();
876
+ reject(new TimeoutError());
877
+ }, this.timeout);
878
+ });
879
+ socket.on("data", (data) => {
880
+ responseData += data.toString();
881
+ const newlineIndex = responseData.indexOf("\n");
882
+ if (newlineIndex !== -1) {
883
+ clearTimeout(timeoutId);
884
+ socket.end();
885
+ try {
886
+ const response = JSON.parse(
887
+ responseData.slice(0, newlineIndex)
888
+ );
889
+ if (response.error) {
890
+ reject(new Error(response.error.message));
891
+ } else {
892
+ resolve(response.result);
893
+ }
894
+ } catch {
895
+ reject(new Error("Invalid response from broker"));
896
+ }
897
+ }
898
+ });
899
+ socket.on("error", (error) => {
900
+ clearTimeout(timeoutId);
901
+ reject(error);
902
+ });
903
+ });
904
+ }
905
+ /**
906
+ * Send request via HTTP
907
+ */
908
+ async httpRequest(method, params) {
909
+ const url = `http://${this.httpHost}:${this.httpPort}/rpc`;
910
+ const id = (0, import_node_crypto2.randomUUID)();
911
+ const request = {
912
+ jsonrpc: "2.0",
913
+ id,
914
+ method,
915
+ params
916
+ };
917
+ const controller = new AbortController();
918
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
919
+ try {
920
+ const response = await fetch(url, {
921
+ method: "POST",
922
+ headers: { "Content-Type": "application/json" },
923
+ body: JSON.stringify(request),
924
+ signal: controller.signal
925
+ });
926
+ clearTimeout(timeoutId);
927
+ if (!response.ok) {
928
+ throw new Error(`HTTP error: ${response.status}`);
929
+ }
930
+ const jsonResponse = await response.json();
931
+ if (jsonResponse.error) {
932
+ throw new Error(jsonResponse.error.message);
933
+ }
934
+ return jsonResponse.result;
935
+ } catch (error) {
936
+ clearTimeout(timeoutId);
937
+ if (error.name === "AbortError") {
938
+ throw new TimeoutError();
939
+ }
940
+ throw error;
941
+ }
942
+ }
943
+ /**
944
+ * Check if broker is available
945
+ */
946
+ async ping() {
947
+ try {
948
+ await this.request("ping", {});
949
+ return true;
950
+ } catch {
951
+ return false;
952
+ }
953
+ }
954
+ };
955
+
956
+ // libs/shield-interceptor/src/policy/evaluator.ts
957
+ var PolicyEvaluator = class {
958
+ client;
959
+ constructor(options) {
960
+ this.client = options.client;
961
+ }
962
+ /**
963
+ * Check if an operation is allowed
964
+ * Always queries the daemon for fresh policy decisions
965
+ */
966
+ async check(operation, target) {
967
+ try {
968
+ const result = await this.client.request(
969
+ "policy_check",
970
+ { operation, target }
971
+ );
972
+ return result;
973
+ } catch (error) {
974
+ return {
975
+ allowed: false,
976
+ reason: `Policy check failed: ${error.message}`
977
+ };
978
+ }
979
+ }
980
+ };
981
+
982
+ // libs/shield-interceptor/src/events/reporter.ts
983
+ var EventReporter = class _EventReporter {
984
+ client;
985
+ logLevel;
986
+ queue = [];
987
+ flushInterval = null;
988
+ failedFlushCount = 0;
989
+ static MAX_QUEUE_SIZE = 500;
990
+ static MAX_RETRIES = 3;
991
+ levelPriority = {
992
+ debug: 0,
993
+ info: 1,
994
+ warn: 2,
995
+ error: 3
996
+ };
997
+ constructor(options) {
998
+ this.client = options.client;
999
+ this.logLevel = options.logLevel;
1000
+ this.flushInterval = setInterval(() => this.flush(), 5e3);
1001
+ }
1002
+ /**
1003
+ * Report an event
1004
+ */
1005
+ report(event) {
1006
+ this.queue.push(event);
1007
+ if (this.queue.length > _EventReporter.MAX_QUEUE_SIZE) {
1008
+ this.queue.splice(0, this.queue.length - _EventReporter.MAX_QUEUE_SIZE);
1009
+ }
1010
+ const level = this.getLogLevel(event);
1011
+ if (this.shouldLog(level)) {
1012
+ const prefix = event.type === "allow" ? "\u2713" : event.type === "deny" ? "\u2717" : "\u2022";
1013
+ console[level](`[AgenShield] ${prefix} ${event.operation}: ${event.target}`);
1014
+ }
1015
+ if (this.queue.length >= 100) {
1016
+ this.flush();
1017
+ }
1018
+ }
1019
+ /**
1020
+ * Report an interception
1021
+ */
1022
+ intercept(operation, target) {
1023
+ this.report({
1024
+ type: "intercept",
1025
+ operation,
1026
+ target,
1027
+ timestamp: /* @__PURE__ */ new Date()
1028
+ });
1029
+ }
1030
+ /**
1031
+ * Report an allowed operation
1032
+ */
1033
+ allow(operation, target, policyId, duration) {
1034
+ this.report({
1035
+ type: "allow",
1036
+ operation,
1037
+ target,
1038
+ timestamp: /* @__PURE__ */ new Date(),
1039
+ policyId,
1040
+ duration
1041
+ });
1042
+ }
1043
+ /**
1044
+ * Report a denied operation
1045
+ */
1046
+ deny(operation, target, policyId, reason) {
1047
+ this.report({
1048
+ type: "deny",
1049
+ operation,
1050
+ target,
1051
+ timestamp: /* @__PURE__ */ new Date(),
1052
+ policyId,
1053
+ error: reason
1054
+ });
1055
+ }
1056
+ /**
1057
+ * Report an error
1058
+ */
1059
+ error(operation, target, error) {
1060
+ this.report({
1061
+ type: "error",
1062
+ operation,
1063
+ target,
1064
+ timestamp: /* @__PURE__ */ new Date(),
1065
+ error
1066
+ });
1067
+ }
1068
+ /**
1069
+ * Flush the event queue
1070
+ */
1071
+ async flush() {
1072
+ if (this.queue.length === 0) return;
1073
+ const events = this.queue.splice(0, this.queue.length);
1074
+ try {
1075
+ await this.client.request("events_batch", { events });
1076
+ this.failedFlushCount = 0;
1077
+ } catch {
1078
+ this.failedFlushCount++;
1079
+ if (this.failedFlushCount < _EventReporter.MAX_RETRIES) {
1080
+ this.queue.unshift(...events);
1081
+ if (this.queue.length > _EventReporter.MAX_QUEUE_SIZE) {
1082
+ this.queue.splice(0, this.queue.length - _EventReporter.MAX_QUEUE_SIZE);
1083
+ }
1084
+ }
1085
+ }
1086
+ }
1087
+ /**
1088
+ * Stop the reporter
1089
+ */
1090
+ stop() {
1091
+ if (this.flushInterval) {
1092
+ clearInterval(this.flushInterval);
1093
+ this.flushInterval = null;
1094
+ }
1095
+ this.flush();
1096
+ }
1097
+ /**
1098
+ * Get the appropriate log level for an event
1099
+ */
1100
+ getLogLevel(event) {
1101
+ switch (event.type) {
1102
+ case "intercept":
1103
+ return "debug";
1104
+ case "allow":
1105
+ return "debug";
1106
+ case "deny":
1107
+ return "warn";
1108
+ case "error":
1109
+ return "error";
1110
+ default:
1111
+ return "info";
1112
+ }
1113
+ }
1114
+ /**
1115
+ * Check if we should log at the given level
1116
+ */
1117
+ shouldLog(level) {
1118
+ return this.levelPriority[level] >= this.levelPriority[this.logLevel];
1119
+ }
1120
+ };
1121
+
1122
+ // libs/shield-interceptor/src/installer.ts
1123
+ var installed = null;
1124
+ var client = null;
1125
+ var policyEvaluator = null;
1126
+ var eventReporter = null;
1127
+ function installInterceptors(configOverrides) {
1128
+ if (installed) {
1129
+ console.warn("AgenShield interceptors already installed");
1130
+ return;
1131
+ }
1132
+ const config = createConfig(configOverrides);
1133
+ client = new AsyncClient({
1134
+ socketPath: config.socketPath,
1135
+ httpHost: config.httpHost,
1136
+ httpPort: config.httpPort,
1137
+ timeout: config.timeout
1138
+ });
1139
+ policyEvaluator = new PolicyEvaluator({
1140
+ client
1141
+ });
1142
+ eventReporter = new EventReporter({
1143
+ client,
1144
+ logLevel: config.logLevel
1145
+ });
1146
+ installed = {};
1147
+ if (config.interceptFetch) {
1148
+ installed.fetch = new FetchInterceptor({
1149
+ client,
1150
+ policyEvaluator,
1151
+ eventReporter,
1152
+ failOpen: config.failOpen
1153
+ });
1154
+ installed.fetch.install();
1155
+ log(config, "debug", "Installed fetch interceptor");
1156
+ }
1157
+ if (config.interceptHttp) {
1158
+ installed.http = new HttpInterceptor({
1159
+ client,
1160
+ policyEvaluator,
1161
+ eventReporter,
1162
+ failOpen: config.failOpen
1163
+ });
1164
+ installed.http.install();
1165
+ log(config, "debug", "Installed http/https interceptor");
1166
+ }
1167
+ if (config.interceptWs) {
1168
+ installed.websocket = new WebSocketInterceptor({
1169
+ client,
1170
+ policyEvaluator,
1171
+ eventReporter,
1172
+ failOpen: config.failOpen
1173
+ });
1174
+ installed.websocket.install();
1175
+ log(config, "debug", "Installed WebSocket interceptor");
1176
+ }
1177
+ if (config.interceptExec) {
1178
+ installed.childProcess = new ChildProcessInterceptor({
1179
+ client,
1180
+ policyEvaluator,
1181
+ eventReporter,
1182
+ failOpen: config.failOpen
1183
+ });
1184
+ installed.childProcess.install();
1185
+ log(config, "debug", "Installed child_process interceptor");
1186
+ }
1187
+ if (config.interceptFs) {
1188
+ installed.fs = new FsInterceptor({
1189
+ client,
1190
+ policyEvaluator,
1191
+ eventReporter,
1192
+ failOpen: config.failOpen
1193
+ });
1194
+ installed.fs.install();
1195
+ log(config, "debug", "Installed fs interceptor");
1196
+ }
1197
+ log(config, "info", "AgenShield interceptors installed");
1198
+ }
1199
+ function log(config, level, message) {
1200
+ const levels = { debug: 0, info: 1, warn: 2, error: 3 };
1201
+ if (levels[level] >= levels[config.logLevel]) {
1202
+ console[level](`[AgenShield] ${message}`);
1203
+ }
1204
+ }
1205
+
1206
+ // libs/shield-interceptor/src/register.ts
1207
+ installInterceptors();
1208
+ console.log("[AgenShield] Interceptors registered via ESM loader");