@agenshield/interceptor 0.6.0 → 0.6.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.
@@ -1 +1 @@
1
- {"version":3,"file":"sync-client.d.ts","sourceRoot":"","sources":["../../src/client/sync-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAgBH,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,iBAAiB;IAOtC;;OAEG;IACH,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC;IAU9D;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAgFzB;;OAEG;IACH,OAAO,CAAC,eAAe;IAqCvB;;OAEG;IACH,IAAI,IAAI,OAAO;CAQhB"}
1
+ {"version":3,"file":"sync-client.d.ts","sourceRoot":"","sources":["../../src/client/sync-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAiBH,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,OAAO,CAAS;gBAEZ,OAAO,EAAE,iBAAiB;IAOtC;;OAEG;IACH,OAAO,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC;IAgB9D;;;;OAIG;IACH,OAAO,CAAC,iBAAiB;IAsFzB;;OAEG;IACH,OAAO,CAAC,eAAe;IAwCvB;;OAEG;IACH,IAAI,IAAI,OAAO;CAQhB"}
package/debug-log.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Debug Logger
3
+ *
4
+ * Writes diagnostic logs to /var/log/agenshield/interceptor.log using
5
+ * a captured (pre-patch) appendFileSync. Safe from interception.
6
+ */
7
+ export declare function debugLog(msg: string): void;
8
+ //# sourceMappingURL=debug-log.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"debug-log.d.ts","sourceRoot":"","sources":["../src/debug-log.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AASH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAM1C"}
package/index.js CHANGED
@@ -59,7 +59,7 @@ function createConfig(overrides) {
59
59
  interceptFetch: env["AGENSHIELD_INTERCEPT_FETCH"] !== "false",
60
60
  interceptHttp: env["AGENSHIELD_INTERCEPT_HTTP"] !== "false",
61
61
  interceptWs: env["AGENSHIELD_INTERCEPT_WS"] !== "false",
62
- interceptFs: env["AGENSHIELD_INTERCEPT_FS"] !== "false",
62
+ interceptFs: false,
63
63
  interceptExec: env["AGENSHIELD_INTERCEPT_EXEC"] !== "false",
64
64
  timeout: parseInt(env["AGENSHIELD_TIMEOUT"] || "30000", 10),
65
65
  ...overrides
@@ -102,6 +102,18 @@ var TimeoutError = class extends AgenShieldError {
102
102
  }
103
103
  };
104
104
 
105
+ // libs/shield-interceptor/src/debug-log.ts
106
+ var fs = __toESM(require("node:fs"), 1);
107
+ var _appendFileSync = fs.appendFileSync.bind(fs);
108
+ var LOG_PATH = "/var/log/agenshield/interceptor.log";
109
+ function debugLog(msg) {
110
+ try {
111
+ _appendFileSync(LOG_PATH, `[${(/* @__PURE__ */ new Date()).toISOString()}] [pid:${process.pid}] ${msg}
112
+ `);
113
+ } catch {
114
+ }
115
+ }
116
+
105
117
  // libs/shield-interceptor/src/interceptors/base.ts
106
118
  var BaseInterceptor = class {
107
119
  client;
@@ -127,7 +139,9 @@ var BaseInterceptor = class {
127
139
  return false;
128
140
  }
129
141
  const port = parsed.port;
130
- return port === String(this.brokerHttpPort) || port === "5200";
142
+ const result = port === String(this.brokerHttpPort) || port === "5200";
143
+ debugLog(`isBrokerUrl url=${url} hostname=${parsed.hostname} port=${port} brokerPort=${this.brokerHttpPort} result=${result}`);
144
+ return result;
131
145
  } catch {
132
146
  return false;
133
147
  }
@@ -143,9 +157,11 @@ var BaseInterceptor = class {
143
157
  */
144
158
  async checkPolicy(operation, target) {
145
159
  const startTime = Date.now();
160
+ debugLog(`base.checkPolicy START op=${operation} target=${target}`);
146
161
  try {
147
162
  this.eventReporter.intercept(operation, target);
148
163
  const result = await this.policyEvaluator.check(operation, target);
164
+ debugLog(`base.checkPolicy evaluator result op=${operation} target=${target} allowed=${result.allowed} policyId=${result.policyId}`);
149
165
  if (!result.allowed) {
150
166
  this.eventReporter.deny(operation, target, result.policyId, result.reason);
151
167
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
@@ -162,8 +178,10 @@ var BaseInterceptor = class {
162
178
  );
163
179
  } catch (error) {
164
180
  if (error instanceof PolicyDeniedError) {
181
+ debugLog(`base.checkPolicy DENIED op=${operation} target=${target} reason=${error.message}`);
165
182
  throw error;
166
183
  }
184
+ debugLog(`base.checkPolicy ERROR op=${operation} target=${target} error=${error.message} failOpen=${this.failOpen}`);
167
185
  if (this.failOpen) {
168
186
  this.eventReporter.error(
169
187
  operation,
@@ -213,10 +231,14 @@ var FetchInterceptor = class extends BaseInterceptor {
213
231
  } else {
214
232
  url = input.url;
215
233
  }
216
- if (this.isBrokerUrl(url)) {
234
+ const isBroker = this.isBrokerUrl(url);
235
+ debugLog(`fetch ENTER url=${url} isBroker=${isBroker}`);
236
+ if (isBroker) {
217
237
  return this.originalFetch(input, init);
218
238
  }
239
+ debugLog(`fetch checkPolicy START url=${url}`);
219
240
  await this.checkPolicy("http_request", url);
241
+ debugLog(`fetch checkPolicy DONE url=${url}`);
220
242
  try {
221
243
  const method = init?.method || "GET";
222
244
  const headers = {};
@@ -256,10 +278,12 @@ var FetchInterceptor = class extends BaseInterceptor {
256
278
  headers: responseHeaders
257
279
  });
258
280
  } catch (error) {
281
+ debugLog(`fetch ERROR url=${url} error=${error.message}`);
259
282
  if (error.name === "PolicyDeniedError") {
260
283
  throw error;
261
284
  }
262
285
  if (this.failOpen) {
286
+ debugLog(`fetch failOpen fallback url=${url}`);
263
287
  return this.originalFetch(input, init);
264
288
  }
265
289
  throw error;
@@ -403,11 +427,11 @@ var WebSocketInterceptor = class extends BaseInterceptor {
403
427
 
404
428
  // libs/shield-interceptor/src/client/sync-client.ts
405
429
  var import_node_child_process = require("node:child_process");
406
- var fs = __toESM(require("node:fs"), 1);
430
+ var fs2 = __toESM(require("node:fs"), 1);
407
431
  var import_node_crypto = require("node:crypto");
408
- var _existsSync = fs.existsSync.bind(fs);
409
- var _readFileSync = fs.readFileSync.bind(fs);
410
- var _unlinkSync = fs.unlinkSync.bind(fs);
432
+ var _existsSync = fs2.existsSync.bind(fs2);
433
+ var _readFileSync = fs2.readFileSync.bind(fs2);
434
+ var _unlinkSync = fs2.unlinkSync.bind(fs2);
411
435
  var _spawnSync = import_node_child_process.spawnSync;
412
436
  var _execSync = import_node_child_process.execSync;
413
437
  var SyncClient = class {
@@ -425,10 +449,16 @@ var SyncClient = class {
425
449
  * Send a synchronous request to the broker
426
450
  */
427
451
  request(method, params) {
452
+ debugLog(`syncClient.request START method=${method}`);
428
453
  try {
429
- return this.socketRequestSync(method, params);
430
- } catch {
431
- return this.httpRequestSync(method, params);
454
+ const result = this.socketRequestSync(method, params);
455
+ debugLog(`syncClient.request socket OK method=${method}`);
456
+ return result;
457
+ } catch (socketErr) {
458
+ debugLog(`syncClient.request socket FAILED: ${socketErr.message}, trying HTTP`);
459
+ const result = this.httpRequestSync(method, params);
460
+ debugLog(`syncClient.request http OK method=${method}`);
461
+ return result;
432
462
  }
433
463
  }
434
464
  /**
@@ -474,13 +504,19 @@ var SyncClient = class {
474
504
  }, ${this.timeout});
475
505
  `;
476
506
  try {
477
- _spawnSync("/opt/agenshield/bin/node-bin", ["-e", script], {
507
+ debugLog(`syncClient.socketRequestSync _spawnSync START node-bin method=${method}`);
508
+ const spawnResult = _spawnSync("/opt/agenshield/bin/node-bin", ["-e", script], {
478
509
  timeout: this.timeout + 1e3,
479
510
  stdio: "ignore",
480
511
  env: { ...process.env, NODE_OPTIONS: "" }
481
512
  });
482
- if (_existsSync(tmpFile)) {
483
- const response = JSON.parse(_readFileSync(tmpFile, "utf-8"));
513
+ debugLog(`syncClient.socketRequestSync _spawnSync DONE status=${spawnResult?.status} signal=${spawnResult?.signal} error=${spawnResult?.error?.message || "none"}`);
514
+ const tmpExists = _existsSync(tmpFile);
515
+ debugLog(`syncClient.socketRequestSync tmpFile exists=${tmpExists}`);
516
+ if (tmpExists) {
517
+ const raw = _readFileSync(tmpFile, "utf-8");
518
+ debugLog(`syncClient.socketRequestSync response raw=${raw.slice(0, 200)}`);
519
+ const response = JSON.parse(raw);
484
520
  _unlinkSync(tmpFile);
485
521
  if (response.error) {
486
522
  throw new Error(response.error);
@@ -510,6 +546,7 @@ var SyncClient = class {
510
546
  params
511
547
  });
512
548
  try {
549
+ debugLog(`syncClient.httpRequestSync curl START url=${url} method=${method}`);
513
550
  const result = _execSync(
514
551
  `/usr/bin/curl -s -X POST -H "Content-Type: application/json" -d '${request.replace(/'/g, "\\'")}' "${url}"`,
515
552
  {
@@ -517,12 +554,14 @@ var SyncClient = class {
517
554
  encoding: "utf-8"
518
555
  }
519
556
  );
557
+ debugLog(`syncClient.httpRequestSync curl DONE len=${result?.length}`);
520
558
  const response = JSON.parse(result);
521
559
  if (response.error) {
522
560
  throw new Error(response.error.message);
523
561
  }
524
562
  return response.result;
525
563
  } catch (error) {
564
+ debugLog(`syncClient.httpRequestSync FAILED: ${error.message}`);
526
565
  throw new Error(`Sync request failed: ${error.message}`);
527
566
  }
528
567
  }
@@ -543,6 +582,7 @@ var SyncClient = class {
543
582
  var childProcessModule = require("node:child_process");
544
583
  var ChildProcessInterceptor = class extends BaseInterceptor {
545
584
  syncClient;
585
+ _checking = false;
546
586
  originalExec = null;
547
587
  originalExecSync = null;
548
588
  originalSpawn = null;
@@ -596,6 +636,11 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
596
636
  const original = this.originalExec;
597
637
  return function interceptedExec(command, ...args) {
598
638
  const callback = typeof args[args.length - 1] === "function" ? args.pop() : void 0;
639
+ debugLog(`cp.exec ENTER command=${command} _checking=${self._checking}`);
640
+ if (self._checking) {
641
+ debugLog(`cp.exec SKIP (re-entrancy) command=${command}`);
642
+ return original(command, ...args, callback);
643
+ }
599
644
  self.eventReporter.intercept("exec", command);
600
645
  self.checkPolicy("exec", command).then(() => {
601
646
  original(command, ...args, callback);
@@ -611,12 +656,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
611
656
  const self = this;
612
657
  const original = this.originalExecSync;
613
658
  const interceptedExecSync = function(command, options) {
614
- self.eventReporter.intercept("exec", command);
659
+ debugLog(`cp.execSync ENTER command=${command} _checking=${self._checking}`);
660
+ if (self._checking) {
661
+ debugLog(`cp.execSync SKIP (re-entrancy) command=${command}`);
662
+ return original(command, options);
663
+ }
664
+ self._checking = true;
615
665
  try {
666
+ self.eventReporter.intercept("exec", command);
667
+ debugLog(`cp.execSync policy_check START command=${command}`);
616
668
  const result = self.syncClient.request(
617
669
  "policy_check",
618
670
  { operation: "exec", target: command }
619
671
  );
672
+ debugLog(`cp.execSync policy_check DONE allowed=${result.allowed} command=${command}`);
620
673
  if (!result.allowed) {
621
674
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
622
675
  operation: "exec",
@@ -624,13 +677,17 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
624
677
  });
625
678
  }
626
679
  } catch (error) {
680
+ debugLog(`cp.execSync policy_check ERROR: ${error.message} command=${command}`);
627
681
  if (error instanceof PolicyDeniedError) {
628
682
  throw error;
629
683
  }
630
684
  if (!self.failOpen) {
631
685
  throw error;
632
686
  }
687
+ } finally {
688
+ self._checking = false;
633
689
  }
690
+ debugLog(`cp.execSync calling original command=${command}`);
634
691
  return original(command, options);
635
692
  };
636
693
  return interceptedExecSync;
@@ -639,6 +696,12 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
639
696
  const self = this;
640
697
  const original = this.originalSpawn;
641
698
  const interceptedSpawn = function(command, args, options) {
699
+ const fullCmd = args ? `${command} ${args.join(" ")}` : command;
700
+ debugLog(`cp.spawn ENTER command=${fullCmd} _checking=${self._checking}`);
701
+ if (self._checking) {
702
+ debugLog(`cp.spawn SKIP (re-entrancy) command=${fullCmd}`);
703
+ return original(command, args, options || {});
704
+ }
642
705
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
643
706
  self.eventReporter.intercept("exec", fullCommand);
644
707
  self.checkPolicy("exec", fullCommand).catch((error) => {
@@ -653,12 +716,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
653
716
  const original = this.originalSpawnSync;
654
717
  return function interceptedSpawnSync(command, args, options) {
655
718
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
656
- self.eventReporter.intercept("exec", fullCommand);
719
+ debugLog(`cp.spawnSync ENTER command=${fullCommand} _checking=${self._checking}`);
720
+ if (self._checking) {
721
+ debugLog(`cp.spawnSync SKIP (re-entrancy) command=${fullCommand}`);
722
+ return original(command, args, options);
723
+ }
724
+ self._checking = true;
657
725
  try {
726
+ self.eventReporter.intercept("exec", fullCommand);
727
+ debugLog(`cp.spawnSync policy_check START command=${fullCommand}`);
658
728
  const result = self.syncClient.request(
659
729
  "policy_check",
660
730
  { operation: "exec", target: fullCommand }
661
731
  );
732
+ debugLog(`cp.spawnSync policy_check DONE allowed=${result.allowed} command=${fullCommand}`);
662
733
  if (!result.allowed) {
663
734
  return {
664
735
  pid: -1,
@@ -671,6 +742,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
671
742
  };
672
743
  }
673
744
  } catch (error) {
745
+ debugLog(`cp.spawnSync policy_check ERROR: ${error.message} command=${fullCommand}`);
674
746
  if (!self.failOpen) {
675
747
  return {
676
748
  pid: -1,
@@ -682,7 +754,10 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
682
754
  error
683
755
  };
684
756
  }
757
+ } finally {
758
+ self._checking = false;
685
759
  }
760
+ debugLog(`cp.spawnSync calling original command=${fullCommand}`);
686
761
  return original(command, args, options);
687
762
  };
688
763
  }
@@ -690,6 +765,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
690
765
  const self = this;
691
766
  const original = this.originalExecFile;
692
767
  return function interceptedExecFile(file, ...args) {
768
+ if (self._checking) {
769
+ return original(file, ...args);
770
+ }
693
771
  self.eventReporter.intercept("exec", file);
694
772
  self.checkPolicy("exec", file).catch((error) => {
695
773
  self.eventReporter.error("exec", file, error.message);
@@ -701,6 +779,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
701
779
  const self = this;
702
780
  const original = this.originalFork;
703
781
  const interceptedFork = function(modulePath, args, options) {
782
+ if (self._checking) {
783
+ return original(modulePath, args, options);
784
+ }
704
785
  const pathStr = modulePath.toString();
705
786
  self.eventReporter.intercept("exec", `fork:${pathStr}`);
706
787
  self.checkPolicy("exec", `fork:${pathStr}`).catch((error) => {
@@ -713,6 +794,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
713
794
  };
714
795
 
715
796
  // libs/shield-interceptor/src/interceptors/fs.ts
797
+ var import_node_url = require("node:url");
716
798
  var fsModule = require("node:fs");
717
799
  var fsPromisesModule = require("node:fs/promises");
718
800
  function safeOverride(target, prop, value) {
@@ -727,9 +809,23 @@ function safeOverride(target, prop, value) {
727
809
  });
728
810
  }
729
811
  }
812
+ function normalizePathArg(p) {
813
+ if (p instanceof URL) {
814
+ return (0, import_node_url.fileURLToPath)(p);
815
+ }
816
+ const s = p.toString();
817
+ if (s.startsWith("file://")) {
818
+ try {
819
+ return (0, import_node_url.fileURLToPath)(new URL(s));
820
+ } catch {
821
+ }
822
+ }
823
+ return s;
824
+ }
730
825
  var FsInterceptor = class extends BaseInterceptor {
731
826
  syncClient;
732
827
  originals = /* @__PURE__ */ new Map();
828
+ _checking = false;
733
829
  constructor(options) {
734
830
  super(options);
735
831
  this.syncClient = new SyncClient({
@@ -785,12 +881,20 @@ var FsInterceptor = class extends BaseInterceptor {
785
881
  this.originals.set(key, original);
786
882
  const self = this;
787
883
  safeOverride(module2, methodName, function intercepted(path, ...args) {
788
- const pathString = path.toString();
884
+ const pathString = normalizePathArg(path);
789
885
  const callback = typeof args[args.length - 1] === "function" ? args.pop() : void 0;
886
+ debugLog(`fs.${methodName} ENTER (async) path=${pathString} _checking=${self._checking}`);
887
+ if (self._checking) {
888
+ debugLog(`fs.${methodName} SKIP (re-entrancy, async) path=${pathString}`);
889
+ original.call(module2, path, ...args, callback);
890
+ return;
891
+ }
790
892
  self.eventReporter.intercept(operation, pathString);
791
893
  self.checkPolicy(operation, pathString).then(() => {
894
+ debugLog(`fs.${methodName} policy OK (async) path=${pathString}`);
792
895
  original.call(module2, path, ...args, callback);
793
896
  }).catch((error) => {
897
+ debugLog(`fs.${methodName} policy ERROR (async): ${error.message} path=${pathString}`);
794
898
  if (callback) {
795
899
  callback(error);
796
900
  }
@@ -804,13 +908,21 @@ var FsInterceptor = class extends BaseInterceptor {
804
908
  this.originals.set(key, original);
805
909
  const self = this;
806
910
  safeOverride(module2, methodName, function interceptedSync(path, ...args) {
807
- const pathString = path.toString();
808
- self.eventReporter.intercept(operation, pathString);
911
+ const pathString = normalizePathArg(path);
912
+ debugLog(`fs.${methodName} ENTER path=${pathString} _checking=${self._checking}`);
913
+ if (self._checking) {
914
+ debugLog(`fs.${methodName} SKIP (re-entrancy) path=${pathString}`);
915
+ return original.call(module2, path, ...args);
916
+ }
917
+ self._checking = true;
809
918
  try {
919
+ self.eventReporter.intercept(operation, pathString);
920
+ debugLog(`fs.${methodName} policy_check START path=${pathString}`);
810
921
  const result = self.syncClient.request(
811
922
  "policy_check",
812
923
  { operation, target: pathString }
813
924
  );
925
+ debugLog(`fs.${methodName} policy_check DONE allowed=${result.allowed} path=${pathString}`);
814
926
  if (!result.allowed) {
815
927
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
816
928
  operation,
@@ -818,13 +930,17 @@ var FsInterceptor = class extends BaseInterceptor {
818
930
  });
819
931
  }
820
932
  } catch (error) {
933
+ debugLog(`fs.${methodName} policy_check ERROR: ${error.message} path=${pathString}`);
821
934
  if (error instanceof PolicyDeniedError) {
822
935
  throw error;
823
936
  }
824
937
  if (!self.failOpen) {
825
938
  throw error;
826
939
  }
940
+ } finally {
941
+ self._checking = false;
827
942
  }
943
+ debugLog(`fs.${methodName} calling original path=${pathString}`);
828
944
  return original.call(module2, path, ...args);
829
945
  });
830
946
  }
@@ -835,9 +951,15 @@ var FsInterceptor = class extends BaseInterceptor {
835
951
  this.originals.set(key, original);
836
952
  const self = this;
837
953
  safeOverride(module2, methodName, async function interceptedPromise(path, ...args) {
838
- const pathString = path.toString();
954
+ const pathString = normalizePathArg(path);
955
+ debugLog(`fsPromises.${methodName} ENTER path=${pathString} _checking=${self._checking}`);
956
+ if (self._checking) {
957
+ debugLog(`fsPromises.${methodName} SKIP (re-entrancy) path=${pathString}`);
958
+ return original.call(module2, path, ...args);
959
+ }
839
960
  self.eventReporter.intercept(operation, pathString);
840
961
  await self.checkPolicy(operation, pathString);
962
+ debugLog(`fsPromises.${methodName} policy OK path=${pathString}`);
841
963
  return original.call(module2, path, ...args);
842
964
  });
843
965
  }
@@ -1 +1 @@
1
- {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/interceptors/base.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,WAAW,CAAC;IACpB,eAAe,EAAE,eAAe,CAAC;IACjC,aAAa,EAAE,aAAa,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,8BAAsB,eAAe;IACnC,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAC3C,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,SAAS,EAAE,OAAO,CAAS;IACrC,OAAO,CAAC,cAAc,CAAS;gBAEnB,OAAO,EAAE,sBAAsB;IAQ3C;;OAEG;IACH,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAa3C;;OAEG;IACH,QAAQ,CAAC,OAAO,IAAI,IAAI;IAExB;;OAEG;IACH,QAAQ,CAAC,SAAS,IAAI,IAAI;IAE1B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;cACa,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IA0ChB;;OAEG;IACH,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAGvC"}
1
+ {"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/interceptors/base.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAC9D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAI3D,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,WAAW,CAAC;IACpB,eAAe,EAAE,eAAe,CAAC;IACjC,aAAa,EAAE,aAAa,CAAC;IAC7B,QAAQ,EAAE,OAAO,CAAC;IAClB,4EAA4E;IAC5E,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,8BAAsB,eAAe;IACnC,SAAS,CAAC,MAAM,EAAE,WAAW,CAAC;IAC9B,SAAS,CAAC,eAAe,EAAE,eAAe,CAAC;IAC3C,SAAS,CAAC,aAAa,EAAE,aAAa,CAAC;IACvC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC5B,SAAS,CAAC,SAAS,EAAE,OAAO,CAAS;IACrC,OAAO,CAAC,cAAc,CAAS;gBAEnB,OAAO,EAAE,sBAAsB;IAQ3C;;OAEG;IACH,SAAS,CAAC,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAe3C;;OAEG;IACH,QAAQ,CAAC,OAAO,IAAI,IAAI;IAExB;;OAEG;IACH,QAAQ,CAAC,SAAS,IAAI,IAAI;IAE1B;;OAEG;IACH,WAAW,IAAI,OAAO;IAItB;;OAEG;cACa,WAAW,CACzB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,IAAI,CAAC;IA8ChB;;OAEG;IACH,SAAS,CAAC,KAAK,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI;CAGvC"}
@@ -6,6 +6,7 @@
6
6
  import { BaseInterceptor, type BaseInterceptorOptions } from './base.js';
7
7
  export declare class ChildProcessInterceptor extends BaseInterceptor {
8
8
  private syncClient;
9
+ private _checking;
9
10
  private originalExec;
10
11
  private originalExecSync;
11
12
  private originalSpawn;
@@ -1 +1 @@
1
- {"version":3,"file":"child-process.d.ts","sourceRoot":"","sources":["../../src/interceptors/child-process.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAQzE,qBAAa,uBAAwB,SAAQ,eAAe;IAC1D,OAAO,CAAC,UAAU,CAAa;IAC/B,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;IAU3C,OAAO,IAAI,IAAI;IAsBf,SAAS,IAAI,IAAI;IAmBjB,OAAO,CAAC,qBAAqB;IAgC7B,OAAO,CAAC,yBAAyB;IAuCjC,OAAO,CAAC,sBAAsB;IAyB9B,OAAO,CAAC,0BAA0B;IAiDlC,OAAO,CAAC,yBAAyB;IAmBjC,OAAO,CAAC,qBAAqB;CAsB9B"}
1
+ {"version":3,"file":"child-process.d.ts","sourceRoot":"","sources":["../../src/interceptors/child-process.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AASzE,qBAAa,uBAAwB,SAAQ,eAAe;IAC1D,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAS;IAC1B,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;IAU3C,OAAO,IAAI,IAAI;IAsBf,SAAS,IAAI,IAAI;IAmBjB,OAAO,CAAC,qBAAqB;IAwC7B,OAAO,CAAC,yBAAyB;IAsDjC,OAAO,CAAC,sBAAsB;IAkC9B,OAAO,CAAC,0BAA0B;IA+DlC,OAAO,CAAC,yBAAyB;IAwBjC,OAAO,CAAC,qBAAqB;CA2B9B"}
@@ -1 +1 @@
1
- {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/interceptors/fetch.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAEzE,qBAAa,gBAAiB,SAAQ,eAAe;IACnD,OAAO,CAAC,aAAa,CAA6B;gBAEtC,OAAO,EAAE,sBAAsB;IAI3C,OAAO,IAAI,IAAI;IAYf,SAAS,IAAI,IAAI;YAQH,gBAAgB;CA0F/B"}
1
+ {"version":3,"file":"fetch.d.ts","sourceRoot":"","sources":["../../src/interceptors/fetch.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AAGzE,qBAAa,gBAAiB,SAAQ,eAAe;IACnD,OAAO,CAAC,aAAa,CAA6B;gBAEtC,OAAO,EAAE,sBAAsB;IAI3C,OAAO,IAAI,IAAI;IAYf,SAAS,IAAI,IAAI;YAQH,gBAAgB;CAiG/B"}
@@ -7,6 +7,7 @@ import { BaseInterceptor, type BaseInterceptorOptions } from './base.js';
7
7
  export declare class FsInterceptor extends BaseInterceptor {
8
8
  private syncClient;
9
9
  private originals;
10
+ private _checking;
10
11
  constructor(options: BaseInterceptorOptions);
11
12
  install(): void;
12
13
  uninstall(): void;
@@ -1 +1 @@
1
- {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/interceptors/fs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AA2BzE,qBAAa,aAAc,SAAQ,eAAe;IAChD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAoC;gBAEzC,OAAO,EAAE,sBAAsB;IAU3C,OAAO,IAAI,IAAI;IAoCf,SAAS,IAAI,IAAI;IAcjB,OAAO,CAAC,eAAe;IAuCvB,OAAO,CAAC,mBAAmB;IAgD3B,OAAO,CAAC,sBAAsB;CA2B/B"}
1
+ {"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/interceptors/fs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAKH,OAAO,EAAE,eAAe,EAAE,KAAK,sBAAsB,EAAE,MAAM,WAAW,CAAC;AA4CzE,qBAAa,aAAc,SAAQ,eAAe;IAChD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,SAAS,CAAoC;IACrD,OAAO,CAAC,SAAS,CAAS;gBAEd,OAAO,EAAE,sBAAsB;IAU3C,OAAO,IAAI,IAAI;IAoCf,SAAS,IAAI,IAAI;IAcjB,OAAO,CAAC,eAAe;IAkDvB,OAAO,CAAC,mBAAmB;IA+D3B,OAAO,CAAC,sBAAsB;CAoC/B"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenshield/interceptor",
3
- "version": "0.6.0",
3
+ "version": "0.6.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.6.0"
28
+ "@agenshield/ipc": "0.6.2"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^24.0.0",
package/register.js CHANGED
@@ -34,7 +34,7 @@ function createConfig(overrides) {
34
34
  interceptFetch: env["AGENSHIELD_INTERCEPT_FETCH"] !== "false",
35
35
  interceptHttp: env["AGENSHIELD_INTERCEPT_HTTP"] !== "false",
36
36
  interceptWs: env["AGENSHIELD_INTERCEPT_WS"] !== "false",
37
- interceptFs: env["AGENSHIELD_INTERCEPT_FS"] !== "false",
37
+ interceptFs: false,
38
38
  interceptExec: env["AGENSHIELD_INTERCEPT_EXEC"] !== "false",
39
39
  timeout: parseInt(env["AGENSHIELD_TIMEOUT"] || "30000", 10),
40
40
  ...overrides
@@ -77,6 +77,18 @@ var TimeoutError = class extends AgenShieldError {
77
77
  }
78
78
  };
79
79
 
80
+ // libs/shield-interceptor/src/debug-log.ts
81
+ var fs = __toESM(require("node:fs"), 1);
82
+ var _appendFileSync = fs.appendFileSync.bind(fs);
83
+ var LOG_PATH = "/var/log/agenshield/interceptor.log";
84
+ function debugLog(msg) {
85
+ try {
86
+ _appendFileSync(LOG_PATH, `[${(/* @__PURE__ */ new Date()).toISOString()}] [pid:${process.pid}] ${msg}
87
+ `);
88
+ } catch {
89
+ }
90
+ }
91
+
80
92
  // libs/shield-interceptor/src/interceptors/base.ts
81
93
  var BaseInterceptor = class {
82
94
  client;
@@ -102,7 +114,9 @@ var BaseInterceptor = class {
102
114
  return false;
103
115
  }
104
116
  const port = parsed.port;
105
- return port === String(this.brokerHttpPort) || port === "5200";
117
+ const result = port === String(this.brokerHttpPort) || port === "5200";
118
+ debugLog(`isBrokerUrl url=${url} hostname=${parsed.hostname} port=${port} brokerPort=${this.brokerHttpPort} result=${result}`);
119
+ return result;
106
120
  } catch {
107
121
  return false;
108
122
  }
@@ -118,9 +132,11 @@ var BaseInterceptor = class {
118
132
  */
119
133
  async checkPolicy(operation, target) {
120
134
  const startTime = Date.now();
135
+ debugLog(`base.checkPolicy START op=${operation} target=${target}`);
121
136
  try {
122
137
  this.eventReporter.intercept(operation, target);
123
138
  const result = await this.policyEvaluator.check(operation, target);
139
+ debugLog(`base.checkPolicy evaluator result op=${operation} target=${target} allowed=${result.allowed} policyId=${result.policyId}`);
124
140
  if (!result.allowed) {
125
141
  this.eventReporter.deny(operation, target, result.policyId, result.reason);
126
142
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
@@ -137,8 +153,10 @@ var BaseInterceptor = class {
137
153
  );
138
154
  } catch (error) {
139
155
  if (error instanceof PolicyDeniedError) {
156
+ debugLog(`base.checkPolicy DENIED op=${operation} target=${target} reason=${error.message}`);
140
157
  throw error;
141
158
  }
159
+ debugLog(`base.checkPolicy ERROR op=${operation} target=${target} error=${error.message} failOpen=${this.failOpen}`);
142
160
  if (this.failOpen) {
143
161
  this.eventReporter.error(
144
162
  operation,
@@ -188,10 +206,14 @@ var FetchInterceptor = class extends BaseInterceptor {
188
206
  } else {
189
207
  url = input.url;
190
208
  }
191
- if (this.isBrokerUrl(url)) {
209
+ const isBroker = this.isBrokerUrl(url);
210
+ debugLog(`fetch ENTER url=${url} isBroker=${isBroker}`);
211
+ if (isBroker) {
192
212
  return this.originalFetch(input, init);
193
213
  }
214
+ debugLog(`fetch checkPolicy START url=${url}`);
194
215
  await this.checkPolicy("http_request", url);
216
+ debugLog(`fetch checkPolicy DONE url=${url}`);
195
217
  try {
196
218
  const method = init?.method || "GET";
197
219
  const headers = {};
@@ -231,10 +253,12 @@ var FetchInterceptor = class extends BaseInterceptor {
231
253
  headers: responseHeaders
232
254
  });
233
255
  } catch (error) {
256
+ debugLog(`fetch ERROR url=${url} error=${error.message}`);
234
257
  if (error.name === "PolicyDeniedError") {
235
258
  throw error;
236
259
  }
237
260
  if (this.failOpen) {
261
+ debugLog(`fetch failOpen fallback url=${url}`);
238
262
  return this.originalFetch(input, init);
239
263
  }
240
264
  throw error;
@@ -378,11 +402,11 @@ var WebSocketInterceptor = class extends BaseInterceptor {
378
402
 
379
403
  // libs/shield-interceptor/src/client/sync-client.ts
380
404
  var import_node_child_process = require("node:child_process");
381
- var fs = __toESM(require("node:fs"), 1);
405
+ var fs2 = __toESM(require("node:fs"), 1);
382
406
  var import_node_crypto = require("node:crypto");
383
- var _existsSync = fs.existsSync.bind(fs);
384
- var _readFileSync = fs.readFileSync.bind(fs);
385
- var _unlinkSync = fs.unlinkSync.bind(fs);
407
+ var _existsSync = fs2.existsSync.bind(fs2);
408
+ var _readFileSync = fs2.readFileSync.bind(fs2);
409
+ var _unlinkSync = fs2.unlinkSync.bind(fs2);
386
410
  var _spawnSync = import_node_child_process.spawnSync;
387
411
  var _execSync = import_node_child_process.execSync;
388
412
  var SyncClient = class {
@@ -400,10 +424,16 @@ var SyncClient = class {
400
424
  * Send a synchronous request to the broker
401
425
  */
402
426
  request(method, params) {
427
+ debugLog(`syncClient.request START method=${method}`);
403
428
  try {
404
- return this.socketRequestSync(method, params);
405
- } catch {
406
- return this.httpRequestSync(method, params);
429
+ const result = this.socketRequestSync(method, params);
430
+ debugLog(`syncClient.request socket OK method=${method}`);
431
+ return result;
432
+ } catch (socketErr) {
433
+ debugLog(`syncClient.request socket FAILED: ${socketErr.message}, trying HTTP`);
434
+ const result = this.httpRequestSync(method, params);
435
+ debugLog(`syncClient.request http OK method=${method}`);
436
+ return result;
407
437
  }
408
438
  }
409
439
  /**
@@ -449,13 +479,19 @@ var SyncClient = class {
449
479
  }, ${this.timeout});
450
480
  `;
451
481
  try {
452
- _spawnSync("/opt/agenshield/bin/node-bin", ["-e", script], {
482
+ debugLog(`syncClient.socketRequestSync _spawnSync START node-bin method=${method}`);
483
+ const spawnResult = _spawnSync("/opt/agenshield/bin/node-bin", ["-e", script], {
453
484
  timeout: this.timeout + 1e3,
454
485
  stdio: "ignore",
455
486
  env: { ...process.env, NODE_OPTIONS: "" }
456
487
  });
457
- if (_existsSync(tmpFile)) {
458
- const response = JSON.parse(_readFileSync(tmpFile, "utf-8"));
488
+ debugLog(`syncClient.socketRequestSync _spawnSync DONE status=${spawnResult?.status} signal=${spawnResult?.signal} error=${spawnResult?.error?.message || "none"}`);
489
+ const tmpExists = _existsSync(tmpFile);
490
+ debugLog(`syncClient.socketRequestSync tmpFile exists=${tmpExists}`);
491
+ if (tmpExists) {
492
+ const raw = _readFileSync(tmpFile, "utf-8");
493
+ debugLog(`syncClient.socketRequestSync response raw=${raw.slice(0, 200)}`);
494
+ const response = JSON.parse(raw);
459
495
  _unlinkSync(tmpFile);
460
496
  if (response.error) {
461
497
  throw new Error(response.error);
@@ -485,6 +521,7 @@ var SyncClient = class {
485
521
  params
486
522
  });
487
523
  try {
524
+ debugLog(`syncClient.httpRequestSync curl START url=${url} method=${method}`);
488
525
  const result = _execSync(
489
526
  `/usr/bin/curl -s -X POST -H "Content-Type: application/json" -d '${request.replace(/'/g, "\\'")}' "${url}"`,
490
527
  {
@@ -492,12 +529,14 @@ var SyncClient = class {
492
529
  encoding: "utf-8"
493
530
  }
494
531
  );
532
+ debugLog(`syncClient.httpRequestSync curl DONE len=${result?.length}`);
495
533
  const response = JSON.parse(result);
496
534
  if (response.error) {
497
535
  throw new Error(response.error.message);
498
536
  }
499
537
  return response.result;
500
538
  } catch (error) {
539
+ debugLog(`syncClient.httpRequestSync FAILED: ${error.message}`);
501
540
  throw new Error(`Sync request failed: ${error.message}`);
502
541
  }
503
542
  }
@@ -518,6 +557,7 @@ var SyncClient = class {
518
557
  var childProcessModule = require("node:child_process");
519
558
  var ChildProcessInterceptor = class extends BaseInterceptor {
520
559
  syncClient;
560
+ _checking = false;
521
561
  originalExec = null;
522
562
  originalExecSync = null;
523
563
  originalSpawn = null;
@@ -571,6 +611,11 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
571
611
  const original = this.originalExec;
572
612
  return function interceptedExec(command, ...args) {
573
613
  const callback = typeof args[args.length - 1] === "function" ? args.pop() : void 0;
614
+ debugLog(`cp.exec ENTER command=${command} _checking=${self._checking}`);
615
+ if (self._checking) {
616
+ debugLog(`cp.exec SKIP (re-entrancy) command=${command}`);
617
+ return original(command, ...args, callback);
618
+ }
574
619
  self.eventReporter.intercept("exec", command);
575
620
  self.checkPolicy("exec", command).then(() => {
576
621
  original(command, ...args, callback);
@@ -586,12 +631,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
586
631
  const self = this;
587
632
  const original = this.originalExecSync;
588
633
  const interceptedExecSync = function(command, options) {
589
- self.eventReporter.intercept("exec", command);
634
+ debugLog(`cp.execSync ENTER command=${command} _checking=${self._checking}`);
635
+ if (self._checking) {
636
+ debugLog(`cp.execSync SKIP (re-entrancy) command=${command}`);
637
+ return original(command, options);
638
+ }
639
+ self._checking = true;
590
640
  try {
641
+ self.eventReporter.intercept("exec", command);
642
+ debugLog(`cp.execSync policy_check START command=${command}`);
591
643
  const result = self.syncClient.request(
592
644
  "policy_check",
593
645
  { operation: "exec", target: command }
594
646
  );
647
+ debugLog(`cp.execSync policy_check DONE allowed=${result.allowed} command=${command}`);
595
648
  if (!result.allowed) {
596
649
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
597
650
  operation: "exec",
@@ -599,13 +652,17 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
599
652
  });
600
653
  }
601
654
  } catch (error) {
655
+ debugLog(`cp.execSync policy_check ERROR: ${error.message} command=${command}`);
602
656
  if (error instanceof PolicyDeniedError) {
603
657
  throw error;
604
658
  }
605
659
  if (!self.failOpen) {
606
660
  throw error;
607
661
  }
662
+ } finally {
663
+ self._checking = false;
608
664
  }
665
+ debugLog(`cp.execSync calling original command=${command}`);
609
666
  return original(command, options);
610
667
  };
611
668
  return interceptedExecSync;
@@ -614,6 +671,12 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
614
671
  const self = this;
615
672
  const original = this.originalSpawn;
616
673
  const interceptedSpawn = function(command, args, options) {
674
+ const fullCmd = args ? `${command} ${args.join(" ")}` : command;
675
+ debugLog(`cp.spawn ENTER command=${fullCmd} _checking=${self._checking}`);
676
+ if (self._checking) {
677
+ debugLog(`cp.spawn SKIP (re-entrancy) command=${fullCmd}`);
678
+ return original(command, args, options || {});
679
+ }
617
680
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
618
681
  self.eventReporter.intercept("exec", fullCommand);
619
682
  self.checkPolicy("exec", fullCommand).catch((error) => {
@@ -628,12 +691,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
628
691
  const original = this.originalSpawnSync;
629
692
  return function interceptedSpawnSync(command, args, options) {
630
693
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
631
- self.eventReporter.intercept("exec", fullCommand);
694
+ debugLog(`cp.spawnSync ENTER command=${fullCommand} _checking=${self._checking}`);
695
+ if (self._checking) {
696
+ debugLog(`cp.spawnSync SKIP (re-entrancy) command=${fullCommand}`);
697
+ return original(command, args, options);
698
+ }
699
+ self._checking = true;
632
700
  try {
701
+ self.eventReporter.intercept("exec", fullCommand);
702
+ debugLog(`cp.spawnSync policy_check START command=${fullCommand}`);
633
703
  const result = self.syncClient.request(
634
704
  "policy_check",
635
705
  { operation: "exec", target: fullCommand }
636
706
  );
707
+ debugLog(`cp.spawnSync policy_check DONE allowed=${result.allowed} command=${fullCommand}`);
637
708
  if (!result.allowed) {
638
709
  return {
639
710
  pid: -1,
@@ -646,6 +717,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
646
717
  };
647
718
  }
648
719
  } catch (error) {
720
+ debugLog(`cp.spawnSync policy_check ERROR: ${error.message} command=${fullCommand}`);
649
721
  if (!self.failOpen) {
650
722
  return {
651
723
  pid: -1,
@@ -657,7 +729,10 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
657
729
  error
658
730
  };
659
731
  }
732
+ } finally {
733
+ self._checking = false;
660
734
  }
735
+ debugLog(`cp.spawnSync calling original command=${fullCommand}`);
661
736
  return original(command, args, options);
662
737
  };
663
738
  }
@@ -665,6 +740,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
665
740
  const self = this;
666
741
  const original = this.originalExecFile;
667
742
  return function interceptedExecFile(file, ...args) {
743
+ if (self._checking) {
744
+ return original(file, ...args);
745
+ }
668
746
  self.eventReporter.intercept("exec", file);
669
747
  self.checkPolicy("exec", file).catch((error) => {
670
748
  self.eventReporter.error("exec", file, error.message);
@@ -676,6 +754,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
676
754
  const self = this;
677
755
  const original = this.originalFork;
678
756
  const interceptedFork = function(modulePath, args, options) {
757
+ if (self._checking) {
758
+ return original(modulePath, args, options);
759
+ }
679
760
  const pathStr = modulePath.toString();
680
761
  self.eventReporter.intercept("exec", `fork:${pathStr}`);
681
762
  self.checkPolicy("exec", `fork:${pathStr}`).catch((error) => {
@@ -688,6 +769,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
688
769
  };
689
770
 
690
771
  // libs/shield-interceptor/src/interceptors/fs.ts
772
+ var import_node_url = require("node:url");
691
773
  var fsModule = require("node:fs");
692
774
  var fsPromisesModule = require("node:fs/promises");
693
775
  function safeOverride(target, prop, value) {
@@ -702,9 +784,23 @@ function safeOverride(target, prop, value) {
702
784
  });
703
785
  }
704
786
  }
787
+ function normalizePathArg(p) {
788
+ if (p instanceof URL) {
789
+ return (0, import_node_url.fileURLToPath)(p);
790
+ }
791
+ const s = p.toString();
792
+ if (s.startsWith("file://")) {
793
+ try {
794
+ return (0, import_node_url.fileURLToPath)(new URL(s));
795
+ } catch {
796
+ }
797
+ }
798
+ return s;
799
+ }
705
800
  var FsInterceptor = class extends BaseInterceptor {
706
801
  syncClient;
707
802
  originals = /* @__PURE__ */ new Map();
803
+ _checking = false;
708
804
  constructor(options) {
709
805
  super(options);
710
806
  this.syncClient = new SyncClient({
@@ -760,12 +856,20 @@ var FsInterceptor = class extends BaseInterceptor {
760
856
  this.originals.set(key, original);
761
857
  const self = this;
762
858
  safeOverride(module2, methodName, function intercepted(path, ...args) {
763
- const pathString = path.toString();
859
+ const pathString = normalizePathArg(path);
764
860
  const callback = typeof args[args.length - 1] === "function" ? args.pop() : void 0;
861
+ debugLog(`fs.${methodName} ENTER (async) path=${pathString} _checking=${self._checking}`);
862
+ if (self._checking) {
863
+ debugLog(`fs.${methodName} SKIP (re-entrancy, async) path=${pathString}`);
864
+ original.call(module2, path, ...args, callback);
865
+ return;
866
+ }
765
867
  self.eventReporter.intercept(operation, pathString);
766
868
  self.checkPolicy(operation, pathString).then(() => {
869
+ debugLog(`fs.${methodName} policy OK (async) path=${pathString}`);
767
870
  original.call(module2, path, ...args, callback);
768
871
  }).catch((error) => {
872
+ debugLog(`fs.${methodName} policy ERROR (async): ${error.message} path=${pathString}`);
769
873
  if (callback) {
770
874
  callback(error);
771
875
  }
@@ -779,13 +883,21 @@ var FsInterceptor = class extends BaseInterceptor {
779
883
  this.originals.set(key, original);
780
884
  const self = this;
781
885
  safeOverride(module2, methodName, function interceptedSync(path, ...args) {
782
- const pathString = path.toString();
783
- self.eventReporter.intercept(operation, pathString);
886
+ const pathString = normalizePathArg(path);
887
+ debugLog(`fs.${methodName} ENTER path=${pathString} _checking=${self._checking}`);
888
+ if (self._checking) {
889
+ debugLog(`fs.${methodName} SKIP (re-entrancy) path=${pathString}`);
890
+ return original.call(module2, path, ...args);
891
+ }
892
+ self._checking = true;
784
893
  try {
894
+ self.eventReporter.intercept(operation, pathString);
895
+ debugLog(`fs.${methodName} policy_check START path=${pathString}`);
785
896
  const result = self.syncClient.request(
786
897
  "policy_check",
787
898
  { operation, target: pathString }
788
899
  );
900
+ debugLog(`fs.${methodName} policy_check DONE allowed=${result.allowed} path=${pathString}`);
789
901
  if (!result.allowed) {
790
902
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
791
903
  operation,
@@ -793,13 +905,17 @@ var FsInterceptor = class extends BaseInterceptor {
793
905
  });
794
906
  }
795
907
  } catch (error) {
908
+ debugLog(`fs.${methodName} policy_check ERROR: ${error.message} path=${pathString}`);
796
909
  if (error instanceof PolicyDeniedError) {
797
910
  throw error;
798
911
  }
799
912
  if (!self.failOpen) {
800
913
  throw error;
801
914
  }
915
+ } finally {
916
+ self._checking = false;
802
917
  }
918
+ debugLog(`fs.${methodName} calling original path=${pathString}`);
803
919
  return original.call(module2, path, ...args);
804
920
  });
805
921
  }
@@ -810,9 +926,15 @@ var FsInterceptor = class extends BaseInterceptor {
810
926
  this.originals.set(key, original);
811
927
  const self = this;
812
928
  safeOverride(module2, methodName, async function interceptedPromise(path, ...args) {
813
- const pathString = path.toString();
929
+ const pathString = normalizePathArg(path);
930
+ debugLog(`fsPromises.${methodName} ENTER path=${pathString} _checking=${self._checking}`);
931
+ if (self._checking) {
932
+ debugLog(`fsPromises.${methodName} SKIP (re-entrancy) path=${pathString}`);
933
+ return original.call(module2, path, ...args);
934
+ }
814
935
  self.eventReporter.intercept(operation, pathString);
815
936
  await self.checkPolicy(operation, pathString);
937
+ debugLog(`fsPromises.${methodName} policy OK path=${pathString}`);
816
938
  return original.call(module2, path, ...args);
817
939
  });
818
940
  }
package/require.js CHANGED
@@ -34,7 +34,7 @@ function createConfig(overrides) {
34
34
  interceptFetch: env["AGENSHIELD_INTERCEPT_FETCH"] !== "false",
35
35
  interceptHttp: env["AGENSHIELD_INTERCEPT_HTTP"] !== "false",
36
36
  interceptWs: env["AGENSHIELD_INTERCEPT_WS"] !== "false",
37
- interceptFs: env["AGENSHIELD_INTERCEPT_FS"] !== "false",
37
+ interceptFs: false,
38
38
  interceptExec: env["AGENSHIELD_INTERCEPT_EXEC"] !== "false",
39
39
  timeout: parseInt(env["AGENSHIELD_TIMEOUT"] || "30000", 10),
40
40
  ...overrides
@@ -77,6 +77,18 @@ var TimeoutError = class extends AgenShieldError {
77
77
  }
78
78
  };
79
79
 
80
+ // libs/shield-interceptor/src/debug-log.ts
81
+ var fs = __toESM(require("node:fs"), 1);
82
+ var _appendFileSync = fs.appendFileSync.bind(fs);
83
+ var LOG_PATH = "/var/log/agenshield/interceptor.log";
84
+ function debugLog(msg) {
85
+ try {
86
+ _appendFileSync(LOG_PATH, `[${(/* @__PURE__ */ new Date()).toISOString()}] [pid:${process.pid}] ${msg}
87
+ `);
88
+ } catch {
89
+ }
90
+ }
91
+
80
92
  // libs/shield-interceptor/src/interceptors/base.ts
81
93
  var BaseInterceptor = class {
82
94
  client;
@@ -102,7 +114,9 @@ var BaseInterceptor = class {
102
114
  return false;
103
115
  }
104
116
  const port = parsed.port;
105
- return port === String(this.brokerHttpPort) || port === "5200";
117
+ const result = port === String(this.brokerHttpPort) || port === "5200";
118
+ debugLog(`isBrokerUrl url=${url} hostname=${parsed.hostname} port=${port} brokerPort=${this.brokerHttpPort} result=${result}`);
119
+ return result;
106
120
  } catch {
107
121
  return false;
108
122
  }
@@ -118,9 +132,11 @@ var BaseInterceptor = class {
118
132
  */
119
133
  async checkPolicy(operation, target) {
120
134
  const startTime = Date.now();
135
+ debugLog(`base.checkPolicy START op=${operation} target=${target}`);
121
136
  try {
122
137
  this.eventReporter.intercept(operation, target);
123
138
  const result = await this.policyEvaluator.check(operation, target);
139
+ debugLog(`base.checkPolicy evaluator result op=${operation} target=${target} allowed=${result.allowed} policyId=${result.policyId}`);
124
140
  if (!result.allowed) {
125
141
  this.eventReporter.deny(operation, target, result.policyId, result.reason);
126
142
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
@@ -137,8 +153,10 @@ var BaseInterceptor = class {
137
153
  );
138
154
  } catch (error) {
139
155
  if (error instanceof PolicyDeniedError) {
156
+ debugLog(`base.checkPolicy DENIED op=${operation} target=${target} reason=${error.message}`);
140
157
  throw error;
141
158
  }
159
+ debugLog(`base.checkPolicy ERROR op=${operation} target=${target} error=${error.message} failOpen=${this.failOpen}`);
142
160
  if (this.failOpen) {
143
161
  this.eventReporter.error(
144
162
  operation,
@@ -188,10 +206,14 @@ var FetchInterceptor = class extends BaseInterceptor {
188
206
  } else {
189
207
  url = input.url;
190
208
  }
191
- if (this.isBrokerUrl(url)) {
209
+ const isBroker = this.isBrokerUrl(url);
210
+ debugLog(`fetch ENTER url=${url} isBroker=${isBroker}`);
211
+ if (isBroker) {
192
212
  return this.originalFetch(input, init);
193
213
  }
214
+ debugLog(`fetch checkPolicy START url=${url}`);
194
215
  await this.checkPolicy("http_request", url);
216
+ debugLog(`fetch checkPolicy DONE url=${url}`);
195
217
  try {
196
218
  const method = init?.method || "GET";
197
219
  const headers = {};
@@ -231,10 +253,12 @@ var FetchInterceptor = class extends BaseInterceptor {
231
253
  headers: responseHeaders
232
254
  });
233
255
  } catch (error) {
256
+ debugLog(`fetch ERROR url=${url} error=${error.message}`);
234
257
  if (error.name === "PolicyDeniedError") {
235
258
  throw error;
236
259
  }
237
260
  if (this.failOpen) {
261
+ debugLog(`fetch failOpen fallback url=${url}`);
238
262
  return this.originalFetch(input, init);
239
263
  }
240
264
  throw error;
@@ -378,11 +402,11 @@ var WebSocketInterceptor = class extends BaseInterceptor {
378
402
 
379
403
  // libs/shield-interceptor/src/client/sync-client.ts
380
404
  var import_node_child_process = require("node:child_process");
381
- var fs = __toESM(require("node:fs"), 1);
405
+ var fs2 = __toESM(require("node:fs"), 1);
382
406
  var import_node_crypto = require("node:crypto");
383
- var _existsSync = fs.existsSync.bind(fs);
384
- var _readFileSync = fs.readFileSync.bind(fs);
385
- var _unlinkSync = fs.unlinkSync.bind(fs);
407
+ var _existsSync = fs2.existsSync.bind(fs2);
408
+ var _readFileSync = fs2.readFileSync.bind(fs2);
409
+ var _unlinkSync = fs2.unlinkSync.bind(fs2);
386
410
  var _spawnSync = import_node_child_process.spawnSync;
387
411
  var _execSync = import_node_child_process.execSync;
388
412
  var SyncClient = class {
@@ -400,10 +424,16 @@ var SyncClient = class {
400
424
  * Send a synchronous request to the broker
401
425
  */
402
426
  request(method, params) {
427
+ debugLog(`syncClient.request START method=${method}`);
403
428
  try {
404
- return this.socketRequestSync(method, params);
405
- } catch {
406
- return this.httpRequestSync(method, params);
429
+ const result = this.socketRequestSync(method, params);
430
+ debugLog(`syncClient.request socket OK method=${method}`);
431
+ return result;
432
+ } catch (socketErr) {
433
+ debugLog(`syncClient.request socket FAILED: ${socketErr.message}, trying HTTP`);
434
+ const result = this.httpRequestSync(method, params);
435
+ debugLog(`syncClient.request http OK method=${method}`);
436
+ return result;
407
437
  }
408
438
  }
409
439
  /**
@@ -449,13 +479,19 @@ var SyncClient = class {
449
479
  }, ${this.timeout});
450
480
  `;
451
481
  try {
452
- _spawnSync("/opt/agenshield/bin/node-bin", ["-e", script], {
482
+ debugLog(`syncClient.socketRequestSync _spawnSync START node-bin method=${method}`);
483
+ const spawnResult = _spawnSync("/opt/agenshield/bin/node-bin", ["-e", script], {
453
484
  timeout: this.timeout + 1e3,
454
485
  stdio: "ignore",
455
486
  env: { ...process.env, NODE_OPTIONS: "" }
456
487
  });
457
- if (_existsSync(tmpFile)) {
458
- const response = JSON.parse(_readFileSync(tmpFile, "utf-8"));
488
+ debugLog(`syncClient.socketRequestSync _spawnSync DONE status=${spawnResult?.status} signal=${spawnResult?.signal} error=${spawnResult?.error?.message || "none"}`);
489
+ const tmpExists = _existsSync(tmpFile);
490
+ debugLog(`syncClient.socketRequestSync tmpFile exists=${tmpExists}`);
491
+ if (tmpExists) {
492
+ const raw = _readFileSync(tmpFile, "utf-8");
493
+ debugLog(`syncClient.socketRequestSync response raw=${raw.slice(0, 200)}`);
494
+ const response = JSON.parse(raw);
459
495
  _unlinkSync(tmpFile);
460
496
  if (response.error) {
461
497
  throw new Error(response.error);
@@ -485,6 +521,7 @@ var SyncClient = class {
485
521
  params
486
522
  });
487
523
  try {
524
+ debugLog(`syncClient.httpRequestSync curl START url=${url} method=${method}`);
488
525
  const result = _execSync(
489
526
  `/usr/bin/curl -s -X POST -H "Content-Type: application/json" -d '${request.replace(/'/g, "\\'")}' "${url}"`,
490
527
  {
@@ -492,12 +529,14 @@ var SyncClient = class {
492
529
  encoding: "utf-8"
493
530
  }
494
531
  );
532
+ debugLog(`syncClient.httpRequestSync curl DONE len=${result?.length}`);
495
533
  const response = JSON.parse(result);
496
534
  if (response.error) {
497
535
  throw new Error(response.error.message);
498
536
  }
499
537
  return response.result;
500
538
  } catch (error) {
539
+ debugLog(`syncClient.httpRequestSync FAILED: ${error.message}`);
501
540
  throw new Error(`Sync request failed: ${error.message}`);
502
541
  }
503
542
  }
@@ -518,6 +557,7 @@ var SyncClient = class {
518
557
  var childProcessModule = require("node:child_process");
519
558
  var ChildProcessInterceptor = class extends BaseInterceptor {
520
559
  syncClient;
560
+ _checking = false;
521
561
  originalExec = null;
522
562
  originalExecSync = null;
523
563
  originalSpawn = null;
@@ -571,6 +611,11 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
571
611
  const original = this.originalExec;
572
612
  return function interceptedExec(command, ...args) {
573
613
  const callback = typeof args[args.length - 1] === "function" ? args.pop() : void 0;
614
+ debugLog(`cp.exec ENTER command=${command} _checking=${self._checking}`);
615
+ if (self._checking) {
616
+ debugLog(`cp.exec SKIP (re-entrancy) command=${command}`);
617
+ return original(command, ...args, callback);
618
+ }
574
619
  self.eventReporter.intercept("exec", command);
575
620
  self.checkPolicy("exec", command).then(() => {
576
621
  original(command, ...args, callback);
@@ -586,12 +631,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
586
631
  const self = this;
587
632
  const original = this.originalExecSync;
588
633
  const interceptedExecSync = function(command, options) {
589
- self.eventReporter.intercept("exec", command);
634
+ debugLog(`cp.execSync ENTER command=${command} _checking=${self._checking}`);
635
+ if (self._checking) {
636
+ debugLog(`cp.execSync SKIP (re-entrancy) command=${command}`);
637
+ return original(command, options);
638
+ }
639
+ self._checking = true;
590
640
  try {
641
+ self.eventReporter.intercept("exec", command);
642
+ debugLog(`cp.execSync policy_check START command=${command}`);
591
643
  const result = self.syncClient.request(
592
644
  "policy_check",
593
645
  { operation: "exec", target: command }
594
646
  );
647
+ debugLog(`cp.execSync policy_check DONE allowed=${result.allowed} command=${command}`);
595
648
  if (!result.allowed) {
596
649
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
597
650
  operation: "exec",
@@ -599,13 +652,17 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
599
652
  });
600
653
  }
601
654
  } catch (error) {
655
+ debugLog(`cp.execSync policy_check ERROR: ${error.message} command=${command}`);
602
656
  if (error instanceof PolicyDeniedError) {
603
657
  throw error;
604
658
  }
605
659
  if (!self.failOpen) {
606
660
  throw error;
607
661
  }
662
+ } finally {
663
+ self._checking = false;
608
664
  }
665
+ debugLog(`cp.execSync calling original command=${command}`);
609
666
  return original(command, options);
610
667
  };
611
668
  return interceptedExecSync;
@@ -614,6 +671,12 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
614
671
  const self = this;
615
672
  const original = this.originalSpawn;
616
673
  const interceptedSpawn = function(command, args, options) {
674
+ const fullCmd = args ? `${command} ${args.join(" ")}` : command;
675
+ debugLog(`cp.spawn ENTER command=${fullCmd} _checking=${self._checking}`);
676
+ if (self._checking) {
677
+ debugLog(`cp.spawn SKIP (re-entrancy) command=${fullCmd}`);
678
+ return original(command, args, options || {});
679
+ }
617
680
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
618
681
  self.eventReporter.intercept("exec", fullCommand);
619
682
  self.checkPolicy("exec", fullCommand).catch((error) => {
@@ -628,12 +691,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
628
691
  const original = this.originalSpawnSync;
629
692
  return function interceptedSpawnSync(command, args, options) {
630
693
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
631
- self.eventReporter.intercept("exec", fullCommand);
694
+ debugLog(`cp.spawnSync ENTER command=${fullCommand} _checking=${self._checking}`);
695
+ if (self._checking) {
696
+ debugLog(`cp.spawnSync SKIP (re-entrancy) command=${fullCommand}`);
697
+ return original(command, args, options);
698
+ }
699
+ self._checking = true;
632
700
  try {
701
+ self.eventReporter.intercept("exec", fullCommand);
702
+ debugLog(`cp.spawnSync policy_check START command=${fullCommand}`);
633
703
  const result = self.syncClient.request(
634
704
  "policy_check",
635
705
  { operation: "exec", target: fullCommand }
636
706
  );
707
+ debugLog(`cp.spawnSync policy_check DONE allowed=${result.allowed} command=${fullCommand}`);
637
708
  if (!result.allowed) {
638
709
  return {
639
710
  pid: -1,
@@ -646,6 +717,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
646
717
  };
647
718
  }
648
719
  } catch (error) {
720
+ debugLog(`cp.spawnSync policy_check ERROR: ${error.message} command=${fullCommand}`);
649
721
  if (!self.failOpen) {
650
722
  return {
651
723
  pid: -1,
@@ -657,7 +729,10 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
657
729
  error
658
730
  };
659
731
  }
732
+ } finally {
733
+ self._checking = false;
660
734
  }
735
+ debugLog(`cp.spawnSync calling original command=${fullCommand}`);
661
736
  return original(command, args, options);
662
737
  };
663
738
  }
@@ -665,6 +740,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
665
740
  const self = this;
666
741
  const original = this.originalExecFile;
667
742
  return function interceptedExecFile(file, ...args) {
743
+ if (self._checking) {
744
+ return original(file, ...args);
745
+ }
668
746
  self.eventReporter.intercept("exec", file);
669
747
  self.checkPolicy("exec", file).catch((error) => {
670
748
  self.eventReporter.error("exec", file, error.message);
@@ -676,6 +754,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
676
754
  const self = this;
677
755
  const original = this.originalFork;
678
756
  const interceptedFork = function(modulePath, args, options) {
757
+ if (self._checking) {
758
+ return original(modulePath, args, options);
759
+ }
679
760
  const pathStr = modulePath.toString();
680
761
  self.eventReporter.intercept("exec", `fork:${pathStr}`);
681
762
  self.checkPolicy("exec", `fork:${pathStr}`).catch((error) => {
@@ -688,6 +769,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
688
769
  };
689
770
 
690
771
  // libs/shield-interceptor/src/interceptors/fs.ts
772
+ var import_node_url = require("node:url");
691
773
  var fsModule = require("node:fs");
692
774
  var fsPromisesModule = require("node:fs/promises");
693
775
  function safeOverride(target, prop, value) {
@@ -702,9 +784,23 @@ function safeOverride(target, prop, value) {
702
784
  });
703
785
  }
704
786
  }
787
+ function normalizePathArg(p) {
788
+ if (p instanceof URL) {
789
+ return (0, import_node_url.fileURLToPath)(p);
790
+ }
791
+ const s = p.toString();
792
+ if (s.startsWith("file://")) {
793
+ try {
794
+ return (0, import_node_url.fileURLToPath)(new URL(s));
795
+ } catch {
796
+ }
797
+ }
798
+ return s;
799
+ }
705
800
  var FsInterceptor = class extends BaseInterceptor {
706
801
  syncClient;
707
802
  originals = /* @__PURE__ */ new Map();
803
+ _checking = false;
708
804
  constructor(options) {
709
805
  super(options);
710
806
  this.syncClient = new SyncClient({
@@ -760,12 +856,20 @@ var FsInterceptor = class extends BaseInterceptor {
760
856
  this.originals.set(key, original);
761
857
  const self = this;
762
858
  safeOverride(module2, methodName, function intercepted(path, ...args) {
763
- const pathString = path.toString();
859
+ const pathString = normalizePathArg(path);
764
860
  const callback = typeof args[args.length - 1] === "function" ? args.pop() : void 0;
861
+ debugLog(`fs.${methodName} ENTER (async) path=${pathString} _checking=${self._checking}`);
862
+ if (self._checking) {
863
+ debugLog(`fs.${methodName} SKIP (re-entrancy, async) path=${pathString}`);
864
+ original.call(module2, path, ...args, callback);
865
+ return;
866
+ }
765
867
  self.eventReporter.intercept(operation, pathString);
766
868
  self.checkPolicy(operation, pathString).then(() => {
869
+ debugLog(`fs.${methodName} policy OK (async) path=${pathString}`);
767
870
  original.call(module2, path, ...args, callback);
768
871
  }).catch((error) => {
872
+ debugLog(`fs.${methodName} policy ERROR (async): ${error.message} path=${pathString}`);
769
873
  if (callback) {
770
874
  callback(error);
771
875
  }
@@ -779,13 +883,21 @@ var FsInterceptor = class extends BaseInterceptor {
779
883
  this.originals.set(key, original);
780
884
  const self = this;
781
885
  safeOverride(module2, methodName, function interceptedSync(path, ...args) {
782
- const pathString = path.toString();
783
- self.eventReporter.intercept(operation, pathString);
886
+ const pathString = normalizePathArg(path);
887
+ debugLog(`fs.${methodName} ENTER path=${pathString} _checking=${self._checking}`);
888
+ if (self._checking) {
889
+ debugLog(`fs.${methodName} SKIP (re-entrancy) path=${pathString}`);
890
+ return original.call(module2, path, ...args);
891
+ }
892
+ self._checking = true;
784
893
  try {
894
+ self.eventReporter.intercept(operation, pathString);
895
+ debugLog(`fs.${methodName} policy_check START path=${pathString}`);
785
896
  const result = self.syncClient.request(
786
897
  "policy_check",
787
898
  { operation, target: pathString }
788
899
  );
900
+ debugLog(`fs.${methodName} policy_check DONE allowed=${result.allowed} path=${pathString}`);
789
901
  if (!result.allowed) {
790
902
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
791
903
  operation,
@@ -793,13 +905,17 @@ var FsInterceptor = class extends BaseInterceptor {
793
905
  });
794
906
  }
795
907
  } catch (error) {
908
+ debugLog(`fs.${methodName} policy_check ERROR: ${error.message} path=${pathString}`);
796
909
  if (error instanceof PolicyDeniedError) {
797
910
  throw error;
798
911
  }
799
912
  if (!self.failOpen) {
800
913
  throw error;
801
914
  }
915
+ } finally {
916
+ self._checking = false;
802
917
  }
918
+ debugLog(`fs.${methodName} calling original path=${pathString}`);
803
919
  return original.call(module2, path, ...args);
804
920
  });
805
921
  }
@@ -810,9 +926,15 @@ var FsInterceptor = class extends BaseInterceptor {
810
926
  this.originals.set(key, original);
811
927
  const self = this;
812
928
  safeOverride(module2, methodName, async function interceptedPromise(path, ...args) {
813
- const pathString = path.toString();
929
+ const pathString = normalizePathArg(path);
930
+ debugLog(`fsPromises.${methodName} ENTER path=${pathString} _checking=${self._checking}`);
931
+ if (self._checking) {
932
+ debugLog(`fsPromises.${methodName} SKIP (re-entrancy) path=${pathString}`);
933
+ return original.call(module2, path, ...args);
934
+ }
814
935
  self.eventReporter.intercept(operation, pathString);
815
936
  await self.checkPolicy(operation, pathString);
937
+ debugLog(`fsPromises.${methodName} policy OK path=${pathString}`);
816
938
  return original.call(module2, path, ...args);
817
939
  });
818
940
  }