@agenshield/interceptor 0.7.0 → 0.7.2

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/index.js CHANGED
@@ -308,140 +308,6 @@ var FetchInterceptor = class extends BaseInterceptor {
308
308
  }
309
309
  };
310
310
 
311
- // libs/shield-interceptor/src/interceptors/http.ts
312
- var httpModule = require("node:http");
313
- var httpsModule = require("node:https");
314
- var HttpInterceptor = class extends BaseInterceptor {
315
- originalHttpRequest = null;
316
- originalHttpGet = null;
317
- originalHttpsRequest = null;
318
- originalHttpsGet = null;
319
- constructor(options) {
320
- super(options);
321
- }
322
- install() {
323
- if (this.installed) return;
324
- this.originalHttpRequest = httpModule.request;
325
- this.originalHttpGet = httpModule.get;
326
- this.originalHttpsRequest = httpsModule.request;
327
- this.originalHttpsGet = httpsModule.get;
328
- httpModule.request = this.createInterceptedRequest("http", this.originalHttpRequest);
329
- httpModule.get = this.createInterceptedGet("http", this.originalHttpGet);
330
- httpsModule.request = this.createInterceptedRequest("https", this.originalHttpsRequest);
331
- httpsModule.get = this.createInterceptedGet("https", this.originalHttpsGet);
332
- this.installed = true;
333
- }
334
- uninstall() {
335
- if (!this.installed) return;
336
- if (this.originalHttpRequest) {
337
- httpModule.request = this.originalHttpRequest;
338
- }
339
- if (this.originalHttpGet) {
340
- httpModule.get = this.originalHttpGet;
341
- }
342
- if (this.originalHttpsRequest) {
343
- httpsModule.request = this.originalHttpsRequest;
344
- }
345
- if (this.originalHttpsGet) {
346
- httpsModule.get = this.originalHttpsGet;
347
- }
348
- this.originalHttpRequest = null;
349
- this.originalHttpGet = null;
350
- this.originalHttpsRequest = null;
351
- this.originalHttpsGet = null;
352
- this.installed = false;
353
- }
354
- createInterceptedRequest(protocol, original) {
355
- const self = this;
356
- return function interceptedRequest(urlOrOptions, optionsOrCallback, callback) {
357
- let url;
358
- let options;
359
- let cb;
360
- if (typeof urlOrOptions === "string" || urlOrOptions instanceof URL) {
361
- url = urlOrOptions.toString();
362
- options = typeof optionsOrCallback === "object" ? optionsOrCallback : {};
363
- cb = typeof optionsOrCallback === "function" ? optionsOrCallback : callback;
364
- } else {
365
- options = urlOrOptions;
366
- url = `${protocol}://${options.hostname || options.host || "localhost"}:${options.port || (protocol === "https" ? 443 : 80)}${options.path || "/"}`;
367
- cb = optionsOrCallback;
368
- }
369
- if (self.isBrokerUrl(url)) {
370
- return original.call(
371
- protocol === "http" ? httpModule : httpsModule,
372
- urlOrOptions,
373
- optionsOrCallback,
374
- callback
375
- );
376
- }
377
- const req = original.call(
378
- protocol === "http" ? httpModule : httpsModule,
379
- urlOrOptions,
380
- optionsOrCallback,
381
- callback
382
- );
383
- self.eventReporter.intercept("http_request", url);
384
- self.checkPolicy("http_request", url).catch((error) => {
385
- req.destroy(error);
386
- });
387
- return req;
388
- };
389
- }
390
- createInterceptedGet(protocol, original) {
391
- const interceptedRequest = this.createInterceptedRequest(
392
- protocol,
393
- protocol === "http" ? this.originalHttpRequest : this.originalHttpsRequest
394
- );
395
- return function interceptedGet(urlOrOptions, optionsOrCallback, callback) {
396
- const req = interceptedRequest(urlOrOptions, optionsOrCallback, callback);
397
- req.end();
398
- return req;
399
- };
400
- }
401
- };
402
-
403
- // libs/shield-interceptor/src/interceptors/websocket.ts
404
- var WebSocketInterceptor = class extends BaseInterceptor {
405
- originalWebSocket = null;
406
- constructor(options) {
407
- super(options);
408
- }
409
- install() {
410
- if (this.installed) return;
411
- if (typeof globalThis.WebSocket === "undefined") {
412
- this.debug("WebSocket not available in this environment");
413
- return;
414
- }
415
- this.originalWebSocket = globalThis.WebSocket;
416
- const self = this;
417
- const OriginalWebSocket = this.originalWebSocket;
418
- class InterceptedWebSocket extends OriginalWebSocket {
419
- constructor(url, protocols) {
420
- const urlString = url.toString();
421
- if (self.isBrokerUrl(urlString)) {
422
- super(url, protocols);
423
- return;
424
- }
425
- self.eventReporter.intercept("websocket", urlString);
426
- super(url, protocols);
427
- self.checkPolicy("websocket", urlString).catch((error) => {
428
- this.close(1008, "Policy denied");
429
- const errorEvent = new Event("error");
430
- this.dispatchEvent(errorEvent);
431
- });
432
- }
433
- }
434
- globalThis.WebSocket = InterceptedWebSocket;
435
- this.installed = true;
436
- }
437
- uninstall() {
438
- if (!this.installed || !this.originalWebSocket) return;
439
- globalThis.WebSocket = this.originalWebSocket;
440
- this.originalWebSocket = null;
441
- this.installed = false;
442
- }
443
- };
444
-
445
311
  // libs/shield-interceptor/src/client/sync-client.ts
446
312
  var import_node_child_process = require("node:child_process");
447
313
  var fs2 = __toESM(require("node:fs"), 1);
@@ -645,6 +511,202 @@ var SyncClient = class {
645
511
  }
646
512
  };
647
513
 
514
+ // libs/shield-interceptor/src/interceptors/http.ts
515
+ var httpModule = require("node:http");
516
+ var httpsModule = require("node:https");
517
+ var HttpInterceptor = class extends BaseInterceptor {
518
+ syncClient;
519
+ originalHttpRequest = null;
520
+ originalHttpGet = null;
521
+ originalHttpsRequest = null;
522
+ originalHttpsGet = null;
523
+ constructor(options) {
524
+ super(options);
525
+ const config = this.interceptorConfig;
526
+ this.syncClient = new SyncClient({
527
+ socketPath: config?.socketPath || "/var/run/agenshield/agenshield.sock",
528
+ httpHost: config?.httpHost || "localhost",
529
+ httpPort: config?.httpPort || 5201,
530
+ timeout: config?.timeout || 3e4
531
+ });
532
+ }
533
+ install() {
534
+ if (this.installed) return;
535
+ this.originalHttpRequest = httpModule.request;
536
+ this.originalHttpGet = httpModule.get;
537
+ this.originalHttpsRequest = httpsModule.request;
538
+ this.originalHttpsGet = httpsModule.get;
539
+ httpModule.request = this.createInterceptedRequest("http", this.originalHttpRequest);
540
+ httpModule.get = this.createInterceptedGet("http", this.originalHttpGet);
541
+ httpsModule.request = this.createInterceptedRequest("https", this.originalHttpsRequest);
542
+ httpsModule.get = this.createInterceptedGet("https", this.originalHttpsGet);
543
+ this.installed = true;
544
+ }
545
+ uninstall() {
546
+ if (!this.installed) return;
547
+ if (this.originalHttpRequest) {
548
+ httpModule.request = this.originalHttpRequest;
549
+ }
550
+ if (this.originalHttpGet) {
551
+ httpModule.get = this.originalHttpGet;
552
+ }
553
+ if (this.originalHttpsRequest) {
554
+ httpsModule.request = this.originalHttpsRequest;
555
+ }
556
+ if (this.originalHttpsGet) {
557
+ httpsModule.get = this.originalHttpsGet;
558
+ }
559
+ this.originalHttpRequest = null;
560
+ this.originalHttpGet = null;
561
+ this.originalHttpsRequest = null;
562
+ this.originalHttpsGet = null;
563
+ this.installed = false;
564
+ }
565
+ /**
566
+ * Build execution context from config for RPC calls
567
+ */
568
+ getPolicyExecutionContext() {
569
+ const config = this.interceptorConfig;
570
+ return {
571
+ callerType: config?.contextType || "agent",
572
+ skillSlug: config?.contextSkillSlug,
573
+ agentId: config?.contextAgentId,
574
+ depth: 0
575
+ };
576
+ }
577
+ /**
578
+ * Synchronous policy check via SyncClient.
579
+ * Returns the full policy result or null if broker is unavailable and failOpen is true.
580
+ */
581
+ syncPolicyCheck(url) {
582
+ const startTime = Date.now();
583
+ try {
584
+ debugLog(`http.syncPolicyCheck START url=${url}`);
585
+ const context = this.getPolicyExecutionContext();
586
+ const result = this.syncClient.request(
587
+ "policy_check",
588
+ { operation: "http_request", target: url, context }
589
+ );
590
+ debugLog(`http.syncPolicyCheck DONE allowed=${result.allowed} url=${url}`);
591
+ if (!result.allowed) {
592
+ this.eventReporter.deny("http_request", url, result.policyId, result.reason);
593
+ throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
594
+ operation: "http_request",
595
+ target: url,
596
+ policyId: result.policyId
597
+ });
598
+ }
599
+ this.eventReporter.allow("http_request", url, result.policyId, Date.now() - startTime);
600
+ return result;
601
+ } catch (error) {
602
+ if (error instanceof PolicyDeniedError) {
603
+ throw error;
604
+ }
605
+ debugLog(`http.syncPolicyCheck ERROR: ${error.message} url=${url}`);
606
+ if (!this.failOpen) {
607
+ throw error;
608
+ }
609
+ return null;
610
+ }
611
+ }
612
+ createInterceptedRequest(protocol, original) {
613
+ const self = this;
614
+ return function interceptedRequest(urlOrOptions, optionsOrCallback, callback) {
615
+ let url;
616
+ let options;
617
+ let cb;
618
+ if (typeof urlOrOptions === "string" || urlOrOptions instanceof URL) {
619
+ url = urlOrOptions.toString();
620
+ options = typeof optionsOrCallback === "object" ? optionsOrCallback : {};
621
+ cb = typeof optionsOrCallback === "function" ? optionsOrCallback : callback;
622
+ } else {
623
+ options = urlOrOptions;
624
+ url = `${protocol}://${options.hostname || options.host || "localhost"}:${options.port || (protocol === "https" ? 443 : 80)}${options.path || "/"}`;
625
+ cb = optionsOrCallback;
626
+ }
627
+ if (self.isBrokerUrl(url)) {
628
+ return original.call(
629
+ protocol === "http" ? httpModule : httpsModule,
630
+ urlOrOptions,
631
+ optionsOrCallback,
632
+ callback
633
+ );
634
+ }
635
+ self.eventReporter.intercept("http_request", url);
636
+ try {
637
+ self.syncPolicyCheck(url);
638
+ } catch (error) {
639
+ debugLog(`http.request DENIED url=${url}`);
640
+ const mod = protocol === "http" ? httpModule : httpsModule;
641
+ const denied = original.call(mod, "http://0.0.0.0:1", { method: "GET" });
642
+ denied.once("error", () => {
643
+ });
644
+ process.nextTick(() => denied.destroy(error));
645
+ return denied;
646
+ }
647
+ return original.call(
648
+ protocol === "http" ? httpModule : httpsModule,
649
+ urlOrOptions,
650
+ optionsOrCallback,
651
+ callback
652
+ );
653
+ };
654
+ }
655
+ createInterceptedGet(protocol, original) {
656
+ const interceptedRequest = this.createInterceptedRequest(
657
+ protocol,
658
+ protocol === "http" ? this.originalHttpRequest : this.originalHttpsRequest
659
+ );
660
+ return function interceptedGet(urlOrOptions, optionsOrCallback, callback) {
661
+ const req = interceptedRequest(urlOrOptions, optionsOrCallback, callback);
662
+ req.end();
663
+ return req;
664
+ };
665
+ }
666
+ };
667
+
668
+ // libs/shield-interceptor/src/interceptors/websocket.ts
669
+ var WebSocketInterceptor = class extends BaseInterceptor {
670
+ originalWebSocket = null;
671
+ constructor(options) {
672
+ super(options);
673
+ }
674
+ install() {
675
+ if (this.installed) return;
676
+ if (typeof globalThis.WebSocket === "undefined") {
677
+ this.debug("WebSocket not available in this environment");
678
+ return;
679
+ }
680
+ this.originalWebSocket = globalThis.WebSocket;
681
+ const self = this;
682
+ const OriginalWebSocket = this.originalWebSocket;
683
+ class InterceptedWebSocket extends OriginalWebSocket {
684
+ constructor(url, protocols) {
685
+ const urlString = url.toString();
686
+ if (self.isBrokerUrl(urlString)) {
687
+ super(url, protocols);
688
+ return;
689
+ }
690
+ self.eventReporter.intercept("websocket", urlString);
691
+ super(url, protocols);
692
+ self.checkPolicy("websocket", urlString).catch((error) => {
693
+ this.close(1008, "Policy denied");
694
+ const errorEvent = new Event("error");
695
+ this.dispatchEvent(errorEvent);
696
+ });
697
+ }
698
+ }
699
+ globalThis.WebSocket = InterceptedWebSocket;
700
+ this.installed = true;
701
+ }
702
+ uninstall() {
703
+ if (!this.installed || !this.originalWebSocket) return;
704
+ globalThis.WebSocket = this.originalWebSocket;
705
+ this.originalWebSocket = null;
706
+ this.installed = false;
707
+ }
708
+ };
709
+
648
710
  // libs/shield-interceptor/src/seatbelt/profile-manager.ts
649
711
  var fs3 = __toESM(require("node:fs"), 1);
650
712
  var crypto = __toESM(require("node:crypto"), 1);
@@ -938,6 +1000,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
938
1000
  */
939
1001
  syncPolicyCheck(fullCommand) {
940
1002
  this._checking = true;
1003
+ const startTime = Date.now();
941
1004
  try {
942
1005
  debugLog(`cp.syncPolicyCheck START command=${fullCommand}`);
943
1006
  const context = this.getPolicyExecutionContext();
@@ -947,12 +1010,14 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
947
1010
  );
948
1011
  debugLog(`cp.syncPolicyCheck DONE allowed=${result.allowed} command=${fullCommand}`);
949
1012
  if (!result.allowed) {
1013
+ this.eventReporter.deny("exec", fullCommand, result.policyId, result.reason);
950
1014
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
951
1015
  operation: "exec",
952
1016
  target: fullCommand,
953
1017
  policyId: result.policyId
954
1018
  });
955
1019
  }
1020
+ this.eventReporter.allow("exec", fullCommand, result.policyId, Date.now() - startTime);
956
1021
  return result;
957
1022
  } catch (error) {
958
1023
  if (error instanceof PolicyDeniedError) {
@@ -1079,10 +1144,14 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1079
1144
  try {
1080
1145
  policyResult = self.syncPolicyCheck(command);
1081
1146
  } catch (error) {
1147
+ debugLog(`cp.exec DENIED command=${command}`);
1148
+ const denied = self.originalSpawn("false", [], { stdio: "pipe" });
1149
+ denied.once("error", () => {
1150
+ });
1082
1151
  if (callback) {
1083
1152
  process.nextTick(() => callback(error, "", ""));
1084
1153
  }
1085
- return original('echo ""');
1154
+ return denied;
1086
1155
  }
1087
1156
  const wrapped = self.wrapCommandStringWithSeatbelt(command, options, policyResult);
1088
1157
  debugLog(`cp.exec calling original command=${wrapped.command}`);
@@ -1140,6 +1209,8 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1140
1209
  } catch (error) {
1141
1210
  debugLog(`cp.spawn DENIED command=${fullCmd}`);
1142
1211
  const denied = original("false", [], { stdio: "pipe" });
1212
+ denied.once("error", () => {
1213
+ });
1143
1214
  process.nextTick(() => {
1144
1215
  denied.emit("error", error);
1145
1216
  });
@@ -1224,10 +1295,14 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1224
1295
  try {
1225
1296
  policyResult = self.syncPolicyCheck(fullCommand);
1226
1297
  } catch (error) {
1298
+ debugLog(`cp.execFile DENIED command=${fullCommand}`);
1299
+ const denied = self.originalSpawn("false", [], { stdio: "pipe" });
1300
+ denied.once("error", () => {
1301
+ });
1227
1302
  if (callback) {
1228
1303
  process.nextTick(() => callback(error, "", ""));
1229
1304
  }
1230
- return original("false");
1305
+ return denied;
1231
1306
  }
1232
1307
  const wrapped = self.wrapWithSeatbelt(file, args, options, policyResult);
1233
1308
  debugLog(`cp.execFile calling original command=${wrapped.command}`);
@@ -1266,6 +1341,8 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1266
1341
  } catch (error) {
1267
1342
  debugLog(`cp.fork DENIED command=${fullCommand}`);
1268
1343
  const denied = self.originalSpawn("false", [], { stdio: "pipe" });
1344
+ denied.once("error", () => {
1345
+ });
1269
1346
  process.nextTick(() => {
1270
1347
  denied.emit("error", error);
1271
1348
  });
@@ -1813,7 +1890,8 @@ function installInterceptors(configOverrides) {
1813
1890
  policyEvaluator,
1814
1891
  eventReporter,
1815
1892
  failOpen: config.failOpen,
1816
- brokerHttpPort: config.httpPort
1893
+ brokerHttpPort: config.httpPort,
1894
+ config
1817
1895
  });
1818
1896
  installed.http.install();
1819
1897
  log(config, "debug", "Installed http/https interceptor");
@@ -1 +1 @@
1
- {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAOrD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAiBtD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,eAAe,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAC3C,IAAI,CAuGN;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CA6B5C;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,WAAW,GAAG,IAAI,CAE9C"}
1
+ {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../src/installer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAOrD,OAAO,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAiBtD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,eAAe,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAC3C,IAAI,CAwGN;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CA6B5C;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,OAAO,CAErC;AAED;;GAEG;AACH,wBAAgB,SAAS,IAAI,WAAW,GAAG,IAAI,CAE9C"}
@@ -1 +1 @@
1
- {"version":3,"file":"child-process.d.ts","sourceRoot":"","sources":["../../src/interceptors/child-process.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAYzE,qBAAa,uBAAwB,SAAQ,eAAe;IAC1D,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,YAAY,CAAyC;IAC7D,OAAO,CAAC,gBAAgB,CAA6C;IACrE,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,iBAAiB,CAA8C;IACvE,OAAO,CAAC,gBAAgB,CAA6C;IACrE,OAAO,CAAC,YAAY,CAAyC;gBAEjD,OAAO,EAAE,sBAAsB;IAkB3C,OAAO,IAAI,IAAI;IAsBf,SAAS,IAAI,IAAI;IAmBjB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAUjC;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAkCvB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAWtB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA+CxB;;;OAGG;IACH,OAAO,CAAC,6BAA6B;IAsCrC,OAAO,CAAC,qBAAqB;IAsD7B,OAAO,CAAC,yBAAyB;IAwCjC,OAAO,CAAC,sBAAsB;IAiD9B,OAAO,CAAC,0BAA0B;IA0DlC,OAAO,CAAC,yBAAyB;IAoEjC,OAAO,CAAC,qBAAqB;CAyD9B"}
1
+ {"version":3,"file":"child-process.d.ts","sourceRoot":"","sources":["../../src/interceptors/child-process.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAGH,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAYzE,qBAAa,uBAAwB,SAAQ,eAAe;IAC1D,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,YAAY,CAAyC;IAC7D,OAAO,CAAC,gBAAgB,CAA6C;IACrE,OAAO,CAAC,aAAa,CAA0C;IAC/D,OAAO,CAAC,iBAAiB,CAA8C;IACvE,OAAO,CAAC,gBAAgB,CAA6C;IACrE,OAAO,CAAC,YAAY,CAAyC;gBAEjD,OAAO,EAAE,sBAAsB;IAkB3C,OAAO,IAAI,IAAI;IAsBf,SAAS,IAAI,IAAI;IAmBjB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAUjC;;;;OAIG;IACH,OAAO,CAAC,eAAe;IAqCvB;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IAgB1B;;OAEG;IACH,OAAO,CAAC,cAAc;IAWtB;;;OAGG;IACH,OAAO,CAAC,gBAAgB;IA+CxB;;;OAGG;IACH,OAAO,CAAC,6BAA6B;IAsCrC,OAAO,CAAC,qBAAqB;IAyD7B,OAAO,CAAC,yBAAyB;IAwCjC,OAAO,CAAC,sBAAsB;IAqD9B,OAAO,CAAC,0BAA0B;IA0DlC,OAAO,CAAC,yBAAyB;IAwEjC,OAAO,CAAC,qBAAqB;CA4D9B"}
@@ -1,10 +1,14 @@
1
1
  /**
2
2
  * HTTP/HTTPS Interceptor
3
3
  *
4
- * Intercepts Node.js http and https module calls.
4
+ * Intercepts Node.js http and https module calls with synchronous policy
5
+ * checking. Uses SyncClient to block before the request fires, preventing
6
+ * the race condition where async policy checks arrive after the request
7
+ * has already completed.
5
8
  */
6
9
  import { BaseInterceptor, type BaseInterceptorOptions } from './base.js';
7
10
  export declare class HttpInterceptor extends BaseInterceptor {
11
+ private syncClient;
8
12
  private originalHttpRequest;
9
13
  private originalHttpGet;
10
14
  private originalHttpsRequest;
@@ -12,6 +16,15 @@ export declare class HttpInterceptor extends BaseInterceptor {
12
16
  constructor(options: BaseInterceptorOptions);
13
17
  install(): void;
14
18
  uninstall(): void;
19
+ /**
20
+ * Build execution context from config for RPC calls
21
+ */
22
+ private getPolicyExecutionContext;
23
+ /**
24
+ * Synchronous policy check via SyncClient.
25
+ * Returns the full policy result or null if broker is unavailable and failOpen is true.
26
+ */
27
+ private syncPolicyCheck;
15
28
  private createInterceptedRequest;
16
29
  private createInterceptedGet;
17
30
  }
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/interceptors/http.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAQzE,qBAAa,eAAgB,SAAQ,eAAe;IAClD,OAAO,CAAC,mBAAmB,CAAoC;IAC/D,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,gBAAgB,CAAgC;gBAE5C,OAAO,EAAE,sBAAsB;IAI3C,OAAO,IAAI,IAAI;IAkBf,SAAS,IAAI,IAAI;IAuBjB,OAAO,CAAC,wBAAwB;IA0DhC,OAAO,CAAC,oBAAoB;CAoB7B"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/interceptors/http.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAazE,qBAAa,eAAgB,SAAQ,eAAe;IAClD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,mBAAmB,CAAoC;IAC/D,OAAO,CAAC,eAAe,CAAgC;IACvD,OAAO,CAAC,oBAAoB,CAAoC;IAChE,OAAO,CAAC,gBAAgB,CAAgC;gBAE5C,OAAO,EAAE,sBAAsB;IAW3C,OAAO,IAAI,IAAI;IAkBf,SAAS,IAAI,IAAI;IAuBjB;;OAEG;IACH,OAAO,CAAC,yBAAyB;IAUjC;;;OAGG;IACH,OAAO,CAAC,eAAe;IAkCvB,OAAO,CAAC,wBAAwB;IA6DhC,OAAO,CAAC,oBAAoB;CAoB7B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenshield/interceptor",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "type": "module",
5
5
  "description": "Node.js runtime interception via ESM loader and CJS preload",
6
6
  "main": "./index.js",
@@ -25,7 +25,7 @@
25
25
  },
26
26
  "license": "MIT",
27
27
  "dependencies": {
28
- "@agenshield/ipc": "0.7.0"
28
+ "@agenshield/ipc": "0.7.2"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^24.0.0",
package/register.js CHANGED
@@ -283,140 +283,6 @@ var FetchInterceptor = class extends BaseInterceptor {
283
283
  }
284
284
  };
285
285
 
286
- // libs/shield-interceptor/src/interceptors/http.ts
287
- var httpModule = require("node:http");
288
- var httpsModule = require("node:https");
289
- var HttpInterceptor = class extends BaseInterceptor {
290
- originalHttpRequest = null;
291
- originalHttpGet = null;
292
- originalHttpsRequest = null;
293
- originalHttpsGet = null;
294
- constructor(options) {
295
- super(options);
296
- }
297
- install() {
298
- if (this.installed) return;
299
- this.originalHttpRequest = httpModule.request;
300
- this.originalHttpGet = httpModule.get;
301
- this.originalHttpsRequest = httpsModule.request;
302
- this.originalHttpsGet = httpsModule.get;
303
- httpModule.request = this.createInterceptedRequest("http", this.originalHttpRequest);
304
- httpModule.get = this.createInterceptedGet("http", this.originalHttpGet);
305
- httpsModule.request = this.createInterceptedRequest("https", this.originalHttpsRequest);
306
- httpsModule.get = this.createInterceptedGet("https", this.originalHttpsGet);
307
- this.installed = true;
308
- }
309
- uninstall() {
310
- if (!this.installed) return;
311
- if (this.originalHttpRequest) {
312
- httpModule.request = this.originalHttpRequest;
313
- }
314
- if (this.originalHttpGet) {
315
- httpModule.get = this.originalHttpGet;
316
- }
317
- if (this.originalHttpsRequest) {
318
- httpsModule.request = this.originalHttpsRequest;
319
- }
320
- if (this.originalHttpsGet) {
321
- httpsModule.get = this.originalHttpsGet;
322
- }
323
- this.originalHttpRequest = null;
324
- this.originalHttpGet = null;
325
- this.originalHttpsRequest = null;
326
- this.originalHttpsGet = null;
327
- this.installed = false;
328
- }
329
- createInterceptedRequest(protocol, original) {
330
- const self = this;
331
- return function interceptedRequest(urlOrOptions, optionsOrCallback, callback) {
332
- let url;
333
- let options;
334
- let cb;
335
- if (typeof urlOrOptions === "string" || urlOrOptions instanceof URL) {
336
- url = urlOrOptions.toString();
337
- options = typeof optionsOrCallback === "object" ? optionsOrCallback : {};
338
- cb = typeof optionsOrCallback === "function" ? optionsOrCallback : callback;
339
- } else {
340
- options = urlOrOptions;
341
- url = `${protocol}://${options.hostname || options.host || "localhost"}:${options.port || (protocol === "https" ? 443 : 80)}${options.path || "/"}`;
342
- cb = optionsOrCallback;
343
- }
344
- if (self.isBrokerUrl(url)) {
345
- return original.call(
346
- protocol === "http" ? httpModule : httpsModule,
347
- urlOrOptions,
348
- optionsOrCallback,
349
- callback
350
- );
351
- }
352
- const req = original.call(
353
- protocol === "http" ? httpModule : httpsModule,
354
- urlOrOptions,
355
- optionsOrCallback,
356
- callback
357
- );
358
- self.eventReporter.intercept("http_request", url);
359
- self.checkPolicy("http_request", url).catch((error) => {
360
- req.destroy(error);
361
- });
362
- return req;
363
- };
364
- }
365
- createInterceptedGet(protocol, original) {
366
- const interceptedRequest = this.createInterceptedRequest(
367
- protocol,
368
- protocol === "http" ? this.originalHttpRequest : this.originalHttpsRequest
369
- );
370
- return function interceptedGet(urlOrOptions, optionsOrCallback, callback) {
371
- const req = interceptedRequest(urlOrOptions, optionsOrCallback, callback);
372
- req.end();
373
- return req;
374
- };
375
- }
376
- };
377
-
378
- // libs/shield-interceptor/src/interceptors/websocket.ts
379
- var WebSocketInterceptor = class extends BaseInterceptor {
380
- originalWebSocket = null;
381
- constructor(options) {
382
- super(options);
383
- }
384
- install() {
385
- if (this.installed) return;
386
- if (typeof globalThis.WebSocket === "undefined") {
387
- this.debug("WebSocket not available in this environment");
388
- return;
389
- }
390
- this.originalWebSocket = globalThis.WebSocket;
391
- const self = this;
392
- const OriginalWebSocket = this.originalWebSocket;
393
- class InterceptedWebSocket extends OriginalWebSocket {
394
- constructor(url, protocols) {
395
- const urlString = url.toString();
396
- if (self.isBrokerUrl(urlString)) {
397
- super(url, protocols);
398
- return;
399
- }
400
- self.eventReporter.intercept("websocket", urlString);
401
- super(url, protocols);
402
- self.checkPolicy("websocket", urlString).catch((error) => {
403
- this.close(1008, "Policy denied");
404
- const errorEvent = new Event("error");
405
- this.dispatchEvent(errorEvent);
406
- });
407
- }
408
- }
409
- globalThis.WebSocket = InterceptedWebSocket;
410
- this.installed = true;
411
- }
412
- uninstall() {
413
- if (!this.installed || !this.originalWebSocket) return;
414
- globalThis.WebSocket = this.originalWebSocket;
415
- this.originalWebSocket = null;
416
- this.installed = false;
417
- }
418
- };
419
-
420
286
  // libs/shield-interceptor/src/client/sync-client.ts
421
287
  var import_node_child_process = require("node:child_process");
422
288
  var fs2 = __toESM(require("node:fs"), 1);
@@ -620,6 +486,202 @@ var SyncClient = class {
620
486
  }
621
487
  };
622
488
 
489
+ // libs/shield-interceptor/src/interceptors/http.ts
490
+ var httpModule = require("node:http");
491
+ var httpsModule = require("node:https");
492
+ var HttpInterceptor = class extends BaseInterceptor {
493
+ syncClient;
494
+ originalHttpRequest = null;
495
+ originalHttpGet = null;
496
+ originalHttpsRequest = null;
497
+ originalHttpsGet = null;
498
+ constructor(options) {
499
+ super(options);
500
+ const config = this.interceptorConfig;
501
+ this.syncClient = new SyncClient({
502
+ socketPath: config?.socketPath || "/var/run/agenshield/agenshield.sock",
503
+ httpHost: config?.httpHost || "localhost",
504
+ httpPort: config?.httpPort || 5201,
505
+ timeout: config?.timeout || 3e4
506
+ });
507
+ }
508
+ install() {
509
+ if (this.installed) return;
510
+ this.originalHttpRequest = httpModule.request;
511
+ this.originalHttpGet = httpModule.get;
512
+ this.originalHttpsRequest = httpsModule.request;
513
+ this.originalHttpsGet = httpsModule.get;
514
+ httpModule.request = this.createInterceptedRequest("http", this.originalHttpRequest);
515
+ httpModule.get = this.createInterceptedGet("http", this.originalHttpGet);
516
+ httpsModule.request = this.createInterceptedRequest("https", this.originalHttpsRequest);
517
+ httpsModule.get = this.createInterceptedGet("https", this.originalHttpsGet);
518
+ this.installed = true;
519
+ }
520
+ uninstall() {
521
+ if (!this.installed) return;
522
+ if (this.originalHttpRequest) {
523
+ httpModule.request = this.originalHttpRequest;
524
+ }
525
+ if (this.originalHttpGet) {
526
+ httpModule.get = this.originalHttpGet;
527
+ }
528
+ if (this.originalHttpsRequest) {
529
+ httpsModule.request = this.originalHttpsRequest;
530
+ }
531
+ if (this.originalHttpsGet) {
532
+ httpsModule.get = this.originalHttpsGet;
533
+ }
534
+ this.originalHttpRequest = null;
535
+ this.originalHttpGet = null;
536
+ this.originalHttpsRequest = null;
537
+ this.originalHttpsGet = null;
538
+ this.installed = false;
539
+ }
540
+ /**
541
+ * Build execution context from config for RPC calls
542
+ */
543
+ getPolicyExecutionContext() {
544
+ const config = this.interceptorConfig;
545
+ return {
546
+ callerType: config?.contextType || "agent",
547
+ skillSlug: config?.contextSkillSlug,
548
+ agentId: config?.contextAgentId,
549
+ depth: 0
550
+ };
551
+ }
552
+ /**
553
+ * Synchronous policy check via SyncClient.
554
+ * Returns the full policy result or null if broker is unavailable and failOpen is true.
555
+ */
556
+ syncPolicyCheck(url) {
557
+ const startTime = Date.now();
558
+ try {
559
+ debugLog(`http.syncPolicyCheck START url=${url}`);
560
+ const context = this.getPolicyExecutionContext();
561
+ const result = this.syncClient.request(
562
+ "policy_check",
563
+ { operation: "http_request", target: url, context }
564
+ );
565
+ debugLog(`http.syncPolicyCheck DONE allowed=${result.allowed} url=${url}`);
566
+ if (!result.allowed) {
567
+ this.eventReporter.deny("http_request", url, result.policyId, result.reason);
568
+ throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
569
+ operation: "http_request",
570
+ target: url,
571
+ policyId: result.policyId
572
+ });
573
+ }
574
+ this.eventReporter.allow("http_request", url, result.policyId, Date.now() - startTime);
575
+ return result;
576
+ } catch (error) {
577
+ if (error instanceof PolicyDeniedError) {
578
+ throw error;
579
+ }
580
+ debugLog(`http.syncPolicyCheck ERROR: ${error.message} url=${url}`);
581
+ if (!this.failOpen) {
582
+ throw error;
583
+ }
584
+ return null;
585
+ }
586
+ }
587
+ createInterceptedRequest(protocol, original) {
588
+ const self = this;
589
+ return function interceptedRequest(urlOrOptions, optionsOrCallback, callback) {
590
+ let url;
591
+ let options;
592
+ let cb;
593
+ if (typeof urlOrOptions === "string" || urlOrOptions instanceof URL) {
594
+ url = urlOrOptions.toString();
595
+ options = typeof optionsOrCallback === "object" ? optionsOrCallback : {};
596
+ cb = typeof optionsOrCallback === "function" ? optionsOrCallback : callback;
597
+ } else {
598
+ options = urlOrOptions;
599
+ url = `${protocol}://${options.hostname || options.host || "localhost"}:${options.port || (protocol === "https" ? 443 : 80)}${options.path || "/"}`;
600
+ cb = optionsOrCallback;
601
+ }
602
+ if (self.isBrokerUrl(url)) {
603
+ return original.call(
604
+ protocol === "http" ? httpModule : httpsModule,
605
+ urlOrOptions,
606
+ optionsOrCallback,
607
+ callback
608
+ );
609
+ }
610
+ self.eventReporter.intercept("http_request", url);
611
+ try {
612
+ self.syncPolicyCheck(url);
613
+ } catch (error) {
614
+ debugLog(`http.request DENIED url=${url}`);
615
+ const mod = protocol === "http" ? httpModule : httpsModule;
616
+ const denied = original.call(mod, "http://0.0.0.0:1", { method: "GET" });
617
+ denied.once("error", () => {
618
+ });
619
+ process.nextTick(() => denied.destroy(error));
620
+ return denied;
621
+ }
622
+ return original.call(
623
+ protocol === "http" ? httpModule : httpsModule,
624
+ urlOrOptions,
625
+ optionsOrCallback,
626
+ callback
627
+ );
628
+ };
629
+ }
630
+ createInterceptedGet(protocol, original) {
631
+ const interceptedRequest = this.createInterceptedRequest(
632
+ protocol,
633
+ protocol === "http" ? this.originalHttpRequest : this.originalHttpsRequest
634
+ );
635
+ return function interceptedGet(urlOrOptions, optionsOrCallback, callback) {
636
+ const req = interceptedRequest(urlOrOptions, optionsOrCallback, callback);
637
+ req.end();
638
+ return req;
639
+ };
640
+ }
641
+ };
642
+
643
+ // libs/shield-interceptor/src/interceptors/websocket.ts
644
+ var WebSocketInterceptor = class extends BaseInterceptor {
645
+ originalWebSocket = null;
646
+ constructor(options) {
647
+ super(options);
648
+ }
649
+ install() {
650
+ if (this.installed) return;
651
+ if (typeof globalThis.WebSocket === "undefined") {
652
+ this.debug("WebSocket not available in this environment");
653
+ return;
654
+ }
655
+ this.originalWebSocket = globalThis.WebSocket;
656
+ const self = this;
657
+ const OriginalWebSocket = this.originalWebSocket;
658
+ class InterceptedWebSocket extends OriginalWebSocket {
659
+ constructor(url, protocols) {
660
+ const urlString = url.toString();
661
+ if (self.isBrokerUrl(urlString)) {
662
+ super(url, protocols);
663
+ return;
664
+ }
665
+ self.eventReporter.intercept("websocket", urlString);
666
+ super(url, protocols);
667
+ self.checkPolicy("websocket", urlString).catch((error) => {
668
+ this.close(1008, "Policy denied");
669
+ const errorEvent = new Event("error");
670
+ this.dispatchEvent(errorEvent);
671
+ });
672
+ }
673
+ }
674
+ globalThis.WebSocket = InterceptedWebSocket;
675
+ this.installed = true;
676
+ }
677
+ uninstall() {
678
+ if (!this.installed || !this.originalWebSocket) return;
679
+ globalThis.WebSocket = this.originalWebSocket;
680
+ this.originalWebSocket = null;
681
+ this.installed = false;
682
+ }
683
+ };
684
+
623
685
  // libs/shield-interceptor/src/seatbelt/profile-manager.ts
624
686
  var fs3 = __toESM(require("node:fs"), 1);
625
687
  var crypto = __toESM(require("node:crypto"), 1);
@@ -913,6 +975,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
913
975
  */
914
976
  syncPolicyCheck(fullCommand) {
915
977
  this._checking = true;
978
+ const startTime = Date.now();
916
979
  try {
917
980
  debugLog(`cp.syncPolicyCheck START command=${fullCommand}`);
918
981
  const context = this.getPolicyExecutionContext();
@@ -922,12 +985,14 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
922
985
  );
923
986
  debugLog(`cp.syncPolicyCheck DONE allowed=${result.allowed} command=${fullCommand}`);
924
987
  if (!result.allowed) {
988
+ this.eventReporter.deny("exec", fullCommand, result.policyId, result.reason);
925
989
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
926
990
  operation: "exec",
927
991
  target: fullCommand,
928
992
  policyId: result.policyId
929
993
  });
930
994
  }
995
+ this.eventReporter.allow("exec", fullCommand, result.policyId, Date.now() - startTime);
931
996
  return result;
932
997
  } catch (error) {
933
998
  if (error instanceof PolicyDeniedError) {
@@ -1054,10 +1119,14 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1054
1119
  try {
1055
1120
  policyResult = self.syncPolicyCheck(command);
1056
1121
  } catch (error) {
1122
+ debugLog(`cp.exec DENIED command=${command}`);
1123
+ const denied = self.originalSpawn("false", [], { stdio: "pipe" });
1124
+ denied.once("error", () => {
1125
+ });
1057
1126
  if (callback) {
1058
1127
  process.nextTick(() => callback(error, "", ""));
1059
1128
  }
1060
- return original('echo ""');
1129
+ return denied;
1061
1130
  }
1062
1131
  const wrapped = self.wrapCommandStringWithSeatbelt(command, options, policyResult);
1063
1132
  debugLog(`cp.exec calling original command=${wrapped.command}`);
@@ -1115,6 +1184,8 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1115
1184
  } catch (error) {
1116
1185
  debugLog(`cp.spawn DENIED command=${fullCmd}`);
1117
1186
  const denied = original("false", [], { stdio: "pipe" });
1187
+ denied.once("error", () => {
1188
+ });
1118
1189
  process.nextTick(() => {
1119
1190
  denied.emit("error", error);
1120
1191
  });
@@ -1199,10 +1270,14 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1199
1270
  try {
1200
1271
  policyResult = self.syncPolicyCheck(fullCommand);
1201
1272
  } catch (error) {
1273
+ debugLog(`cp.execFile DENIED command=${fullCommand}`);
1274
+ const denied = self.originalSpawn("false", [], { stdio: "pipe" });
1275
+ denied.once("error", () => {
1276
+ });
1202
1277
  if (callback) {
1203
1278
  process.nextTick(() => callback(error, "", ""));
1204
1279
  }
1205
- return original("false");
1280
+ return denied;
1206
1281
  }
1207
1282
  const wrapped = self.wrapWithSeatbelt(file, args, options, policyResult);
1208
1283
  debugLog(`cp.execFile calling original command=${wrapped.command}`);
@@ -1241,6 +1316,8 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1241
1316
  } catch (error) {
1242
1317
  debugLog(`cp.fork DENIED command=${fullCommand}`);
1243
1318
  const denied = self.originalSpawn("false", [], { stdio: "pipe" });
1319
+ denied.once("error", () => {
1320
+ });
1244
1321
  process.nextTick(() => {
1245
1322
  denied.emit("error", error);
1246
1323
  });
@@ -1788,7 +1865,8 @@ function installInterceptors(configOverrides) {
1788
1865
  policyEvaluator,
1789
1866
  eventReporter,
1790
1867
  failOpen: config.failOpen,
1791
- brokerHttpPort: config.httpPort
1868
+ brokerHttpPort: config.httpPort,
1869
+ config
1792
1870
  });
1793
1871
  installed.http.install();
1794
1872
  log(config, "debug", "Installed http/https interceptor");
package/require.js CHANGED
@@ -283,140 +283,6 @@ var FetchInterceptor = class extends BaseInterceptor {
283
283
  }
284
284
  };
285
285
 
286
- // libs/shield-interceptor/src/interceptors/http.ts
287
- var httpModule = require("node:http");
288
- var httpsModule = require("node:https");
289
- var HttpInterceptor = class extends BaseInterceptor {
290
- originalHttpRequest = null;
291
- originalHttpGet = null;
292
- originalHttpsRequest = null;
293
- originalHttpsGet = null;
294
- constructor(options) {
295
- super(options);
296
- }
297
- install() {
298
- if (this.installed) return;
299
- this.originalHttpRequest = httpModule.request;
300
- this.originalHttpGet = httpModule.get;
301
- this.originalHttpsRequest = httpsModule.request;
302
- this.originalHttpsGet = httpsModule.get;
303
- httpModule.request = this.createInterceptedRequest("http", this.originalHttpRequest);
304
- httpModule.get = this.createInterceptedGet("http", this.originalHttpGet);
305
- httpsModule.request = this.createInterceptedRequest("https", this.originalHttpsRequest);
306
- httpsModule.get = this.createInterceptedGet("https", this.originalHttpsGet);
307
- this.installed = true;
308
- }
309
- uninstall() {
310
- if (!this.installed) return;
311
- if (this.originalHttpRequest) {
312
- httpModule.request = this.originalHttpRequest;
313
- }
314
- if (this.originalHttpGet) {
315
- httpModule.get = this.originalHttpGet;
316
- }
317
- if (this.originalHttpsRequest) {
318
- httpsModule.request = this.originalHttpsRequest;
319
- }
320
- if (this.originalHttpsGet) {
321
- httpsModule.get = this.originalHttpsGet;
322
- }
323
- this.originalHttpRequest = null;
324
- this.originalHttpGet = null;
325
- this.originalHttpsRequest = null;
326
- this.originalHttpsGet = null;
327
- this.installed = false;
328
- }
329
- createInterceptedRequest(protocol, original) {
330
- const self = this;
331
- return function interceptedRequest(urlOrOptions, optionsOrCallback, callback) {
332
- let url;
333
- let options;
334
- let cb;
335
- if (typeof urlOrOptions === "string" || urlOrOptions instanceof URL) {
336
- url = urlOrOptions.toString();
337
- options = typeof optionsOrCallback === "object" ? optionsOrCallback : {};
338
- cb = typeof optionsOrCallback === "function" ? optionsOrCallback : callback;
339
- } else {
340
- options = urlOrOptions;
341
- url = `${protocol}://${options.hostname || options.host || "localhost"}:${options.port || (protocol === "https" ? 443 : 80)}${options.path || "/"}`;
342
- cb = optionsOrCallback;
343
- }
344
- if (self.isBrokerUrl(url)) {
345
- return original.call(
346
- protocol === "http" ? httpModule : httpsModule,
347
- urlOrOptions,
348
- optionsOrCallback,
349
- callback
350
- );
351
- }
352
- const req = original.call(
353
- protocol === "http" ? httpModule : httpsModule,
354
- urlOrOptions,
355
- optionsOrCallback,
356
- callback
357
- );
358
- self.eventReporter.intercept("http_request", url);
359
- self.checkPolicy("http_request", url).catch((error) => {
360
- req.destroy(error);
361
- });
362
- return req;
363
- };
364
- }
365
- createInterceptedGet(protocol, original) {
366
- const interceptedRequest = this.createInterceptedRequest(
367
- protocol,
368
- protocol === "http" ? this.originalHttpRequest : this.originalHttpsRequest
369
- );
370
- return function interceptedGet(urlOrOptions, optionsOrCallback, callback) {
371
- const req = interceptedRequest(urlOrOptions, optionsOrCallback, callback);
372
- req.end();
373
- return req;
374
- };
375
- }
376
- };
377
-
378
- // libs/shield-interceptor/src/interceptors/websocket.ts
379
- var WebSocketInterceptor = class extends BaseInterceptor {
380
- originalWebSocket = null;
381
- constructor(options) {
382
- super(options);
383
- }
384
- install() {
385
- if (this.installed) return;
386
- if (typeof globalThis.WebSocket === "undefined") {
387
- this.debug("WebSocket not available in this environment");
388
- return;
389
- }
390
- this.originalWebSocket = globalThis.WebSocket;
391
- const self = this;
392
- const OriginalWebSocket = this.originalWebSocket;
393
- class InterceptedWebSocket extends OriginalWebSocket {
394
- constructor(url, protocols) {
395
- const urlString = url.toString();
396
- if (self.isBrokerUrl(urlString)) {
397
- super(url, protocols);
398
- return;
399
- }
400
- self.eventReporter.intercept("websocket", urlString);
401
- super(url, protocols);
402
- self.checkPolicy("websocket", urlString).catch((error) => {
403
- this.close(1008, "Policy denied");
404
- const errorEvent = new Event("error");
405
- this.dispatchEvent(errorEvent);
406
- });
407
- }
408
- }
409
- globalThis.WebSocket = InterceptedWebSocket;
410
- this.installed = true;
411
- }
412
- uninstall() {
413
- if (!this.installed || !this.originalWebSocket) return;
414
- globalThis.WebSocket = this.originalWebSocket;
415
- this.originalWebSocket = null;
416
- this.installed = false;
417
- }
418
- };
419
-
420
286
  // libs/shield-interceptor/src/client/sync-client.ts
421
287
  var import_node_child_process = require("node:child_process");
422
288
  var fs2 = __toESM(require("node:fs"), 1);
@@ -620,6 +486,202 @@ var SyncClient = class {
620
486
  }
621
487
  };
622
488
 
489
+ // libs/shield-interceptor/src/interceptors/http.ts
490
+ var httpModule = require("node:http");
491
+ var httpsModule = require("node:https");
492
+ var HttpInterceptor = class extends BaseInterceptor {
493
+ syncClient;
494
+ originalHttpRequest = null;
495
+ originalHttpGet = null;
496
+ originalHttpsRequest = null;
497
+ originalHttpsGet = null;
498
+ constructor(options) {
499
+ super(options);
500
+ const config = this.interceptorConfig;
501
+ this.syncClient = new SyncClient({
502
+ socketPath: config?.socketPath || "/var/run/agenshield/agenshield.sock",
503
+ httpHost: config?.httpHost || "localhost",
504
+ httpPort: config?.httpPort || 5201,
505
+ timeout: config?.timeout || 3e4
506
+ });
507
+ }
508
+ install() {
509
+ if (this.installed) return;
510
+ this.originalHttpRequest = httpModule.request;
511
+ this.originalHttpGet = httpModule.get;
512
+ this.originalHttpsRequest = httpsModule.request;
513
+ this.originalHttpsGet = httpsModule.get;
514
+ httpModule.request = this.createInterceptedRequest("http", this.originalHttpRequest);
515
+ httpModule.get = this.createInterceptedGet("http", this.originalHttpGet);
516
+ httpsModule.request = this.createInterceptedRequest("https", this.originalHttpsRequest);
517
+ httpsModule.get = this.createInterceptedGet("https", this.originalHttpsGet);
518
+ this.installed = true;
519
+ }
520
+ uninstall() {
521
+ if (!this.installed) return;
522
+ if (this.originalHttpRequest) {
523
+ httpModule.request = this.originalHttpRequest;
524
+ }
525
+ if (this.originalHttpGet) {
526
+ httpModule.get = this.originalHttpGet;
527
+ }
528
+ if (this.originalHttpsRequest) {
529
+ httpsModule.request = this.originalHttpsRequest;
530
+ }
531
+ if (this.originalHttpsGet) {
532
+ httpsModule.get = this.originalHttpsGet;
533
+ }
534
+ this.originalHttpRequest = null;
535
+ this.originalHttpGet = null;
536
+ this.originalHttpsRequest = null;
537
+ this.originalHttpsGet = null;
538
+ this.installed = false;
539
+ }
540
+ /**
541
+ * Build execution context from config for RPC calls
542
+ */
543
+ getPolicyExecutionContext() {
544
+ const config = this.interceptorConfig;
545
+ return {
546
+ callerType: config?.contextType || "agent",
547
+ skillSlug: config?.contextSkillSlug,
548
+ agentId: config?.contextAgentId,
549
+ depth: 0
550
+ };
551
+ }
552
+ /**
553
+ * Synchronous policy check via SyncClient.
554
+ * Returns the full policy result or null if broker is unavailable and failOpen is true.
555
+ */
556
+ syncPolicyCheck(url) {
557
+ const startTime = Date.now();
558
+ try {
559
+ debugLog(`http.syncPolicyCheck START url=${url}`);
560
+ const context = this.getPolicyExecutionContext();
561
+ const result = this.syncClient.request(
562
+ "policy_check",
563
+ { operation: "http_request", target: url, context }
564
+ );
565
+ debugLog(`http.syncPolicyCheck DONE allowed=${result.allowed} url=${url}`);
566
+ if (!result.allowed) {
567
+ this.eventReporter.deny("http_request", url, result.policyId, result.reason);
568
+ throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
569
+ operation: "http_request",
570
+ target: url,
571
+ policyId: result.policyId
572
+ });
573
+ }
574
+ this.eventReporter.allow("http_request", url, result.policyId, Date.now() - startTime);
575
+ return result;
576
+ } catch (error) {
577
+ if (error instanceof PolicyDeniedError) {
578
+ throw error;
579
+ }
580
+ debugLog(`http.syncPolicyCheck ERROR: ${error.message} url=${url}`);
581
+ if (!this.failOpen) {
582
+ throw error;
583
+ }
584
+ return null;
585
+ }
586
+ }
587
+ createInterceptedRequest(protocol, original) {
588
+ const self = this;
589
+ return function interceptedRequest(urlOrOptions, optionsOrCallback, callback) {
590
+ let url;
591
+ let options;
592
+ let cb;
593
+ if (typeof urlOrOptions === "string" || urlOrOptions instanceof URL) {
594
+ url = urlOrOptions.toString();
595
+ options = typeof optionsOrCallback === "object" ? optionsOrCallback : {};
596
+ cb = typeof optionsOrCallback === "function" ? optionsOrCallback : callback;
597
+ } else {
598
+ options = urlOrOptions;
599
+ url = `${protocol}://${options.hostname || options.host || "localhost"}:${options.port || (protocol === "https" ? 443 : 80)}${options.path || "/"}`;
600
+ cb = optionsOrCallback;
601
+ }
602
+ if (self.isBrokerUrl(url)) {
603
+ return original.call(
604
+ protocol === "http" ? httpModule : httpsModule,
605
+ urlOrOptions,
606
+ optionsOrCallback,
607
+ callback
608
+ );
609
+ }
610
+ self.eventReporter.intercept("http_request", url);
611
+ try {
612
+ self.syncPolicyCheck(url);
613
+ } catch (error) {
614
+ debugLog(`http.request DENIED url=${url}`);
615
+ const mod = protocol === "http" ? httpModule : httpsModule;
616
+ const denied = original.call(mod, "http://0.0.0.0:1", { method: "GET" });
617
+ denied.once("error", () => {
618
+ });
619
+ process.nextTick(() => denied.destroy(error));
620
+ return denied;
621
+ }
622
+ return original.call(
623
+ protocol === "http" ? httpModule : httpsModule,
624
+ urlOrOptions,
625
+ optionsOrCallback,
626
+ callback
627
+ );
628
+ };
629
+ }
630
+ createInterceptedGet(protocol, original) {
631
+ const interceptedRequest = this.createInterceptedRequest(
632
+ protocol,
633
+ protocol === "http" ? this.originalHttpRequest : this.originalHttpsRequest
634
+ );
635
+ return function interceptedGet(urlOrOptions, optionsOrCallback, callback) {
636
+ const req = interceptedRequest(urlOrOptions, optionsOrCallback, callback);
637
+ req.end();
638
+ return req;
639
+ };
640
+ }
641
+ };
642
+
643
+ // libs/shield-interceptor/src/interceptors/websocket.ts
644
+ var WebSocketInterceptor = class extends BaseInterceptor {
645
+ originalWebSocket = null;
646
+ constructor(options) {
647
+ super(options);
648
+ }
649
+ install() {
650
+ if (this.installed) return;
651
+ if (typeof globalThis.WebSocket === "undefined") {
652
+ this.debug("WebSocket not available in this environment");
653
+ return;
654
+ }
655
+ this.originalWebSocket = globalThis.WebSocket;
656
+ const self = this;
657
+ const OriginalWebSocket = this.originalWebSocket;
658
+ class InterceptedWebSocket extends OriginalWebSocket {
659
+ constructor(url, protocols) {
660
+ const urlString = url.toString();
661
+ if (self.isBrokerUrl(urlString)) {
662
+ super(url, protocols);
663
+ return;
664
+ }
665
+ self.eventReporter.intercept("websocket", urlString);
666
+ super(url, protocols);
667
+ self.checkPolicy("websocket", urlString).catch((error) => {
668
+ this.close(1008, "Policy denied");
669
+ const errorEvent = new Event("error");
670
+ this.dispatchEvent(errorEvent);
671
+ });
672
+ }
673
+ }
674
+ globalThis.WebSocket = InterceptedWebSocket;
675
+ this.installed = true;
676
+ }
677
+ uninstall() {
678
+ if (!this.installed || !this.originalWebSocket) return;
679
+ globalThis.WebSocket = this.originalWebSocket;
680
+ this.originalWebSocket = null;
681
+ this.installed = false;
682
+ }
683
+ };
684
+
623
685
  // libs/shield-interceptor/src/seatbelt/profile-manager.ts
624
686
  var fs3 = __toESM(require("node:fs"), 1);
625
687
  var crypto = __toESM(require("node:crypto"), 1);
@@ -913,6 +975,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
913
975
  */
914
976
  syncPolicyCheck(fullCommand) {
915
977
  this._checking = true;
978
+ const startTime = Date.now();
916
979
  try {
917
980
  debugLog(`cp.syncPolicyCheck START command=${fullCommand}`);
918
981
  const context = this.getPolicyExecutionContext();
@@ -922,12 +985,14 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
922
985
  );
923
986
  debugLog(`cp.syncPolicyCheck DONE allowed=${result.allowed} command=${fullCommand}`);
924
987
  if (!result.allowed) {
988
+ this.eventReporter.deny("exec", fullCommand, result.policyId, result.reason);
925
989
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
926
990
  operation: "exec",
927
991
  target: fullCommand,
928
992
  policyId: result.policyId
929
993
  });
930
994
  }
995
+ this.eventReporter.allow("exec", fullCommand, result.policyId, Date.now() - startTime);
931
996
  return result;
932
997
  } catch (error) {
933
998
  if (error instanceof PolicyDeniedError) {
@@ -1054,10 +1119,14 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1054
1119
  try {
1055
1120
  policyResult = self.syncPolicyCheck(command);
1056
1121
  } catch (error) {
1122
+ debugLog(`cp.exec DENIED command=${command}`);
1123
+ const denied = self.originalSpawn("false", [], { stdio: "pipe" });
1124
+ denied.once("error", () => {
1125
+ });
1057
1126
  if (callback) {
1058
1127
  process.nextTick(() => callback(error, "", ""));
1059
1128
  }
1060
- return original('echo ""');
1129
+ return denied;
1061
1130
  }
1062
1131
  const wrapped = self.wrapCommandStringWithSeatbelt(command, options, policyResult);
1063
1132
  debugLog(`cp.exec calling original command=${wrapped.command}`);
@@ -1115,6 +1184,8 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1115
1184
  } catch (error) {
1116
1185
  debugLog(`cp.spawn DENIED command=${fullCmd}`);
1117
1186
  const denied = original("false", [], { stdio: "pipe" });
1187
+ denied.once("error", () => {
1188
+ });
1118
1189
  process.nextTick(() => {
1119
1190
  denied.emit("error", error);
1120
1191
  });
@@ -1199,10 +1270,14 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1199
1270
  try {
1200
1271
  policyResult = self.syncPolicyCheck(fullCommand);
1201
1272
  } catch (error) {
1273
+ debugLog(`cp.execFile DENIED command=${fullCommand}`);
1274
+ const denied = self.originalSpawn("false", [], { stdio: "pipe" });
1275
+ denied.once("error", () => {
1276
+ });
1202
1277
  if (callback) {
1203
1278
  process.nextTick(() => callback(error, "", ""));
1204
1279
  }
1205
- return original("false");
1280
+ return denied;
1206
1281
  }
1207
1282
  const wrapped = self.wrapWithSeatbelt(file, args, options, policyResult);
1208
1283
  debugLog(`cp.execFile calling original command=${wrapped.command}`);
@@ -1241,6 +1316,8 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
1241
1316
  } catch (error) {
1242
1317
  debugLog(`cp.fork DENIED command=${fullCommand}`);
1243
1318
  const denied = self.originalSpawn("false", [], { stdio: "pipe" });
1319
+ denied.once("error", () => {
1320
+ });
1244
1321
  process.nextTick(() => {
1245
1322
  denied.emit("error", error);
1246
1323
  });
@@ -1788,7 +1865,8 @@ function installInterceptors(configOverrides) {
1788
1865
  policyEvaluator,
1789
1866
  eventReporter,
1790
1867
  failOpen: config.failOpen,
1791
- brokerHttpPort: config.httpPort
1868
+ brokerHttpPort: config.httpPort,
1869
+ config
1792
1870
  });
1793
1871
  installed.http.install();
1794
1872
  log(config, "debug", "Installed http/https interceptor");