Skip to content

Commit 745ed4e

Browse files
authored
Use granted permissions when invoking apply_patch (openai#14429)
1 parent 23e55d7 commit 745ed4e

File tree

3 files changed

+148
-29
lines changed

3 files changed

+148
-29
lines changed

codex-rs/core/src/apply_patch.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use crate::codex::TurnContext;
22
use crate::function_tool::FunctionCallError;
33
use crate::protocol::FileChange;
4+
use crate::protocol::FileSystemSandboxPolicy;
45
use crate::safety::SafetyCheck;
56
use crate::safety::assess_patch_safety;
67
use crate::tools::sandboxing::ExecApprovalRequirement;
@@ -34,13 +35,14 @@ pub(crate) struct ApplyPatchExec {
3435

3536
pub(crate) async fn apply_patch(
3637
turn_context: &TurnContext,
38+
file_system_sandbox_policy: &FileSystemSandboxPolicy,
3739
action: ApplyPatchAction,
3840
) -> InternalApplyPatchInvocation {
3941
match assess_patch_safety(
4042
&action,
4143
turn_context.approval_policy.value(),
4244
turn_context.sandbox_policy.get(),
43-
&turn_context.file_system_sandbox_policy,
45+
file_system_sandbox_policy,
4446
&turn_context.cwd,
4547
turn_context.windows_sandbox_level,
4648
) {

codex-rs/core/src/sandboxing/mod.rs

Lines changed: 101 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,26 @@ fn merge_file_system_policy_with_additional_permissions(
388388
}
389389
}
390390

391+
pub(crate) fn effective_file_system_sandbox_policy(
392+
file_system_policy: &FileSystemSandboxPolicy,
393+
additional_permissions: Option<&PermissionProfile>,
394+
) -> FileSystemSandboxPolicy {
395+
let Some(additional_permissions) = additional_permissions else {
396+
return file_system_policy.clone();
397+
};
398+
399+
let (extra_reads, extra_writes) = additional_permission_roots(additional_permissions);
400+
if extra_reads.is_empty() && extra_writes.is_empty() {
401+
file_system_policy.clone()
402+
} else {
403+
merge_file_system_policy_with_additional_permissions(
404+
file_system_policy,
405+
extra_reads,
406+
extra_writes,
407+
)
408+
}
409+
}
410+
391411
fn merge_read_only_access_with_additional_reads(
392412
read_only_access: &ReadOnlyAccess,
393413
extra_reads: Vec<AbsolutePathBuf>,
@@ -587,18 +607,10 @@ impl SandboxManager {
587607
);
588608
let (effective_file_system_policy, effective_network_policy) =
589609
if let Some(additional_permissions) = additional_permissions {
590-
let (extra_reads, extra_writes) =
591-
additional_permission_roots(&additional_permissions);
592-
let file_system_sandbox_policy =
593-
if extra_reads.is_empty() && extra_writes.is_empty() {
594-
file_system_policy.clone()
595-
} else {
596-
merge_file_system_policy_with_additional_permissions(
597-
file_system_policy,
598-
extra_reads,
599-
extra_writes,
600-
)
601-
};
610+
let file_system_sandbox_policy = effective_file_system_sandbox_policy(
611+
file_system_policy,
612+
Some(&additional_permissions),
613+
);
602614
let network_sandbox_policy =
603615
if merge_network_access(network_policy.is_enabled(), &additional_permissions) {
604616
NetworkSandboxPolicy::Enabled
@@ -721,6 +733,7 @@ mod tests {
721733
#[cfg(target_os = "macos")]
722734
use super::EffectiveSandboxPermissions;
723735
use super::SandboxManager;
736+
use super::effective_file_system_sandbox_policy;
724737
#[cfg(target_os = "macos")]
725738
use super::intersect_permission_profiles;
726739
use super::merge_file_system_policy_with_additional_permissions;
@@ -1364,4 +1377,80 @@ mod tests {
13641377
true
13651378
);
13661379
}
1380+
1381+
#[test]
1382+
fn effective_file_system_sandbox_policy_returns_base_policy_without_additional_permissions() {
1383+
let temp_dir = TempDir::new().expect("create temp dir");
1384+
let cwd = AbsolutePathBuf::from_absolute_path(
1385+
canonicalize(temp_dir.path()).expect("canonicalize temp dir"),
1386+
)
1387+
.expect("absolute temp dir");
1388+
let denied_path = cwd.join("denied").expect("denied path");
1389+
let base_policy = FileSystemSandboxPolicy::restricted(vec![
1390+
FileSystemSandboxEntry {
1391+
path: FileSystemPath::Special {
1392+
value: FileSystemSpecialPath::Root,
1393+
},
1394+
access: FileSystemAccessMode::Read,
1395+
},
1396+
FileSystemSandboxEntry {
1397+
path: FileSystemPath::Path { path: denied_path },
1398+
access: FileSystemAccessMode::None,
1399+
},
1400+
]);
1401+
1402+
let effective_policy = effective_file_system_sandbox_policy(&base_policy, None);
1403+
1404+
assert_eq!(effective_policy, base_policy);
1405+
}
1406+
1407+
#[test]
1408+
fn effective_file_system_sandbox_policy_merges_additional_write_roots() {
1409+
let temp_dir = TempDir::new().expect("create temp dir");
1410+
let cwd = AbsolutePathBuf::from_absolute_path(
1411+
canonicalize(temp_dir.path()).expect("canonicalize temp dir"),
1412+
)
1413+
.expect("absolute temp dir");
1414+
let allowed_path = cwd.join("allowed").expect("allowed path");
1415+
let denied_path = cwd.join("denied").expect("denied path");
1416+
let base_policy = FileSystemSandboxPolicy::restricted(vec![
1417+
FileSystemSandboxEntry {
1418+
path: FileSystemPath::Special {
1419+
value: FileSystemSpecialPath::Root,
1420+
},
1421+
access: FileSystemAccessMode::Read,
1422+
},
1423+
FileSystemSandboxEntry {
1424+
path: FileSystemPath::Path {
1425+
path: denied_path.clone(),
1426+
},
1427+
access: FileSystemAccessMode::None,
1428+
},
1429+
]);
1430+
let additional_permissions = PermissionProfile {
1431+
file_system: Some(FileSystemPermissions {
1432+
read: Some(vec![]),
1433+
write: Some(vec![allowed_path.clone()]),
1434+
}),
1435+
..Default::default()
1436+
};
1437+
1438+
let effective_policy =
1439+
effective_file_system_sandbox_policy(&base_policy, Some(&additional_permissions));
1440+
1441+
assert_eq!(
1442+
effective_policy.entries.contains(&FileSystemSandboxEntry {
1443+
path: FileSystemPath::Path { path: denied_path },
1444+
access: FileSystemAccessMode::None,
1445+
}),
1446+
true
1447+
);
1448+
assert_eq!(
1449+
effective_policy.entries.contains(&FileSystemSandboxEntry {
1450+
path: FileSystemPath::Path { path: allowed_path },
1451+
access: FileSystemAccessMode::Write,
1452+
}),
1453+
true
1454+
);
1455+
}
13671456
}

codex-rs/core/src/tools/handlers/apply_patch.rs

Lines changed: 44 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ use crate::client_common::tools::ToolSpec;
1111
use crate::codex::Session;
1212
use crate::codex::TurnContext;
1313
use crate::function_tool::FunctionCallError;
14+
use crate::sandboxing::effective_file_system_sandbox_policy;
15+
use crate::sandboxing::merge_permission_profiles;
1416
use crate::tools::context::FunctionToolOutput;
1517
use crate::tools::context::SharedTurnDiffTracker;
1618
use crate::tools::context::ToolInvocation;
@@ -89,6 +91,38 @@ fn write_permissions_for_paths(file_paths: &[AbsolutePathBuf]) -> Option<Permiss
8991
crate::sandboxing::normalize_additional_permissions(permissions).ok()
9092
}
9193

94+
async fn effective_patch_permissions(
95+
session: &Session,
96+
turn: &TurnContext,
97+
action: &ApplyPatchAction,
98+
) -> (
99+
Vec<AbsolutePathBuf>,
100+
crate::tools::handlers::EffectiveAdditionalPermissions,
101+
codex_protocol::permissions::FileSystemSandboxPolicy,
102+
) {
103+
let file_paths = file_paths_for_action(action);
104+
let granted_permissions = merge_permission_profiles(
105+
session.granted_session_permissions().await.as_ref(),
106+
session.granted_turn_permissions().await.as_ref(),
107+
);
108+
let effective_additional_permissions = apply_granted_turn_permissions(
109+
session,
110+
crate::sandboxing::SandboxPermissions::UseDefault,
111+
write_permissions_for_paths(&file_paths),
112+
)
113+
.await;
114+
let file_system_sandbox_policy = effective_file_system_sandbox_policy(
115+
&turn.file_system_sandbox_policy,
116+
granted_permissions.as_ref(),
117+
);
118+
119+
(
120+
file_paths,
121+
effective_additional_permissions,
122+
file_system_sandbox_policy,
123+
)
124+
}
125+
92126
#[async_trait]
93127
impl ToolHandler for ApplyPatchHandler {
94128
type Output = FunctionToolOutput;
@@ -138,20 +172,17 @@ impl ToolHandler for ApplyPatchHandler {
138172
let command = vec!["apply_patch".to_string(), patch_input.clone()];
139173
match codex_apply_patch::maybe_parse_apply_patch_verified(&command, &cwd) {
140174
codex_apply_patch::MaybeApplyPatchVerified::Body(changes) => {
141-
match apply_patch::apply_patch(turn.as_ref(), changes).await {
175+
let (file_paths, effective_additional_permissions, file_system_sandbox_policy) =
176+
effective_patch_permissions(session.as_ref(), turn.as_ref(), &changes).await;
177+
match apply_patch::apply_patch(turn.as_ref(), &file_system_sandbox_policy, changes)
178+
.await
179+
{
142180
InternalApplyPatchInvocation::Output(item) => {
143181
let content = item?;
144182
Ok(FunctionToolOutput::from_text(content, Some(true)))
145183
}
146184
InternalApplyPatchInvocation::DelegateToExec(apply) => {
147185
let changes = convert_apply_patch_to_protocol(&apply.action);
148-
let file_paths = file_paths_for_action(&apply.action);
149-
let effective_additional_permissions = apply_granted_turn_permissions(
150-
session.as_ref(),
151-
crate::sandboxing::SandboxPermissions::UseDefault,
152-
write_permissions_for_paths(&file_paths),
153-
)
154-
.await;
155186
let emitter =
156187
ToolEmitter::apply_patch(changes.clone(), apply.auto_approved);
157188
let event_ctx = ToolEventCtx::new(
@@ -247,20 +278,17 @@ pub(crate) async fn intercept_apply_patch(
247278
turn.as_ref(),
248279
)
249280
.await;
250-
match apply_patch::apply_patch(turn.as_ref(), changes).await {
281+
let (approval_keys, effective_additional_permissions, file_system_sandbox_policy) =
282+
effective_patch_permissions(session.as_ref(), turn.as_ref(), &changes).await;
283+
match apply_patch::apply_patch(turn.as_ref(), &file_system_sandbox_policy, changes)
284+
.await
285+
{
251286
InternalApplyPatchInvocation::Output(item) => {
252287
let content = item?;
253288
Ok(Some(FunctionToolOutput::from_text(content, Some(true))))
254289
}
255290
InternalApplyPatchInvocation::DelegateToExec(apply) => {
256291
let changes = convert_apply_patch_to_protocol(&apply.action);
257-
let approval_keys = file_paths_for_action(&apply.action);
258-
let effective_additional_permissions = apply_granted_turn_permissions(
259-
session.as_ref(),
260-
crate::sandboxing::SandboxPermissions::UseDefault,
261-
write_permissions_for_paths(&approval_keys),
262-
)
263-
.await;
264292
let emitter = ToolEmitter::apply_patch(changes.clone(), apply.auto_approved);
265293
let event_ctx = ToolEventCtx::new(
266294
session.as_ref(),

0 commit comments

Comments
 (0)