Sandbox
All bash commands from the LLM run in isolated Docker containers. The sandbox is mandatory - there is no option to run commands directly on the host.
Overview
The sandbox protects against:
- Malicious commands - LLM generating harmful commands (intentional or via prompt injection)
- Accidental damage - Commands that could damage the host system
- Resource exhaustion - Fork bombs, memory exhaustion, disk filling
- Data exfiltration - Unauthorized access to host files or secrets
- Privilege escalation - Attempts to gain root or host access
Architecture
┌─────────────────────────────────────────────────────────────┐│ HOST SYSTEM ││ ┌────────────────────────────────────────────────────────┐ ││ │ Ash Agent │ ││ │ - Runs on host │ ││ │ - Has access to config (~/.ash/) │ ││ │ - Has access to SQLite database │ ││ │ - Communicates with LLM API │ ││ └────────────────────────────────────────────────────────┘ ││ │ ││ Tool Execution ││ ▼ ││ ┌────────────────────────────────────────────────────────┐ ││ │ Docker Container (Sandbox) │ ││ │ ┌──────────────────────────────────────────────────┐ │ ││ │ │ Bash commands execute here │ │ ││ │ │ - Isolated filesystem │ │ ││ │ │ - Limited resources │ │ ││ │ │ - Unprivileged user │ │ ││ │ │ - Optional network access │ │ ││ │ └──────────────────────────────────────────────────┘ │ ││ └────────────────────────────────────────────────────────┘ │└─────────────────────────────────────────────────────────────┘Configuration
[sandbox]image = "ash-sandbox:latest"timeout = 60memory_limit = "512m"cpu_limit = 1.0runtime = "runc"network_mode = "bridge"dns_servers = []http_proxy = ""workspace_access = "rw"sessions_access = "none"Options
| Option | Type | Default | Description |
|---|---|---|---|
image | string | "ash-sandbox:latest" | Docker image name |
timeout | int | 60 | Command timeout in seconds |
memory_limit | string | "512m" | Container memory limit |
cpu_limit | float | 1.0 | CPU cores allowed |
runtime | string | "runc" | Container runtime |
network_mode | string | "bridge" | Network isolation mode |
dns_servers | list | [] | Custom DNS servers |
http_proxy | string | "" | HTTP proxy URL |
workspace_access | string | "rw" | Workspace mount mode |
sessions_access | string | "none" | Sessions directory mount mode |
Security Controls
Container Isolation
| Control | Implementation | Purpose |
|---|---|---|
| Read-only root filesystem | --read-only | Prevent persistent changes |
| Dropped capabilities | cap_drop: ALL | Remove Linux capabilities |
| No privilege escalation | no-new-privileges | Prevent setuid exploitation |
| Process limit | pids_limit: 100 | Fork bomb protection |
| Memory limit | mem_limit: 512m | Memory exhaustion protection |
| CPU limit | cpu_limit: 1.0 | CPU exhaustion protection |
| Non-root user | USER sandbox | Reduced privilege |
| Removed setuid binaries | Dockerfile cleanup | Prevent privilege escalation |
Filesystem Access
| Path | Access | Notes |
|---|---|---|
/ (root) | Read-only | Immutable base system |
/etc, /usr, /bin | Read-only | System directories protected |
/workspace | Configurable (none/ro/rw) | Mounted from host workspace |
/tmp | Read-write (tmpfs, 64MB) | Temporary files, noexec |
/home/sandbox | Read-write (tmpfs, 64MB) | User home, noexec |
/var/tmp | Read-write (tmpfs, 32MB) | Temporary files, noexec |
/run | Read-write (tmpfs, 16MB) | Runtime files, noexec |
/root | No access | Root home inaccessible |
Network Isolation
| Mode | Behavior |
|---|---|
none | Completely isolated, no network |
bridge | Standard Docker networking, can reach internet |
[sandbox]network_mode = "none" # Fully isolatedDNS Filtering
Use filtered DNS servers:
[sandbox]network_mode = "bridge"dns_servers = ["9.9.9.9", "149.112.112.112"] # Quad9 filtered DNSHTTP Proxy
Route traffic through a proxy for monitoring:
[sandbox]http_proxy = "http://localhost:8888"Workspace Access
Control how the workspace is mounted:
| Mode | Description |
|---|---|
none | Workspace not mounted |
ro | Read-only access |
rw | Read-write access |
[sandbox]workspace_access = "ro" # Read-only for safetySessions Access
Control whether the agent can read session history from the sandbox:
| Mode | Description |
|---|---|
none | Sessions not mounted (default) |
ro | Read-only access to session transcripts |
[sandbox]sessions_access = "ro" # Allow reading past conversationsContainer Runtime
runc (Default)
Standard OCI runtime:
[sandbox]runtime = "runc"gVisor (runsc)
Enhanced isolation with gVisor:
[sandbox]runtime = "runsc"Components
Sandbox Manager
Location: src/ash/sandbox/manager.py
Manages container lifecycle:
class SandboxManager: async def create(self) -> Container: """Create a new sandbox container."""
async def execute( self, command: str, timeout: int = 60, ) -> ExecutionResult: """Execute command in sandbox."""
async def cleanup(self) -> None: """Remove stopped containers."""Sandbox Executor
Location: src/ash/sandbox/executor.py
Handles command execution:
class SandboxExecutor: async def run( self, command: str, *, timeout: int, working_dir: str = "/workspace", ) -> ExecutionResult: """Run command with resource limits."""Expected Behaviors
MUST Allow
- Command execution - Bash commands run and return output
- Python execution -
python3available for scripting - Common tools -
git,curl,jq,vim,less,treeavailable - Workspace access - Read/write to
/workspacewhen configured - Temp file creation - Write to
/tmpfor temporary files - Network requests - HTTP/HTTPS when
network_mode: bridge - Exit codes - Non-zero exit codes preserved and reported
- Stderr capture - Error output captured and returned
MUST Block
- System modification - Writing to
/etc,/usr,/bin, etc. - Privilege escalation -
sudo,su, setuid binaries - Container escape - Access to host filesystem outside mounts
- Resource exhaustion - Fork bombs, memory bombs limited
- Persistent malware - Read-only filesystem prevents persistence
- Host secret access - No access to host environment variables
- Unlimited execution - Commands timeout after configured limit
CLI Commands
Building the Sandbox
uv run ash sandbox buildManaging Containers
# Clean up stopped containersuv run ash sandbox cleanVerification
Automated Tests
Run the security verification suite with pytest:
uv run pytest tests/test_sandbox_verify.py -vThis runs tests across 5 categories:
- SECURITY - User isolation, filesystem restrictions
- RESOURCES - Timeouts, tmpfs, noexec
- NETWORK - DNS, HTTP, HTTPS connectivity (use
-m "not network"to skip) - FUNCTIONAL - Available tools and utilities
- EDGE_CASES - Special characters, output handling
Manual Prompt Tests
Use the /test-sandbox skill for manual verification prompts. Key scenarios:
rm -rf /-> “Read-only file system”sudo whoami-> “command not found” or “permission denied”- Fork bomb
:(){ :|:& };:-> Contained by pids limit - Memory bomb -> Killed by memory limit
Troubleshooting
Container fails to start
Check Docker is running:
docker infoCommand timeout
Increase the timeout:
[sandbox]timeout = 120Out of memory
Increase memory limit:
[sandbox]memory_limit = "1g"Incident Response
If a sandbox escape or security issue is discovered:
- Stop the service -
ash sandbox cleanremoves all containers - Review logs - Check what commands were executed
- Update image -
ash sandbox build --forcerebuilds with fixes - Report issue - File security issue in repository