@@ -13,11 +13,9 @@ use std::sync::atomic::Ordering;
1313use std:: time:: Duration ;
1414
1515use codex_features:: Feature ;
16- use codex_protocol:: protocol:: AskForApproval ;
16+ use codex_protocol:: models:: PermissionProfile ;
17+ use codex_protocol:: permissions:: NetworkSandboxPolicy ;
1718use codex_protocol:: protocol:: EventMsg ;
18- use codex_protocol:: protocol:: Op ;
19- use codex_protocol:: protocol:: SandboxPolicy ;
20- use codex_protocol:: user_input:: UserInput ;
2119#[ cfg( target_os = "linux" ) ]
2220use codex_sandboxing:: landlock:: CODEX_LINUX_SANDBOX_ARG0 ;
2321use core_test_support:: assert_regex_match;
@@ -59,6 +57,15 @@ async fn apply_patch_harness_with(
5957 Box :: pin ( TestCodexHarness :: with_remote_aware_builder ( builder) ) . await
6058}
6159
60+ fn restrictive_workspace_write_profile ( ) -> PermissionProfile {
61+ PermissionProfile :: workspace_write_with (
62+ & [ ] ,
63+ NetworkSandboxPolicy :: Restricted ,
64+ /*exclude_tmpdir_env_var*/ true ,
65+ /*exclude_slash_tmp*/ true ,
66+ )
67+ }
68+
6269pub async fn mount_apply_patch (
6370 harness : & TestCodexHarness ,
6471 call_id : & str ,
@@ -354,28 +361,7 @@ async fn apply_patch_cli_move_without_content_change_has_no_turn_diff(
354361 let call_id = "apply-move-no-change" ;
355362 mount_apply_patch ( & harness, call_id, patch, "ok" , model_output) . await ;
356363
357- let model = test. session_configured . model . clone ( ) ;
358- codex
359- . submit ( Op :: UserTurn {
360- environments : None ,
361- items : vec ! [ UserInput :: Text {
362- text: "rename without content change" . into( ) ,
363- text_elements: Vec :: new( ) ,
364- } ] ,
365- final_output_json_schema : None ,
366- cwd : harness. cwd ( ) . to_path_buf ( ) ,
367- approval_policy : AskForApproval :: Never ,
368- approvals_reviewer : None ,
369- sandbox_policy : SandboxPolicy :: DangerFullAccess ,
370- permission_profile : None ,
371- model,
372- effort : None ,
373- summary : None ,
374- service_tier : None ,
375- collaboration_mode : None ,
376- personality : None ,
377- } )
378- . await ?;
364+ harness. submit ( "rename without content change" ) . await ?;
379365
380366 let mut saw_turn_diff = false ;
381367 wait_for_event ( & codex, |event| match event {
@@ -641,16 +627,10 @@ async fn apply_patch_cli_rejects_path_traversal_outside_workspace(
641627 let call_id = "apply-path-traversal" ;
642628 mount_apply_patch ( & harness, call_id, patch, "fail" , model_output) . await ;
643629
644- let sandbox_policy = SandboxPolicy :: WorkspaceWrite {
645- writable_roots : vec ! [ ] ,
646- network_access : false ,
647- exclude_tmpdir_env_var : true ,
648- exclude_slash_tmp : true ,
649- } ;
650630 harness
651- . submit_with_policy (
631+ . submit_with_permission_profile (
652632 "attempt to escape workspace via apply_patch" ,
653- sandbox_policy ,
633+ restrictive_workspace_write_profile ( ) ,
654634 )
655635 . await ?;
656636
@@ -696,14 +676,11 @@ async fn apply_patch_cli_rejects_move_path_traversal_outside_workspace(
696676 let call_id = "apply-move-traversal" ;
697677 mount_apply_patch ( & harness, call_id, patch, "fail" , model_output) . await ;
698678
699- let sandbox_policy = SandboxPolicy :: WorkspaceWrite {
700- writable_roots : vec ! [ ] ,
701- network_access : false ,
702- exclude_tmpdir_env_var : true ,
703- exclude_slash_tmp : true ,
704- } ;
705679 harness
706- . submit_with_policy ( "attempt move traversal via apply_patch" , sandbox_policy)
680+ . submit_with_permission_profile (
681+ "attempt move traversal via apply_patch" ,
682+ restrictive_workspace_write_profile ( ) ,
683+ )
707684 . await ?;
708685
709686 let out = harness. apply_patch_output ( call_id, model_output) . await ;
@@ -992,27 +969,7 @@ async fn apply_patch_custom_tool_streaming_emits_updated_changes() -> Result<()>
992969 )
993970 . await ;
994971
995- codex
996- . submit ( Op :: UserTurn {
997- environments : None ,
998- items : vec ! [ UserInput :: Text {
999- text: "create streamed file" . into( ) ,
1000- text_elements: Vec :: new( ) ,
1001- } ] ,
1002- final_output_json_schema : None ,
1003- cwd : harness. cwd ( ) . to_path_buf ( ) ,
1004- approval_policy : AskForApproval :: Never ,
1005- approvals_reviewer : None ,
1006- sandbox_policy : SandboxPolicy :: DangerFullAccess ,
1007- permission_profile : None ,
1008- model : test. session_configured . model . clone ( ) ,
1009- effort : None ,
1010- summary : None ,
1011- service_tier : None ,
1012- collaboration_mode : None ,
1013- personality : None ,
1014- } )
1015- . await ?;
972+ harness. submit ( "create streamed file" ) . await ?;
1016973
1017974 let mut updates = Vec :: new ( ) ;
1018975 wait_for_event ( & codex, |event| match event {
@@ -1090,28 +1047,7 @@ async fn apply_patch_shell_command_heredoc_with_cd_emits_turn_diff() -> Result<(
10901047 ] ;
10911048 mount_sse_sequence ( harness. server ( ) , bodies) . await ;
10921049
1093- let model = test. session_configured . model . clone ( ) ;
1094- codex
1095- . submit ( Op :: UserTurn {
1096- environments : None ,
1097- items : vec ! [ UserInput :: Text {
1098- text: "apply via shell heredoc with cd" . into( ) ,
1099- text_elements: Vec :: new( ) ,
1100- } ] ,
1101- final_output_json_schema : None ,
1102- cwd : harness. cwd ( ) . to_path_buf ( ) ,
1103- approval_policy : AskForApproval :: Never ,
1104- approvals_reviewer : None ,
1105- sandbox_policy : SandboxPolicy :: DangerFullAccess ,
1106- permission_profile : None ,
1107- model,
1108- effort : None ,
1109- summary : None ,
1110- service_tier : None ,
1111- collaboration_mode : None ,
1112- personality : None ,
1113- } )
1114- . await ?;
1050+ harness. submit ( "apply via shell heredoc with cd" ) . await ?;
11151051
11161052 let mut saw_turn_diff = None ;
11171053 let mut saw_patch_begin = false ;
@@ -1176,28 +1112,7 @@ async fn apply_patch_shell_command_failure_propagates_error_and_skips_diff() ->
11761112 ] ;
11771113 mount_sse_sequence ( harness. server ( ) , bodies) . await ;
11781114
1179- let model = test. session_configured . model . clone ( ) ;
1180- codex
1181- . submit ( Op :: UserTurn {
1182- environments : None ,
1183- items : vec ! [ UserInput :: Text {
1184- text: "apply patch via shell" . into( ) ,
1185- text_elements: Vec :: new( ) ,
1186- } ] ,
1187- final_output_json_schema : None ,
1188- cwd : harness. cwd ( ) . to_path_buf ( ) ,
1189- approval_policy : AskForApproval :: Never ,
1190- approvals_reviewer : None ,
1191- sandbox_policy : SandboxPolicy :: DangerFullAccess ,
1192- permission_profile : None ,
1193- model,
1194- effort : None ,
1195- summary : None ,
1196- service_tier : None ,
1197- collaboration_mode : None ,
1198- personality : None ,
1199- } )
1200- . await ?;
1115+ harness. submit ( "apply patch via shell" ) . await ?;
12011116
12021117 let mut saw_turn_diff = false ;
12031118 wait_for_event ( & codex, |event| match event {
@@ -1333,28 +1248,7 @@ async fn apply_patch_emits_turn_diff_event_with_unified_diff(
13331248 let patch = format ! ( "*** Begin Patch\n *** Add File: {file}\n +hello\n *** End Patch\n " ) ;
13341249 mount_apply_patch ( & harness, call_id, patch. as_str ( ) , "ok" , model_output) . await ;
13351250
1336- let model = test. session_configured . model . clone ( ) ;
1337- codex
1338- . submit ( Op :: UserTurn {
1339- environments : None ,
1340- items : vec ! [ UserInput :: Text {
1341- text: "emit diff" . into( ) ,
1342- text_elements: Vec :: new( ) ,
1343- } ] ,
1344- final_output_json_schema : None ,
1345- cwd : harness. cwd ( ) . to_path_buf ( ) ,
1346- approval_policy : AskForApproval :: Never ,
1347- approvals_reviewer : None ,
1348- sandbox_policy : SandboxPolicy :: DangerFullAccess ,
1349- permission_profile : None ,
1350- model,
1351- effort : None ,
1352- summary : None ,
1353- service_tier : None ,
1354- collaboration_mode : None ,
1355- personality : None ,
1356- } )
1357- . await ?;
1251+ harness. submit ( "emit diff" ) . await ?;
13581252
13591253 let mut saw_turn_diff = None ;
13601254 wait_for_event ( & codex, |event| match event {
@@ -1402,28 +1296,7 @@ async fn apply_patch_turn_diff_for_rename_with_content_change(
14021296 let patch = "*** Begin Patch\n *** Update File: old.txt\n *** Move to: new.txt\n @@\n -old\n +new\n *** End Patch" ;
14031297 mount_apply_patch ( & harness, call_id, patch, "ok" , model_output) . await ;
14041298
1405- let model = test. session_configured . model . clone ( ) ;
1406- codex
1407- . submit ( Op :: UserTurn {
1408- environments : None ,
1409- items : vec ! [ UserInput :: Text {
1410- text: "rename with change" . into( ) ,
1411- text_elements: Vec :: new( ) ,
1412- } ] ,
1413- final_output_json_schema : None ,
1414- cwd : harness. cwd ( ) . to_path_buf ( ) ,
1415- approval_policy : AskForApproval :: Never ,
1416- approvals_reviewer : None ,
1417- sandbox_policy : SandboxPolicy :: DangerFullAccess ,
1418- permission_profile : None ,
1419- model,
1420- effort : None ,
1421- summary : None ,
1422- service_tier : None ,
1423- collaboration_mode : None ,
1424- personality : None ,
1425- } )
1426- . await ?;
1299+ harness. submit ( "rename with change" ) . await ?;
14271300
14281301 let mut last_diff: Option < String > = None ;
14291302 wait_for_event ( & codex, |event| match event {
@@ -1480,28 +1353,7 @@ async fn apply_patch_aggregates_diff_across_multiple_tool_calls() -> Result<()>
14801353 ] ) ;
14811354 mount_sse_sequence ( harness. server ( ) , vec ! [ s1, s2, s3] ) . await ;
14821355
1483- let model = test. session_configured . model . clone ( ) ;
1484- codex
1485- . submit ( Op :: UserTurn {
1486- environments : None ,
1487- items : vec ! [ UserInput :: Text {
1488- text: "aggregate diffs" . into( ) ,
1489- text_elements: Vec :: new( ) ,
1490- } ] ,
1491- final_output_json_schema : None ,
1492- cwd : harness. cwd ( ) . to_path_buf ( ) ,
1493- approval_policy : AskForApproval :: Never ,
1494- approvals_reviewer : None ,
1495- sandbox_policy : SandboxPolicy :: DangerFullAccess ,
1496- permission_profile : None ,
1497- model,
1498- effort : None ,
1499- summary : None ,
1500- service_tier : None ,
1501- collaboration_mode : None ,
1502- personality : None ,
1503- } )
1504- . await ?;
1356+ harness. submit ( "aggregate diffs" ) . await ?;
15051357
15061358 let mut last_diff: Option < String > = None ;
15071359 wait_for_event ( & codex, |event| match event {
@@ -1558,28 +1410,7 @@ async fn apply_patch_aggregates_diff_preserves_success_after_failure() -> Result
15581410 ] ;
15591411 mount_sse_sequence ( harness. server ( ) , responses) . await ;
15601412
1561- let model = test. session_configured . model . clone ( ) ;
1562- codex
1563- . submit ( Op :: UserTurn {
1564- environments : None ,
1565- items : vec ! [ UserInput :: Text {
1566- text: "apply patch twice with failure" . into( ) ,
1567- text_elements: Vec :: new( ) ,
1568- } ] ,
1569- final_output_json_schema : None ,
1570- cwd : harness. cwd ( ) . to_path_buf ( ) ,
1571- approval_policy : AskForApproval :: Never ,
1572- approvals_reviewer : None ,
1573- sandbox_policy : SandboxPolicy :: DangerFullAccess ,
1574- permission_profile : None ,
1575- model,
1576- effort : None ,
1577- summary : None ,
1578- service_tier : None ,
1579- collaboration_mode : None ,
1580- personality : None ,
1581- } )
1582- . await ?;
1413+ harness. submit ( "apply patch twice with failure" ) . await ?;
15831414
15841415 let mut last_diff: Option < String > = None ;
15851416 wait_for_event_with_timeout (
0 commit comments