@@ -185,13 +185,20 @@ fn strip_supervisor_only_env(cmd: &mut Command) {
185185 }
186186}
187187
188- fn inject_provider_env ( cmd : & mut Command , provider_env : & HashMap < String , String > ) {
188+ fn inject_provider_env (
189+ cmd : & mut Command ,
190+ provider_env : & HashMap < String , String > ,
191+ repair_standard_sbin : bool ,
192+ ) {
189193 for ( key, value) in provider_env {
190194 if is_supervisor_only_env_var ( key) {
191195 continue ;
192196 }
193197 if key == "PATH" {
194- cmd. env ( key, child_env:: path_with_standard_sbin_paths ( value) ) ;
198+ cmd. env (
199+ key,
200+ child_env:: maybe_path_with_standard_sbin_paths ( value, repair_standard_sbin) ,
201+ ) ;
195202 } else {
196203 cmd. env ( key, value) ;
197204 }
@@ -688,8 +695,16 @@ fn shell_command_arg_index(args: &[String]) -> Option<usize> {
688695 None
689696}
690697
691- fn process_args_with_standard_sbin_paths ( program : & str , args : & [ String ] ) -> Vec < String > {
698+ fn process_args_with_standard_sbin_paths (
699+ program : & str ,
700+ args : & [ String ] ,
701+ repair_standard_sbin : bool ,
702+ ) -> Vec < String > {
692703 let mut args = args. to_vec ( ) ;
704+ if !repair_standard_sbin {
705+ return args;
706+ }
707+
693708 let basename = program. rsplit ( '/' ) . next ( ) . unwrap_or ( program) ;
694709 if matches ! ( basename, "bash" | "sh" )
695710 && let Some ( command_index) = shell_command_arg_index ( & args)
@@ -779,12 +794,13 @@ impl ProcessHandle {
779794 ca_paths : Option < & ( PathBuf , PathBuf ) > ,
780795 provider_env : & HashMap < String , String > ,
781796 ) -> Result < Self > {
782- let args = process_args_with_standard_sbin_paths ( program, args) ;
797+ let repair_standard_sbin = child_env:: standard_sbin_path_repair_enabled ( policy) ;
798+ let args = process_args_with_standard_sbin_paths ( program, args, repair_standard_sbin) ;
783799 let mut cmd = Command :: new ( program) ;
784800 cmd. args ( args)
785801 . kill_on_drop ( true )
786802 . env ( openshell_core:: sandbox_env:: SANDBOX , "1" )
787- . env ( "PATH" , child_env:: child_path_from_env ( ) ) ;
803+ . env ( "PATH" , child_env:: child_path_from_env ( repair_standard_sbin ) ) ;
788804
789805 let mut pty_master = None ;
790806 let mut terminal_slave_fd = None ;
@@ -815,7 +831,7 @@ impl ProcessHandle {
815831 // supervisor credentials from its inherited environment.
816832 strip_supervisor_only_env ( & mut cmd) ;
817833
818- inject_provider_env ( & mut cmd, provider_env) ;
834+ inject_provider_env ( & mut cmd, provider_env, repair_standard_sbin ) ;
819835 apply_canonical_process_environment (
820836 & mut cmd,
821837 policy,
@@ -981,12 +997,13 @@ impl ProcessHandle {
981997 ca_paths : Option < & ( PathBuf , PathBuf ) > ,
982998 provider_env : & HashMap < String , String > ,
983999 ) -> Result < Self > {
984- let args = process_args_with_standard_sbin_paths ( program, args) ;
1000+ let repair_standard_sbin = child_env:: standard_sbin_path_repair_enabled ( policy) ;
1001+ let args = process_args_with_standard_sbin_paths ( program, args, repair_standard_sbin) ;
9851002 let mut cmd = Command :: new ( program) ;
9861003 cmd. args ( args)
9871004 . kill_on_drop ( true )
9881005 . env ( openshell_core:: sandbox_env:: SANDBOX , "1" )
989- . env ( "PATH" , child_env:: child_path_from_env ( ) ) ;
1006+ . env ( "PATH" , child_env:: child_path_from_env ( repair_standard_sbin ) ) ;
9901007
9911008 let mut pty_master = None ;
9921009 let mut terminal_slave_fd = None ;
@@ -1017,7 +1034,7 @@ impl ProcessHandle {
10171034 // inherited environment.
10181035 strip_supervisor_only_env ( & mut cmd) ;
10191036
1020- inject_provider_env ( & mut cmd, provider_env) ;
1037+ inject_provider_env ( & mut cmd, provider_env, repair_standard_sbin ) ;
10211038 apply_canonical_process_environment (
10221039 & mut cmd,
10231040 policy,
@@ -2485,7 +2502,7 @@ mod tests {
24852502 fn process_args_wrap_shell_c_command_with_standard_sbin_paths ( ) {
24862503 let args = vec ! [ "-lc" . to_string( ) , "nvidia-smi -L" . to_string( ) ] ;
24872504
2488- let wrapped = process_args_with_standard_sbin_paths ( "sh" , & args) ;
2505+ let wrapped = process_args_with_standard_sbin_paths ( "sh" , & args, true ) ;
24892506
24902507 assert_eq ! ( wrapped[ 0 ] , "-lc" ) ;
24912508 assert ! ( wrapped[ 1 ] . contains( "/usr/sbin" ) ) ;
@@ -2496,7 +2513,16 @@ mod tests {
24962513 fn process_args_leave_non_shell_commands_unchanged ( ) {
24972514 let args = vec ! [ "nvidia-smi -L" . to_string( ) ] ;
24982515
2499- let wrapped = process_args_with_standard_sbin_paths ( "python" , & args) ;
2516+ let wrapped = process_args_with_standard_sbin_paths ( "python" , & args, true ) ;
2517+
2518+ assert_eq ! ( wrapped, args) ;
2519+ }
2520+
2521+ #[ test]
2522+ fn process_args_do_not_wrap_when_standard_sbin_repair_disabled ( ) {
2523+ let args = vec ! [ "-lc" . to_string( ) , "nvidia-smi -L" . to_string( ) ] ;
2524+
2525+ let wrapped = process_args_with_standard_sbin_paths ( "sh" , & args, false ) ;
25002526
25012527 assert_eq ! ( wrapped, args) ;
25022528 }
@@ -3059,15 +3085,15 @@ mod tests {
30593085 ) )
30603086 . collect ( ) ;
30613087
3062- inject_provider_env ( & mut cmd, & provider_env) ;
3088+ inject_provider_env ( & mut cmd, & provider_env, false ) ;
30633089
30643090 let output = cmd. output ( ) . await . expect ( "spawn env" ) ;
30653091 let stdout = String :: from_utf8 ( output. stdout ) . expect ( "utf8" ) ;
30663092 assert ! ( stdout. contains( "ANTHROPIC_API_KEY=openshell:resolve:env:ANTHROPIC_API_KEY" ) ) ;
30673093 }
30683094
30693095 #[ tokio:: test]
3070- async fn inject_provider_env_appends_standard_sbin_to_path ( ) {
3096+ async fn inject_provider_env_appends_standard_sbin_to_path_when_enabled ( ) {
30713097 let mut cmd = Command :: new ( "/usr/bin/env" ) ;
30723098 cmd. env_clear ( )
30733099 . stdin ( StdStdio :: null ( ) )
@@ -3079,7 +3105,7 @@ mod tests {
30793105 "/sandbox/.venv/bin:/usr/local/bin:/usr/bin:/bin" . to_string ( ) ,
30803106 ) ] ) ;
30813107
3082- inject_provider_env ( & mut cmd, & provider_env) ;
3108+ inject_provider_env ( & mut cmd, & provider_env, true ) ;
30833109
30843110 let output = cmd. output ( ) . await . expect ( "spawn env" ) ;
30853111 assert ! ( output. status. success( ) ) ;
@@ -3089,6 +3115,31 @@ mod tests {
30893115 } ) ) ;
30903116 }
30913117
3118+ #[ tokio:: test]
3119+ async fn inject_provider_env_leaves_path_unchanged_when_standard_sbin_repair_disabled ( ) {
3120+ let mut cmd = Command :: new ( "/usr/bin/env" ) ;
3121+ cmd. env_clear ( )
3122+ . stdin ( StdStdio :: null ( ) )
3123+ . stdout ( StdStdio :: piped ( ) )
3124+ . stderr ( StdStdio :: null ( ) ) ;
3125+
3126+ let provider_env = HashMap :: from ( [ (
3127+ "PATH" . to_string ( ) ,
3128+ "/sandbox/.venv/bin:/usr/local/bin:/usr/bin:/bin" . to_string ( ) ,
3129+ ) ] ) ;
3130+
3131+ inject_provider_env ( & mut cmd, & provider_env, false ) ;
3132+
3133+ let output = cmd. output ( ) . await . expect ( "spawn env" ) ;
3134+ assert ! ( output. status. success( ) ) ;
3135+ let stdout = String :: from_utf8 ( output. stdout ) . expect ( "utf8" ) ;
3136+ assert ! (
3137+ stdout
3138+ . lines( )
3139+ . any( |line| { line == "PATH=/sandbox/.venv/bin:/usr/local/bin:/usr/bin:/bin" } )
3140+ ) ;
3141+ }
3142+
30923143 #[ cfg( unix) ]
30933144 fn sandbox_policy_with_read_write (
30943145 path : PathBuf ,
@@ -3966,7 +4017,7 @@ mod tests {
39664017 ) ,
39674018 ] ) ;
39684019
3969- inject_provider_env ( & mut cmd, & provider_env) ;
4020+ inject_provider_env ( & mut cmd, & provider_env, false ) ;
39704021
39714022 let output = cmd. output ( ) . await . expect ( "spawn env" ) ;
39724023 assert ! ( output. status. success( ) ) ;
0 commit comments