@@ -3,7 +3,10 @@ use codex_core::config::Constrained;
33use codex_core:: protocol:: AskForApproval ;
44use codex_core:: protocol:: EventMsg ;
55use codex_core:: protocol:: Op ;
6+ use codex_core:: protocol:: SandboxPolicy ;
7+ use codex_protocol:: models:: DeveloperInstructions ;
68use codex_protocol:: user_input:: UserInput ;
9+ use codex_utils_absolute_path:: AbsolutePathBuf ;
710use core_test_support:: responses:: ev_completed;
811use core_test_support:: responses:: ev_response_created;
912use core_test_support:: responses:: mount_sse_once;
@@ -14,6 +17,8 @@ use core_test_support::test_codex::test_codex;
1417use core_test_support:: wait_for_event;
1518use pretty_assertions:: assert_eq;
1619use std:: collections:: HashSet ;
20+ use std:: path:: PathBuf ;
21+ use tempfile:: TempDir ;
1722
1823fn permissions_texts ( input : & [ serde_json:: Value ] ) -> Vec < String > {
1924 input
@@ -42,15 +47,35 @@ fn sse_completed(id: &str) -> String {
4247 sse ( vec ! [ ev_response_created( id) , ev_completed( id) ] )
4348}
4449
50+ fn workspace_write_policy ( root : AbsolutePathBuf ) -> SandboxPolicy {
51+ SandboxPolicy :: WorkspaceWrite {
52+ writable_roots : vec ! [ root] ,
53+ network_access : false ,
54+ exclude_tmpdir_env_var : true ,
55+ exclude_slash_tmp : true ,
56+ }
57+ }
58+
59+ fn expected_permissions_message (
60+ sandbox_policy : & SandboxPolicy ,
61+ approval_policy : AskForApproval ,
62+ cwd : & PathBuf ,
63+ ) -> String {
64+ DeveloperInstructions :: from_policy ( sandbox_policy, approval_policy, cwd) . into_text ( )
65+ }
66+
4567#[ tokio:: test( flavor = "multi_thread" , worker_threads = 2 ) ]
4668async fn permissions_message_sent_once_on_start ( ) -> Result < ( ) > {
4769 skip_if_no_network ! ( Ok ( ( ) ) ) ;
4870
4971 let server = start_mock_server ( ) . await ;
5072 let req = mount_sse_once ( & server, sse_completed ( "resp-1" ) ) . await ;
5173
74+ let writable = TempDir :: new ( ) ?;
75+ let sandbox_policy = workspace_write_policy ( AbsolutePathBuf :: try_from ( writable. path ( ) ) ?) ;
5276 let mut builder = test_codex ( ) . with_config ( |config| {
5377 config. approval_policy = Constrained :: allow_any ( AskForApproval :: OnRequest ) ;
78+ config. sandbox_policy = Constrained :: allow_any ( sandbox_policy. clone ( ) ) ;
5479 } ) ;
5580 let test = builder. build ( & server) . await ?;
5681
@@ -68,7 +93,9 @@ async fn permissions_message_sent_once_on_start() -> Result<()> {
6893 let body = request. body_json ( ) ;
6994 let input = body[ "input" ] . as_array ( ) . expect ( "input array" ) ;
7095 let permissions = permissions_texts ( input) ;
71- assert_eq ! ( permissions. len( ) , 1 ) ;
96+ let expected =
97+ expected_permissions_message ( & sandbox_policy, AskForApproval :: OnRequest , & test. config . cwd ) ;
98+ assert_eq ! ( permissions, vec![ expected] ) ;
7299
73100 Ok ( ( ) )
74101}
@@ -81,10 +108,17 @@ async fn permissions_message_added_on_override_change() -> Result<()> {
81108 let req1 = mount_sse_once ( & server, sse_completed ( "resp-1" ) ) . await ;
82109 let req2 = mount_sse_once ( & server, sse_completed ( "resp-2" ) ) . await ;
83110
111+ let writable = TempDir :: new ( ) ?;
112+ let sandbox_policy = workspace_write_policy ( AbsolutePathBuf :: try_from ( writable. path ( ) ) ?) ;
84113 let mut builder = test_codex ( ) . with_config ( |config| {
85114 config. approval_policy = Constrained :: allow_any ( AskForApproval :: OnRequest ) ;
115+ config. sandbox_policy = Constrained :: allow_any ( sandbox_policy. clone ( ) ) ;
86116 } ) ;
87117 let test = builder. build ( & server) . await ?;
118+ let expected_on_request =
119+ expected_permissions_message ( & sandbox_policy, AskForApproval :: OnRequest , & test. config . cwd ) ;
120+ let expected_never =
121+ expected_permissions_message ( & sandbox_policy, AskForApproval :: Never , & test. config . cwd ) ;
88122
89123 test. codex
90124 . submit ( Op :: UserInput {
@@ -124,8 +158,11 @@ async fn permissions_message_added_on_override_change() -> Result<()> {
124158 let permissions_1 = permissions_texts ( input1) ;
125159 let permissions_2 = permissions_texts ( input2) ;
126160
127- assert_eq ! ( permissions_1. len( ) , 1 ) ;
128- assert_eq ! ( permissions_2. len( ) , 2 ) ;
161+ assert_eq ! ( permissions_1, vec![ expected_on_request. clone( ) ] ) ;
162+ assert_eq ! (
163+ permissions_2,
164+ vec![ expected_on_request, expected_never. clone( ) ]
165+ ) ;
129166 let unique = permissions_2. into_iter ( ) . collect :: < HashSet < String > > ( ) ;
130167 assert_eq ! ( unique. len( ) , 2 ) ;
131168
@@ -140,10 +177,15 @@ async fn permissions_message_not_added_when_no_change() -> Result<()> {
140177 let req1 = mount_sse_once ( & server, sse_completed ( "resp-1" ) ) . await ;
141178 let req2 = mount_sse_once ( & server, sse_completed ( "resp-2" ) ) . await ;
142179
180+ let writable = TempDir :: new ( ) ?;
181+ let sandbox_policy = workspace_write_policy ( AbsolutePathBuf :: try_from ( writable. path ( ) ) ?) ;
143182 let mut builder = test_codex ( ) . with_config ( |config| {
144183 config. approval_policy = Constrained :: allow_any ( AskForApproval :: OnRequest ) ;
184+ config. sandbox_policy = Constrained :: allow_any ( sandbox_policy. clone ( ) ) ;
145185 } ) ;
146186 let test = builder. build ( & server) . await ?;
187+ let expected =
188+ expected_permissions_message ( & sandbox_policy, AskForApproval :: OnRequest , & test. config . cwd ) ;
147189
148190 test. codex
149191 . submit ( Op :: UserInput {
@@ -172,9 +214,8 @@ async fn permissions_message_not_added_when_no_change() -> Result<()> {
172214 let permissions_1 = permissions_texts ( input1) ;
173215 let permissions_2 = permissions_texts ( input2) ;
174216
175- assert_eq ! ( permissions_1. len( ) , 1 ) ;
176- assert_eq ! ( permissions_2. len( ) , 1 ) ;
177- assert_eq ! ( permissions_1, permissions_2) ;
217+ assert_eq ! ( permissions_1, vec![ expected. clone( ) ] ) ;
218+ assert_eq ! ( permissions_2, vec![ expected] ) ;
178219
179220 Ok ( ( ) )
180221}
@@ -188,12 +229,22 @@ async fn resume_replays_permissions_messages() -> Result<()> {
188229 let _req2 = mount_sse_once ( & server, sse_completed ( "resp-2" ) ) . await ;
189230 let req3 = mount_sse_once ( & server, sse_completed ( "resp-3" ) ) . await ;
190231
232+ let writable = TempDir :: new ( ) ?;
233+ let sandbox_policy = workspace_write_policy ( AbsolutePathBuf :: try_from ( writable. path ( ) ) ?) ;
191234 let mut builder = test_codex ( ) . with_config ( |config| {
192235 config. approval_policy = Constrained :: allow_any ( AskForApproval :: OnRequest ) ;
236+ config. sandbox_policy = Constrained :: allow_any ( sandbox_policy. clone ( ) ) ;
193237 } ) ;
194238 let initial = builder. build ( & server) . await ?;
195239 let rollout_path = initial. session_configured . rollout_path . clone ( ) ;
196240 let home = initial. home . clone ( ) ;
241+ let expected_on_request = expected_permissions_message (
242+ & sandbox_policy,
243+ AskForApproval :: OnRequest ,
244+ & initial. config . cwd ,
245+ ) ;
246+ let expected_never =
247+ expected_permissions_message ( & sandbox_policy, AskForApproval :: Never , & initial. config . cwd ) ;
197248
198249 initial
199250 . codex
@@ -244,7 +295,14 @@ async fn resume_replays_permissions_messages() -> Result<()> {
244295 let body3 = req3. single_request ( ) . body_json ( ) ;
245296 let input = body3[ "input" ] . as_array ( ) . expect ( "input array" ) ;
246297 let permissions = permissions_texts ( input) ;
247- assert_eq ! ( permissions. len( ) , 3 ) ;
298+ assert_eq ! (
299+ permissions,
300+ vec![
301+ expected_on_request. clone( ) ,
302+ expected_never,
303+ expected_on_request
304+ ]
305+ ) ;
248306 let unique = permissions. into_iter ( ) . collect :: < HashSet < String > > ( ) ;
249307 assert_eq ! ( unique. len( ) , 2 ) ;
250308
@@ -261,12 +319,27 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
261319 let req3 = mount_sse_once ( & server, sse_completed ( "resp-3" ) ) . await ;
262320 let req4 = mount_sse_once ( & server, sse_completed ( "resp-4" ) ) . await ;
263321
322+ let writable = TempDir :: new ( ) ?;
323+ let sandbox_policy = workspace_write_policy ( AbsolutePathBuf :: try_from ( writable. path ( ) ) ?) ;
264324 let mut builder = test_codex ( ) . with_config ( |config| {
265325 config. approval_policy = Constrained :: allow_any ( AskForApproval :: OnRequest ) ;
326+ config. sandbox_policy = Constrained :: allow_any ( sandbox_policy. clone ( ) ) ;
266327 } ) ;
267328 let initial = builder. build ( & server) . await ?;
268329 let rollout_path = initial. session_configured . rollout_path . clone ( ) ;
269330 let home = initial. home . clone ( ) ;
331+ let expected_on_request = expected_permissions_message (
332+ & sandbox_policy,
333+ AskForApproval :: OnRequest ,
334+ & initial. config . cwd ,
335+ ) ;
336+ let expected_never =
337+ expected_permissions_message ( & sandbox_policy, AskForApproval :: Never , & initial. config . cwd ) ;
338+ let expected_unless_trusted = expected_permissions_message (
339+ & sandbox_policy,
340+ AskForApproval :: UnlessTrusted ,
341+ & initial. config . cwd ,
342+ ) ;
270343
271344 initial
272345 . codex
@@ -305,7 +378,10 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
305378 let body2 = req2. single_request ( ) . body_json ( ) ;
306379 let input2 = body2[ "input" ] . as_array ( ) . expect ( "input array" ) ;
307380 let permissions_base = permissions_texts ( input2) ;
308- assert_eq ! ( permissions_base. len( ) , 2 ) ;
381+ assert_eq ! (
382+ permissions_base,
383+ vec![ expected_on_request. clone( ) , expected_never. clone( ) ]
384+ ) ;
309385
310386 builder = builder. with_config ( |config| {
311387 config. approval_policy = Constrained :: allow_any ( AskForApproval :: UnlessTrusted ) ;
@@ -325,12 +401,14 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
325401 let body3 = req3. single_request ( ) . body_json ( ) ;
326402 let input3 = body3[ "input" ] . as_array ( ) . expect ( "input array" ) ;
327403 let permissions_resume = permissions_texts ( input3) ;
328- assert_eq ! ( permissions_resume. len( ) , permissions_base. len( ) + 1 ) ;
329404 assert_eq ! (
330- & permissions_resume[ ..permissions_base. len( ) ] ,
331- permissions_base. as_slice( )
405+ permissions_resume,
406+ vec![
407+ expected_on_request. clone( ) ,
408+ expected_never. clone( ) ,
409+ expected_unless_trusted. clone( )
410+ ]
332411 ) ;
333- assert ! ( !permissions_base. contains( permissions_resume. last( ) . expect( "new permissions" ) ) ) ;
334412
335413 let mut fork_config = initial. config . clone ( ) ;
336414 fork_config. approval_policy = Constrained :: allow_any ( AskForApproval :: UnlessTrusted ) ;
@@ -352,15 +430,15 @@ async fn resume_and_fork_append_permissions_messages() -> Result<()> {
352430 let body4 = req4. single_request ( ) . body_json ( ) ;
353431 let input4 = body4[ "input" ] . as_array ( ) . expect ( "input array" ) ;
354432 let permissions_fork = permissions_texts ( input4) ;
355- assert_eq ! ( permissions_fork. len( ) , permissions_base. len( ) + 2 ) ;
356433 assert_eq ! (
357- & permissions_fork[ ..permissions_base. len( ) ] ,
358- permissions_base. as_slice( )
434+ permissions_fork,
435+ vec![
436+ expected_on_request,
437+ expected_never,
438+ expected_unless_trusted. clone( ) ,
439+ expected_unless_trusted
440+ ]
359441 ) ;
360- let new_permissions = & permissions_fork[ permissions_base. len ( ) ..] ;
361- assert_eq ! ( new_permissions. len( ) , 2 ) ;
362- assert_eq ! ( new_permissions[ 0 ] , new_permissions[ 1 ] ) ;
363- assert ! ( !permissions_base. contains( & new_permissions[ 0 ] ) ) ;
364442
365443 Ok ( ( ) )
366444}
0 commit comments