@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.
- 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 +143 -20
- 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 +143 -20
- package/require.js +143 -20
|
@@ -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,12 +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
|
-
stdio: "ignore"
|
|
510
|
+
stdio: "ignore",
|
|
511
|
+
env: { ...process.env, NODE_OPTIONS: "" }
|
|
480
512
|
});
|
|
481
|
-
|
|
482
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
807
|
-
|
|
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
|
|
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;
|
|
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.
|
|
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.
|
|
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,12 +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
|
-
stdio: "ignore"
|
|
485
|
+
stdio: "ignore",
|
|
486
|
+
env: { ...process.env, NODE_OPTIONS: "" }
|
|
455
487
|
});
|
|
456
|
-
|
|
457
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
782
|
-
|
|
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
|
|
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
|
-
|
|
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,12 +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
|
-
stdio: "ignore"
|
|
485
|
+
stdio: "ignore",
|
|
486
|
+
env: { ...process.env, NODE_OPTIONS: "" }
|
|
455
487
|
});
|
|
456
|
-
|
|
457
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
782
|
-
|
|
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
|
|
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
|
}
|