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