1763261503
* docs: v0.8.46 CHANGELOG — platform archives, palette, sub-agents, sandbox, web install, search fixes Closes #2188 * feat(v0.8.46): quick fixes — palette, model picker Esc, sub-agent sidebar, shell chip, model name casing, CVE bump (#2212) * fix: bump qs to >=6.15.2 for CVE-2026-8723 Add qs override in feishu-bridge package.json to force transitive dependency resolution to >=6.15.2, addressing CVE-2026-8723. Refs: #2198 * fix: Esc in model picker applies last-highlighted choice Previously Esc reverted to the initial model when the user hadn't moved the selection. Now Esc always applies the currently highlighted model and thinking-effort tier, making Esc consistent with Enter. Also updates the picker footer hint from 'Esc cancel' to 'Esc apply'. Refs: #2196 * feat: show '⏳ shell running' chip in TUI footer Adds a footer_shell_chip function that displays a '⏳ shell running' status chip in the footer's right cluster whenever a foreground shell command is active via exec_shell. The chip is always visible regardless of user-configured status items. Refs: #2194 * feat: auto-collapse finished sub-agents in sidebar When a sub-agent completes (status = 'done'), its detail lines (id, steps, duration, progress) are now hidden in the sidebar agents panel. Only the summary label line is shown, keeping the sidebar compact. Running agents still show full detail. Refs: #2195 * feat: refresh Whale dark palette for better contrast Improve contrast and layer separation in the Whale dark theme: - Deepen base background for more depth (10,17,32) - Lighten panel (22,34,56) for clearer distinction from bg - Lighten elevated surface (36,52,78) for better elevation - Lighten selection (48,68,100) for clearer selected state - Boost text hint (138,150,174) and dim (118,130,156) readability - Brighter border (52,88,145) for better edge definition - Update tool surface colors for consistency Refs: #2197 * fix: preserve model name casing in normalize_model_name_for_provider When the user enters a model name like 'DeepSeek-V4-Flash', the normalizer was lowercasing it to 'deepseek-v4-flash' via the canonical_official_deepseek_model_id function. Now the normalizer preserves the caller's casing when the input already matches a known model id case-insensitively. Compact aliases like 'deepseek-v4pro' are still rewritten to 'deepseek-v4-pro'. Refs: #2109 * feat(web): install download tile with arch detection, SHA256, China mirrors + companion binary fix (#2213) * fix(web): download both codewhale and codewhale-tui binaries in install snippets The SNIPPETS map only fetched one binary per platform, causing the dispatcher to fail with MISSING_COMPANION_BINARY. Every arch now downloads both codewhale AND codewhale-tui side-by-side. - macOS/Linux: added second curl + combined chmod/xattr/mv for tui - Windows: added second Invoke-WebRequest for codewhale-tui.exe - VERIFY: PowerShell now hashes both binaries; Unix --ignore-missing covers all present binaries in a single sha256sum pass * feat(web): add install download tile with arch detection, SHA256, and China mirrors (#2192) * feat(sandbox/linux): process hardening — PR_SET_DUMPABLE, NO_NEW_PRIVS, RLIMIT_CORE (#2214) * feat(sandbox/linux): add process hardening module — PR_SET_DUMPABLE, NO_NEW_PRIVS, RLIMIT_CORE (#2183) * feat(sandbox/linux): seccomp filter + bwrap passthrough - seccomp: BPF filter whitelisting safe syscalls, denying ptrace/mount/kexec and other dangerous syscalls. Uses raw BPF instructions via libc prctl to avoid external dependencies (#2182). - bwrap: optional bubblewrap passthrough when /usr/bin/bwrap is present and [sandbox] prefer_bwrap=true in config. Creates read-only rootfs with write access limited to the working directory (#2184). - landlock detect_denial extended to recognize seccomp SIGSYS/"Bad system call" patterns alongside existing Landlock EACCES/EPERM detection. - SandboxManager gains prefer_bwrap field; set_prefer_bwrap on ShellManager. - EngineConfig gains prefer_bwrap field, wired through main/ui/runtime_threads. - Diagnostics now reports bwrap_available and cgroup_version. - config.example.toml documents the prefer_bwrap key. Pre-existing clippy fixes picked up in the same build: - collapsible_if in ui.rs version-check - cmp_owned in goal.rs test - consecutive str::replace in normalize_auth_mode Closes #2182, closes #2184 * docs: add cross-links to issue and PR templates in CONTRIBUTING.md (#2215) - Link .github/ISSUE_TEMPLATE/bug_report.md and feature_request.md from the Reporting Issues section - Link .github/PULL_REQUEST_TEMPLATE.md from the Pull Request Guidelines section * feat(release): bundle platform archives with install scripts (#2216) - Add bundle job to release workflow that creates per-platform archives (tar.gz for Linux/macOS, .zip for Windows) containing both codewhale and codewhale-tui binaries plus install scripts - Create install.bat (Windows) — copies binaries to %USERPROFILE%\bin - Create install.sh (Unix) — copies binaries to ~/.local/bin - Windows gets a portable .zip variant without install script - Release notes updated to promote archives as primary download method - Individual binaries retained for npm wrapper and scripting Closes #2193 * fix(web_search): fall back to DuckDuckGo when Bing returns zero results (#2130) When the configured search provider is Bing and the query returns zero results (common for technical/compound queries), fall through to the DuckDuckGo path instead of reporting empty. A provenance message is surfaced: "Bing returned no results; used DuckDuckGo fallback". Also adds Security and Code of Conduct cross-links to CONTRIBUTING.md per the sub-agent renovation (#2203). * docs: SANDBOX.md threat model + RFCs for persistence and MCP + SandboxExecutor trait - docs/SANDBOX.md: complete threat model describing each platform's sandbox (Seatbelt, Landlock, seccomp, process hardening, bwrap, Windows v1). Covers defense-in-depth layering, config keys, denial detection, limitations. - docs/rfcs/2189-persistence-sqlite.md: RFC for SQLite migration (drafted by sub-agent) - docs/rfcs/2190-mcp-modularization.md: RFC for MCP crate split into protocol/client/server with OAuth support - crates/tui/src/sandbox/policy.rs: SandboxExecutor trait definition and SafetyLevel→SandboxPolicyBehavior mapping function with tests Closes #2180, closes #2186, closes #2189, closes #2190 * feat: sandbox parity tests + remove sub-agent 100-turn cap - Add sandbox parity tests covering platform detection, denial patterns, bwrap preference, and policy consistency across modes (#2187) - Remove arbitrary 100-turn sub-agent cap: DEFAULT_MAX_STEPS changed from 100 to u32::MAX. Sub-agents now run until they produce a final text response, are cancelled by the parent, or hit a configured explicit budget (#2034) Closes #2187, closes #2034
272 lines
10 KiB
Markdown
272 lines
10 KiB
Markdown
# Sandbox threat model
|
|
|
|
CodeWhale executes shell commands spawned by AI reasoning. The sandbox
|
|
module restricts what those commands can do to the host system. This
|
|
document describes what each platform's sandbox actually enforces,
|
|
what is best-effort, and what is explicitly out of scope.
|
|
|
|
## Platform overview
|
|
|
|
| Mechanism | Platform | Type | Status |
|
|
|---|---|---|---|
|
|
| Seatbelt | macOS | Mandatory access control | Enforced |
|
|
| Landlock | Linux | Filesystem access control | Enforced |
|
|
| seccomp BPF | Linux | Syscall filter | Enforced |
|
|
| Process hardening | Linux | Kernel prctl / rlimit | Enforced |
|
|
| Bubblewrap (bwrap) | Linux | Namespace isolation | Optional |
|
|
| Windows Job Object | Windows | Process-tree containment | v1 (PR #2220) |
|
|
|
|
## Threat model: what each layer addresses
|
|
|
|
### 1. Process hardening (Linux only)
|
|
|
|
**When it runs:** Before any threads are spawned, before Tokio boots,
|
|
before any data is loaded into memory.
|
|
|
|
**What it does:**
|
|
|
|
- `PR_SET_DUMPABLE=0` — prevents ptrace, makes `/proc/<pid>/` root-owned
|
|
- `PR_SET_NO_NEW_PRIVS=1` — irreversible; no child can ever gain privileges
|
|
- `RLIMIT_CORE=0` — no core dumps, so sensitive data never hits disk
|
|
|
|
**What it protects against:**
|
|
- Process inspection via ptrace/strace/gdb
|
|
- Privilege escalation via setuid/setgid/fscaps
|
|
- Core dumps leaking API keys, tokens, prompt content
|
|
|
|
**What it does NOT protect against:**
|
|
- A compromised child reading its parent's `/proc/<pid>/mem` (already blocked
|
|
by `PR_SET_DUMPABLE=0` making `/proc/<pid>/` root-owned)
|
|
- Kernel exploits that bypass prctl
|
|
|
|
### 2. Landlock (Linux, kernel 5.13+)
|
|
|
|
**When it runs:** Applied to each child process at spawn time via a
|
|
helper script or `landlock_restrict_self`. Only restrictable by the
|
|
process itself — parent cannot force Landlock on a child.
|
|
|
|
**What it does:**
|
|
- Restricts filesystem access to a whitelist of paths
|
|
- Handles: `EXECUTE`, `READ_FILE`, `READ_DIR`, `WRITE_FILE`, `REMOVE_DIR`,
|
|
`REMOVE_FILE`, `MAKE_DIR`, `MAKE_REG`, `MAKE_SYM`, `TRUNCATE`
|
|
|
|
**What it protects against:**
|
|
- Reading files outside the workspace (e.g., `/etc/passwd`, `~/.ssh`)
|
|
- Writing to system directories (`/usr`, `/bin`, `/lib`)
|
|
- Creating or deleting files in protected locations
|
|
|
|
**What it does NOT protect against:**
|
|
- Network access (Landlock is filesystem-only)
|
|
- Process inspection (use seccomp for this)
|
|
- Reading files that are already mapped (Landlock applies at `open()` time)
|
|
|
|
**Detection:** `detect_denial()` checks stderr for `Permission denied`,
|
|
`Operation not permitted`, `EACCES`, `EPERM`.
|
|
|
|
### 3. seccomp BPF (Linux only)
|
|
|
|
**When it runs:** Installed via `prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER)`
|
|
on the child process.
|
|
|
|
**What it does:**
|
|
- Whitelist of ~100 safe syscalls (file I/O, memory, process, IPC,
|
|
synchronization, signals, time)
|
|
- **Explicitly denied:** `ptrace`, `mount`, `umount2`, `kexec_load`,
|
|
`kexec_file_load`, `init_module`, `finit_module`, `delete_module`,
|
|
`bpf`, `reboot`, `swapon`, `swapoff`, `pivot_root`,
|
|
`setuid`/`setgid`/`setreuid`/`setregid`/`setresuid`/`setresgid`,
|
|
`personality`
|
|
- Any syscall not on the whitelist → `SECCOMP_RET_KILL_PROCESS` (SIGSYS)
|
|
|
|
**What it protects against:**
|
|
- Process hijacking via ptrace
|
|
- Mounting filesystems (bypassing Landlock read-only restrictions)
|
|
- Loading kernel modules
|
|
- Loading BPF programs (would bypass seccomp itself!)
|
|
- Rebooting the system
|
|
- Privilege changes via setuid/setgid
|
|
|
|
**What it does NOT protect against:**
|
|
- Legitimate use of allowed syscalls for malicious purposes
|
|
- Side-channel attacks via allowed syscalls (e.g., timing)
|
|
|
|
**Detection:** `detect_denial()` checks exit code 31 (SIGSYS) or stderr
|
|
for `Bad system call`, `bad system call`, `SIGSYS`, `seccomp`.
|
|
|
|
### 4. Bubblewrap / bwrap (Linux, optional)
|
|
|
|
**When it runs:** If `/usr/bin/bwrap` is present AND the config key
|
|
`[sandbox] prefer_bwrap = true` is set. Runs as an outer wrapper around
|
|
the child command.
|
|
|
|
**What it does:**
|
|
- Creates a new mount namespace with `--unshare-all`
|
|
- Read-only bind-mounts the entire root filesystem
|
|
- Bind-mounts the workspace directory with read-write access
|
|
- Changes into the workspace with `--chdir`
|
|
|
|
**What it protects against:**
|
|
- Any filesystem write outside the workspace (stronger than Landlock alone
|
|
because it's enforced at the namespace level, not just filesystem access)
|
|
- Accidental modification of system files
|
|
|
|
**What it does NOT protect against:**
|
|
- Network access (bwrap does not create a network namespace by default with
|
|
`--unshare-all`; the child still has full network access)
|
|
- Process inspection
|
|
- Memory attacks
|
|
|
|
**Installation:** User must install bubblewrap themselves:
|
|
- Ubuntu/Debian: `apt install bubblewrap`
|
|
- Fedora: `dnf install bubblewrap`
|
|
- Arch: `pacman -S bubblewrap`
|
|
|
|
CodeWhale does NOT vendor bwrap.
|
|
|
|
**Fallback:** If bwrap is not installed, the sandbox falls back to Landlock
|
|
only.
|
|
|
|
### 5. Seatbelt (macOS)
|
|
|
|
**When it runs:** Applied via the `sandbox-exec` wrapper command. The
|
|
seatbelt profile is generated dynamically based on the `SandboxPolicy`.
|
|
|
|
**What it does:**
|
|
- Restricts filesystem access based on the policy profile
|
|
- Can restrict network access (when `network_access: false`)
|
|
|
|
**What it protects against:**
|
|
- Reading/writing files outside allowed paths
|
|
- Network connections (when configured)
|
|
|
|
**What it does NOT protect against:**
|
|
- Process inspection (Seatbelt does not block ptrace)
|
|
- Syscall-level attacks
|
|
|
|
**Detection:** Checks stderr for `file-write` and `network` denial patterns.
|
|
|
|
### 6. Windows Job Object (v1, PR #2220)
|
|
|
|
**When it runs:** Applied at process spawn time via
|
|
`PROC_THREAD_ATTRIBUTE_JOB_LIST` and restricted token assignment.
|
|
|
|
**What it does (v1):**
|
|
- Job Object with `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE` — all child
|
|
processes terminate when the parent exits
|
|
- Memory cap: 1 GB per process, 2 GB per job
|
|
- Active process limit: 64
|
|
- UI restrictions: no desktop handle access
|
|
- Restricted token: drops Administrators group SID, sets medium-low
|
|
integrity level
|
|
|
|
**What is deferred (v2):**
|
|
- WFP (Windows Filtering Platform) firewall rules — network is open in v1
|
|
- Filesystem ACL integration at spawn time (stub exists)
|
|
- AppContainer isolation
|
|
- Registry key isolation
|
|
|
|
**Detection:** Checks stderr for `Access is denied`, `STATUS_ACCESS_DENIED`,
|
|
`ERROR_ACCESS_DENIED`, `ERROR_PRIVILEGE_NOT_HELD`,
|
|
`ERROR_ACCESS_DISABLED_BY_POLICY`, and integrity/AppContainer patterns.
|
|
|
|
## Defense in depth
|
|
|
|
The Linux sandbox applies layers in order:
|
|
|
|
```
|
|
Process hardening (prctl) ← before threads
|
|
↓
|
|
Landlock (filesystem) ← at child spawn
|
|
↓
|
|
seccomp BPF (syscalls) ← at child spawn
|
|
↓
|
|
bwrap (namespace isolation) ← optional outer wrapper
|
|
```
|
|
|
|
Each layer addresses a different threat surface. seccomp cannot protect the
|
|
filesystem (that's Landlock's job). Landlock cannot stop ptrace (that's
|
|
seccomp + PR_SET_DUMPABLE). bwrap adds namespace-level isolation that
|
|
neither Landlock nor seccomp can provide.
|
|
|
|
## Configuration
|
|
|
|
Relevant config keys in `~/.codewhale/config.toml`:
|
|
|
|
```toml
|
|
# Sandbox policy mode
|
|
sandbox_mode = "workspace-write" # read-only | workspace-write | danger-full-access | external-sandbox
|
|
|
|
# Linux bubblewrap passthrough
|
|
prefer_bwrap = false # requires `bubblewrap` package installed
|
|
|
|
# External sandbox backend
|
|
sandbox_backend = "none" # "none" or "opensandbox"
|
|
sandbox_url = "http://localhost:8080"
|
|
sandbox_api_key = "YOUR_API_KEY"
|
|
```
|
|
|
|
Environment variable overrides:
|
|
|
|
- `DEEPSEEK_SANDBOX_MODE` → `sandbox_mode`
|
|
- `DEEPSEEK_PREFER_BWRAP=true` → `prefer_bwrap`
|
|
- `DEEPSEEK_SANDBOX_BACKEND` → `sandbox_backend`
|
|
- `DEEPSEEK_SANDBOX_URL` → `sandbox_url`
|
|
- `DEEPSEEK_SANDBOX_API_KEY` → `sandbox_api_key`
|
|
|
|
## Detecting sandbox denials
|
|
|
|
When a command fails, the sandbox manager checks for denial patterns:
|
|
|
|
| Platform | Denial mechanism | Exit code | Stderr patterns |
|
|
|---|---|---|---|
|
|
| macOS Seatbelt | sandbox-exec violation | non-zero | `file-write`, `network` |
|
|
| Linux Landlock | EACCES / EPERM | non-zero | `Permission denied`, `Operation not permitted` |
|
|
| Linux seccomp | SIGSYS (31) | 31 or 159 | `Bad system call`, `SIGSYS` |
|
|
| Linux bwrap | Mount/namespace failure | non-zero | varies |
|
|
| Windows | Access denied / privilege | non-zero | `Access is denied`, `ERROR_PRIVILEGE_NOT_HELD` |
|
|
|
|
The `was_denied()` method on `SandboxManager` aggregates all platform-specific
|
|
checks. The `denial_message()` method returns a human-readable explanation.
|
|
|
|
## Limitations
|
|
|
|
### What the sandbox does NOT protect against
|
|
|
|
- **Network attacks** — only macOS Seatbelt can block network; Linux and
|
|
Windows v1 leave network open
|
|
- **Memory attacks** — no platform prevents a child process from reading
|
|
its own memory or exploiting memory corruption bugs
|
|
- **Timing side channels** — allowed syscalls on Linux can be used for
|
|
timing-based information leaks
|
|
- **Resource exhaustion** — the Linux job object limits memory and process
|
|
count, but does not limit CPU, file descriptors, or disk I/O
|
|
- **Kernel vulnerabilities** — if the kernel itself has a vulnerability,
|
|
the sandbox cannot prevent exploitation (this applies to all platforms)
|
|
- **Supply chain** — if the child process downloads and executes untrusted
|
|
code, the sandbox limits what that code can do, but does not prevent the
|
|
download
|
|
|
|
### Platform-specific gaps
|
|
|
|
- **Linux:** Landlock only protects filesystem access. seccomp adds syscall
|
|
filtering but uses a whitelist that may need updates for new syscalls.
|
|
- **macOS:** Seatbelt profiles are generated at runtime. A misconfigured
|
|
profile could be too permissive.
|
|
- **Windows v1:** No filesystem ACL enforcement at spawn time. Network is
|
|
fully open. Job Object is process-tree only.
|
|
|
|
## Related
|
|
|
|
- `crates/tui/src/sandbox/` — implementation
|
|
- `crates/config/src/lib.rs` — config keys
|
|
- `crates/tui/src/tools/diagnostics.rs` — `diagnostics` tool reports
|
|
`sandbox_available`, `sandbox_type`, `bwrap_available`, `cgroup_version`
|
|
- `config.example.toml` — annotated config reference
|
|
- Issue #2180 — this document
|
|
- Issue #2182 — seccomp filter implementation
|
|
- Issue #2183 — process hardening
|
|
- Issue #2184 — bwrap passthrough
|
|
- Issue #2185 — Windows Job Object v1
|
|
- Issue #2186 — SandboxExecutor trait unification
|
|
- Issue #2187 — sandbox parity tests
|