mirror of
https://github.com/openai/codex.git
synced 2026-11-13 06:45:07 +00:00
Resolve permission requests in the executor context (#42146)
## Why `request_permissions` paths and grants need to be evaluated against the selected executor environment, including its path convention, home directory, workspace roots, and temporary directories. ## What changed - Resolve relative and home-relative permission paths using the executor context, reject mismatched path conventions and lossy paths, and support legacy `read` and `write` path lists. - Move grant intersection into core so requested and granted permissions use the originating environment's sandbox context. Preserve deny entries conservatively when a special path cannot be resolved. - Keep the full originating environment with pending permission requests so delayed responses are normalized against the same context. ## Testing - Cover POSIX, Windows, UNC, relative, and home-relative path resolution and invalid path contexts. - Verify end-to-end app-server grants are limited to the requested workspace scope and unresolved temporary-directory denies are preserved. GitOrigin-RevId: 730a2aacd391262e92a6314f3a5b6c262e3dca10
This commit is contained in:
1 parent
86b7d12742
commit
cd8bd62c6e
10 files changed
+492
-307
No files matched your search
@@ -2,7 +2,6 @@ use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt::Debug;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::atomic::AtomicU64;
|
||||
@@ -121,6 +120,7 @@ use codex_protocol::models::SandboxEnforcement;
|
||||
use codex_protocol::openai_models::ModelInfo;
|
||||
use codex_protocol::openai_models::ModelPreset;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicy;
|
||||
use codex_protocol::permissions::FileSystemSandboxPolicyContext;
|
||||
use codex_protocol::permissions::NetworkSandboxPolicy;
|
||||
use codex_protocol::protocol::FileChange;
|
||||
use codex_protocol::protocol::HasLegacyEvent;
|
||||
@@ -156,7 +156,7 @@ use codex_rollout::state_db;
|
||||
use codex_rollout_trace::AgentResultTracePayload;
|
||||
use codex_rollout_trace::ThreadStartedTraceMetadata;
|
||||
use codex_rollout_trace::ThreadTraceContext;
|
||||
use codex_sandboxing::policy_transforms::intersect_permission_profiles;
|
||||
use codex_sandboxing::policy_transforms::intersect_permission_profiles_with_context;
|
||||
use codex_shell_command::parse_command::parse_command;
|
||||
use codex_terminal_detection::user_agent;
|
||||
use codex_thread_store::CreateThreadParams;
|
||||
@@ -2757,12 +2757,27 @@ impl Session {
|
||||
step_context: &Arc<StepContext>,
|
||||
call_id: String,
|
||||
args: RequestPermissionsArgs,
|
||||
environment: TurnEnvironmentSelection,
|
||||
environment_selection: TurnEnvironmentSelection,
|
||||
cancellation_token: CancellationToken,
|
||||
) -> Option<RequestPermissionsResponse> {
|
||||
let turn_context = &step_context.turn;
|
||||
let approval_policy = step_context.settings.approval_policy();
|
||||
let approvals_reviewer = step_context.settings.approvals_reviewer();
|
||||
let Some(environment) = step_context
|
||||
.environments
|
||||
.turn_environments()
|
||||
.find(|candidate| {
|
||||
candidate.selection.environment_id == environment_selection.environment_id
|
||||
})
|
||||
.cloned()
|
||||
else {
|
||||
return Some(RequestPermissionsResponse {
|
||||
permissions: RequestPermissionProfile::default(),
|
||||
scope: PermissionGrantScope::Turn,
|
||||
strict_auto_review: false,
|
||||
});
|
||||
};
|
||||
let cwd = environment.cwd();
|
||||
match approval_policy {
|
||||
AskForApproval::Never => {
|
||||
return Some(RequestPermissionsResponse {
|
||||
@@ -2786,19 +2801,14 @@ impl Session {
|
||||
}
|
||||
|
||||
let requested_permissions = args.permissions;
|
||||
// TODO(anp): Migrate request_permissions to support paths from foreign environments.
|
||||
let Ok(native_environment_cwd) = environment.cwd.to_abs_path() else {
|
||||
warn!(
|
||||
cwd = %environment.cwd,
|
||||
"request_permissions requires a cwd native to the Codex host"
|
||||
);
|
||||
let sandbox_context = environment.sandbox_context(/*additional_permissions*/ None);
|
||||
let Some(context) = sandbox_context.policy_context() else {
|
||||
return Some(RequestPermissionsResponse {
|
||||
permissions: RequestPermissionProfile::default(),
|
||||
scope: PermissionGrantScope::Turn,
|
||||
strict_auto_review: false,
|
||||
});
|
||||
};
|
||||
|
||||
if crate::guardian::routes_approval_policy_to_guardian(approval_policy, approvals_reviewer)
|
||||
{
|
||||
let originating_turn_state = {
|
||||
@@ -2868,17 +2878,30 @@ impl Session {
|
||||
let response = Self::normalize_request_permissions_response(
|
||||
requested_permissions,
|
||||
response,
|
||||
native_environment_cwd.as_path(),
|
||||
&context,
|
||||
);
|
||||
self.record_granted_request_permissions_for_turn(
|
||||
&response,
|
||||
&environment.environment_id,
|
||||
&environment.selection.environment_id,
|
||||
originating_turn_state.as_ref(),
|
||||
)
|
||||
.await;
|
||||
return Some(response);
|
||||
}
|
||||
|
||||
// The interactive approval event remains host-native until its public
|
||||
// app-server/TUI boundary migrates in the next stack stage.
|
||||
let Ok(native_cwd) = cwd.to_abs_path() else {
|
||||
warn!(
|
||||
cwd = %cwd,
|
||||
"request_permissions interactive approval requires a cwd native to the Codex host"
|
||||
);
|
||||
return Some(RequestPermissionsResponse {
|
||||
permissions: RequestPermissionProfile::default(),
|
||||
scope: PermissionGrantScope::Turn,
|
||||
strict_auto_review: false,
|
||||
});
|
||||
};
|
||||
let _elicitation = self.services.elicitations.register();
|
||||
let (tx_response, rx_response) = oneshot::channel();
|
||||
let prev_entry = {
|
||||
@@ -2905,11 +2928,11 @@ impl Session {
|
||||
let event = EventMsg::RequestPermissions(RequestPermissionsEvent {
|
||||
call_id: call_id.clone(),
|
||||
turn_id: turn_context.sub_id.clone(),
|
||||
environment_id: Some(environment.environment_id.clone()),
|
||||
environment_id: Some(environment.selection.environment_id.clone()),
|
||||
started_at_ms: now_unix_timestamp_ms(),
|
||||
reason: args.reason,
|
||||
permissions: requested_permissions,
|
||||
cwd: Some(native_environment_cwd),
|
||||
cwd: Some(native_cwd),
|
||||
});
|
||||
self.send_event(turn_context.as_ref(), event).await;
|
||||
tokio::select! {
|
||||
@@ -3020,29 +3043,25 @@ impl Session {
|
||||
};
|
||||
match entry {
|
||||
Some(entry) => {
|
||||
// TODO(anp): Migrate request_permissions to support paths from foreign environments.
|
||||
let response = match entry.environment.cwd.to_abs_path() {
|
||||
Ok(native_environment_cwd) => Self::normalize_request_permissions_response(
|
||||
let sandbox_context = entry
|
||||
.environment
|
||||
.sandbox_context(/*additional_permissions*/ None);
|
||||
let response = if let Some(context) = sandbox_context.policy_context() {
|
||||
Self::normalize_request_permissions_response(
|
||||
entry.requested_permissions,
|
||||
response,
|
||||
native_environment_cwd.as_path(),
|
||||
),
|
||||
Err(err) => {
|
||||
warn!(
|
||||
cwd = %entry.environment.cwd,
|
||||
%err,
|
||||
"request_permissions requires a cwd native to the Codex host"
|
||||
);
|
||||
RequestPermissionsResponse {
|
||||
permissions: RequestPermissionProfile::default(),
|
||||
scope: PermissionGrantScope::Turn,
|
||||
strict_auto_review: false,
|
||||
}
|
||||
&context,
|
||||
)
|
||||
} else {
|
||||
RequestPermissionsResponse {
|
||||
permissions: RequestPermissionProfile::default(),
|
||||
scope: PermissionGrantScope::Turn,
|
||||
strict_auto_review: false,
|
||||
}
|
||||
};
|
||||
self.record_granted_request_permissions_for_turn(
|
||||
&response,
|
||||
&entry.environment.environment_id,
|
||||
&entry.environment.selection.environment_id,
|
||||
originating_turn_state.as_ref(),
|
||||
)
|
||||
.await;
|
||||
@@ -3057,7 +3076,7 @@ impl Session {
|
||||
fn normalize_request_permissions_response(
|
||||
requested_permissions: RequestPermissionProfile,
|
||||
response: RequestPermissionsResponse,
|
||||
cwd: &Path,
|
||||
context: &FileSystemSandboxPolicyContext<'_>,
|
||||
) -> RequestPermissionsResponse {
|
||||
if response.strict_auto_review && matches!(response.scope, PermissionGrantScope::Session) {
|
||||
return RequestPermissionsResponse {
|
||||
@@ -3072,10 +3091,10 @@ impl Session {
|
||||
}
|
||||
|
||||
RequestPermissionsResponse {
|
||||
permissions: intersect_permission_profiles(
|
||||
permissions: intersect_permission_profiles_with_context(
|
||||
requested_permissions.into(),
|
||||
response.permissions.into(),
|
||||
cwd,
|
||||
context,
|
||||
)
|
||||
.into(),
|
||||
scope: response.scope,
|
||||
|
||||
Reference in new issue
Block a user