oxicode_sdk/security/
exec_policy.rs1use serde::{Deserialize, Serialize};
6use std::collections::HashSet;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
10#[serde(rename_all = "snake_case")]
11pub enum AllowlistMode {
12 #[default]
14 Permissive,
15 Enforced,
17}
18
19#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct ExecPolicy {
22 #[serde(default)]
24 pub allowlist_mode: AllowlistMode,
25 #[serde(default)]
27 pub allowed_commands: HashSet<String>,
28 #[serde(default)]
30 pub default_safe_commands: HashSet<String>,
31}
32
33impl ExecPolicy {
34 pub fn permissive() -> Self {
36 Self {
37 allowlist_mode: AllowlistMode::Permissive,
38 allowed_commands: HashSet::new(),
39 default_safe_commands: Self::safe_defaults(),
40 }
41 }
42
43 pub fn enforced(commands: Vec<&str>) -> Self {
45 Self {
46 allowlist_mode: AllowlistMode::Enforced,
47 allowed_commands: commands.into_iter().map(String::from).collect(),
48 default_safe_commands: Self::safe_defaults(),
49 }
50 }
51
52 pub fn is_binary_allowed(&self, binary: &str) -> bool {
54 match self.allowlist_mode {
55 AllowlistMode::Permissive => true,
56 AllowlistMode::Enforced => {
57 self.allowed_commands.contains(binary)
58 || self.default_safe_commands.contains(binary)
59 }
60 }
61 }
62
63 fn safe_defaults() -> HashSet<String> {
64 [
65 "git", "grep", "find", "cat", "ls", "head", "tail", "wc", "sort", "uniq",
66 ]
67 .iter()
68 .map(|s| s.to_string())
69 .collect()
70 }
71}
72
73impl Default for ExecPolicy {
74 fn default() -> Self {
75 Self::permissive()
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::*;
82
83 #[test]
84 fn permissive_allows_all() {
85 let policy = ExecPolicy::permissive();
86 assert!(policy.is_binary_allowed("rm"));
87 assert!(policy.is_binary_allowed("anything"));
88 }
89
90 #[test]
91 fn enforced_allows_listed() {
92 let policy = ExecPolicy::enforced(vec!["echo", "git"]);
93 assert!(policy.is_binary_allowed("echo"));
94 assert!(policy.is_binary_allowed("git"));
95 assert!(!policy.is_binary_allowed("rm"));
96 }
97
98 #[test]
99 fn enforced_safe_defaults() {
100 let policy = ExecPolicy::enforced(vec![]);
101 assert!(policy.is_binary_allowed("git"));
103 assert!(policy.is_binary_allowed("grep"));
104 assert!(policy.is_binary_allowed("cat"));
105 }
106}