@agenshield/interceptor 0.6.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.
- package/client/sync-client.d.ts.map +1 -1
- package/debug-log.d.ts +8 -0
- package/debug-log.d.ts.map +1 -0
- package/index.js +140 -18
- package/interceptors/base.d.ts.map +1 -1
- package/interceptors/child-process.d.ts +1 -0
- package/interceptors/child-process.d.ts.map +1 -1
- package/interceptors/fetch.d.ts.map +1 -1
- package/interceptors/fs.d.ts +1 -0
- package/interceptors/fs.d.ts.map +1 -1
- package/package.json +2 -2
- package/register.js +140 -18
- package/require.js +140 -18
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sync-client.d.ts","sourceRoot":"","sources":["../../src/client/sync-client.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
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 @@
|
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
430
|
+
var fs2 = __toESM(require("node:fs"), 1);
|
|
407
431
|
var import_node_crypto = require("node:crypto");
|
|
408
|
-
var _existsSync =
|
|
409
|
-
var _readFileSync =
|
|
410
|
-
var _unlinkSync =
|
|
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
|
-
|
|
430
|
-
|
|
431
|
-
return
|
|
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
|
|
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
|
-
|
|
483
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
808
|
-
|
|
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
|
|
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;
|
|
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;
|
|
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;
|
|
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"}
|
package/interceptors/fs.d.ts
CHANGED
|
@@ -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;
|
package/interceptors/fs.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fs.d.ts","sourceRoot":"","sources":["../../src/interceptors/fs.ts"],"names":[],"mappings":"AAAA;;;;GAIG;
|
|
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.
|
|
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.6.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
405
|
+
var fs2 = __toESM(require("node:fs"), 1);
|
|
382
406
|
var import_node_crypto = require("node:crypto");
|
|
383
|
-
var _existsSync =
|
|
384
|
-
var _readFileSync =
|
|
385
|
-
var _unlinkSync =
|
|
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
|
-
|
|
405
|
-
|
|
406
|
-
return
|
|
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
|
|
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
|
-
|
|
458
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
783
|
-
|
|
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
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
405
|
+
var fs2 = __toESM(require("node:fs"), 1);
|
|
382
406
|
var import_node_crypto = require("node:crypto");
|
|
383
|
-
var _existsSync =
|
|
384
|
-
var _readFileSync =
|
|
385
|
-
var _unlinkSync =
|
|
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
|
-
|
|
405
|
-
|
|
406
|
-
return
|
|
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
|
|
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
|
-
|
|
458
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
783
|
-
|
|
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
|
|
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
|
}
|