Repository: memorysafety/sudo-rs
Pull request #166 (2023-03-28 21:56:37)

Add explicit support for unset text items in "Defaults"
This replaces `""` with `None` in the `sudo-defaults` crate.


Patch:
diff --git a/lib/sudo-defaults/src/lib.rs b/lib/sudo-defaults/src/lib.rs
index 2b10828a..069e4f76 100644
--- a/lib/sudo-defaults/src/lib.rs
+++ b/lib/sudo-defaults/src/lib.rs
@@ -4,7 +4,7 @@
 pub enum SudoDefault {
     Flag(bool),
     Integer(OptTuple<i128>, fn(&str) -> Option<i128>),
-    Text(OptTuple<&'static str>),
+    Text(OptTuple<Option<&'static str>>),
     List(&'static [&'static str]),
     Enum(OptTuple<StrEnum<'static>>),
 }
@@ -34,8 +34,8 @@ defaults! {
     umask                     = 0o22 (!= 0o777)    [0..=0o777; radix: 8]
 
     editor                    = "/usr/bin/editor"
-    lecture_file              = ""
-    secure_path               = "" (!= "")
+    lecture_file              = None
+    secure_path               = None (!= None)
     verifypw                  = "all" (!= "never") [all, always, any, never]
 
     env_keep                  = ["COLORS", "DISPLAY", "HOSTNAME", "KRB5CCNAME", "LS_COLORS", "PATH",
@@ -83,9 +83,9 @@ mod test {
         test! { visiblepw => Flag(false) };
         test! { passwd_tries => Integer(OptTuple { default: 3, negated: None }, _) };
         test! { umask => Integer(OptTuple { default: 18, negated: Some(511) }, _) };
-        test! { editor => Text(OptTuple { default: "/usr/bin/editor", negated: None }) };
+        test! { editor => Text(OptTuple { default: Some("/usr/bin/editor"), negated: None }) };
         test! { lecture_file => Text(_) };
-        test! { secure_path => Text(OptTuple { default: "", negated: Some("") }) };
+        test! { secure_path => Text(OptTuple { default: None, negated: Some(None) }) };
         test! { env_keep => List(_) };
         test! { env_check => List(["COLORTERM", "LANG", "LANGUAGE", "LC_*", "LINGUAS", "TERM", "TZ"]) };
         test! { env_delete => List(_) };
diff --git a/lib/sudo-defaults/src/settings_dsl.rs b/lib/sudo-defaults/src/settings_dsl.rs
index fb50e19c..d68a09f8 100644
--- a/lib/sudo-defaults/src/settings_dsl.rs
+++ b/lib/sudo-defaults/src/settings_dsl.rs
@@ -2,7 +2,7 @@ macro_rules! add_from {
     ($ctor:ident, $type:ty) => {
         impl From<$type> for $crate::SudoDefault {
             fn from(value: $type) -> Self {
-                $crate::SudoDefault::$ctor(value)
+                $crate::SudoDefault::$ctor(value.into())
             }
         }
     };
@@ -11,7 +11,7 @@ macro_rules! add_from {
         impl From<$type> for $crate::SudoDefault {
             fn from(value: $type) -> Self {
                 $crate::SudoDefault::$ctor(OptTuple {
-                    default: value,
+                    default: value.into(),
                     negated: None,
                 }$(, $vetting_function)?)
             }
@@ -20,8 +20,8 @@ macro_rules! add_from {
         impl From<($type, $type)> for $crate::SudoDefault {
             fn from((value, neg): ($type, $type)) -> Self {
                 $crate::SudoDefault::$ctor(OptTuple {
-                    default: value,
-                    negated: Some(neg),
+                    default: value.into(),
+                    negated: Some(neg.into()),
                 }$(, $vetting_function)?)
             }
         }
@@ -70,6 +70,7 @@ macro_rules! defaults {
             add_from!(Flag, bool);
             add_from!(Integer, i128, negatable, |text| i128::from_str_radix(text, 10).ok());
             add_from!(Text, &'static str, negatable);
+            add_from!(Text, Option<&'static str>, negatable);
             add_from!(List, &'static [&'static str]);
             add_from!(Enum, StrEnum<'static>, negatable);
 
diff --git a/lib/sudoers/src/ast.rs b/lib/sudoers/src/ast.rs
index afc7ea14..d3aba146 100644
--- a/lib/sudoers/src/ast.rs
+++ b/lib/sudoers/src/ast.rs
@@ -84,7 +84,7 @@ pub type TextEnum = sudo_defaults::StrEnum<'static>;
 #[derive(Debug)]
 pub enum ConfigValue {
     Flag(bool),
-    Text(String),
+    Text(Option<Box<str>>),
     Num(i128),
     List(Mode, Vec<String>),
     Enum(TextEnum),
@@ -582,7 +582,7 @@ fn get_directive(
                 Some(Setting::List(_)) => ConfigValue::List(Mode::Set, vec![]),
                 Some(Setting::Text(OptTuple {
                     negated: Some(val), ..
-                })) => ConfigValue::Text(val.to_string()),
+                })) => ConfigValue::Text(val.map(|x| x.into())),
                 Some(Setting::Enum(OptTuple {
                     negated: Some(val), ..
                 })) => ConfigValue::Enum(val),
@@ -633,7 +633,10 @@ fn get_directive(
                     }
                     Setting::Text(_) => {
                         let text = text_item(stream)?;
-                        make(Defaults(name, ConfigValue::Text(text)))
+                        make(Defaults(
+                            name,
+                            ConfigValue::Text(Some(text.into_boxed_str())),
+                        ))
                     }
                     Setting::Enum(sudo_defaults::OptTuple { default: key, .. }) => {
                         let text = text_item(stream)?;
diff --git a/lib/sudoers/src/lib.rs b/lib/sudoers/src/lib.rs
index e47768f0..b0365801 100644
--- a/lib/sudoers/src/lib.rs
+++ b/lib/sudoers/src/lib.rs
@@ -263,7 +263,7 @@ fn match_identifier(user: &impl UnixUser, ident: &ast::Identifier) -> bool {
 #[derive(Debug, Clone)]
 pub struct Settings {
     pub flags: HashSet<String>,
-    pub str_value: HashMap<String, String>,
+    pub str_value: HashMap<String, Option<Box<str>>>,
     pub enum_value: HashMap<String, TextEnum>,
     pub int_value: HashMap<String, i128>,
     pub list: HashMap<String, HashSet<String>>,
@@ -288,7 +288,8 @@ impl Default for Settings {
                     }
                 }
                 SudoDefault::Text(OptTuple { default, .. }) => {
-                    this.str_value.insert(key.to_string(), default.to_string());
+                    this.str_value
+                        .insert(key.to_string(), default.map(|x| x.into()));
                 }
                 SudoDefault::Enum(OptTuple { default, .. }) => {
                     this.enum_value.insert(key.to_string(), default);
diff --git a/lib/sudoers/src/policy.rs b/lib/sudoers/src/policy.rs
index 6224934c..b854ca11 100644
--- a/lib/sudoers/src/policy.rs
+++ b/lib/sudoers/src/policy.rs
@@ -61,10 +61,6 @@ impl PreJudgementPolicy for Sudoers {
             .str_value
             .get("secure_path")
             .expect("secure_path missing from settings");
-        if path.is_empty() {
-            None
-        } else {
-            Some(path)
-        }
+        path.as_deref()
     }
 }


Test patch:
diff --git a/lib/sudoers/src/test/mod.rs b/lib/sudoers/src/test/mod.rs
index 00f68aac..fb4d62ef 100644
--- a/lib/sudoers/src/test/mod.rs
+++ b/lib/sudoers/src/test/mod.rs
@@ -284,7 +284,7 @@ fn default_bool_test() {
     assert!(settings.flags.contains("env_reset"));
     assert!(!settings.flags.contains("use_pty"));
     assert!(settings.list["env_keep"].is_empty());
-    assert_eq!(settings.str_value["secure_path"], "");
+    assert_eq!(settings.str_value["secure_path"], None);
     assert_eq!(settings.int_value["umask"], 0o777);
 }
 
@@ -312,8 +312,11 @@ fn default_set_test() {
             .map(|x| x.to_string())
             .collect()
     );
-    assert_eq!(settings.str_value["lecture_file"], "/etc/sudoers");
-    assert_eq!(settings.str_value["secure_path"], "/etc");
+    assert_eq!(
+        settings.str_value["lecture_file"].as_deref(),
+        Some("/etc/sudoers")
+    );
+    assert_eq!(settings.str_value["secure_path"].as_deref(), Some("/etc"));
     assert_eq!(settings.int_value["umask"], 0o123);
     assert_eq!(settings.int_value["passwd_tries"], 5);