orchestrator_config/config/
runner.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
5#[serde(rename_all = "snake_case")]
6pub enum RunnerPolicy {
7 Unsafe,
9 #[default]
11 Allowlist,
12}
13
14#[derive(Debug, Clone, Serialize, Deserialize, Default, PartialEq, Eq)]
16#[serde(rename_all = "snake_case")]
17pub enum RunnerExecutorKind {
18 #[default]
20 Shell,
21}
22
23#[derive(Debug, Clone, Serialize, Deserialize)]
25pub struct RunnerConfig {
26 pub shell: String,
28 #[serde(default = "default_shell_arg")]
30 pub shell_arg: String,
31 #[serde(default)]
33 pub policy: RunnerPolicy,
34 #[serde(default)]
36 pub executor: RunnerExecutorKind,
37 #[serde(default = "default_allowed_shells")]
39 pub allowed_shells: Vec<String>,
40 #[serde(default = "default_allowed_shell_args")]
42 pub allowed_shell_args: Vec<String>,
43 #[serde(default = "default_env_allowlist")]
45 pub env_allowlist: Vec<String>,
46 #[serde(default = "default_redaction_patterns")]
48 pub redaction_patterns: Vec<String>,
49}
50
51fn default_shell_arg() -> String {
52 "-lc".to_string()
53}
54
55fn default_allowed_shells() -> Vec<String> {
56 vec![
57 "/bin/bash".to_string(),
58 "/bin/zsh".to_string(),
59 "/bin/sh".to_string(),
60 ]
61}
62
63fn default_allowed_shell_args() -> Vec<String> {
64 vec!["-lc".to_string(), "-c".to_string()]
65}
66
67fn default_env_allowlist() -> Vec<String> {
68 vec![
69 "PATH".to_string(),
70 "HOME".to_string(),
71 "USER".to_string(),
72 "LANG".to_string(),
73 "TERM".to_string(),
74 ]
75}
76
77fn default_redaction_patterns() -> Vec<String> {
78 vec![
79 "token".to_string(),
80 "password".to_string(),
81 "secret".to_string(),
82 "api_key".to_string(),
83 "authorization".to_string(),
84 ]
85}
86
87impl Default for RunnerConfig {
88 fn default() -> Self {
89 Self {
90 shell: "/bin/bash".to_string(),
91 shell_arg: default_shell_arg(),
92 policy: RunnerPolicy::Allowlist,
93 executor: RunnerExecutorKind::Shell,
94 allowed_shells: default_allowed_shells(),
95 allowed_shell_args: default_allowed_shell_args(),
96 env_allowlist: default_env_allowlist(),
97 redaction_patterns: default_redaction_patterns(),
98 }
99 }
100}
101
102#[derive(Debug, Clone, Default, Serialize, Deserialize)]
104pub struct ResumeConfig {
105 #[serde(default)]
107 pub auto: bool,
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn test_runner_config_default() {
116 let cfg = RunnerConfig::default();
117 assert_eq!(cfg.shell, "/bin/bash");
118 assert_eq!(cfg.shell_arg, "-lc");
119 assert_eq!(cfg.policy, RunnerPolicy::Allowlist);
120 assert_eq!(cfg.executor, RunnerExecutorKind::Shell);
121 assert_eq!(cfg.allowed_shells.len(), 3);
122 assert!(cfg.allowed_shells.contains(&"/bin/bash".to_string()));
123 assert_eq!(cfg.allowed_shell_args, vec!["-lc", "-c"]);
124 assert!(cfg.env_allowlist.contains(&"PATH".to_string()));
125 assert!(cfg.env_allowlist.contains(&"HOME".to_string()));
126 assert!(cfg.redaction_patterns.contains(&"token".to_string()));
127 assert!(cfg.redaction_patterns.contains(&"secret".to_string()));
128 }
129
130 #[test]
131 fn test_runner_policy_default() {
132 let policy = RunnerPolicy::default();
133 assert_eq!(policy, RunnerPolicy::Allowlist);
134 }
135
136 #[test]
137 fn test_runner_executor_kind_default() {
138 let kind = RunnerExecutorKind::default();
139 assert_eq!(kind, RunnerExecutorKind::Shell);
140 }
141
142 #[test]
143 fn test_runner_config_serde_round_trip() {
144 let cfg = RunnerConfig::default();
145 let json = serde_json::to_string(&cfg).expect("serialize runner config");
146 let cfg2: RunnerConfig = serde_json::from_str(&json).expect("deserialize runner config");
147 assert_eq!(cfg2.shell, cfg.shell);
148 assert_eq!(cfg2.policy, cfg.policy);
149 }
150
151 #[test]
152 fn test_runner_config_deserialize_minimal() {
153 let json = r#"{"shell": "/bin/sh"}"#;
154 let cfg: RunnerConfig = serde_json::from_str(json).expect("deserialize minimal runner");
155 assert_eq!(cfg.shell, "/bin/sh");
156 assert_eq!(cfg.shell_arg, "-lc");
158 assert_eq!(cfg.policy, RunnerPolicy::Allowlist);
159 assert!(!cfg.allowed_shells.is_empty());
160 }
161
162 #[test]
163 fn test_unsafe_serializes_as_unsafe() {
164 let cfg = RunnerConfig {
165 policy: RunnerPolicy::Unsafe,
166 ..RunnerConfig::default()
167 };
168 let json = serde_json::to_string(&cfg).expect("serialize unsafe runner");
169 assert!(json.contains(r#""policy":"unsafe""#));
170 }
171}