@agenshield/broker 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/daemon-forward.d.ts +25 -0
- package/daemon-forward.d.ts.map +1 -0
- package/handlers/events-batch.d.ts +13 -0
- package/handlers/events-batch.d.ts.map +1 -0
- package/handlers/exec.d.ts.map +1 -1
- package/handlers/index.d.ts +2 -0
- package/handlers/index.d.ts.map +1 -1
- package/handlers/policy-check.d.ts +20 -0
- package/handlers/policy-check.d.ts.map +1 -0
- package/handlers/types.d.ts +1 -0
- package/handlers/types.d.ts.map +1 -1
- package/http-fallback.d.ts +3 -0
- package/http-fallback.d.ts.map +1 -1
- package/index.js +260 -26
- package/main.js +539 -57
- package/package.json +2 -2
- package/policies/builtin.d.ts +3 -1
- package/policies/builtin.d.ts.map +1 -1
- package/policies/command-allowlist.d.ts +7 -0
- package/policies/command-allowlist.d.ts.map +1 -1
- package/policies/enforcer.d.ts +5 -0
- package/policies/enforcer.d.ts.map +1 -1
- package/server.d.ts +3 -0
- package/server.d.ts.map +1 -1
- package/types.d.ts +4 -0
- package/types.d.ts.map +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Daemon Policy Forwarding
|
|
3
|
+
*
|
|
4
|
+
* Shared module for forwarding policy checks to the daemon's RPC endpoint.
|
|
5
|
+
* Used by both the policy_check handler and the top-level processRequest()
|
|
6
|
+
* in server.ts / http-fallback.ts when the broker's local enforcer denies
|
|
7
|
+
* a request but the daemon may have a user-defined policy that allows it.
|
|
8
|
+
*/
|
|
9
|
+
export interface DaemonPolicyResult {
|
|
10
|
+
allowed: boolean;
|
|
11
|
+
policyId?: string;
|
|
12
|
+
reason?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Forward a policy check to the daemon's RPC endpoint.
|
|
16
|
+
*
|
|
17
|
+
* The daemon evaluates user-defined policies (created in the UI).
|
|
18
|
+
* We only accept the daemon's result if it returns `allowed: true`
|
|
19
|
+
* AND includes a `policyId` (explicit user policy match).
|
|
20
|
+
* A default-allow (no policyId) is NOT trusted — we keep the broker denial.
|
|
21
|
+
*
|
|
22
|
+
* @returns The daemon's result if it explicitly allows, or null to keep broker denial.
|
|
23
|
+
*/
|
|
24
|
+
export declare function forwardPolicyToDaemon(operation: string, target: string, daemonUrl: string): Promise<DaemonPolicyResult | null>;
|
|
25
|
+
//# sourceMappingURL=daemon-forward.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon-forward.d.ts","sourceRoot":"","sources":["../src/daemon-forward.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;;;;;;;;GASG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAiDpC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Events Batch Handler
|
|
3
|
+
*
|
|
4
|
+
* Accepts batches of interceptor events for audit logging.
|
|
5
|
+
* The interceptor's EventReporter periodically flushes events
|
|
6
|
+
* to the broker via this RPC method.
|
|
7
|
+
*/
|
|
8
|
+
import type { HandlerContext, HandlerResult } from '../types.js';
|
|
9
|
+
import type { HandlerDependencies } from './types.js';
|
|
10
|
+
export declare function handleEventsBatch(params: Record<string, unknown>, context: HandlerContext, deps: HandlerDependencies): Promise<HandlerResult<{
|
|
11
|
+
received: number;
|
|
12
|
+
}>>;
|
|
13
|
+
//# sourceMappingURL=events-batch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events-batch.d.ts","sourceRoot":"","sources":["../../src/handlers/events-batch.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAc,MAAM,aAAa,CAAC;AAC7E,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAOtD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,aAAa,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAAC,CAuB9C"}
|
package/handlers/exec.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/handlers/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAc,UAAU,EAAE,MAAM,aAAa,CAAC;AACzF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"exec.d.ts","sourceRoot":"","sources":["../../src/handlers/exec.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAc,UAAU,EAAE,MAAM,aAAa,CAAC;AACzF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAgGtD,wBAAsB,UAAU,CAC9B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC,CA+GpC"}
|
package/handlers/index.d.ts
CHANGED
|
@@ -8,5 +8,7 @@ export { handleOpenUrl } from './open-url.js';
|
|
|
8
8
|
export { handleSecretInject } from './secret-inject.js';
|
|
9
9
|
export { handlePing } from './ping.js';
|
|
10
10
|
export { handleSkillInstall, handleSkillUninstall } from './skill-install.js';
|
|
11
|
+
export { handlePolicyCheck } from './policy-check.js';
|
|
12
|
+
export { handleEventsBatch } from './events-batch.js';
|
|
11
13
|
export type { HandlerDependencies } from './types.js';
|
|
12
14
|
//# sourceMappingURL=index.d.ts.map
|
package/handlers/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/handlers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAC9C,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,WAAW,CAAC;AACvC,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAEtD,YAAY,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Check Handler
|
|
3
|
+
*
|
|
4
|
+
* Handles policy_check RPC calls from the interceptor.
|
|
5
|
+
* The interceptor sends { operation, target } and this handler
|
|
6
|
+
* evaluates the inner operation against the policy enforcer.
|
|
7
|
+
*
|
|
8
|
+
* If the broker's enforcer denies the request, we forward to the
|
|
9
|
+
* daemon's RPC endpoint which checks user-defined policies.
|
|
10
|
+
*/
|
|
11
|
+
import type { HandlerContext, HandlerResult } from '../types.js';
|
|
12
|
+
import type { HandlerDependencies } from './types.js';
|
|
13
|
+
interface PolicyCheckResultData {
|
|
14
|
+
allowed: boolean;
|
|
15
|
+
policyId?: string;
|
|
16
|
+
reason?: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function handlePolicyCheck(params: Record<string, unknown>, context: HandlerContext, deps: HandlerDependencies): Promise<HandlerResult<PolicyCheckResultData>>;
|
|
19
|
+
export {};
|
|
20
|
+
//# sourceMappingURL=policy-check.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-check.d.ts","sourceRoot":"","sources":["../../src/handlers/policy-check.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AAQtD,UAAU,qBAAqB;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAKD,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,EAAE,cAAc,EACvB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,aAAa,CAAC,qBAAqB,CAAC,CAAC,CAoE/C"}
|
package/handlers/types.d.ts
CHANGED
package/handlers/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/handlers/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,WAAW,CAAC;IACzB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/handlers/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kCAAkC,CAAC;AAEzE;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,WAAW,EAAE,WAAW,CAAC;IACzB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAC;IAClD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACzD,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
package/http-fallback.d.ts
CHANGED
|
@@ -7,16 +7,19 @@
|
|
|
7
7
|
import type { BrokerConfig } from './types.js';
|
|
8
8
|
import type { PolicyEnforcer } from './policies/enforcer.js';
|
|
9
9
|
import type { AuditLogger } from './audit/logger.js';
|
|
10
|
+
import type { CommandAllowlist } from './policies/command-allowlist.js';
|
|
10
11
|
export interface HttpFallbackServerOptions {
|
|
11
12
|
config: BrokerConfig;
|
|
12
13
|
policyEnforcer: PolicyEnforcer;
|
|
13
14
|
auditLogger: AuditLogger;
|
|
15
|
+
commandAllowlist: CommandAllowlist;
|
|
14
16
|
}
|
|
15
17
|
export declare class HttpFallbackServer {
|
|
16
18
|
private server;
|
|
17
19
|
private config;
|
|
18
20
|
private policyEnforcer;
|
|
19
21
|
private auditLogger;
|
|
22
|
+
private commandAllowlist;
|
|
20
23
|
constructor(options: HttpFallbackServerOptions);
|
|
21
24
|
/**
|
|
22
25
|
* Start the HTTP fallback server
|
package/http-fallback.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"http-fallback.d.ts","sourceRoot":"","sources":["../src/http-fallback.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"http-fallback.d.ts","sourceRoot":"","sources":["../src/http-fallback.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EACV,YAAY,EAIb,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AACrD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AAsBxE,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,YAAY,CAAC;IACrB,cAAc,EAAE,cAAc,CAAC;IAC/B,WAAW,EAAE,WAAW,CAAC;IACzB,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,MAAM,CAAe;IAC7B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,gBAAgB,CAAmB;gBAE/B,OAAO,EAAE,yBAAyB;IAO9C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB5B;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAY3B;;OAEG;YACW,aAAa;IA6C3B;;OAEG;IACH,OAAO,CAAC,WAAW;IAUnB;;OAEG;YACW,cAAc;IAuI5B;;OAEG;IACH,OAAO,CAAC,UAAU;IAsBlB;;OAEG;IACH,OAAO,CAAC,aAAa;IAWrB;;OAEG;IACH,OAAO,CAAC,aAAa;CAWtB"}
|
package/index.js
CHANGED
|
@@ -249,6 +249,47 @@ function matchPattern(name, pattern) {
|
|
|
249
249
|
// libs/shield-broker/src/handlers/exec.ts
|
|
250
250
|
import * as path2 from "node:path";
|
|
251
251
|
import { spawn } from "node:child_process";
|
|
252
|
+
|
|
253
|
+
// libs/shield-broker/src/daemon-forward.ts
|
|
254
|
+
var DAEMON_RPC_TIMEOUT = 2e3;
|
|
255
|
+
async function forwardPolicyToDaemon(operation, target, daemonUrl) {
|
|
256
|
+
try {
|
|
257
|
+
const controller = new AbortController();
|
|
258
|
+
const timeout = setTimeout(() => controller.abort(), DAEMON_RPC_TIMEOUT);
|
|
259
|
+
const response = await fetch(`${daemonUrl}/rpc`, {
|
|
260
|
+
method: "POST",
|
|
261
|
+
headers: { "Content-Type": "application/json" },
|
|
262
|
+
body: JSON.stringify({
|
|
263
|
+
jsonrpc: "2.0",
|
|
264
|
+
id: `broker-fwd-${Date.now()}`,
|
|
265
|
+
method: "policy_check",
|
|
266
|
+
params: { operation, target }
|
|
267
|
+
}),
|
|
268
|
+
signal: controller.signal
|
|
269
|
+
});
|
|
270
|
+
clearTimeout(timeout);
|
|
271
|
+
if (!response.ok) {
|
|
272
|
+
return null;
|
|
273
|
+
}
|
|
274
|
+
const json = await response.json();
|
|
275
|
+
if (json.error || !json.result) {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
const result = json.result;
|
|
279
|
+
if (result.policyId) {
|
|
280
|
+
return {
|
|
281
|
+
allowed: !!result.allowed,
|
|
282
|
+
policyId: result.policyId,
|
|
283
|
+
reason: result.reason
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
287
|
+
} catch {
|
|
288
|
+
return null;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// libs/shield-broker/src/handlers/exec.ts
|
|
252
293
|
var MAX_OUTPUT_SIZE = 10 * 1024 * 1024;
|
|
253
294
|
var DEFAULT_WORKSPACE = "/Users/clawagent/workspace";
|
|
254
295
|
var FS_COMMANDS = /* @__PURE__ */ new Set([
|
|
@@ -381,12 +422,16 @@ async function handleExec(params, context, deps) {
|
|
|
381
422
|
if (url) {
|
|
382
423
|
const networkCheck = await deps.policyEnforcer.check("http_request", { url }, context);
|
|
383
424
|
if (!networkCheck.allowed) {
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
425
|
+
const daemonUrl = deps.daemonUrl || "http://127.0.0.1:5200";
|
|
426
|
+
const override = await forwardPolicyToDaemon("http_request", url, daemonUrl);
|
|
427
|
+
if (!override || !override.allowed) {
|
|
428
|
+
const reason = `URL not allowed: ${url} - ${networkCheck.reason}`;
|
|
429
|
+
deps.onExecDenied?.(command, reason);
|
|
430
|
+
return {
|
|
431
|
+
success: false,
|
|
432
|
+
error: { code: 1009, message: reason }
|
|
433
|
+
};
|
|
434
|
+
}
|
|
390
435
|
}
|
|
391
436
|
}
|
|
392
437
|
}
|
|
@@ -805,6 +850,86 @@ async function handleSkillUninstall(params, context, deps) {
|
|
|
805
850
|
}
|
|
806
851
|
}
|
|
807
852
|
|
|
853
|
+
// libs/shield-broker/src/handlers/policy-check.ts
|
|
854
|
+
var DEFAULT_DAEMON_URL = "http://127.0.0.1:5200";
|
|
855
|
+
async function handlePolicyCheck(params, context, deps) {
|
|
856
|
+
const { operation, target } = params;
|
|
857
|
+
if (!operation) {
|
|
858
|
+
return {
|
|
859
|
+
success: false,
|
|
860
|
+
error: { code: -32602, message: "Missing required parameter: operation" }
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
let checkParams;
|
|
864
|
+
switch (operation) {
|
|
865
|
+
case "http_request":
|
|
866
|
+
case "open_url":
|
|
867
|
+
checkParams = { url: target || "" };
|
|
868
|
+
break;
|
|
869
|
+
case "file_read":
|
|
870
|
+
case "file_write":
|
|
871
|
+
case "file_list":
|
|
872
|
+
checkParams = { path: target || "" };
|
|
873
|
+
break;
|
|
874
|
+
case "exec":
|
|
875
|
+
checkParams = { command: target || "" };
|
|
876
|
+
break;
|
|
877
|
+
case "secret_inject":
|
|
878
|
+
checkParams = { name: target || "" };
|
|
879
|
+
break;
|
|
880
|
+
default:
|
|
881
|
+
checkParams = { target: target || "" };
|
|
882
|
+
break;
|
|
883
|
+
}
|
|
884
|
+
const result = await deps.policyEnforcer.check(operation, checkParams, context);
|
|
885
|
+
if (result.allowed) {
|
|
886
|
+
return {
|
|
887
|
+
success: true,
|
|
888
|
+
data: {
|
|
889
|
+
allowed: true,
|
|
890
|
+
policyId: result.policyId,
|
|
891
|
+
reason: result.reason
|
|
892
|
+
}
|
|
893
|
+
};
|
|
894
|
+
}
|
|
895
|
+
const daemonUrl = deps.daemonUrl || DEFAULT_DAEMON_URL;
|
|
896
|
+
const daemonResult = await forwardPolicyToDaemon(operation, target || "", daemonUrl);
|
|
897
|
+
if (daemonResult && daemonResult.allowed) {
|
|
898
|
+
return { success: true, data: daemonResult };
|
|
899
|
+
}
|
|
900
|
+
return {
|
|
901
|
+
success: true,
|
|
902
|
+
data: {
|
|
903
|
+
allowed: false,
|
|
904
|
+
policyId: result.policyId,
|
|
905
|
+
reason: result.reason
|
|
906
|
+
}
|
|
907
|
+
};
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
// libs/shield-broker/src/handlers/events-batch.ts
|
|
911
|
+
async function handleEventsBatch(params, context, deps) {
|
|
912
|
+
const { events } = params;
|
|
913
|
+
const eventList = events || [];
|
|
914
|
+
for (const event of eventList) {
|
|
915
|
+
const entry = {
|
|
916
|
+
id: event.id || context.requestId,
|
|
917
|
+
timestamp: event.timestamp ? new Date(event.timestamp) : /* @__PURE__ */ new Date(),
|
|
918
|
+
operation: event.operation || "events_batch",
|
|
919
|
+
channel: "socket",
|
|
920
|
+
allowed: event.allowed ?? true,
|
|
921
|
+
target: event.target || "",
|
|
922
|
+
result: event.allowed === false ? "denied" : "success",
|
|
923
|
+
durationMs: 0
|
|
924
|
+
};
|
|
925
|
+
await deps.auditLogger.log(entry);
|
|
926
|
+
}
|
|
927
|
+
return {
|
|
928
|
+
success: true,
|
|
929
|
+
data: { received: eventList.length }
|
|
930
|
+
};
|
|
931
|
+
}
|
|
932
|
+
|
|
808
933
|
// libs/shield-broker/src/server.ts
|
|
809
934
|
var UnixSocketServer = class {
|
|
810
935
|
server = null;
|
|
@@ -812,12 +937,14 @@ var UnixSocketServer = class {
|
|
|
812
937
|
policyEnforcer;
|
|
813
938
|
auditLogger;
|
|
814
939
|
secretVault;
|
|
940
|
+
commandAllowlist;
|
|
815
941
|
connections = /* @__PURE__ */ new Set();
|
|
816
942
|
constructor(options) {
|
|
817
943
|
this.config = options.config;
|
|
818
944
|
this.policyEnforcer = options.policyEnforcer;
|
|
819
945
|
this.auditLogger = options.auditLogger;
|
|
820
946
|
this.secretVault = options.secretVault;
|
|
947
|
+
this.commandAllowlist = options.commandAllowlist;
|
|
821
948
|
}
|
|
822
949
|
/**
|
|
823
950
|
* Start the Unix socket server
|
|
@@ -921,20 +1048,29 @@ var UnixSocketServer = class {
|
|
|
921
1048
|
request.params,
|
|
922
1049
|
context
|
|
923
1050
|
);
|
|
924
|
-
|
|
1051
|
+
let finalPolicy = policyResult;
|
|
1052
|
+
if (!policyResult.allowed && request.method !== "policy_check") {
|
|
1053
|
+
const target = this.extractTarget(request);
|
|
1054
|
+
const daemonUrl = this.config.daemonUrl || "http://127.0.0.1:5200";
|
|
1055
|
+
const override = await forwardPolicyToDaemon(request.method, target, daemonUrl);
|
|
1056
|
+
if (override) {
|
|
1057
|
+
finalPolicy = override;
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
if (!finalPolicy.allowed) {
|
|
925
1061
|
await this.auditLogger.log({
|
|
926
1062
|
id: requestId,
|
|
927
1063
|
timestamp: /* @__PURE__ */ new Date(),
|
|
928
1064
|
operation: request.method,
|
|
929
1065
|
channel: "socket",
|
|
930
1066
|
allowed: false,
|
|
931
|
-
policyId:
|
|
1067
|
+
policyId: finalPolicy.policyId,
|
|
932
1068
|
target: this.extractTarget(request),
|
|
933
1069
|
result: "denied",
|
|
934
|
-
errorMessage:
|
|
1070
|
+
errorMessage: finalPolicy.reason,
|
|
935
1071
|
durationMs: Date.now() - startTime
|
|
936
1072
|
});
|
|
937
|
-
return this.errorResponse(request.id, 1001,
|
|
1073
|
+
return this.errorResponse(request.id, 1001, finalPolicy.reason || "Policy denied");
|
|
938
1074
|
}
|
|
939
1075
|
const handler = this.getHandler(request.method);
|
|
940
1076
|
if (!handler) {
|
|
@@ -943,7 +1079,9 @@ var UnixSocketServer = class {
|
|
|
943
1079
|
const result = await handler(request.params, context, {
|
|
944
1080
|
policyEnforcer: this.policyEnforcer,
|
|
945
1081
|
auditLogger: this.auditLogger,
|
|
946
|
-
secretVault: this.secretVault
|
|
1082
|
+
secretVault: this.secretVault,
|
|
1083
|
+
commandAllowlist: this.commandAllowlist,
|
|
1084
|
+
daemonUrl: this.config.daemonUrl
|
|
947
1085
|
});
|
|
948
1086
|
await this.auditLogger.log({
|
|
949
1087
|
id: requestId,
|
|
@@ -951,7 +1089,7 @@ var UnixSocketServer = class {
|
|
|
951
1089
|
operation: request.method,
|
|
952
1090
|
channel: "socket",
|
|
953
1091
|
allowed: true,
|
|
954
|
-
policyId:
|
|
1092
|
+
policyId: finalPolicy.policyId,
|
|
955
1093
|
target: this.extractTarget(request),
|
|
956
1094
|
result: result.success ? "success" : "error",
|
|
957
1095
|
errorMessage: result.error?.message,
|
|
@@ -990,7 +1128,9 @@ var UnixSocketServer = class {
|
|
|
990
1128
|
secret_inject: handleSecretInject,
|
|
991
1129
|
ping: handlePing,
|
|
992
1130
|
skill_install: handleSkillInstall,
|
|
993
|
-
skill_uninstall: handleSkillUninstall
|
|
1131
|
+
skill_uninstall: handleSkillUninstall,
|
|
1132
|
+
policy_check: handlePolicyCheck,
|
|
1133
|
+
events_batch: handleEventsBatch
|
|
994
1134
|
};
|
|
995
1135
|
return handlerMap[method];
|
|
996
1136
|
}
|
|
@@ -1021,7 +1161,9 @@ var HTTP_ALLOWED_OPERATIONS = /* @__PURE__ */ new Set([
|
|
|
1021
1161
|
"file_read",
|
|
1022
1162
|
"file_list",
|
|
1023
1163
|
"open_url",
|
|
1024
|
-
"ping"
|
|
1164
|
+
"ping",
|
|
1165
|
+
"policy_check",
|
|
1166
|
+
"events_batch"
|
|
1025
1167
|
]);
|
|
1026
1168
|
var HTTP_DENIED_OPERATIONS = /* @__PURE__ */ new Set([
|
|
1027
1169
|
"exec",
|
|
@@ -1033,10 +1175,12 @@ var HttpFallbackServer = class {
|
|
|
1033
1175
|
config;
|
|
1034
1176
|
policyEnforcer;
|
|
1035
1177
|
auditLogger;
|
|
1178
|
+
commandAllowlist;
|
|
1036
1179
|
constructor(options) {
|
|
1037
1180
|
this.config = options.config;
|
|
1038
1181
|
this.policyEnforcer = options.policyEnforcer;
|
|
1039
1182
|
this.auditLogger = options.auditLogger;
|
|
1183
|
+
this.commandAllowlist = options.commandAllowlist;
|
|
1040
1184
|
}
|
|
1041
1185
|
/**
|
|
1042
1186
|
* Start the HTTP fallback server
|
|
@@ -1157,20 +1301,29 @@ var HttpFallbackServer = class {
|
|
|
1157
1301
|
request.params,
|
|
1158
1302
|
context
|
|
1159
1303
|
);
|
|
1160
|
-
|
|
1304
|
+
let finalPolicy = policyResult;
|
|
1305
|
+
if (!policyResult.allowed && request.method !== "policy_check") {
|
|
1306
|
+
const target = this.extractTarget(request);
|
|
1307
|
+
const daemonUrl = this.config.daemonUrl || "http://127.0.0.1:5200";
|
|
1308
|
+
const override = await forwardPolicyToDaemon(request.method, target, daemonUrl);
|
|
1309
|
+
if (override) {
|
|
1310
|
+
finalPolicy = override;
|
|
1311
|
+
}
|
|
1312
|
+
}
|
|
1313
|
+
if (!finalPolicy.allowed) {
|
|
1161
1314
|
await this.auditLogger.log({
|
|
1162
1315
|
id: requestId,
|
|
1163
1316
|
timestamp: /* @__PURE__ */ new Date(),
|
|
1164
1317
|
operation: request.method,
|
|
1165
1318
|
channel: "http",
|
|
1166
1319
|
allowed: false,
|
|
1167
|
-
policyId:
|
|
1320
|
+
policyId: finalPolicy.policyId,
|
|
1168
1321
|
target: this.extractTarget(request),
|
|
1169
1322
|
result: "denied",
|
|
1170
|
-
errorMessage:
|
|
1323
|
+
errorMessage: finalPolicy.reason,
|
|
1171
1324
|
durationMs: Date.now() - startTime
|
|
1172
1325
|
});
|
|
1173
|
-
return this.errorResponse(request.id, 1001,
|
|
1326
|
+
return this.errorResponse(request.id, 1001, finalPolicy.reason || "Policy denied");
|
|
1174
1327
|
}
|
|
1175
1328
|
const handler = this.getHandler(request.method);
|
|
1176
1329
|
if (!handler) {
|
|
@@ -1179,8 +1332,10 @@ var HttpFallbackServer = class {
|
|
|
1179
1332
|
const result = await handler(request.params, context, {
|
|
1180
1333
|
policyEnforcer: this.policyEnforcer,
|
|
1181
1334
|
auditLogger: this.auditLogger,
|
|
1182
|
-
secretVault: null
|
|
1335
|
+
secretVault: null,
|
|
1183
1336
|
// Not available over HTTP
|
|
1337
|
+
commandAllowlist: this.commandAllowlist,
|
|
1338
|
+
daemonUrl: this.config.daemonUrl
|
|
1184
1339
|
});
|
|
1185
1340
|
await this.auditLogger.log({
|
|
1186
1341
|
id: requestId,
|
|
@@ -1188,7 +1343,7 @@ var HttpFallbackServer = class {
|
|
|
1188
1343
|
operation: request.method,
|
|
1189
1344
|
channel: "http",
|
|
1190
1345
|
allowed: true,
|
|
1191
|
-
policyId:
|
|
1346
|
+
policyId: finalPolicy.policyId,
|
|
1192
1347
|
target: this.extractTarget(request),
|
|
1193
1348
|
result: result.success ? "success" : "error",
|
|
1194
1349
|
errorMessage: result.error?.message,
|
|
@@ -1222,7 +1377,9 @@ var HttpFallbackServer = class {
|
|
|
1222
1377
|
file_read: handleFileRead,
|
|
1223
1378
|
file_list: handleFileList,
|
|
1224
1379
|
open_url: handleOpenUrl,
|
|
1225
|
-
ping: handlePing
|
|
1380
|
+
ping: handlePing,
|
|
1381
|
+
policy_check: handlePolicyCheck,
|
|
1382
|
+
events_batch: handleEventsBatch
|
|
1226
1383
|
};
|
|
1227
1384
|
return handlerMap[method];
|
|
1228
1385
|
}
|
|
@@ -1261,6 +1418,34 @@ var PolicyEnforcer = class {
|
|
|
1261
1418
|
this.policies = options.defaultPolicies;
|
|
1262
1419
|
this.loadPolicies();
|
|
1263
1420
|
}
|
|
1421
|
+
/**
|
|
1422
|
+
* Normalize a policy rule — infer operations from target when missing,
|
|
1423
|
+
* default priority to 0.
|
|
1424
|
+
*/
|
|
1425
|
+
normalizeRule(rule) {
|
|
1426
|
+
const normalized = { ...rule };
|
|
1427
|
+
if (!normalized.priority && normalized.priority !== 0) {
|
|
1428
|
+
normalized.priority = 0;
|
|
1429
|
+
}
|
|
1430
|
+
if (normalized.operations && normalized.operations.length > 0) {
|
|
1431
|
+
return normalized;
|
|
1432
|
+
}
|
|
1433
|
+
switch (normalized.target) {
|
|
1434
|
+
case "url":
|
|
1435
|
+
normalized.operations = ["http_request", "open_url"];
|
|
1436
|
+
break;
|
|
1437
|
+
case "command":
|
|
1438
|
+
normalized.operations = ["exec"];
|
|
1439
|
+
break;
|
|
1440
|
+
case "skill":
|
|
1441
|
+
normalized.operations = ["skill_install", "skill_uninstall"];
|
|
1442
|
+
break;
|
|
1443
|
+
default:
|
|
1444
|
+
normalized.operations = ["*"];
|
|
1445
|
+
break;
|
|
1446
|
+
}
|
|
1447
|
+
return normalized;
|
|
1448
|
+
}
|
|
1264
1449
|
/**
|
|
1265
1450
|
* Load policies from disk
|
|
1266
1451
|
*/
|
|
@@ -1273,7 +1458,7 @@ var PolicyEnforcer = class {
|
|
|
1273
1458
|
this.policies = {
|
|
1274
1459
|
...this.policies,
|
|
1275
1460
|
...loaded,
|
|
1276
|
-
rules: [...this.policies.rules, ...loaded.rules || []]
|
|
1461
|
+
rules: [...this.policies.rules, ...(loaded.rules || []).map((r) => this.normalizeRule(r))]
|
|
1277
1462
|
};
|
|
1278
1463
|
this.lastLoad = Date.now();
|
|
1279
1464
|
} catch (error) {
|
|
@@ -1289,7 +1474,7 @@ var PolicyEnforcer = class {
|
|
|
1289
1474
|
const content = fs4.readFileSync(path4.join(customDir, file), "utf-8");
|
|
1290
1475
|
const custom = JSON.parse(content);
|
|
1291
1476
|
if (custom.rules) {
|
|
1292
|
-
this.policies.rules.push(...custom.rules);
|
|
1477
|
+
this.policies.rules.push(...custom.rules.map((r) => this.normalizeRule(r)));
|
|
1293
1478
|
}
|
|
1294
1479
|
}
|
|
1295
1480
|
}
|
|
@@ -1339,6 +1524,12 @@ var PolicyEnforcer = class {
|
|
|
1339
1524
|
if (!constraintResult.allowed) {
|
|
1340
1525
|
return constraintResult;
|
|
1341
1526
|
}
|
|
1527
|
+
if (["file_read", "file_write", "file_list"].includes(operation) && this.policies.fsConstraints) {
|
|
1528
|
+
return { allowed: true, reason: "Allowed by file system constraints" };
|
|
1529
|
+
}
|
|
1530
|
+
if (operation === "http_request" && this.policies.networkConstraints) {
|
|
1531
|
+
return { allowed: true, reason: "Allowed by network constraints" };
|
|
1532
|
+
}
|
|
1342
1533
|
return {
|
|
1343
1534
|
allowed: this.policies.defaultAction === "allow",
|
|
1344
1535
|
reason: this.policies.defaultAction === "deny" ? "No matching allow policy" : void 0
|
|
@@ -1518,6 +1709,28 @@ var BuiltinPolicies = [
|
|
|
1518
1709
|
enabled: true,
|
|
1519
1710
|
priority: 1e3
|
|
1520
1711
|
},
|
|
1712
|
+
// Allow interceptor policy checks (internal RPC — must not be subject to policy gate)
|
|
1713
|
+
{
|
|
1714
|
+
id: "builtin-allow-policy-check",
|
|
1715
|
+
name: "Allow interceptor policy checks",
|
|
1716
|
+
action: "allow",
|
|
1717
|
+
target: "command",
|
|
1718
|
+
operations: ["policy_check"],
|
|
1719
|
+
patterns: ["*"],
|
|
1720
|
+
enabled: true,
|
|
1721
|
+
priority: 1e3
|
|
1722
|
+
},
|
|
1723
|
+
// Allow interceptor event reporting (internal RPC)
|
|
1724
|
+
{
|
|
1725
|
+
id: "builtin-allow-events-batch",
|
|
1726
|
+
name: "Allow interceptor event reporting",
|
|
1727
|
+
action: "allow",
|
|
1728
|
+
target: "command",
|
|
1729
|
+
operations: ["events_batch"],
|
|
1730
|
+
patterns: ["*"],
|
|
1731
|
+
enabled: true,
|
|
1732
|
+
priority: 1e3
|
|
1733
|
+
},
|
|
1521
1734
|
// Allow skill installation/uninstallation (daemon management operations)
|
|
1522
1735
|
{
|
|
1523
1736
|
id: "builtin-allow-skill-management",
|
|
@@ -1538,9 +1751,13 @@ var BuiltinPolicies = [
|
|
|
1538
1751
|
operations: ["http_request"],
|
|
1539
1752
|
patterns: [
|
|
1540
1753
|
"http://localhost:*",
|
|
1754
|
+
"http://localhost:*/**",
|
|
1541
1755
|
"http://127.0.0.1:*",
|
|
1756
|
+
"http://127.0.0.1:*/**",
|
|
1542
1757
|
"https://localhost:*",
|
|
1543
|
-
"https://
|
|
1758
|
+
"https://localhost:*/**",
|
|
1759
|
+
"https://127.0.0.1:*",
|
|
1760
|
+
"https://127.0.0.1:*/**"
|
|
1544
1761
|
],
|
|
1545
1762
|
enabled: true,
|
|
1546
1763
|
priority: 100
|
|
@@ -1640,10 +1857,15 @@ var BuiltinPolicies = [
|
|
|
1640
1857
|
target: "url",
|
|
1641
1858
|
operations: ["http_request"],
|
|
1642
1859
|
patterns: [
|
|
1860
|
+
"https://api.anthropic.com",
|
|
1643
1861
|
"https://api.anthropic.com/**",
|
|
1862
|
+
"https://api.openai.com",
|
|
1644
1863
|
"https://api.openai.com/**",
|
|
1864
|
+
"https://api.cohere.ai",
|
|
1645
1865
|
"https://api.cohere.ai/**",
|
|
1866
|
+
"https://generativelanguage.googleapis.com",
|
|
1646
1867
|
"https://generativelanguage.googleapis.com/**",
|
|
1868
|
+
"https://api.mistral.ai",
|
|
1647
1869
|
"https://api.mistral.ai/**"
|
|
1648
1870
|
],
|
|
1649
1871
|
enabled: true,
|
|
@@ -1657,10 +1879,15 @@ var BuiltinPolicies = [
|
|
|
1657
1879
|
target: "url",
|
|
1658
1880
|
operations: ["http_request"],
|
|
1659
1881
|
patterns: [
|
|
1882
|
+
"https://registry.npmjs.org",
|
|
1660
1883
|
"https://registry.npmjs.org/**",
|
|
1884
|
+
"https://pypi.org",
|
|
1661
1885
|
"https://pypi.org/**",
|
|
1886
|
+
"https://files.pythonhosted.org",
|
|
1662
1887
|
"https://files.pythonhosted.org/**",
|
|
1888
|
+
"https://crates.io",
|
|
1663
1889
|
"https://crates.io/**",
|
|
1890
|
+
"https://rubygems.org",
|
|
1664
1891
|
"https://rubygems.org/**"
|
|
1665
1892
|
],
|
|
1666
1893
|
enabled: true,
|
|
@@ -1674,23 +1901,28 @@ var BuiltinPolicies = [
|
|
|
1674
1901
|
target: "url",
|
|
1675
1902
|
operations: ["http_request"],
|
|
1676
1903
|
patterns: [
|
|
1904
|
+
"https://github.com",
|
|
1677
1905
|
"https://github.com/**",
|
|
1906
|
+
"https://api.github.com",
|
|
1678
1907
|
"https://api.github.com/**",
|
|
1908
|
+
"https://raw.githubusercontent.com",
|
|
1679
1909
|
"https://raw.githubusercontent.com/**",
|
|
1910
|
+
"https://gist.github.com",
|
|
1680
1911
|
"https://gist.github.com/**"
|
|
1681
1912
|
],
|
|
1682
1913
|
enabled: true,
|
|
1683
1914
|
priority: 50
|
|
1684
1915
|
}
|
|
1685
1916
|
];
|
|
1686
|
-
function getDefaultPolicies() {
|
|
1917
|
+
function getDefaultPolicies(options) {
|
|
1918
|
+
const agentHome = options?.agentHome || process.env["AGENSHIELD_AGENT_HOME"] || "/Users/clawagent";
|
|
1687
1919
|
return {
|
|
1688
1920
|
version: "1.0.0",
|
|
1689
1921
|
defaultAction: "deny",
|
|
1690
1922
|
rules: [...BuiltinPolicies],
|
|
1691
1923
|
fsConstraints: {
|
|
1692
1924
|
allowedPaths: [
|
|
1693
|
-
|
|
1925
|
+
agentHome,
|
|
1694
1926
|
"/tmp/agenshield"
|
|
1695
1927
|
],
|
|
1696
1928
|
deniedPatterns: [
|
|
@@ -2656,6 +2888,7 @@ export {
|
|
|
2656
2888
|
SecretVault,
|
|
2657
2889
|
UnixSocketServer,
|
|
2658
2890
|
getDefaultPolicies,
|
|
2891
|
+
handleEventsBatch,
|
|
2659
2892
|
handleExec,
|
|
2660
2893
|
handleFileList,
|
|
2661
2894
|
handleFileRead,
|
|
@@ -2663,6 +2896,7 @@ export {
|
|
|
2663
2896
|
handleHttpRequest,
|
|
2664
2897
|
handleOpenUrl,
|
|
2665
2898
|
handlePing,
|
|
2899
|
+
handlePolicyCheck,
|
|
2666
2900
|
handleSecretInject,
|
|
2667
2901
|
handleSkillInstall,
|
|
2668
2902
|
handleSkillUninstall
|