mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
Key request-permission grants by environment (#25850)
## Stack 1. This PR (#25850) - Key request-permission grants by environment: stores and applies sticky permission grants per environment id. 2. #25858 - Add `environmentId` to `request_permissions`: lets the model target a selected environment and resolves relative permission paths against it. 3. #25862 - Propagate permission approval environment id: carries the selected environment id through approval events, app-server requests, TUI prompts, and delegate forwarding. 4. #25867 - Add remote request permissions integration coverage: verifies the selected remote environment across request, approval, grant reuse, and exec. #25858, #25862, and #25867 are stacked on this PR and should be reviewed after it. ## Why Multi-environment CCA turns can attach both local and remote executors, but request-permission grants were still effectively cwd-only. Pending permission requests tracked a cwd, while stored turn/session grants had no environment identity, so sticky grants could be reused through the wrong executor context. This makes the first permission-grant step environment-aware without changing the external `request_permissions` payload shape: omitted environment targeting remains bound to the primary turn environment. ## What Changed - Store turn- and session-scoped request-permission grants by `environment_id`. - Keep the selected `TurnEnvironmentSelection` with pending `request_permissions` calls so approval responses normalize and record grants against the same environment. - Resolve relative `request_permissions` file paths against the primary turn environment cwd instead of deprecated `turn.cwd`. - Apply sticky grants in `shell`, `exec_command`, and `apply_patch` by selected environment id while still using the actual tool cwd for cwd-relative permission materialization. - Update Guardian and request-permissions coverage for the environment-keyed grant behavior. ## Testing Not run locally. Added or updated focused coverage for: - `request_permission_grants_are_environment_keyed` - `request_permissions_tool_resolves_relative_paths_against_primary_environment` - related Guardian/request-permissions sticky grant tests
This commit is contained in:
@@ -265,6 +265,7 @@ fn apply_patch_payload_command(payload: &ToolPayload) -> Option<String> {
|
||||
async fn effective_patch_permissions(
|
||||
session: &Session,
|
||||
turn: &TurnContext,
|
||||
environment_id: &str,
|
||||
action: &ApplyPatchAction,
|
||||
cwd: &AbsolutePathBuf,
|
||||
) -> (
|
||||
@@ -274,8 +275,14 @@ async fn effective_patch_permissions(
|
||||
) {
|
||||
let file_paths = file_paths_for_action(action);
|
||||
let granted_permissions = merge_permission_profiles(
|
||||
session.granted_session_permissions().await.as_ref(),
|
||||
session.granted_turn_permissions().await.as_ref(),
|
||||
session
|
||||
.granted_session_permissions(environment_id)
|
||||
.await
|
||||
.as_ref(),
|
||||
session
|
||||
.granted_turn_permissions(environment_id)
|
||||
.await
|
||||
.as_ref(),
|
||||
);
|
||||
let base_file_system_sandbox_policy = turn.file_system_sandbox_policy();
|
||||
let file_system_sandbox_policy = effective_file_system_sandbox_policy(
|
||||
@@ -284,6 +291,7 @@ async fn effective_patch_permissions(
|
||||
);
|
||||
let effective_additional_permissions = apply_granted_turn_permissions(
|
||||
session,
|
||||
environment_id,
|
||||
cwd.as_path(),
|
||||
crate::sandboxing::SandboxPermissions::UseDefault,
|
||||
write_permissions_for_paths(&file_paths, &file_system_sandbox_policy, cwd),
|
||||
@@ -353,8 +361,14 @@ impl ToolExecutor<ToolInvocation> for ApplyPatchHandler {
|
||||
{
|
||||
codex_apply_patch::MaybeApplyPatchVerified::Body(changes) => {
|
||||
let (file_paths, effective_additional_permissions, file_system_sandbox_policy) =
|
||||
effective_patch_permissions(session.as_ref(), turn.as_ref(), &changes, &cwd)
|
||||
.await;
|
||||
effective_patch_permissions(
|
||||
session.as_ref(),
|
||||
turn.as_ref(),
|
||||
&turn_environment.environment_id,
|
||||
&changes,
|
||||
&cwd,
|
||||
)
|
||||
.await;
|
||||
match apply_patch::apply_patch(turn.as_ref(), &file_system_sandbox_policy, changes)
|
||||
.await
|
||||
{
|
||||
@@ -506,7 +520,14 @@ pub(crate) async fn intercept_apply_patch(
|
||||
{
|
||||
codex_apply_patch::MaybeApplyPatchVerified::Body(changes) => {
|
||||
let (approval_keys, effective_additional_permissions, file_system_sandbox_policy) =
|
||||
effective_patch_permissions(session.as_ref(), turn.as_ref(), &changes, cwd).await;
|
||||
effective_patch_permissions(
|
||||
session.as_ref(),
|
||||
turn.as_ref(),
|
||||
&turn_environment.environment_id,
|
||||
&changes,
|
||||
cwd,
|
||||
)
|
||||
.await;
|
||||
match apply_patch::apply_patch(turn.as_ref(), &file_system_sandbox_policy, changes)
|
||||
.await
|
||||
{
|
||||
|
||||
@@ -250,7 +250,8 @@ pub(super) fn implicit_granted_permissions(
|
||||
|
||||
pub(super) async fn apply_granted_turn_permissions(
|
||||
session: &Session,
|
||||
cwd: &std::path::Path,
|
||||
environment_id: &str,
|
||||
cwd: &Path,
|
||||
sandbox_permissions: SandboxPermissions,
|
||||
additional_permissions: Option<AdditionalPermissionProfile>,
|
||||
) -> EffectiveAdditionalPermissions {
|
||||
@@ -262,8 +263,8 @@ pub(super) async fn apply_granted_turn_permissions(
|
||||
};
|
||||
}
|
||||
|
||||
let granted_session_permissions = session.granted_session_permissions().await;
|
||||
let granted_turn_permissions = session.granted_turn_permissions().await;
|
||||
let granted_session_permissions = session.granted_session_permissions(environment_id).await;
|
||||
let granted_turn_permissions = session.granted_turn_permissions(environment_id).await;
|
||||
let granted_permissions = merge_permission_profiles(
|
||||
granted_session_permissions.as_ref(),
|
||||
granted_turn_permissions.as_ref(),
|
||||
|
||||
@@ -48,9 +48,13 @@ impl ToolExecutor<ToolInvocation> for RequestPermissionsHandler {
|
||||
}
|
||||
};
|
||||
|
||||
#[allow(deprecated)]
|
||||
let Some(turn_environment) = turn.environments.primary() else {
|
||||
return Err(FunctionCallError::RespondToModel(
|
||||
"request_permissions requires a primary environment".to_string(),
|
||||
));
|
||||
};
|
||||
let mut args: RequestPermissionsArgs =
|
||||
parse_arguments_with_base_path(&arguments, &turn.cwd)?;
|
||||
parse_arguments_with_base_path(&arguments, &turn_environment.cwd)?;
|
||||
args.permissions = normalize_additional_permissions(args.permissions.into())
|
||||
.map(codex_protocol::request_permissions::RequestPermissionProfile::from)
|
||||
.map_err(FunctionCallError::RespondToModel)?;
|
||||
|
||||
@@ -84,10 +84,10 @@ async fn run_exec_like(args: RunExecLikeArgs) -> Result<FunctionToolOutput, Func
|
||||
let exec_permission_approvals_enabled =
|
||||
session.features().enabled(Feature::ExecPermissionApprovals);
|
||||
let requested_additional_permissions = additional_permissions.clone();
|
||||
#[allow(deprecated)]
|
||||
let effective_additional_permissions = apply_granted_turn_permissions(
|
||||
session.as_ref(),
|
||||
turn.cwd.as_path(),
|
||||
&turn_environment.environment_id,
|
||||
exec_params.cwd.as_path(),
|
||||
exec_params.sandbox_permissions,
|
||||
additional_permissions,
|
||||
)
|
||||
|
||||
@@ -173,6 +173,7 @@ impl ToolExecutor<ToolInvocation> for ExecCommandHandler {
|
||||
let requested_additional_permissions = additional_permissions.clone();
|
||||
let effective_additional_permissions = apply_granted_turn_permissions(
|
||||
context.session.as_ref(),
|
||||
&turn_environment.environment_id,
|
||||
cwd.as_path(),
|
||||
sandbox_permissions,
|
||||
additional_permissions,
|
||||
|
||||
Reference in New Issue
Block a user