@@ -969,6 +969,25 @@ pub struct ExecPolicy {
969969 /// produce no stdout/stderr output for this duration. Default: 30.
970970 #[ serde( default = "default_no_output_timeout" ) ]
971971 pub no_output_timeout_secs : u64 ,
972+ /// Environment variables to forward from the OpenFang process into
973+ /// `shell_exec` subprocesses.
974+ ///
975+ /// By default, subprocesses run with `env_clear()` and only receive a
976+ /// minimal safe set (PATH, HOME, TMPDIR, LANG, TERM, etc. — see
977+ /// `subprocess_sandbox::SAFE_ENV_VARS`). Anything else — including
978+ /// user-defined variables present in the container/host environment —
979+ /// is stripped. This list lets operators explicitly re-add specific
980+ /// variables to the subprocess environment.
981+ ///
982+ /// Each entry is an env var name. A single entry of `"*"` forwards
983+ /// every variable present in the parent process. Use with care — `*`
984+ /// will leak API keys and other secrets into child processes.
985+ ///
986+ /// Aliases `env_passthrough` and `env_allowlist` are accepted for
987+ /// backwards compatibility with users who configured these names
988+ /// before the field existed (issue #1169).
989+ #[ serde( default , alias = "env_passthrough" , alias = "env_allowlist" ) ]
990+ pub shell_env_passthrough : Vec < String > ,
972991}
973992
974993fn default_no_output_timeout ( ) -> u64 {
@@ -990,6 +1009,7 @@ impl Default for ExecPolicy {
9901009 timeout_secs : 30 ,
9911010 max_output_bytes : 100 * 1024 ,
9921011 no_output_timeout_secs : default_no_output_timeout ( ) ,
1012+ shell_env_passthrough : Vec :: new ( ) ,
9931013 }
9941014 }
9951015}
@@ -4600,4 +4620,54 @@ mod tests {
46004620 let config: KernelConfig = toml:: from_str ( toml_str) . unwrap ( ) ;
46014621 assert_eq ! ( config. heartbeat. default_timeout_secs, 300 ) ;
46024622 }
4623+
4624+ // ── Issue #1169: shell_env_passthrough on ExecPolicy ──────────────
4625+
4626+ #[ test]
4627+ fn test_exec_policy_passthrough_default_empty ( ) {
4628+ let policy = ExecPolicy :: default ( ) ;
4629+ assert ! ( policy. shell_env_passthrough. is_empty( ) ) ;
4630+ }
4631+
4632+ #[ test]
4633+ fn test_exec_policy_passthrough_deserializes ( ) {
4634+ let toml_str = r#"
4635+ mode = "full"
4636+ shell_env_passthrough = ["TZ", "GOG_ACCOUNT"]
4637+ "# ;
4638+ let policy: ExecPolicy = toml:: from_str ( toml_str) . unwrap ( ) ;
4639+ assert_eq ! ( policy. shell_env_passthrough, vec![ "TZ" , "GOG_ACCOUNT" ] ) ;
4640+ }
4641+
4642+ #[ test]
4643+ fn test_exec_policy_passthrough_alias_env_passthrough ( ) {
4644+ // Backwards-compat alias from the issue body (#1169).
4645+ let toml_str = r#"
4646+ mode = "full"
4647+ env_passthrough = ["TZ", "GOG_ACCOUNT"]
4648+ "# ;
4649+ let policy: ExecPolicy = toml:: from_str ( toml_str) . unwrap ( ) ;
4650+ assert_eq ! ( policy. shell_env_passthrough, vec![ "TZ" , "GOG_ACCOUNT" ] ) ;
4651+ }
4652+
4653+ #[ test]
4654+ fn test_exec_policy_passthrough_alias_env_allowlist ( ) {
4655+ // Backwards-compat alias from the issue body (#1169).
4656+ let toml_str = r#"
4657+ mode = "full"
4658+ env_allowlist = ["TZ", "HOME"]
4659+ "# ;
4660+ let policy: ExecPolicy = toml:: from_str ( toml_str) . unwrap ( ) ;
4661+ assert_eq ! ( policy. shell_env_passthrough, vec![ "TZ" , "HOME" ] ) ;
4662+ }
4663+
4664+ #[ test]
4665+ fn test_exec_policy_passthrough_wildcard ( ) {
4666+ let toml_str = r#"
4667+ mode = "full"
4668+ shell_env_passthrough = ["*"]
4669+ "# ;
4670+ let policy: ExecPolicy = toml:: from_str ( toml_str) . unwrap ( ) ;
4671+ assert_eq ! ( policy. shell_env_passthrough, vec![ "*" ] ) ;
4672+ }
46034673}
0 commit comments