Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 22 additions & 12 deletions crates/agent/src/sandboxing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
//! caller see the same answer (and so the `target_os` gate lives in one
//! place instead of scattered across the agent crate).
//!
//! The current policy is: enabled iff the project is local, the platform has an
//! integration, and the user has not persistently allowed unsandboxed execution
//! (the `allow_unsandboxed` sandbox setting). Setting `allow_unsandboxed`
//! The current policy is: enabled iff the user has the `sandboxing` feature
//! flag turned on, the project is local, the platform has an integration, and
//! the user has not persistently allowed unsandboxed execution (the
//! `allow_unsandboxed` sandbox setting). Setting `allow_unsandboxed`
//! persistently turns sandboxing off for the model-facing surface entirely:
//! the plain (non-sandboxed) `terminal` tool is exposed and the system prompt
//! omits the sandbox section, since every command would run without a wrap
Expand All @@ -18,12 +19,14 @@
//!
//! macOS (Seatbelt), Linux (Bubblewrap), and Windows (Bubblewrap via WSL)
//! have real sandbox integrations; on platforms without one the per-command
//! wrap is a no-op, so commands run with the agent's ambient permissions.
//! wrap is a no-op, so commands run with the agent's ambient permissions even
//! when the flag is on.
//!
//! Naming note: this module is about agent terminal sandboxing specifically.
//! Other agent operations (e.g. file edits) are gated separately.

use agent_settings::{AgentSettings, SandboxPermissions};
use feature_flags::{FeatureFlagAppExt as _, SandboxingFeatureFlag};
use gpui::App;
use http_proxy::HostPattern;
use project::Project;
Expand Down Expand Up @@ -173,6 +176,12 @@ pub fn settings_sandbox_policy(persistent: &SandboxPermissions) -> SandboxPolicy
SandboxPolicy { fs, network }
}

/// Whether agent-run terminal commands should be wrapped in an OS-level
/// sandbox for this process. See module docs for the policy.
pub(crate) fn sandboxing_enabled(cx: &App) -> bool {
cx.has_flag::<SandboxingFeatureFlag>()
}

/// Whether the sandboxed terminal can be exposed for this project.
///
/// The persistent `allow_unsandboxed` setting turns sandboxing off for the
Expand All @@ -184,19 +193,20 @@ pub fn settings_sandbox_policy(persistent: &SandboxPermissions) -> SandboxPolicy
/// prompt in place, since the model is still operating in the sandbox model and
/// only escaping individual commands (tracked in `ThreadSandboxGrants`).
pub(crate) fn sandboxing_enabled_for_project(project: &Project, cx: &App) -> bool {
sandboxing_available_for_project(project)
sandboxing_available_for_project(project, cx)
&& !AgentSettings::get_global(cx)
.sandbox_permissions
.allow_unsandboxed
}

/// Whether sandboxing is *applicable* for this project at all — the project is
/// local and the platform has a sandbox integration — independent of the
/// persistent `allow_unsandboxed` setting. Used by the UI to distinguish
/// "sandboxing isn't relevant here" (don't show the indicator) from "sandboxing
/// is available but turned off in settings" (show it, struck out).
pub(crate) fn sandboxing_available_for_project(project: &Project) -> bool {
project.is_local()
/// Whether sandboxing is *applicable* for this project at all — the feature is
/// enabled, the project is local, and the platform has a sandbox integration —
/// independent of the persistent `allow_unsandboxed` setting. Used by the UI to
/// distinguish "sandboxing isn't relevant here" (don't show the indicator) from
/// "sandboxing is available but turned off in settings" (show it, struck out).
pub(crate) fn sandboxing_available_for_project(project: &Project, cx: &App) -> bool {
sandboxing_enabled(cx)
&& project.is_local()
&& cfg!(any(
target_os = "macos",
target_os = "linux",
Expand Down
10 changes: 5 additions & 5 deletions crates/agent/src/thread.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1846,12 +1846,12 @@ impl Thread {
sandboxing_enabled_for_project(self.project.read(cx), cx)
}

/// Whether sandboxing is *applicable* for this thread's project (local
/// project, supported platform), regardless of whether it's been turned off
/// in settings. The UI shows the sandbox indicator whenever this is true,
/// drawing it struck-out when sandboxing is disabled.
/// Whether sandboxing is *applicable* for this thread's project (feature on,
/// local project, supported platform), regardless of whether it's been
/// turned off in settings. The UI shows the sandbox indicator whenever this
/// is true, drawing it struck-out when sandboxing is disabled.
pub fn sandboxing_available(&self, cx: &App) -> bool {
sandboxing_available_for_project(self.project.read(cx))
sandboxing_available_for_project(self.project.read(cx), cx)
}

/// The directory subtrees the sandbox always grants write access to for this
Expand Down
10 changes: 10 additions & 0 deletions crates/feature_flags/src/flags.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,3 +118,13 @@ impl FeatureFlag for AutoWatchFeatureFlag {
type Value = PresenceFlag;
}
register_feature_flag!(AutoWatchFeatureFlag);

/// Wraps agent-run terminal commands in an OS-level sandbox where supported.
/// When off, terminal commands run with the agent's full ambient permissions.
pub struct SandboxingFeatureFlag;

impl FeatureFlag for SandboxingFeatureFlag {
const NAME: &'static str = "sandboxing";
type Value = PresenceFlag;
}
register_feature_flag!(SandboxingFeatureFlag);
Loading