1use crate::util::runtime_error;
2use crate::RhaiResult;
3use std::collections::HashSet;
4
5#[derive(Clone, Debug)]
6pub struct Config {
7 pub(crate) command_policy: ListPolicy,
8 pub(crate) env_policy: ListPolicy,
9 pub(crate) default_timeout_ms: Option<u64>,
10}
11
12impl Default for Config {
13 fn default() -> Self {
14 Self {
15 command_policy: ListPolicy::Unrestricted,
16 env_policy: ListPolicy::Unrestricted,
17 default_timeout_ms: None,
18 }
19 }
20}
21
22impl Config {
23 pub fn allow_commands<I, S>(mut self, commands: I) -> Self
24 where
25 I: IntoIterator<Item = S>,
26 S: Into<String>,
27 {
28 self.command_policy
29 .insert_allow(commands.into_iter().map(Into::into));
30 self
31 }
32
33 pub fn deny_commands<I, S>(mut self, commands: I) -> Self
34 where
35 I: IntoIterator<Item = S>,
36 S: Into<String>,
37 {
38 self.command_policy
39 .insert_deny(commands.into_iter().map(Into::into));
40 self
41 }
42
43 pub fn allow_env_vars<I, S>(mut self, keys: I) -> Self
44 where
45 I: IntoIterator<Item = S>,
46 S: Into<String>,
47 {
48 self.env_policy
49 .insert_allow(keys.into_iter().map(Into::into));
50 self
51 }
52
53 pub fn deny_env_vars<I, S>(mut self, keys: I) -> Self
54 where
55 I: IntoIterator<Item = S>,
56 S: Into<String>,
57 {
58 self.env_policy
59 .insert_deny(keys.into_iter().map(Into::into));
60 self
61 }
62
63 pub fn default_timeout_ms(mut self, timeout: u64) -> Self {
64 if timeout == 0 {
65 panic!("default_timeout_ms must be greater than zero");
66 }
67 self.default_timeout_ms = Some(timeout);
68 self
69 }
70
71 pub(crate) fn ensure_command_allowed(&self, name: &str) -> RhaiResult<()> {
72 if self.command_policy.is_allowed(name) {
73 Ok(())
74 } else {
75 Err(runtime_error(format!("command '{name}' is not permitted")))
76 }
77 }
78
79 pub(crate) fn ensure_env_allowed(&self, key: &str) -> RhaiResult<()> {
80 if self.env_policy.is_allowed(key) {
81 Ok(())
82 } else {
83 Err(runtime_error(format!(
84 "environment variable '{key}' is not permitted"
85 )))
86 }
87 }
88}
89
90#[derive(Clone, Debug)]
91pub(crate) enum ListPolicy {
92 Unrestricted,
93 Allow(HashSet<String>),
94 Deny(HashSet<String>),
95}
96
97impl ListPolicy {
98 fn insert_allow<I>(&mut self, values: I)
99 where
100 I: IntoIterator<Item = String>,
101 {
102 match self {
103 ListPolicy::Unrestricted => {
104 let mut set = HashSet::new();
105 set.extend(values);
106 *self = ListPolicy::Allow(set);
107 }
108 ListPolicy::Allow(existing) => existing.extend(values),
109 ListPolicy::Deny(_) => {
110 panic!("deny list already specified; allow list cannot be combined")
111 }
112 }
113 }
114
115 fn insert_deny<I>(&mut self, values: I)
116 where
117 I: IntoIterator<Item = String>,
118 {
119 match self {
120 ListPolicy::Unrestricted => {
121 let mut set = HashSet::new();
122 set.extend(values);
123 *self = ListPolicy::Deny(set);
124 }
125 ListPolicy::Deny(existing) => existing.extend(values),
126 ListPolicy::Allow(_) => {
127 panic!("allow list already specified; deny list cannot be combined")
128 }
129 }
130 }
131
132 fn is_allowed(&self, value: &str) -> bool {
133 match self {
134 ListPolicy::Unrestricted => true,
135 ListPolicy::Allow(list) => list.contains(value),
136 ListPolicy::Deny(list) => !list.contains(value),
137 }
138 }
139}