@agenshield/interceptor 0.5.0 → 0.6.1

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;IA6EzB;;OAEG;IACH,OAAO,CAAC,eAAe;IAoCvB;;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
@@ -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,12 +504,19 @@ var SyncClient = class {
474
504
  }, ${this.timeout});
475
505
  `;
476
506
  try {
477
- _spawnSync("node", ["-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
- stdio: "ignore"
510
+ stdio: "ignore",
511
+ env: { ...process.env, NODE_OPTIONS: "" }
480
512
  });
481
- if (_existsSync(tmpFile)) {
482
- 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);
483
520
  _unlinkSync(tmpFile);
484
521
  if (response.error) {
485
522
  throw new Error(response.error);
@@ -509,19 +546,22 @@ var SyncClient = class {
509
546
  params
510
547
  });
511
548
  try {
549
+ debugLog(`syncClient.httpRequestSync curl START url=${url} method=${method}`);
512
550
  const result = _execSync(
513
- `curl -s -X POST -H "Content-Type: application/json" -d '${request.replace(/'/g, "\\'")}' "${url}"`,
551
+ `/usr/bin/curl -s -X POST -H "Content-Type: application/json" -d '${request.replace(/'/g, "\\'")}' "${url}"`,
514
552
  {
515
553
  timeout: this.timeout,
516
554
  encoding: "utf-8"
517
555
  }
518
556
  );
557
+ debugLog(`syncClient.httpRequestSync curl DONE len=${result?.length}`);
519
558
  const response = JSON.parse(result);
520
559
  if (response.error) {
521
560
  throw new Error(response.error.message);
522
561
  }
523
562
  return response.result;
524
563
  } catch (error) {
564
+ debugLog(`syncClient.httpRequestSync FAILED: ${error.message}`);
525
565
  throw new Error(`Sync request failed: ${error.message}`);
526
566
  }
527
567
  }
@@ -542,6 +582,7 @@ var SyncClient = class {
542
582
  var childProcessModule = require("node:child_process");
543
583
  var ChildProcessInterceptor = class extends BaseInterceptor {
544
584
  syncClient;
585
+ _checking = false;
545
586
  originalExec = null;
546
587
  originalExecSync = null;
547
588
  originalSpawn = null;
@@ -595,6 +636,11 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
595
636
  const original = this.originalExec;
596
637
  return function interceptedExec(command, ...args) {
597
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
+ }
598
644
  self.eventReporter.intercept("exec", command);
599
645
  self.checkPolicy("exec", command).then(() => {
600
646
  original(command, ...args, callback);
@@ -610,12 +656,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
610
656
  const self = this;
611
657
  const original = this.originalExecSync;
612
658
  const interceptedExecSync = function(command, options) {
613
- 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;
614
665
  try {
666
+ self.eventReporter.intercept("exec", command);
667
+ debugLog(`cp.execSync policy_check START command=${command}`);
615
668
  const result = self.syncClient.request(
616
669
  "policy_check",
617
670
  { operation: "exec", target: command }
618
671
  );
672
+ debugLog(`cp.execSync policy_check DONE allowed=${result.allowed} command=${command}`);
619
673
  if (!result.allowed) {
620
674
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
621
675
  operation: "exec",
@@ -623,13 +677,17 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
623
677
  });
624
678
  }
625
679
  } catch (error) {
680
+ debugLog(`cp.execSync policy_check ERROR: ${error.message} command=${command}`);
626
681
  if (error instanceof PolicyDeniedError) {
627
682
  throw error;
628
683
  }
629
684
  if (!self.failOpen) {
630
685
  throw error;
631
686
  }
687
+ } finally {
688
+ self._checking = false;
632
689
  }
690
+ debugLog(`cp.execSync calling original command=${command}`);
633
691
  return original(command, options);
634
692
  };
635
693
  return interceptedExecSync;
@@ -638,6 +696,12 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
638
696
  const self = this;
639
697
  const original = this.originalSpawn;
640
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
+ }
641
705
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
642
706
  self.eventReporter.intercept("exec", fullCommand);
643
707
  self.checkPolicy("exec", fullCommand).catch((error) => {
@@ -652,12 +716,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
652
716
  const original = this.originalSpawnSync;
653
717
  return function interceptedSpawnSync(command, args, options) {
654
718
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
655
- 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;
656
725
  try {
726
+ self.eventReporter.intercept("exec", fullCommand);
727
+ debugLog(`cp.spawnSync policy_check START command=${fullCommand}`);
657
728
  const result = self.syncClient.request(
658
729
  "policy_check",
659
730
  { operation: "exec", target: fullCommand }
660
731
  );
732
+ debugLog(`cp.spawnSync policy_check DONE allowed=${result.allowed} command=${fullCommand}`);
661
733
  if (!result.allowed) {
662
734
  return {
663
735
  pid: -1,
@@ -670,6 +742,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
670
742
  };
671
743
  }
672
744
  } catch (error) {
745
+ debugLog(`cp.spawnSync policy_check ERROR: ${error.message} command=${fullCommand}`);
673
746
  if (!self.failOpen) {
674
747
  return {
675
748
  pid: -1,
@@ -681,7 +754,10 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
681
754
  error
682
755
  };
683
756
  }
757
+ } finally {
758
+ self._checking = false;
684
759
  }
760
+ debugLog(`cp.spawnSync calling original command=${fullCommand}`);
685
761
  return original(command, args, options);
686
762
  };
687
763
  }
@@ -689,6 +765,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
689
765
  const self = this;
690
766
  const original = this.originalExecFile;
691
767
  return function interceptedExecFile(file, ...args) {
768
+ if (self._checking) {
769
+ return original(file, ...args);
770
+ }
692
771
  self.eventReporter.intercept("exec", file);
693
772
  self.checkPolicy("exec", file).catch((error) => {
694
773
  self.eventReporter.error("exec", file, error.message);
@@ -700,6 +779,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
700
779
  const self = this;
701
780
  const original = this.originalFork;
702
781
  const interceptedFork = function(modulePath, args, options) {
782
+ if (self._checking) {
783
+ return original(modulePath, args, options);
784
+ }
703
785
  const pathStr = modulePath.toString();
704
786
  self.eventReporter.intercept("exec", `fork:${pathStr}`);
705
787
  self.checkPolicy("exec", `fork:${pathStr}`).catch((error) => {
@@ -712,6 +794,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
712
794
  };
713
795
 
714
796
  // libs/shield-interceptor/src/interceptors/fs.ts
797
+ var import_node_url = require("node:url");
715
798
  var fsModule = require("node:fs");
716
799
  var fsPromisesModule = require("node:fs/promises");
717
800
  function safeOverride(target, prop, value) {
@@ -726,9 +809,23 @@ function safeOverride(target, prop, value) {
726
809
  });
727
810
  }
728
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
+ }
729
825
  var FsInterceptor = class extends BaseInterceptor {
730
826
  syncClient;
731
827
  originals = /* @__PURE__ */ new Map();
828
+ _checking = false;
732
829
  constructor(options) {
733
830
  super(options);
734
831
  this.syncClient = new SyncClient({
@@ -784,12 +881,20 @@ var FsInterceptor = class extends BaseInterceptor {
784
881
  this.originals.set(key, original);
785
882
  const self = this;
786
883
  safeOverride(module2, methodName, function intercepted(path, ...args) {
787
- const pathString = path.toString();
884
+ const pathString = normalizePathArg(path);
788
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
+ }
789
892
  self.eventReporter.intercept(operation, pathString);
790
893
  self.checkPolicy(operation, pathString).then(() => {
894
+ debugLog(`fs.${methodName} policy OK (async) path=${pathString}`);
791
895
  original.call(module2, path, ...args, callback);
792
896
  }).catch((error) => {
897
+ debugLog(`fs.${methodName} policy ERROR (async): ${error.message} path=${pathString}`);
793
898
  if (callback) {
794
899
  callback(error);
795
900
  }
@@ -803,13 +908,21 @@ var FsInterceptor = class extends BaseInterceptor {
803
908
  this.originals.set(key, original);
804
909
  const self = this;
805
910
  safeOverride(module2, methodName, function interceptedSync(path, ...args) {
806
- const pathString = path.toString();
807
- 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;
808
918
  try {
919
+ self.eventReporter.intercept(operation, pathString);
920
+ debugLog(`fs.${methodName} policy_check START path=${pathString}`);
809
921
  const result = self.syncClient.request(
810
922
  "policy_check",
811
923
  { operation, target: pathString }
812
924
  );
925
+ debugLog(`fs.${methodName} policy_check DONE allowed=${result.allowed} path=${pathString}`);
813
926
  if (!result.allowed) {
814
927
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
815
928
  operation,
@@ -817,13 +930,17 @@ var FsInterceptor = class extends BaseInterceptor {
817
930
  });
818
931
  }
819
932
  } catch (error) {
933
+ debugLog(`fs.${methodName} policy_check ERROR: ${error.message} path=${pathString}`);
820
934
  if (error instanceof PolicyDeniedError) {
821
935
  throw error;
822
936
  }
823
937
  if (!self.failOpen) {
824
938
  throw error;
825
939
  }
940
+ } finally {
941
+ self._checking = false;
826
942
  }
943
+ debugLog(`fs.${methodName} calling original path=${pathString}`);
827
944
  return original.call(module2, path, ...args);
828
945
  });
829
946
  }
@@ -834,9 +951,15 @@ var FsInterceptor = class extends BaseInterceptor {
834
951
  this.originals.set(key, original);
835
952
  const self = this;
836
953
  safeOverride(module2, methodName, async function interceptedPromise(path, ...args) {
837
- 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
+ }
838
960
  self.eventReporter.intercept(operation, pathString);
839
961
  await self.checkPolicy(operation, pathString);
962
+ debugLog(`fsPromises.${methodName} policy OK path=${pathString}`);
840
963
  return original.call(module2, path, ...args);
841
964
  });
842
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.5.0",
3
+ "version": "0.6.1",
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.5.0"
28
+ "@agenshield/ipc": "0.6.1"
29
29
  },
30
30
  "devDependencies": {
31
31
  "@types/node": "^24.0.0",
package/register.js CHANGED
@@ -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,12 +479,19 @@ var SyncClient = class {
449
479
  }, ${this.timeout});
450
480
  `;
451
481
  try {
452
- _spawnSync("node", ["-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
- stdio: "ignore"
485
+ stdio: "ignore",
486
+ env: { ...process.env, NODE_OPTIONS: "" }
455
487
  });
456
- if (_existsSync(tmpFile)) {
457
- 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);
458
495
  _unlinkSync(tmpFile);
459
496
  if (response.error) {
460
497
  throw new Error(response.error);
@@ -484,19 +521,22 @@ var SyncClient = class {
484
521
  params
485
522
  });
486
523
  try {
524
+ debugLog(`syncClient.httpRequestSync curl START url=${url} method=${method}`);
487
525
  const result = _execSync(
488
- `curl -s -X POST -H "Content-Type: application/json" -d '${request.replace(/'/g, "\\'")}' "${url}"`,
526
+ `/usr/bin/curl -s -X POST -H "Content-Type: application/json" -d '${request.replace(/'/g, "\\'")}' "${url}"`,
489
527
  {
490
528
  timeout: this.timeout,
491
529
  encoding: "utf-8"
492
530
  }
493
531
  );
532
+ debugLog(`syncClient.httpRequestSync curl DONE len=${result?.length}`);
494
533
  const response = JSON.parse(result);
495
534
  if (response.error) {
496
535
  throw new Error(response.error.message);
497
536
  }
498
537
  return response.result;
499
538
  } catch (error) {
539
+ debugLog(`syncClient.httpRequestSync FAILED: ${error.message}`);
500
540
  throw new Error(`Sync request failed: ${error.message}`);
501
541
  }
502
542
  }
@@ -517,6 +557,7 @@ var SyncClient = class {
517
557
  var childProcessModule = require("node:child_process");
518
558
  var ChildProcessInterceptor = class extends BaseInterceptor {
519
559
  syncClient;
560
+ _checking = false;
520
561
  originalExec = null;
521
562
  originalExecSync = null;
522
563
  originalSpawn = null;
@@ -570,6 +611,11 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
570
611
  const original = this.originalExec;
571
612
  return function interceptedExec(command, ...args) {
572
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
+ }
573
619
  self.eventReporter.intercept("exec", command);
574
620
  self.checkPolicy("exec", command).then(() => {
575
621
  original(command, ...args, callback);
@@ -585,12 +631,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
585
631
  const self = this;
586
632
  const original = this.originalExecSync;
587
633
  const interceptedExecSync = function(command, options) {
588
- 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;
589
640
  try {
641
+ self.eventReporter.intercept("exec", command);
642
+ debugLog(`cp.execSync policy_check START command=${command}`);
590
643
  const result = self.syncClient.request(
591
644
  "policy_check",
592
645
  { operation: "exec", target: command }
593
646
  );
647
+ debugLog(`cp.execSync policy_check DONE allowed=${result.allowed} command=${command}`);
594
648
  if (!result.allowed) {
595
649
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
596
650
  operation: "exec",
@@ -598,13 +652,17 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
598
652
  });
599
653
  }
600
654
  } catch (error) {
655
+ debugLog(`cp.execSync policy_check ERROR: ${error.message} command=${command}`);
601
656
  if (error instanceof PolicyDeniedError) {
602
657
  throw error;
603
658
  }
604
659
  if (!self.failOpen) {
605
660
  throw error;
606
661
  }
662
+ } finally {
663
+ self._checking = false;
607
664
  }
665
+ debugLog(`cp.execSync calling original command=${command}`);
608
666
  return original(command, options);
609
667
  };
610
668
  return interceptedExecSync;
@@ -613,6 +671,12 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
613
671
  const self = this;
614
672
  const original = this.originalSpawn;
615
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
+ }
616
680
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
617
681
  self.eventReporter.intercept("exec", fullCommand);
618
682
  self.checkPolicy("exec", fullCommand).catch((error) => {
@@ -627,12 +691,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
627
691
  const original = this.originalSpawnSync;
628
692
  return function interceptedSpawnSync(command, args, options) {
629
693
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
630
- 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;
631
700
  try {
701
+ self.eventReporter.intercept("exec", fullCommand);
702
+ debugLog(`cp.spawnSync policy_check START command=${fullCommand}`);
632
703
  const result = self.syncClient.request(
633
704
  "policy_check",
634
705
  { operation: "exec", target: fullCommand }
635
706
  );
707
+ debugLog(`cp.spawnSync policy_check DONE allowed=${result.allowed} command=${fullCommand}`);
636
708
  if (!result.allowed) {
637
709
  return {
638
710
  pid: -1,
@@ -645,6 +717,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
645
717
  };
646
718
  }
647
719
  } catch (error) {
720
+ debugLog(`cp.spawnSync policy_check ERROR: ${error.message} command=${fullCommand}`);
648
721
  if (!self.failOpen) {
649
722
  return {
650
723
  pid: -1,
@@ -656,7 +729,10 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
656
729
  error
657
730
  };
658
731
  }
732
+ } finally {
733
+ self._checking = false;
659
734
  }
735
+ debugLog(`cp.spawnSync calling original command=${fullCommand}`);
660
736
  return original(command, args, options);
661
737
  };
662
738
  }
@@ -664,6 +740,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
664
740
  const self = this;
665
741
  const original = this.originalExecFile;
666
742
  return function interceptedExecFile(file, ...args) {
743
+ if (self._checking) {
744
+ return original(file, ...args);
745
+ }
667
746
  self.eventReporter.intercept("exec", file);
668
747
  self.checkPolicy("exec", file).catch((error) => {
669
748
  self.eventReporter.error("exec", file, error.message);
@@ -675,6 +754,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
675
754
  const self = this;
676
755
  const original = this.originalFork;
677
756
  const interceptedFork = function(modulePath, args, options) {
757
+ if (self._checking) {
758
+ return original(modulePath, args, options);
759
+ }
678
760
  const pathStr = modulePath.toString();
679
761
  self.eventReporter.intercept("exec", `fork:${pathStr}`);
680
762
  self.checkPolicy("exec", `fork:${pathStr}`).catch((error) => {
@@ -687,6 +769,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
687
769
  };
688
770
 
689
771
  // libs/shield-interceptor/src/interceptors/fs.ts
772
+ var import_node_url = require("node:url");
690
773
  var fsModule = require("node:fs");
691
774
  var fsPromisesModule = require("node:fs/promises");
692
775
  function safeOverride(target, prop, value) {
@@ -701,9 +784,23 @@ function safeOverride(target, prop, value) {
701
784
  });
702
785
  }
703
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
+ }
704
800
  var FsInterceptor = class extends BaseInterceptor {
705
801
  syncClient;
706
802
  originals = /* @__PURE__ */ new Map();
803
+ _checking = false;
707
804
  constructor(options) {
708
805
  super(options);
709
806
  this.syncClient = new SyncClient({
@@ -759,12 +856,20 @@ var FsInterceptor = class extends BaseInterceptor {
759
856
  this.originals.set(key, original);
760
857
  const self = this;
761
858
  safeOverride(module2, methodName, function intercepted(path, ...args) {
762
- const pathString = path.toString();
859
+ const pathString = normalizePathArg(path);
763
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
+ }
764
867
  self.eventReporter.intercept(operation, pathString);
765
868
  self.checkPolicy(operation, pathString).then(() => {
869
+ debugLog(`fs.${methodName} policy OK (async) path=${pathString}`);
766
870
  original.call(module2, path, ...args, callback);
767
871
  }).catch((error) => {
872
+ debugLog(`fs.${methodName} policy ERROR (async): ${error.message} path=${pathString}`);
768
873
  if (callback) {
769
874
  callback(error);
770
875
  }
@@ -778,13 +883,21 @@ var FsInterceptor = class extends BaseInterceptor {
778
883
  this.originals.set(key, original);
779
884
  const self = this;
780
885
  safeOverride(module2, methodName, function interceptedSync(path, ...args) {
781
- const pathString = path.toString();
782
- 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;
783
893
  try {
894
+ self.eventReporter.intercept(operation, pathString);
895
+ debugLog(`fs.${methodName} policy_check START path=${pathString}`);
784
896
  const result = self.syncClient.request(
785
897
  "policy_check",
786
898
  { operation, target: pathString }
787
899
  );
900
+ debugLog(`fs.${methodName} policy_check DONE allowed=${result.allowed} path=${pathString}`);
788
901
  if (!result.allowed) {
789
902
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
790
903
  operation,
@@ -792,13 +905,17 @@ var FsInterceptor = class extends BaseInterceptor {
792
905
  });
793
906
  }
794
907
  } catch (error) {
908
+ debugLog(`fs.${methodName} policy_check ERROR: ${error.message} path=${pathString}`);
795
909
  if (error instanceof PolicyDeniedError) {
796
910
  throw error;
797
911
  }
798
912
  if (!self.failOpen) {
799
913
  throw error;
800
914
  }
915
+ } finally {
916
+ self._checking = false;
801
917
  }
918
+ debugLog(`fs.${methodName} calling original path=${pathString}`);
802
919
  return original.call(module2, path, ...args);
803
920
  });
804
921
  }
@@ -809,9 +926,15 @@ var FsInterceptor = class extends BaseInterceptor {
809
926
  this.originals.set(key, original);
810
927
  const self = this;
811
928
  safeOverride(module2, methodName, async function interceptedPromise(path, ...args) {
812
- 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
+ }
813
935
  self.eventReporter.intercept(operation, pathString);
814
936
  await self.checkPolicy(operation, pathString);
937
+ debugLog(`fsPromises.${methodName} policy OK path=${pathString}`);
815
938
  return original.call(module2, path, ...args);
816
939
  });
817
940
  }
package/require.js CHANGED
@@ -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,12 +479,19 @@ var SyncClient = class {
449
479
  }, ${this.timeout});
450
480
  `;
451
481
  try {
452
- _spawnSync("node", ["-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
- stdio: "ignore"
485
+ stdio: "ignore",
486
+ env: { ...process.env, NODE_OPTIONS: "" }
455
487
  });
456
- if (_existsSync(tmpFile)) {
457
- 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);
458
495
  _unlinkSync(tmpFile);
459
496
  if (response.error) {
460
497
  throw new Error(response.error);
@@ -484,19 +521,22 @@ var SyncClient = class {
484
521
  params
485
522
  });
486
523
  try {
524
+ debugLog(`syncClient.httpRequestSync curl START url=${url} method=${method}`);
487
525
  const result = _execSync(
488
- `curl -s -X POST -H "Content-Type: application/json" -d '${request.replace(/'/g, "\\'")}' "${url}"`,
526
+ `/usr/bin/curl -s -X POST -H "Content-Type: application/json" -d '${request.replace(/'/g, "\\'")}' "${url}"`,
489
527
  {
490
528
  timeout: this.timeout,
491
529
  encoding: "utf-8"
492
530
  }
493
531
  );
532
+ debugLog(`syncClient.httpRequestSync curl DONE len=${result?.length}`);
494
533
  const response = JSON.parse(result);
495
534
  if (response.error) {
496
535
  throw new Error(response.error.message);
497
536
  }
498
537
  return response.result;
499
538
  } catch (error) {
539
+ debugLog(`syncClient.httpRequestSync FAILED: ${error.message}`);
500
540
  throw new Error(`Sync request failed: ${error.message}`);
501
541
  }
502
542
  }
@@ -517,6 +557,7 @@ var SyncClient = class {
517
557
  var childProcessModule = require("node:child_process");
518
558
  var ChildProcessInterceptor = class extends BaseInterceptor {
519
559
  syncClient;
560
+ _checking = false;
520
561
  originalExec = null;
521
562
  originalExecSync = null;
522
563
  originalSpawn = null;
@@ -570,6 +611,11 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
570
611
  const original = this.originalExec;
571
612
  return function interceptedExec(command, ...args) {
572
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
+ }
573
619
  self.eventReporter.intercept("exec", command);
574
620
  self.checkPolicy("exec", command).then(() => {
575
621
  original(command, ...args, callback);
@@ -585,12 +631,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
585
631
  const self = this;
586
632
  const original = this.originalExecSync;
587
633
  const interceptedExecSync = function(command, options) {
588
- 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;
589
640
  try {
641
+ self.eventReporter.intercept("exec", command);
642
+ debugLog(`cp.execSync policy_check START command=${command}`);
590
643
  const result = self.syncClient.request(
591
644
  "policy_check",
592
645
  { operation: "exec", target: command }
593
646
  );
647
+ debugLog(`cp.execSync policy_check DONE allowed=${result.allowed} command=${command}`);
594
648
  if (!result.allowed) {
595
649
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
596
650
  operation: "exec",
@@ -598,13 +652,17 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
598
652
  });
599
653
  }
600
654
  } catch (error) {
655
+ debugLog(`cp.execSync policy_check ERROR: ${error.message} command=${command}`);
601
656
  if (error instanceof PolicyDeniedError) {
602
657
  throw error;
603
658
  }
604
659
  if (!self.failOpen) {
605
660
  throw error;
606
661
  }
662
+ } finally {
663
+ self._checking = false;
607
664
  }
665
+ debugLog(`cp.execSync calling original command=${command}`);
608
666
  return original(command, options);
609
667
  };
610
668
  return interceptedExecSync;
@@ -613,6 +671,12 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
613
671
  const self = this;
614
672
  const original = this.originalSpawn;
615
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
+ }
616
680
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
617
681
  self.eventReporter.intercept("exec", fullCommand);
618
682
  self.checkPolicy("exec", fullCommand).catch((error) => {
@@ -627,12 +691,20 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
627
691
  const original = this.originalSpawnSync;
628
692
  return function interceptedSpawnSync(command, args, options) {
629
693
  const fullCommand = args ? `${command} ${args.join(" ")}` : command;
630
- 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;
631
700
  try {
701
+ self.eventReporter.intercept("exec", fullCommand);
702
+ debugLog(`cp.spawnSync policy_check START command=${fullCommand}`);
632
703
  const result = self.syncClient.request(
633
704
  "policy_check",
634
705
  { operation: "exec", target: fullCommand }
635
706
  );
707
+ debugLog(`cp.spawnSync policy_check DONE allowed=${result.allowed} command=${fullCommand}`);
636
708
  if (!result.allowed) {
637
709
  return {
638
710
  pid: -1,
@@ -645,6 +717,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
645
717
  };
646
718
  }
647
719
  } catch (error) {
720
+ debugLog(`cp.spawnSync policy_check ERROR: ${error.message} command=${fullCommand}`);
648
721
  if (!self.failOpen) {
649
722
  return {
650
723
  pid: -1,
@@ -656,7 +729,10 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
656
729
  error
657
730
  };
658
731
  }
732
+ } finally {
733
+ self._checking = false;
659
734
  }
735
+ debugLog(`cp.spawnSync calling original command=${fullCommand}`);
660
736
  return original(command, args, options);
661
737
  };
662
738
  }
@@ -664,6 +740,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
664
740
  const self = this;
665
741
  const original = this.originalExecFile;
666
742
  return function interceptedExecFile(file, ...args) {
743
+ if (self._checking) {
744
+ return original(file, ...args);
745
+ }
667
746
  self.eventReporter.intercept("exec", file);
668
747
  self.checkPolicy("exec", file).catch((error) => {
669
748
  self.eventReporter.error("exec", file, error.message);
@@ -675,6 +754,9 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
675
754
  const self = this;
676
755
  const original = this.originalFork;
677
756
  const interceptedFork = function(modulePath, args, options) {
757
+ if (self._checking) {
758
+ return original(modulePath, args, options);
759
+ }
678
760
  const pathStr = modulePath.toString();
679
761
  self.eventReporter.intercept("exec", `fork:${pathStr}`);
680
762
  self.checkPolicy("exec", `fork:${pathStr}`).catch((error) => {
@@ -687,6 +769,7 @@ var ChildProcessInterceptor = class extends BaseInterceptor {
687
769
  };
688
770
 
689
771
  // libs/shield-interceptor/src/interceptors/fs.ts
772
+ var import_node_url = require("node:url");
690
773
  var fsModule = require("node:fs");
691
774
  var fsPromisesModule = require("node:fs/promises");
692
775
  function safeOverride(target, prop, value) {
@@ -701,9 +784,23 @@ function safeOverride(target, prop, value) {
701
784
  });
702
785
  }
703
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
+ }
704
800
  var FsInterceptor = class extends BaseInterceptor {
705
801
  syncClient;
706
802
  originals = /* @__PURE__ */ new Map();
803
+ _checking = false;
707
804
  constructor(options) {
708
805
  super(options);
709
806
  this.syncClient = new SyncClient({
@@ -759,12 +856,20 @@ var FsInterceptor = class extends BaseInterceptor {
759
856
  this.originals.set(key, original);
760
857
  const self = this;
761
858
  safeOverride(module2, methodName, function intercepted(path, ...args) {
762
- const pathString = path.toString();
859
+ const pathString = normalizePathArg(path);
763
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
+ }
764
867
  self.eventReporter.intercept(operation, pathString);
765
868
  self.checkPolicy(operation, pathString).then(() => {
869
+ debugLog(`fs.${methodName} policy OK (async) path=${pathString}`);
766
870
  original.call(module2, path, ...args, callback);
767
871
  }).catch((error) => {
872
+ debugLog(`fs.${methodName} policy ERROR (async): ${error.message} path=${pathString}`);
768
873
  if (callback) {
769
874
  callback(error);
770
875
  }
@@ -778,13 +883,21 @@ var FsInterceptor = class extends BaseInterceptor {
778
883
  this.originals.set(key, original);
779
884
  const self = this;
780
885
  safeOverride(module2, methodName, function interceptedSync(path, ...args) {
781
- const pathString = path.toString();
782
- 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;
783
893
  try {
894
+ self.eventReporter.intercept(operation, pathString);
895
+ debugLog(`fs.${methodName} policy_check START path=${pathString}`);
784
896
  const result = self.syncClient.request(
785
897
  "policy_check",
786
898
  { operation, target: pathString }
787
899
  );
900
+ debugLog(`fs.${methodName} policy_check DONE allowed=${result.allowed} path=${pathString}`);
788
901
  if (!result.allowed) {
789
902
  throw new PolicyDeniedError(result.reason || "Operation denied by policy", {
790
903
  operation,
@@ -792,13 +905,17 @@ var FsInterceptor = class extends BaseInterceptor {
792
905
  });
793
906
  }
794
907
  } catch (error) {
908
+ debugLog(`fs.${methodName} policy_check ERROR: ${error.message} path=${pathString}`);
795
909
  if (error instanceof PolicyDeniedError) {
796
910
  throw error;
797
911
  }
798
912
  if (!self.failOpen) {
799
913
  throw error;
800
914
  }
915
+ } finally {
916
+ self._checking = false;
801
917
  }
918
+ debugLog(`fs.${methodName} calling original path=${pathString}`);
802
919
  return original.call(module2, path, ...args);
803
920
  });
804
921
  }
@@ -809,9 +926,15 @@ var FsInterceptor = class extends BaseInterceptor {
809
926
  this.originals.set(key, original);
810
927
  const self = this;
811
928
  safeOverride(module2, methodName, async function interceptedPromise(path, ...args) {
812
- 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
+ }
813
935
  self.eventReporter.intercept(operation, pathString);
814
936
  await self.checkPolicy(operation, pathString);
937
+ debugLog(`fsPromises.${methodName} policy OK path=${pathString}`);
815
938
  return original.call(module2, path, ...args);
816
939
  });
817
940
  }