mirror of
https://github.com/tvytlx/ai-agent-deep-dive.git
synced 2026-04-03 07:34:50 +08:00
180 lines
7.6 KiB
JavaScript
180 lines
7.6 KiB
JavaScript
/**
|
|
* Configuration for Sandbox Runtime
|
|
* This is the main configuration interface that consumers pass to SandboxManager.initialize()
|
|
*/
|
|
import { z } from 'zod';
|
|
/**
|
|
* Schema for domain patterns (e.g., "example.com", "*.npmjs.org")
|
|
* Validates that domain patterns are safe and don't include overly broad wildcards
|
|
*/
|
|
const domainPatternSchema = z.string().refine(val => {
|
|
// Reject protocols, paths, ports, etc.
|
|
if (val.includes('://') || val.includes('/') || val.includes(':')) {
|
|
return false;
|
|
}
|
|
// Allow localhost
|
|
if (val === 'localhost')
|
|
return true;
|
|
// Allow wildcard domains like *.example.com
|
|
if (val.startsWith('*.')) {
|
|
const domain = val.slice(2);
|
|
// After the *. there must be a valid domain with at least one more dot
|
|
// e.g., *.example.com is valid, *.com is not (too broad)
|
|
if (!domain.includes('.') ||
|
|
domain.startsWith('.') ||
|
|
domain.endsWith('.')) {
|
|
return false;
|
|
}
|
|
// Count dots - must have at least 2 parts after the wildcard (e.g., example.com)
|
|
const parts = domain.split('.');
|
|
return parts.length >= 2 && parts.every(p => p.length > 0);
|
|
}
|
|
// Reject any other use of wildcards (e.g., *, *., etc.)
|
|
if (val.includes('*')) {
|
|
return false;
|
|
}
|
|
// Regular domains must have at least one dot and only valid characters
|
|
return val.includes('.') && !val.startsWith('.') && !val.endsWith('.');
|
|
}, {
|
|
message: 'Invalid domain pattern. Must be a valid domain (e.g., "example.com") or wildcard (e.g., "*.example.com"). Overly broad patterns like "*.com" or "*" are not allowed for security reasons.',
|
|
});
|
|
/**
|
|
* Schema for filesystem paths
|
|
*/
|
|
const filesystemPathSchema = z.string().min(1, 'Path cannot be empty');
|
|
/**
|
|
* Schema for MITM proxy configuration
|
|
* Allows routing specific domains through an upstream MITM proxy via Unix socket
|
|
*/
|
|
const MitmProxyConfigSchema = z.object({
|
|
socketPath: z.string().min(1).describe('Unix socket path to the MITM proxy'),
|
|
domains: z
|
|
.array(domainPatternSchema)
|
|
.min(1)
|
|
.describe('Domains to route through the MITM proxy (e.g., ["api.example.com", "*.internal.org"])'),
|
|
});
|
|
/**
|
|
* Network configuration schema for validation
|
|
*/
|
|
export const NetworkConfigSchema = z.object({
|
|
allowedDomains: z
|
|
.array(domainPatternSchema)
|
|
.describe('List of allowed domains (e.g., ["github.com", "*.npmjs.org"])'),
|
|
deniedDomains: z
|
|
.array(domainPatternSchema)
|
|
.describe('List of denied domains'),
|
|
allowUnixSockets: z
|
|
.array(z.string())
|
|
.optional()
|
|
.describe('macOS only: Unix socket paths to allow. Ignored on Linux (seccomp cannot filter by path).'),
|
|
allowAllUnixSockets: z
|
|
.boolean()
|
|
.optional()
|
|
.describe('If true, allow all Unix sockets (disables blocking on both platforms).'),
|
|
allowLocalBinding: z
|
|
.boolean()
|
|
.optional()
|
|
.describe('Whether to allow binding to local ports (default: false)'),
|
|
httpProxyPort: z
|
|
.number()
|
|
.int()
|
|
.min(1)
|
|
.max(65535)
|
|
.optional()
|
|
.describe('Port of an external HTTP proxy to use instead of starting a local one. When provided, the library will skip starting its own HTTP proxy and use this port. The external proxy must handle domain filtering.'),
|
|
socksProxyPort: z
|
|
.number()
|
|
.int()
|
|
.min(1)
|
|
.max(65535)
|
|
.optional()
|
|
.describe('Port of an external SOCKS proxy to use instead of starting a local one. When provided, the library will skip starting its own SOCKS proxy and use this port. The external proxy must handle domain filtering.'),
|
|
mitmProxy: MitmProxyConfigSchema.optional().describe('Optional MITM proxy configuration. Routes matching domains through an upstream proxy via Unix socket while SRT still handles allow/deny filtering.'),
|
|
});
|
|
/**
|
|
* Filesystem configuration schema for validation
|
|
*/
|
|
export const FilesystemConfigSchema = z.object({
|
|
denyRead: z.array(filesystemPathSchema).describe('Paths denied for reading'),
|
|
allowRead: z
|
|
.array(filesystemPathSchema)
|
|
.optional()
|
|
.describe('Paths to re-allow reading within denied regions (takes precedence over denyRead). ' +
|
|
'Use with denyRead to deny a broad region then allow back specific subdirectories.'),
|
|
allowWrite: z
|
|
.array(filesystemPathSchema)
|
|
.describe('Paths allowed for writing'),
|
|
denyWrite: z
|
|
.array(filesystemPathSchema)
|
|
.describe('Paths denied for writing (takes precedence over allowWrite)'),
|
|
allowGitConfig: z
|
|
.boolean()
|
|
.optional()
|
|
.describe('Allow writes to .git/config files (default: false). Enables git remote URL updates while keeping .git/hooks protected.'),
|
|
});
|
|
/**
|
|
* Configuration schema for ignoring specific sandbox violations
|
|
* Maps command patterns to filesystem paths to ignore violations for.
|
|
*/
|
|
export const IgnoreViolationsConfigSchema = z
|
|
.record(z.string(), z.array(z.string()))
|
|
.describe('Map of command patterns to filesystem paths to ignore violations for. Use "*" to match all commands');
|
|
/**
|
|
* Ripgrep configuration schema
|
|
*/
|
|
export const RipgrepConfigSchema = z.object({
|
|
command: z.string().describe('The ripgrep command to execute'),
|
|
args: z
|
|
.array(z.string())
|
|
.optional()
|
|
.describe('Additional arguments to pass before ripgrep args'),
|
|
argv0: z
|
|
.string()
|
|
.optional()
|
|
.describe('Override argv[0] when spawning (for multicall binaries that dispatch on argv[0])'),
|
|
});
|
|
/**
|
|
* Seccomp configuration schema (Linux only)
|
|
* Allows specifying custom paths to seccomp binaries
|
|
*/
|
|
export const SeccompConfigSchema = z.object({
|
|
bpfPath: z
|
|
.string()
|
|
.optional()
|
|
.describe('Path to the unix-block.bpf filter file'),
|
|
applyPath: z.string().optional().describe('Path to the apply-seccomp binary'),
|
|
});
|
|
/**
|
|
* Main configuration schema for Sandbox Runtime validation
|
|
*/
|
|
export const SandboxRuntimeConfigSchema = z.object({
|
|
network: NetworkConfigSchema.describe('Network restrictions configuration'),
|
|
filesystem: FilesystemConfigSchema.describe('Filesystem restrictions configuration'),
|
|
ignoreViolations: IgnoreViolationsConfigSchema.optional().describe('Optional configuration for ignoring specific violations'),
|
|
enableWeakerNestedSandbox: z
|
|
.boolean()
|
|
.optional()
|
|
.describe('Enable weaker nested sandbox mode (for Docker environments)'),
|
|
enableWeakerNetworkIsolation: z
|
|
.boolean()
|
|
.optional()
|
|
.describe('Enable weaker network isolation to allow access to com.apple.trustd.agent (macOS only). ' +
|
|
'This is needed for Go programs (gh, gcloud, terraform, kubectl, etc.) to verify TLS certificates ' +
|
|
'when using httpProxyPort with a MITM proxy and custom CA. Enabling this opens a potential data ' +
|
|
'exfiltration vector through the trustd service. Only enable if you need Go TLS verification.'),
|
|
ripgrep: RipgrepConfigSchema.optional().describe('Custom ripgrep configuration (default: { command: "rg" })'),
|
|
mandatoryDenySearchDepth: z
|
|
.number()
|
|
.int()
|
|
.min(1)
|
|
.max(10)
|
|
.optional()
|
|
.describe('Maximum directory depth to search for dangerous files on Linux (default: 3). ' +
|
|
'Higher values provide more protection but slower performance.'),
|
|
allowPty: z
|
|
.boolean()
|
|
.optional()
|
|
.describe('Allow pseudo-terminal (pty) operations (macOS only)'),
|
|
seccomp: SeccompConfigSchema.optional().describe('Custom seccomp binary paths (Linux only).'),
|
|
});
|
|
//# sourceMappingURL=sandbox-config.js.map
|