@absolutejs/agent-sandbox
Advanced tools
+22
-6
@@ -20,6 +20,8 @@ // @bun | ||
| }; | ||
| var checkCredentials = (allowed, requested) => { | ||
| for (const alias of Object.values(requested ?? {})) { | ||
| if (!allowed?.includes(alias)) | ||
| throw new Error(`Credential alias is not granted: ${alias}`); | ||
| var checkCredentials = (allowed, requested, normalizeName) => { | ||
| for (const [name, alias] of Object.entries(requested ?? {})) { | ||
| const normalized = normalizeName(name); | ||
| const binding = Object.entries(allowed ?? {}).find(([candidate]) => normalizeName(candidate) === normalized)?.[1]; | ||
| if (!binding?.includes(alias)) | ||
| throw new Error(`Credential binding is not granted: ${name} -> ${alias}`); | ||
| } | ||
@@ -54,3 +56,7 @@ }; | ||
| throw new Error("HTTP request body exceeds grant"); | ||
| checkCredentials(capability.credentialAliases, action.credentials); | ||
| for (const name of Object.keys(action.credentials ?? {})) { | ||
| if (forbiddenHeaders.has(name.toLowerCase()) && name.toLowerCase() !== "authorization") | ||
| throw new Error(`Credential target header is denied: ${name}`); | ||
| } | ||
| checkCredentials(capability.credentialBindings, action.credentials, (value) => value.toLowerCase()); | ||
| }; | ||
@@ -72,3 +78,13 @@ var authorizeFilesystem = (capability, action) => { | ||
| throw new Error("Working directory is not granted"); | ||
| checkCredentials(capability.credentialAliases, action.credentials); | ||
| const forbiddenCredentialTargets = new Set([ | ||
| "PATH", | ||
| "LD_PRELOAD", | ||
| "NODE_OPTIONS", | ||
| "BUN_OPTIONS" | ||
| ]); | ||
| for (const name of Object.keys(action.credentials ?? {})) { | ||
| if (forbiddenCredentialTargets.has(name.toUpperCase()) || name.toUpperCase().startsWith("DYLD_")) | ||
| throw new Error(`Process credential target is denied: ${name}`); | ||
| } | ||
| checkCredentials(capability.credentialBindings, action.credentials, (value) => value); | ||
| }; | ||
@@ -75,0 +91,0 @@ var authorizeAgentSandboxAction = (capability, action) => { |
+4
-2
@@ -12,3 +12,4 @@ export type AgentIdentityPin = { | ||
| pathPrefixes?: string[]; | ||
| credentialAliases?: string[]; | ||
| /** Exact header name to permitted broker credential aliases. */ | ||
| credentialBindings?: Record<string, string[]>; | ||
| maxRequestBytes?: number; | ||
@@ -35,3 +36,4 @@ maxResponseBytes?: number; | ||
| workingDirectories?: string[]; | ||
| credentialAliases?: string[]; | ||
| /** Exact environment/input name to permitted broker credential aliases. */ | ||
| credentialBindings?: Record<string, string[]>; | ||
| timeoutMs?: number; | ||
@@ -38,0 +40,0 @@ maxOutputBytes?: number; |
+1
-1
| { | ||
| "name": "@absolutejs/agent-sandbox", | ||
| "version": "0.1.1", | ||
| "version": "0.2.0", | ||
| "description": "Deny-by-default capability grants and provider-neutral HTTP, filesystem, process, and credential-safe action adapters for AI agents.", | ||
@@ -5,0 +5,0 @@ "type": "module", |
+3
-1
@@ -8,3 +8,5 @@ # @absolutejs/agent-sandbox | ||
| HTTP access is restricted by origin, method, path, body/response size, and | ||
| credential alias. Filesystem access uses boundary-safe absolute roots. Process | ||
| credential binding. Each broker alias is bound to an exact header or process | ||
| input name; transport-controlled headers and process-loader variables remain | ||
| denied even if requested. Filesystem access uses boundary-safe absolute roots. Process | ||
| access uses exact executable allowlists, argument prefixes, working roots, | ||
@@ -11,0 +13,0 @@ timeouts, and output limits. Credentials are resolved inside adapters and never |
25143
4.95%551
3.38%26
8.33%