Files
Hunter Bown 1763261503 v0.8.46: release archives, sandbox depth, quick fixes, web install, docs
* 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
2026-05-26 09:52:22 -05:00

10 KiB

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:

# 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_MODEsandbox_mode
  • DEEPSEEK_PREFER_BWRAP=trueprefer_bwrap
  • DEEPSEEK_SANDBOX_BACKENDsandbox_backend
  • DEEPSEEK_SANDBOX_URLsandbox_url
  • DEEPSEEK_SANDBOX_API_KEYsandbox_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.
  • crates/tui/src/sandbox/ — implementation
  • crates/config/src/lib.rs — config keys
  • crates/tui/src/tools/diagnostics.rsdiagnostics 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