> ## Documentation Index
> Fetch the complete documentation index at: https://comis-fix-skill-import-vetting-gate.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Sandbox

> Prompt-skill safeguards and ordinary exec process-isolation boundaries

Comis has prompt-skill content safeguards and a separate OS-level sandbox for ordinary `exec` child processes. These controls have different scopes, and neither is a universal sandbox for every skill, tool, integration, or agent. For the detailed process boundary, see [Exec Sandbox](/security/exec-sandbox).

<Note>
  **Sandbox vocabulary.** Documentation has historically used "sandbox" for two
  different mechanisms:

  * **Prompt-skill safeguards** -- load-time sanitization and optional pattern
    scanning for Markdown prompt-skill bodies. They are not process isolation.
    Agent-level tool policy and runtime limits apply separately. SKILL.md
    `permissions` and `allowedTools` declarations are currently advisory metadata,
    not an enforced per-skill boundary.
  * **Exec sandbox** -- OS-level confinement for child processes launched through
    the ordinary `exec` tool when a supported provider is available and active.
    It does not wrap MCP stdio servers, browser processes, in-process tools, or
    every other way the daemon can reach the network or filesystem.

  On Linux, working Bubblewrap provides the strongest supported boundary. macOS
  `sandbox-exec` is deprecated and best effort. If the provider is missing,
  disabled, or auto-disabled on a constrained container host, ordinary `exec`
  can run directly under the daemon account with a warning.
</Note>

## Overview

For Markdown prompt skills, the relevant layers are:

<Steps>
  <Step title="Content Scanning">
    Sanitized prompt-skill text is inspected for known patterns across six categories at load time when scanning is enabled.
  </Step>

  <Step title="Sanitization Pipeline">
    Skill body cleaned through 4-step pipeline: HTML comment stripping, Unicode normalization, invisible character removal, and size enforcement.
  </Step>

  <Step title="Tool Policy Enforcement">
    Registered tools are filtered by the agent's configured profile and allow/deny lists. This is agent-level policy, not enforcement of SKILL.md permission declarations.
  </Step>

  <Step title="Execution Limits">
    Agent-loop budgets, circuit breakers, step limits, and optional Node.js permissions constrain their respective runtime paths. They do not form one per-skill process sandbox.
  </Step>
</Steps>

## Content Scanning

The content scanner inspects sanitized Markdown prompt-skill bodies at load time for known dangerous patterns. It does not inspect executable dependencies, MCP server binaries, browser code, or arbitrary files a skill later tells an agent to use. It is a pure function; callers handle audit emission and blocking decisions.

<Info>
  **Source:** `packages/skills/src/skills/prompt/content-scanner.ts` -- patterns across 6 categories. Patterns imported from `@comis/core` injection-patterns module.
</Info>

### Scan Categories

Six categories of malicious content are detected:

<AccordionGroup>
  <Accordion title="exec_injection (4 patterns) -- CRITICAL">
    Targets actual injection syntax operators combined with dangerous binaries. These patterns detect subshell injection, backtick injection, `eval()` usage, and pipe-to-shell patterns.

    | Pattern ID       | Severity | Description                                                    |
    | ---------------- | -------- | -------------------------------------------------------------- |
    | `EXEC_SUBSHELL`  | CRITICAL | Subshell command injection: `$(command)` with dangerous binary |
    | `EXEC_BACKTICK`  | CRITICAL | Backtick command injection with dangerous binary               |
    | `EXEC_EVAL`      | CRITICAL | `eval()` with string argument                                  |
    | `EXEC_PIPE_BASH` | CRITICAL | Pipe to shell interpreter                                      |
  </Accordion>

  <Accordion title="env_harvesting (3 patterns) -- WARN">
    Targets mass-dump patterns that extract all environment variables. Individual `$VAR` references are NOT flagged because they are common in configuration documentation.

    | Pattern ID         | Severity | Description                                        |
    | ------------------ | -------- | -------------------------------------------------- |
    | `ENV_PRINTENV`     | WARN     | `printenv` command dumps all environment variables |
    | `ENV_PROC_ENVIRON` | WARN     | Direct read of process environment via `/proc`     |
    | `ENV_MASS_DUMP`    | WARN     | Environment dump piped to exfiltration or encoding |
  </Accordion>

  <Accordion title="crypto_mining (3 patterns) -- CRITICAL/WARN">
    Very low false-positive risk. These terms almost never appear in legitimate AI skill instructions.

    | Pattern ID            | Severity | Description                         |
    | --------------------- | -------- | ----------------------------------- |
    | `CRYPTO_STRATUM`      | CRITICAL | Mining pool protocol (`stratum://`) |
    | `CRYPTO_MINER_BINARY` | CRITICAL | Known cryptocurrency miner binary   |
    | `CRYPTO_POOL_DOMAIN`  | WARN     | Mining pool domain pattern          |
  </Accordion>

  <Accordion title="network_exfiltration (3 patterns) -- WARN/CRITICAL">
    Focuses on piped execution patterns (curl/wget output piped to interpreter) rather than standalone URL references. Reverse shell patterns are elevated to CRITICAL.

    | Pattern ID          | Severity | Description                           |
    | ------------------- | -------- | ------------------------------------- |
    | `NET_CURL_PIPE`     | WARN     | curl output piped to interpreter      |
    | `NET_WGET_EXEC`     | WARN     | wget output to stdout piped elsewhere |
    | `NET_REVERSE_SHELL` | CRITICAL | Reverse shell pattern                 |
  </Accordion>

  <Accordion title="obfuscated_encoding (3 patterns) -- WARN/CRITICAL">
    Only flags long encoded blocks (likely obfuscated payloads) or decode-and-execute chains. Short base64 examples in documentation are not flagged.

    | Pattern ID               | Severity | Description                             |
    | ------------------------ | -------- | --------------------------------------- |
    | `OBF_BASE64_LONG`        | WARN     | Long base64-encoded string (80+ chars)  |
    | `OBF_HEX_LONG`           | WARN     | Long hex-escaped string (20+ sequences) |
    | `OBF_BASE64_DECODE_PIPE` | CRITICAL | base64 decode piped to another command  |
  </Accordion>

  <Accordion title="xml_breakout (2 patterns) -- CRITICAL">
    Detects attempts to escape the skill XML structure and inject system-level instructions at a higher privilege level.

    | Pattern ID        | Severity | Description                                            |
    | ----------------- | -------- | ------------------------------------------------------ |
    | `XML_SKILL_CLOSE` | CRITICAL | Closing tag for skill XML structure (breakout attempt) |
    | `XML_SYSTEM_TAG`  | CRITICAL | System-level message tag (breakout attempt)            |
  </Accordion>
</AccordionGroup>

### Severity Levels

| Severity     | Meaning                                                           | Categories                                                                                                                                             |
| ------------ | ----------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **CRITICAL** | Active exploitation attempt. Skill should be blocked.             | `exec_injection`, `crypto_mining` (stratum, miner binary), `network_exfiltration` (reverse shell), `obfuscated_encoding` (decode pipe), `xml_breakout` |
| **WARN**     | Suspicious pattern with possible legitimate use. Flag for review. | `env_harvesting`, `crypto_mining` (pool domain), `network_exfiltration` (curl/wget pipe), `obfuscated_encoding` (long encoded strings)                 |

### Scan Result Interface

```typescript theme={}
interface ContentScanResult {
  clean: boolean;                     // true if no patterns matched
  findings: ContentScanFinding[];     // array of all matched patterns
}

interface ContentScanFinding {
  ruleId: string;          // Pattern ID (e.g., "EXEC_SUBSHELL")
  category: ScanCategory;  // Category name
  severity: ScanSeverity;  // "CRITICAL" or "WARN"
  description: string;     // Human-readable description
  matchedText: string;     // Matched content (truncated to 100 chars)
  position: number;        // Character offset in content
  lineNumber: number;      // 1-based line number of the match in the original content
}
```

For user-facing guide, see [Security Scanning](/skills/security-scanning).

## Sanitization Pipeline

The 4-step sanitization pipeline processes skill body content before it reaches the system prompt. All functions are pure with no side effects.

<Info>
  **Source:** `packages/skills/src/skills/prompt/sanitizer.ts` -- strict pipeline order: strip HTML comments, NFKC normalize, strip invisible, enforce size.
</Info>

### Step 1: Strip HTML Comments

Removes all `&lt;!-- ... --&gt;` sequences using non-greedy regex (`/&lt;!--[\s\S]*?--&gt;/g`). Non-greedy matching handles multiple separate comments correctly, stopping at the first `--&gt;` rather than the last.

Returns the count of comments removed for audit logging.

### Step 2: Unicode NFKC Normalization

Applies NFKC normalization (compatibility decomposition + canonical composition) via `String.prototype.normalize("NFKC")`. This:

* Decomposes fullwidth characters to their ASCII equivalents (e.g., fullwidth A to A)
* Decomposes ligatures into component characters
* Normalizes compatibility characters to their canonical forms

This prevents homoglyph-based obfuscation where visually similar characters bypass pattern matching.

### Step 3: Strip Invisible Characters

Removes zero-width and invisible Unicode characters that could hide malicious content:

| Character | Code Point      | Name                         |
| --------- | --------------- | ---------------------------- |
| ZWJ       | U+200D          | Zero-width joiner            |
| ZWNJ      | U+200C          | Zero-width non-joiner        |
| ZWSP      | U+200B          | Zero-width space             |
| SHY       | U+00AD          | Soft hyphen                  |
| LRM       | U+200E          | Left-to-right mark           |
| RLM       | U+200F          | Right-to-left mark           |
| Tag block | U+E0000-U+E007F | Unicode tag block characters |

Also detects and reports Unicode tag block bypass attempts (characters in the U+E0000-U+E007F range used to encode hidden instructions).

### Step 4: Size Limit Enforcement

Truncates the sanitized output at `maxBodyLength` characters. Default: **20,000 characters**.

* Size enforcement applies to the output AFTER all other steps
* This prevents unnecessary truncation when HTML comments inflate the raw input size
* When truncation occurs, `[TRUNCATED]` marker is appended

### Pipeline Result

```typescript theme={}
interface SanitizeResult {
  body: string;                  // Final sanitized content
  htmlCommentsStripped: number;  // Count of HTML comments removed
  truncated: boolean;            // Whether size limit was hit
  tagBlockDetected: boolean;     // Unicode tag block bypass detected
}
```

## Tool Policy Enforcement

Tool policies control which tools are available to each agent during skill execution.

<Info>
  **Source:** `packages/skills/src/skills/policy/tool-policy.ts` -- config-driven filtering with 5 profiles and group expansion.
</Info>

### Built-in Profiles

| Profile             | Tools                                                                                                                                                                    | Use Case                                               |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------ |
| `minimal`           | `read`, `write`                                                                                                                                                          | Basic file operations only                             |
| `coding`            | `read`, `edit`, `write`, `grep`, `find`, `ls`, `apply_patch`, `exec`, `process`                                                                                          | Software development tasks                             |
| `messaging`         | `message`, `session_status`                                                                                                                                              | Communication-only agents                              |
| `supervisor`        | `agents_manage`, `obs_query`, `sessions_manage`, `memory_manage`, `channels_manage`, `tokens_manage`, `models_manage`, `skills_manage`, `mcp_manage`, `heartbeat_manage` | Administrative agents                                  |
| `full`              | All tools (empty array = unrestricted)                                                                                                                                   | Fully trusted agents                                   |
| `cron-minimal`      | `web_search`, `message`, `read_file`, `write_file`, `list_dir`, `memory_store`, `memory_search`, `cron`, `discover`                                                      | Conservative preset for cron jobs (opt-in only)        |
| `heartbeat-minimal` | `message`, `memory_store`, `memory_search`, `discover`                                                                                                                   | Conservative preset for heartbeat agents (opt-in only) |

### Resolution Order

1. **Profile baseline** -- populate allowed set from the profile's tool list
2. **Allow list additions** -- add explicitly allowed tools (with group expansion)
3. **Deny list removals** -- remove denied tools (deny always wins)

### Skill Manifest Declarations

Prompt skills can declare `allowedTools` and `permissions` in SKILL.md. Comis
parses those fields, but they are not currently connected to an enforced
per-skill runtime filter or OS permission boundary. Treat them as descriptive
metadata. The agent's `toolPolicy`, capability gates, and each tool's own
validation are the enforceable controls.

For user-facing guide, see [Tool Policy](/skills/tool-policy).

## Execution Limits

Runtime constraints bound parts of the agent loop. They are not proof that a
skill or every process it invokes is isolated.

### Budget Protection

Each agent has a configurable token budget that limits total LLM spend. When the budget is exhausted, further tool calls are rejected. See [Agent Safety](/agents/safety) for configuration details.

### Circuit Breaker

The circuit breaker tracks consecutive LLM failures per agent. After a configurable number of failures, the circuit opens and rejects further requests until a cooldown period expires. This prevents cascading failures from propagating through the system. See [Agent Safety](/agents/safety) for configuration details.

### Step Limit

Each execution has a maximum number of tool-use steps. When the limit is reached, the execution completes with the current state. This prevents infinite loops where the LLM keeps calling tools without converging on a response.

### Source Profiles

Built-in tools that ingest external content have per-tool source profiles controlling byte/char limits and extraction strategies. These are clamped to hard ceilings to prevent runaway context injection:

| Tool          | Max Response Bytes | Max Chars | Extraction Strategy |
| ------------- | ------------------ | --------- | ------------------- |
| `web_fetch`   | 2 MB               | 50,000    | readability         |
| `web_search`  | 500 KB             | 40,000    | structured          |
| `bash`        | 500 KB             | 50,000    | tail                |
| `file_read`   | 1 MB               | 100,000   | raw                 |
| `mcp_default` | 2 MB               | 50,000    | raw                 |

Hard ceilings: 5 MB max response bytes, 500K max chars. Operator overrides cannot exceed these values.

<Info>
  **Source:** `packages/skills/src/tools/builtin/tool-source-profiles.ts` -- per-tool defaults with hard ceiling clamping. Operator overrides via per-agent config.
</Info>

## Defense Layer Summary

| Layer                      | When                                 | What                                                                                  | Action on Violation                                                                                             |
| -------------------------- | ------------------------------------ | ------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| Content Scanning           | Skill load time                      | Inspects skill body for malicious patterns                                            | Blocks skill loading (CRITICAL) or flags for review (WARN)                                                      |
| Sanitization               | Skill load time                      | Strips hidden content and normalizes encoding                                         | Silently removes dangerous content                                                                              |
| Agent Tool Policy          | Execution time                       | Filters registered tools by agent profile/allow/deny                                  | Tool call rejected                                                                                              |
| Budget                     | Execution time                       | Tracks cumulative token spend                                                         | Execution halted                                                                                                |
| Circuit Breaker            | Execution time                       | Tracks consecutive LLM failures                                                       | Requests rejected until cooldown                                                                                |
| Step Limit                 | Execution time                       | Counts tool-use iterations                                                            | Execution completed with current state                                                                          |
| Source Profiles            | Tool result time                     | Caps external content size                                                            | Content truncated to limit                                                                                      |
| Exec Sandbox               | Ordinary `exec` child-process launch | Uses Bubblewrap on supported Linux or best-effort `sandbox-exec` on macOS when active | Missing/disabled provider logs a warning; ordinary `exec` may run under the daemon account without OS isolation |
| Node Permissions           | Supported Node.js launch paths       | Optionally restricts configured file system and network access                        | `ERR_ACCESS_DENIED` on covered operations                                                                       |
| Bound `comis-agent` binary | Jail construction                    | sha256-verifies the in-jail CLI binary; binds it read-only (`--ro-bind`)              | Loud WARN; CLI surface left unavailable (script surface intact)                                                 |

### Node.js Permissions

When enabled on supported launch paths, the Node.js permission model restricts
configured filesystem and network operations. It is disabled by default and is
not a general sandbox for arbitrary child processes, MCP servers, browsers, or
non-Node tools.

See [Node Permissions](/reference/node-permissions) for full configuration reference.

### Bound `comis-agent` binary

The in-jail [`comis-agent` CLI](/reference/comis-agent-cli) is itself a defense surface, so the binary the daemon makes available inside an agent's jail is locked down two ways:

* **Read-only bind (`--ro-bind`).** The binary is bind-mounted read-only into the jail (source path equals destination, so [`COMIS_AGENT_BIN`](/reference/environment-variables#comis_agent_bin) / PATH resolves it) — exactly like the daemon's own Node binary. A **writable** interpreter or binary inside a sandbox is a host-RCE vector, so it is never bound read-write and never copied to a writable location. The writable-path audit (`bwrap-hardening.linux.test.ts`) proves a write to the bound binary from inside the jail **fails** (the read-only bind rejects it).
* **sha256-pinned.** A committed build manifest (`comis-agent-manifest.json`) pins the sha256 of the Comis-built binary. At jail construction the daemon **re-hashes the bound bytes** and refuses to bind a binary whose hash does not match the pin — a swapped or tampered binary is never made available.

**Honest-degrade.** When the binary is missing, fails the hash check, or in-jail Node is unavailable, the daemon emits a loud WARN, leaves `COMIS_AGENT_BIN` unset, and the `comis-agent` CLI surface is unavailable — while the `orchestrate(script)` surface still runs (the degrade is scoped to the CLI, never a silent bind of an unverified binary). See the [`comis-agent` CLI reference](/reference/comis-agent-cli#pinned-and-honest-the-bound-binary).

## Exec Sandbox (OS-Level)

When `agents.{id}.skills.execSandbox.enabled` is `"always"`, the ordinary
`exec` tool uses the detected provider if one is available. This boundary is
limited to that exec child process. Provider selection is based on
`process.platform`:

| Platform         | Provider                                                                | Module                                                               |
| ---------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------- |
| Linux            | `BwrapProvider` (bubblewrap), strongest supported boundary when working | `packages/skills/src/tools/builtin/sandbox/bwrap-provider.ts`        |
| macOS (`darwin`) | `SandboxExecProvider` (`sandbox-exec`), deprecated and best effort      | `packages/skills/src/tools/builtin/sandbox/sandbox-exec-provider.ts` |
| Anything else    | *none -- exec tool runs unsandboxed with a WARN log*                    | --                                                                   |

`detectSandboxProvider()` first probes the candidate binary's availability
(`bwrap --version` or `sandbox-exec -h`); if the binary is missing it logs
a structured warning with the install hint
(`apt install bubblewrap`) and can return `undefined`, leaving ordinary `exec`
to run without an OS sandbox. On bare-metal Linux where Bubblewrap exists but
its smoke test fails, Comis keeps the provider so exec calls fail rather than
silently bypassing that broken provider. Container hosts can explicitly rely
on the container boundary and run ordinary exec without an inner sandbox.

<Info>
  Source: `detectSandboxProvider()` in `packages/skills/src/tools/builtin/sandbox/detect-provider.ts`
</Info>

For the full mount table, attack scope, and configuration walkthrough, see
[Exec Sandbox](/security/exec-sandbox).

### Network Modes

The exec sandbox supports two network modes, selected via `SandboxOptions.network`:

| Mode           | Config value                                       | bwrap args                                         | Description                                                                              |
| -------------- | -------------------------------------------------- | -------------------------------------------------- | ---------------------------------------------------------------------------------------- |
| Open (default) | `{ mode: "open" }` or `undefined`                  | `--unshare-all --share-net`                        | Standard exec sandbox; full network access                                               |
| Broker-only    | `{ mode: "broker-only", brokerSocketPath: "..." }` | `--unshare-all --unshare-net --bind <path> <path>` | Driven-CLI sandbox; only the broker unix socket is reachable via bind-mount (Linux only) |

`broker-only` mode is set automatically for driven-CLI spawns — it is not configured directly by operators. On Linux it uses `--unshare-net` to remove all network access except the broker unix socket bind-mount. On macOS, `broker-only` mode is not available (bubblewrap requirement); the broker still provides TLS termination and injection, but without network namespace enforcement.

### Secure Credential Home

When `secureCredentialHome: true` is set on the sandbox options (automatically applied for driven-CLI spawns), the following bind mounts are omitted so credential files are unreachable inside the sandbox:

* `~/.claude` (read-write bind removed)
* `~/.claude.json` (read-only bind removed)
* `~/.local/share/claude` (read-write bind removed)

This means `cat ~/.claude/.credentials.json` inside the sandbox returns "no such file or directory". The Claude Code CLI cannot read its own credential cache from inside the namespace.

The `broker-only` network mode and `secureCredentialHome` work together as the credential isolation layer for driven-CLI spawns. [Credential Broker →](/security/credential-broker)

<Info>
  Source: `packages/skills/src/tools/builtin/sandbox/types.ts` — `SandboxOptions.network` union; `packages/skills/src/tools/builtin/sandbox/bwrap-provider.ts` — `broker-only` branch at line 234, `secureCredentialHome` bind removal at lines 196–218.
</Info>

## Configuration Reference

All sandbox-related configuration fields consolidated:

| Config Path                                         | Type       | Default    | Description                                                   |
| --------------------------------------------------- | ---------- | ---------- | ------------------------------------------------------------- |
| `agents.{id}.skills.toolPolicy.profile`             | `string`   | `"full"`   | Agent tool-policy profile name; narrow it for untrusted input |
| `agents.{id}.skills.toolPolicy.allow`               | `string[]` | `[]`       | Additional tools to allow                                     |
| `agents.{id}.skills.toolPolicy.deny`                | `string[]` | `[]`       | Tools to deny (overrides allow)                               |
| `agents.{id}.budget.maxTokens`                      | `number`   | varies     | Maximum tokens per execution                                  |
| `agents.{id}.budget.maxCostUsd`                     | `number`   | varies     | Maximum cost per execution                                    |
| `agents.{id}.skills.execSandbox.enabled`            | `enum`     | `"always"` | OS-level sandbox mode: `"always"` or `"never"`                |
| `agents.{id}.skills.execSandbox.readOnlyAllowPaths` | `string[]` | `[]`       | Additional read-only paths inside sandbox                     |
| `security.permission.enableNodePermissions`         | `boolean`  | `false`    | Enable the optional Node.js permission model on covered paths |
| `security.permission.allowedFsPaths`                | `string[]` | `[]`       | Allowed file system paths                                     |
| `security.permission.allowedNetHosts`               | `string[]` | `[]`       | Allowed network hosts                                         |

<Info>
  **Source:** Config schemas in `packages/core/src/config/schema-security.ts` and `packages/core/src/config/schema-agent.ts`.
</Info>

<CardGroup cols={2}>
  <Card title="Security Model" icon="shield" href="/security">
    Defense-in-depth security architecture
  </Card>

  <Card title="Tool Security" icon="shield-check" href="/reference/tool-security">
    SSRF guard, tool policies, content scanner
  </Card>

  <Card title="Action Classifier" icon="tags" href="/reference/action-classifier">
    Complete action registry
  </Card>

  <Card title="Node Permissions" icon="lock" href="/reference/node-permissions">
    Node.js permission model
  </Card>

  <Card title="Sandbox Guide" icon="box" href="/security/sandbox">
    User-facing sandbox setup guide
  </Card>
</CardGroup>
