sysknife_daemon/actions/
apparmor.rs1use super::{command_mechanism, ActionSpec};
18use sysknife_types::RiskLevel;
19
20pub fn specs() -> Vec<ActionSpec> {
26 vec![
27 apparmor_status(),
28 apparmor_enforce("/etc/apparmor.d/usr.bin.firefox"),
29 apparmor_complain("/etc/apparmor.d/usr.bin.firefox"),
30 ]
31}
32
33pub fn apparmor_status() -> ActionSpec {
41 ActionSpec {
42 action_name: "AppArmorStatus",
43 mechanism: command_mechanism("sudo", ["aa-status"]),
44 risk_level: RiskLevel::Low,
45 reboot_required: false,
46 rollback_available: false,
47 }
48}
49
50pub fn apparmor_enforce(profile_path: &str) -> ActionSpec {
56 ActionSpec {
57 action_name: "AppArmorEnforce",
58 mechanism: command_mechanism("sudo", ["aa-enforce", profile_path]),
59 risk_level: RiskLevel::High,
60 reboot_required: false,
61 rollback_available: false,
62 }
63}
64
65pub fn apparmor_complain(profile_path: &str) -> ActionSpec {
75 ActionSpec {
76 action_name: "AppArmorComplain",
77 mechanism: command_mechanism("sudo", ["aa-complain", profile_path]),
78 risk_level: RiskLevel::High,
79 reboot_required: false,
80 rollback_available: false,
81 }
82}
83
84#[cfg(test)]
89mod tests {
90 use super::*;
91 use crate::actions::ActionMechanism;
92
93 fn extract_args(spec: &ActionSpec) -> (&'static str, Vec<String>) {
94 match &spec.mechanism {
95 ActionMechanism::Command { program, args } => (*program, args.clone()),
96 _ => panic!("expected Command mechanism"),
97 }
98 }
99
100 #[test]
103 fn apparmor_status_action_name() {
104 assert_eq!(apparmor_status().action_name, "AppArmorStatus");
105 }
106
107 #[test]
108 fn apparmor_status_risk_is_low() {
109 assert_eq!(apparmor_status().risk_level, RiskLevel::Low);
110 }
111
112 #[test]
113 fn apparmor_status_argv() {
114 let spec = apparmor_status();
115 let (prog, args) = extract_args(&spec);
116 assert_eq!(prog, "sudo");
117 let a: Vec<&str> = args.iter().map(String::as_str).collect();
118 assert_eq!(a, vec!["aa-status"]);
119 }
120
121 #[test]
124 fn apparmor_enforce_action_name() {
125 assert_eq!(
126 apparmor_enforce("/etc/apparmor.d/usr.bin.firefox").action_name,
127 "AppArmorEnforce"
128 );
129 }
130
131 #[test]
132 fn apparmor_enforce_risk_is_high() {
133 assert_eq!(
134 apparmor_enforce("/etc/apparmor.d/usr.bin.firefox").risk_level,
135 RiskLevel::High
136 );
137 }
138
139 #[test]
140 fn apparmor_enforce_argv() {
141 let profile = "/etc/apparmor.d/usr.bin.firefox";
142 let spec = apparmor_enforce(profile);
143 let (prog, args) = extract_args(&spec);
144 assert_eq!(prog, "sudo");
145 let a: Vec<&str> = args.iter().map(String::as_str).collect();
146 assert_eq!(a[0], "aa-enforce");
147 assert_eq!(a[1], profile);
148 }
149
150 #[test]
153 fn apparmor_complain_action_name() {
154 assert_eq!(
155 apparmor_complain("/etc/apparmor.d/usr.bin.firefox").action_name,
156 "AppArmorComplain"
157 );
158 }
159
160 #[test]
161 fn apparmor_complain_risk_is_high() {
162 assert_eq!(
165 apparmor_complain("/etc/apparmor.d/usr.bin.firefox").risk_level,
166 RiskLevel::High
167 );
168 }
169
170 #[test]
171 fn apparmor_complain_argv() {
172 let profile = "/etc/apparmor.d/usr.bin.nginx";
173 let spec = apparmor_complain(profile);
174 let (prog, args) = extract_args(&spec);
175 assert_eq!(prog, "sudo");
176 let a: Vec<&str> = args.iter().map(String::as_str).collect();
177 assert_eq!(a[0], "aa-complain");
178 assert_eq!(a[1], profile);
179 }
180
181 #[test]
182 fn enforce_uses_aa_enforce_not_aa_complain() {
183 let spec = apparmor_enforce("/etc/apparmor.d/test");
184 let (_, args) = extract_args(&spec);
185 let a: Vec<&str> = args.iter().map(String::as_str).collect();
186 assert!(a.contains(&"aa-enforce"));
187 assert!(!a.contains(&"aa-complain"));
188 }
189
190 #[test]
191 fn complain_uses_aa_complain_not_aa_enforce() {
192 let spec = apparmor_complain("/etc/apparmor.d/test");
193 let (_, args) = extract_args(&spec);
194 let a: Vec<&str> = args.iter().map(String::as_str).collect();
195 assert!(a.contains(&"aa-complain"));
196 assert!(!a.contains(&"aa-enforce"));
197 }
198
199 #[test]
202 fn specs_covers_all_action_names() {
203 let expected = ["AppArmorStatus", "AppArmorEnforce", "AppArmorComplain"];
204 let spec_names: Vec<&str> = specs().iter().map(|s| s.action_name).collect();
205 for name in &expected {
206 assert!(spec_names.contains(name), "specs() missing {name}");
207 }
208 }
209}