Skip to content
Merged
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

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions codex-rs/app-server-protocol/src/protocol/v2/config.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::ApprovalsReviewer;
use super::AskForApproval;
use super::SandboxMode;
use super::WindowsSandboxSetupMode;
use super::shared::default_enabled;
use codex_experimental_api_macros::ExperimentalApi;
use codex_protocol::config_types::AutoCompactTokenLimitScope;
Expand Down Expand Up @@ -358,6 +359,7 @@ pub struct ConfigRequirements {
#[experimental("configRequirements/read.allowedApprovalsReviewers")]
pub allowed_approvals_reviewers: Option<Vec<ApprovalsReviewer>>,
pub allowed_sandbox_modes: Option<Vec<SandboxMode>>,
pub allowed_windows_sandbox_implementations: Option<Vec<WindowsSandboxSetupMode>>,
pub allowed_permissions: Option<Vec<String>>,
pub allowed_web_search_modes: Option<Vec<WebSearchMode>>,
pub allow_managed_hooks_only: Option<bool>,
Expand Down
1 change: 1 addition & 0 deletions codex-rs/app-server-protocol/src/protocol/v2/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1670,6 +1670,7 @@ fn config_requirements_granular_allowed_approval_policy_is_marked_experimental()
}]),
allowed_approvals_reviewers: None,
allowed_sandbox_modes: None,
allowed_windows_sandbox_implementations: None,
allowed_permissions: None,
allowed_web_search_modes: None,
allow_managed_hooks_only: None,
Expand Down
41 changes: 41 additions & 0 deletions codex-rs/app-server/src/request_processors/config_processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use codex_app_server_protocol::NetworkRequirements;
use codex_app_server_protocol::NetworkUnixSocketPermission;
use codex_app_server_protocol::SandboxMode;
use codex_app_server_protocol::ServerNotification;
use codex_app_server_protocol::WindowsSandboxSetupMode;
use codex_chatgpt::connectors;
use codex_config::ConfigRequirementsToml;
use codex_config::HookEventsToml;
Expand Down Expand Up @@ -420,6 +421,23 @@ fn map_requirements_toml_to_api(requirements: ConfigRequirementsToml) -> ConfigR
.filter_map(map_sandbox_mode_requirement_to_api)
.collect()
}),
allowed_windows_sandbox_implementations: requirements.windows.and_then(|windows| {
windows
.allowed_sandbox_implementations
.map(|implementations| {
implementations
.into_iter()
.map(|implementation| match implementation {
codex_config::types::WindowsSandboxModeToml::Elevated => {
WindowsSandboxSetupMode::Elevated
}
codex_config::types::WindowsSandboxModeToml::Unelevated => {
WindowsSandboxSetupMode::Unelevated
}
})
.collect()
})
}),
allowed_permissions: requirements.allowed_permissions,
allowed_web_search_modes: requirements.allowed_web_search_modes.map(|modes| {
let mut normalized = modes
Expand Down Expand Up @@ -634,8 +652,10 @@ fn config_write_error(code: ConfigWriteErrorCode, message: impl Into<String>) ->
#[cfg(test)]
mod tests {
use super::map_requirements_toml_to_api;
use codex_app_server_protocol::WindowsSandboxSetupMode;
use codex_config::ComputerUseRequirementsToml;
use codex_config::ConfigRequirementsToml;
use codex_config::WindowsRequirementsToml;
use pretty_assertions::assert_eq;

#[test]
Expand Down Expand Up @@ -687,4 +707,25 @@ mod tests {
Some(false)
);
}

#[test]
fn requirements_api_includes_allowed_windows_sandbox_implementations() {
let mapped = map_requirements_toml_to_api(ConfigRequirementsToml {
windows: Some(WindowsRequirementsToml {
allowed_sandbox_implementations: Some(vec![
codex_config::types::WindowsSandboxModeToml::Elevated,
codex_config::types::WindowsSandboxModeToml::Unelevated,
]),
}),
..ConfigRequirementsToml::default()
});

assert_eq!(
mapped.allowed_windows_sandbox_implementations,
Some(vec![
WindowsSandboxSetupMode::Elevated,
WindowsSandboxSetupMode::Unelevated,
])
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,53 +41,52 @@ impl WindowsSandboxRequestProcessor {
request_id: &ConnectionRequestId,
params: WindowsSandboxSetupStartParams,
) -> Result<(), JSONRPCErrorError> {
// Validate requirements before acknowledging setup so callers do not get a
// `started` response for a Windows sandbox mode that cannot be persisted.
let command_cwd = params
.cwd
.map(PathBuf::from)
.unwrap_or_else(|| self.config.cwd.to_path_buf());
let config = self
.config_manager
.load_for_cwd(
/*request_overrides*/ None,
ConfigOverrides {
cwd: Some(command_cwd.clone()),
..Default::default()
},
Some(command_cwd.clone()),
)
.await
.map_err(|err| config_load_error(&err))?;
let setup_mode = resolve_allowed_windows_sandbox_setup_mode(
config.config_layer_stack.requirements(),
params.mode,
)?;

self.outgoing
.send_response(
request_id.clone(),
WindowsSandboxSetupStartResponse { started: true },
)
.await;

let mode = match params.mode {
WindowsSandboxSetupMode::Elevated => CoreWindowsSandboxSetupMode::Elevated,
WindowsSandboxSetupMode::Unelevated => CoreWindowsSandboxSetupMode::Unelevated,
};
let config = Arc::clone(&self.config);
let config_manager = self.config_manager.clone();
let command_cwd = params
.cwd
.map(PathBuf::from)
.unwrap_or_else(|| config.cwd.to_path_buf());
let outgoing = Arc::clone(&self.outgoing);
let connection_id = request_id.connection_id;

tokio::spawn(async move {
let derived_config = config_manager
.load_for_cwd(
/*request_overrides*/ None,
ConfigOverrides {
cwd: Some(command_cwd.clone()),
..Default::default()
},
Some(command_cwd.clone()),
)
.await;
let setup_result = match derived_config {
Ok(config) => {
let setup_request = WindowsSandboxSetupRequest {
mode,
permission_profile: config.permissions.effective_permission_profile(),
workspace_roots: config.effective_workspace_roots(),
command_cwd,
env_map: std::env::vars().collect(),
codex_home: config.codex_home.to_path_buf(),
};
codex_core::windows_sandbox::run_windows_sandbox_setup(setup_request).await
}
Err(err) => Err(err.into()),
let setup_request = WindowsSandboxSetupRequest {
mode: setup_mode,
permission_profile: config.permissions.effective_permission_profile(),
workspace_roots: config.effective_workspace_roots(),
command_cwd,
env_map: std::env::vars().collect(),
codex_home: config.codex_home.to_path_buf(),
};
let setup_result =
codex_core::windows_sandbox::run_windows_sandbox_setup(setup_request).await;
let notification = WindowsSandboxSetupCompletedNotification {
mode: match mode {
mode: match setup_mode {
CoreWindowsSandboxSetupMode::Elevated => WindowsSandboxSetupMode::Elevated,
CoreWindowsSandboxSetupMode::Unelevated => WindowsSandboxSetupMode::Unelevated,
},
Expand All @@ -105,6 +104,28 @@ impl WindowsSandboxRequestProcessor {
}
}

/// Resolves the requested API mode after checking that managed requirements allow it.
fn resolve_allowed_windows_sandbox_setup_mode(
requirements: &codex_config::ConfigRequirements,
requested_mode: WindowsSandboxSetupMode,
) -> Result<CoreWindowsSandboxSetupMode, JSONRPCErrorError> {
let (setup_mode, config_mode) = match requested_mode {
WindowsSandboxSetupMode::Elevated => (
CoreWindowsSandboxSetupMode::Elevated,
codex_config::types::WindowsSandboxModeToml::Elevated,
),
WindowsSandboxSetupMode::Unelevated => (
CoreWindowsSandboxSetupMode::Unelevated,
codex_config::types::WindowsSandboxModeToml::Unelevated,
),
};
requirements
.windows_sandbox_mode
.can_set(&Some(config_mode))
.map_err(|err| invalid_request(format!("invalid Windows sandbox setup mode: {err}")))?;
Ok(setup_mode)
}

fn determine_windows_sandbox_readiness(config: &Config) -> WindowsSandboxReadinessResponse {
if !cfg!(windows) {
return WindowsSandboxReadinessResponse {
Expand Down Expand Up @@ -140,6 +161,34 @@ fn determine_windows_sandbox_readiness_from_state(
#[cfg(test)]
mod tests {
use super::*;
use crate::error_code::INVALID_REQUEST_ERROR_CODE;
use codex_config::ConfigRequirements;
use codex_config::Constrained;
use codex_config::ConstrainedWithSource;
use codex_config::types::WindowsSandboxModeToml;

#[test]
fn resolve_allowed_windows_sandbox_setup_mode_rejects_disallowed_mode() {
let requirements = ConfigRequirements {
windows_sandbox_mode: ConstrainedWithSource::new(
Constrained::allow_only(Some(WindowsSandboxModeToml::Elevated)),
/*source*/ None,
),
..Default::default()
};

let err = resolve_allowed_windows_sandbox_setup_mode(
&requirements,
WindowsSandboxSetupMode::Unelevated,
)
.expect_err("unelevated setup should be rejected");

assert_eq!(err.code, INVALID_REQUEST_ERROR_CODE);
assert!(
err.message.contains("invalid Windows sandbox setup mode"),
"{err:?}"
);
}

#[test]
fn determine_windows_sandbox_readiness_reports_not_configured_when_disabled() {
Expand Down
Loading
Loading