diff --git a/crates/tui/src/sandbox/mod.rs b/crates/tui/src/sandbox/mod.rs index 7fd278f8..cffb4f77 100644 --- a/crates/tui/src/sandbox/mod.rs +++ b/crates/tui/src/sandbox/mod.rs @@ -10,7 +10,9 @@ //! //! - **macOS**: Uses Seatbelt (sandbox-exec) for mandatory access control //! - **Linux**: Uses Landlock (kernel 5.13+) for filesystem access control -//! - **Windows**: Windows Sandbox/AppContainer/Restricted token (best-effort) +//! - **Windows**: No OS sandbox is advertised yet. The planned first helper +//! contract is process-tree containment only via a Windows Job Object; it +//! must not claim filesystem, network, registry, or AppContainer isolation. //! //! # Usage //! @@ -179,7 +181,10 @@ pub enum SandboxType { #[cfg(target_os = "linux")] LinuxLandlock, - /// Windows sandboxing (Windows Sandbox/AppContainer/Restricted token). + /// Windows process-containment helper. + /// + /// Not advertised until a helper enforces Job Object cleanup. This does + /// not imply filesystem, network, registry, or AppContainer isolation. #[cfg(target_os = "windows")] Windows, } @@ -427,10 +432,12 @@ impl SandboxManager { } } - /// Prepare a Windows-sandboxed execution environment. + /// Prepare a Windows helper execution environment. /// - /// Note: Windows sandboxing requires a helper process for full isolation. - /// This implementation marks intent and defers enforcement to a helper. + /// Windows support is currently not advertised by `get_platform_sandbox`. + /// This branch only exists for forced tests and future helper wiring. + /// The first supported helper contract is process-tree containment only; + /// it must not be presented as filesystem or network isolation. #[cfg(target_os = "windows")] fn prepare_windows(spec: &CommandSpec) -> ExecEnv { let mut command = vec![spec.program.clone()]; diff --git a/crates/tui/src/sandbox/windows.rs b/crates/tui/src/sandbox/windows.rs index 956f001d..5731d05e 100644 --- a/crates/tui/src/sandbox/windows.rs +++ b/crates/tui/src/sandbox/windows.rs @@ -1,12 +1,14 @@ -//! Windows sandbox implementation (best-effort placeholder). +//! Windows sandbox helper contract. //! -//! Windows sandboxing can be implemented using: -//! - Windows Sandbox (full isolation) -//! - AppContainer (process isolation) -//! - Restricted tokens (reduced privileges) +//! Current status: DeepSeek TUI does not advertise an in-process Windows +//! sandbox. Future Windows support must run commands through a dedicated +//! helper that provides process-tree containment with a Job Object and +//! `JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`. //! -//! This module selects a preferred approach and exposes helpers used by the -//! sandbox manager. Full enforcement should be implemented in a helper binary. +//! The first Windows helper slice is process containment only. It must not +//! claim read-only filesystem isolation, workspace-write enforcement, network +//! blocking, registry isolation, or AppContainer-level isolation until those +//! guarantees are implemented and tested separately. use std::path::Path; @@ -14,33 +16,23 @@ use super::SandboxPolicy; #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum WindowsSandboxKind { - WindowsSandbox, - AppContainer, - RestrictedToken, + ProcessContainment, } impl std::fmt::Display for WindowsSandboxKind { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - WindowsSandboxKind::WindowsSandbox => write!(f, "sandbox"), - WindowsSandboxKind::AppContainer => write!(f, "appcontainer"), - WindowsSandboxKind::RestrictedToken => write!(f, "restricted-token"), + WindowsSandboxKind::ProcessContainment => write!(f, "process-containment"), } } } pub fn is_available() -> bool { - windows_sandbox_available() || appcontainer_available() || restricted_token_available() + false } pub fn select_best_kind(_policy: &SandboxPolicy, _cwd: &Path) -> WindowsSandboxKind { - if windows_sandbox_available() { - WindowsSandboxKind::WindowsSandbox - } else if appcontainer_available() { - WindowsSandboxKind::AppContainer - } else { - WindowsSandboxKind::RestrictedToken - } + WindowsSandboxKind::ProcessContainment } pub fn detect_denial(exit_code: i32, stderr: &str) -> bool { @@ -59,21 +51,16 @@ pub fn detect_denial(exit_code: i32, stderr: &str) -> bool { patterns.iter().any(|p| stderr.contains(p)) } +#[cfg(test)] +mod tests { + use super::*; -fn windows_sandbox_available() -> bool { - let Ok(system_root) = std::env::var("SystemRoot") else { - return false; - }; - Path::new(&system_root) - .join("System32") - .join("WindowsSandbox.exe") - .exists() -} - -fn appcontainer_available() -> bool { - true -} - -fn restricted_token_available() -> bool { - true + #[test] + fn windows_sandbox_is_not_advertised_until_helper_exists() { + assert!(!is_available()); + assert_eq!( + select_best_kind(&SandboxPolicy::default(), Path::new(".")), + WindowsSandboxKind::ProcessContainment + ); + } } diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index c31a979a..ad39a438 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -161,10 +161,13 @@ drives turns through Chat Completions. ### Security -- **`sandbox/`** - macOS sandboxing support +- **`sandbox/`** - platform sandbox policy preparation and denial reporting - `mod.rs` - Sandbox type definitions - `policy.rs` - Sandbox policy configuration - `seatbelt.rs` - macOS Seatbelt profile generation + - `landlock.rs` - Linux Landlock detection and future helper contract + - `windows.rs` - Windows helper contract; not advertised until a Job + Object process-containment helper exists ### Utilities @@ -281,7 +284,10 @@ command = "echo 'Running tool: $TOOL_NAME'" 1. **Streaming-first**: All LLM responses stream for responsiveness 2. **Tool safety**: Non-YOLO mode requires approval for destructive operations, including side-effectful MCP tools 3. **Extensibility**: MCP, skills, and hooks allow customization without code changes -4. **Cross-platform**: Core works on Linux/macOS/Windows, sandboxing macOS-only +4. **Cross-platform**: Core works on Linux/macOS/Windows. Sandbox guarantees + are platform-specific: macOS Seatbelt is the active policy path; Linux and + Windows require helper enforcement before they should be treated as full OS + sandboxing. 5. **Minimal dependencies**: Careful dependency selection for build speed 6. **Local-first runtime API**: HTTP/SSE endpoints are intended for trusted localhost access and are served by the `crates/tui` runtime today diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 90bafbc2..46ca872a 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -364,6 +364,12 @@ If you are upgrading from older releases: - `allow_shell` (bool, optional): defaults to `true` (sandboxed). - `approval_policy` (string, optional): `on-request`, `untrusted`, or `never`. Runtime `approval_mode` editing in `/config` also accepts `on-request` and `untrusted` aliases. - `sandbox_mode` (string, optional): `read-only`, `workspace-write`, `danger-full-access`, `external-sandbox`. + Platform support is not identical. macOS uses Seatbelt for policy + enforcement. Linux support is helper-gated around Landlock. Windows does not + currently advertise an OS sandbox; the planned Windows helper contract starts + with process-tree containment only and must not be described as read-only + filesystem isolation, workspace-write enforcement, network blocking, + registry isolation, or AppContainer isolation until those are implemented. - `managed_config_path` (string, optional): managed config file loaded after user/env config. - `requirements_path` (string, optional): requirements file used to enforce allowed approval/sandbox values. - `max_subagents` (int, optional): defaults to `10` and is clamped to `1..=20`.