diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 72fe9f0f..f69055a0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -27,16 +27,16 @@ jobs: account required pam_permit.so session [success=1 default=ignore] pam_permit.so session requisite pam_permit.so - session required pam_permit.so" | tee /etc/pam.d/sr' + session required pam_permit.so" | tee /etc/pam.d/dosr' - name: Install RootAsRole run: cargo xtask install -bip sudo - name: print config - run: sr cat /etc/security/rootasrole.json + run: dosr cat /etc/security/rootasrole.json - name: getenv run: env - - name: Run Sr + - name: Run dosr env: RUST_LOG: debug - run: /usr/bin/sr -h - - name: Run Chsr with sr - run: sr /usr/bin/chsr -h + run: /usr/bin/dosr -h + - name: Run Chsr with dosr + run: dosr /usr/bin/chsr -h diff --git a/.vscode/launch.json b/.vscode/launch.json index a3f110d6..40c2e5c8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,7 +8,7 @@ "type": "lldb", "request": "launch", "name": "Launch", - "program": "${workspaceFolder}/target/debug/sr", + "program": "${workspaceFolder}/target/debug/dosr", "args": ["ls"], "cwd": "${workspaceFolder}" } diff --git a/.vscode/tasks.json b/.vscode/tasks.json index c1707c71..db39d329 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -14,7 +14,7 @@ "args": [ "/usr/bin/setcap", "=p", - "${cwd}/bin/sr" + "${cwd}/bin/dosr" ], "options": { "cwd": "${cwd}" diff --git a/Cargo.toml b/Cargo.toml index 015acb8a..21b6472e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,11 +4,11 @@ members = ["xtask", "rar-common"] [package] name = "rootasrole" # The project version is managed on json file in resources/rootasrole.json -version = "3.1.1" +version = "3.1.3" rust-version = "1.76.0" authors = ["Eddie Billoir "] edition = "2021" -default-run = "sr" +default-run = "dosr" description = "An alternative to sudo that uses Linux capabilities and Role based access control." license = "LGPL-3.0-or-later" repository = "https://github.com/LeChatP/RootAsRole" @@ -41,7 +41,7 @@ debug = true #srlibs = [ "pam-client", "bitflags" ] [[bin]] -name = "sr" +name = "dosr" path = "src/sr/main.rs" required-features = ["finder"] @@ -64,7 +64,7 @@ serde_json = "1.0" toml = "0.8" [dependencies] -rar-common = { path = "rar-common", version = "3.1.0", package = "rootasrole-core" } +rar-common = { path = "rar-common", version = "3.1.3", package = "rootasrole-core" } log = "0.4" libc = "0.2" strum = { version = "0.26", features = ["derive"] } @@ -76,8 +76,7 @@ serde = { version = "1.0", features=["rc", "derive"] } serde_json = "1.0" cbor4ii = { version = "1.0.0", features = ["serde", "serde1", "use_std"] } glob = "0.3" -pam-client2 = "0.5" -bitflags = { version = "2.6" } +bitflags = { version = "2.9" } shell-words = "1.1" linked_hash_set = { version = "0.1" } derivative = "2.2" @@ -89,9 +88,11 @@ pest = "2.7" pest_derive = "2.7" const_format = "0.2" hex = "0.4" -bon = "3.5.1" -serde_json_borrow = "0.7.1" -konst = "0.3.16" +bon = "3" +serde_json_borrow = "0.8" +konst = "0.3" +nonstick = "0.1.1" +libpam-sys = "0.2.0" [dev-dependencies] log = "0.4" @@ -121,7 +122,7 @@ changelog = "target/debian/changelog" [package.metadata.generate-rpm] assets = [ - { source = "target/release/sr", dest = "/usr/bin/sr", user = "root", group = "root", mode = "0555", caps = "=p" }, + { source = "target/release/dosr", dest = "/usr/bin/dosr", user = "root", group = "root", mode = "0555", caps = "=p" }, { source = "target/release/chsr", dest = "/usr/bin/chsr", user = "root", group = "root", mode = "0555" }, { source = "resources/rh/rh_sr_pam.conf", dest = "/etc/pam.d/sr", user = "root", group = "root", mode = "0644", config = true }, { source = "resources/rootasrole.json", dest = "/etc/security/rootasrole.json", user = "root", group = "root", mode = "0644", config = true }, diff --git a/README.md b/README.md index b026c00e..71a5d047 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ -# RootAsRole (V3.1.1) — A better alternative to `sudo(-rs)`/`su` • ⚡ Blazing fast • 🛡️ Memory-safe • 🔐 Security-oriented +# RootAsRole (V3.1.3) — A better alternative to `sudo(-rs RootAsRole is a Linux/Unix privilege delegation tool based on **Role-Based Access Control (RBAC)**. It empowers administrators to assign precise privileges — not full root — to users and commands. @@ -44,7 +44,7 @@ RootAsRole solves this: ## 📊 Why It’s Better Than Others -| Feature | setcap?? | doas | sudo | sudo-rs | sr (RootAsRole) | +| Feature | setcap?? | doas | sudo | sudo-rs | dosr (RootAsRole) | |------------------------------------------|-------------------|------------|--------------------------------|--------------------------------|----------------------------------------------| | **Change user/groups** | N/A | ✅ | ✅ | ✅ | ✅✅ mandatory or optional | | **Environment variables** | N/A | partial | ✅ | partial | ✅ | @@ -97,7 +97,7 @@ RootAsRole solves this:
 Execute privileged commands with a role-based access control system
 
-Usage: sr [OPTIONS] [COMMAND]...
+Usage: dosr [OPTIONS] [COMMAND]...
 
 Arguments:
   [COMMAND]...  Command to execute
@@ -116,7 +116,8 @@ Execute privileged commands with a role-based access control system
 
 If you're accustomed to utilizing the sudo tool and find it difficult to break that habit, consider creating an alias : 
 ```sh
-alias sudo="sr"
+alias sudo="dosr"
+alias sr="dosr"
 ```
 
 ## 🏎️ Performance
@@ -132,7 +133,7 @@ RootAsRole **3.1.0** introduced **CBOR** support, significantly boosting perform
 
 ### Why Performance Matters
 
-When using **Ansible** (or any automation tool), every task that uses `become: true` will invoke `sr` on the target host.
+When using **Ansible** (or any automation tool), every task that uses `become: true` will invoke `dosr` on the target host.
 With **RootAsRole (RaR)**, each role and task introduces additional access control logic --- this doesn’t slow you down.
 
 💡 **Here’s the reality**: You can reach the performance of **1 `sudo` rule** with **~4000 RaR rules**.
diff --git a/book/src/README.md b/book/src/README.md
index ee0c29a2..1def54fc 100644
--- a/book/src/README.md
+++ b/book/src/README.md
@@ -17,13 +17,13 @@
 
 ## Usage
 
-The main command line tool is `sr`. It allows you to execute a command by simply typing:
+The main command line tool is `dosr`. It allows you to execute a command by simply typing:
   
 ```bash
-sr 
+dosr 
 ```
 
-You can find more information about this command in the [sr](sr/README.md) section.
+You can find more information about this command in the [dosr](sr/README.md) section.
 
 The `chsr` command allows you to configure the roles and capabilities of the system. You can find more information about this command in the [Configure RootAsRole](chsr/README.md) section.
 
@@ -51,7 +51,7 @@ By using a role-based access control model, this project allows us to better man
 You are using your personal computer and you want to install a new package. By default, RootAsRole add one role with 2 tasks : one task for using `chsr` command that grant only the `CAP_LINUX_IMMUTABLE` capability as `root` user (unprivileged), and one task for all commands but without `CAP_LINUX_IMMUTABLE` privilege. As installing a package may require almost all capabilities, you can use the default role to install a package. Indeed, if you wish to install apache2, you'll need `CAP_NET_BIND_SERVICE`, if you install docker you'll need many privileges, virtualbox needs `CAP_SYS_MODULE`, etc. So, you can use the default role to install a package:
 
 ```bash
-sr apt install 
+dosr apt install 
 ```
 
 ### Scenario 2: Granting users the right to restart their system
@@ -59,17 +59,17 @@ sr apt install 
 You are the system administrator of a company and you want to delegate the right to restart the server to a user. You can use `chsr` to create a role and grant the right to restart the server to users.
 
 ```bash
-sr chsr role r_users add # Create a new role
-sr chsr role r_users grant -g users # Grant the role to the group users
-sr chsr role r_users task t_reboot add # Create a new task
-sr chsr role r_users task t_reboot cmd whitelist add reboot # Add the reboot command to the task
-sr chsr role r_users task t_reboot cred caps whitelist add CAP_SYS_BOOT # Add the CAP_SYS_BOOT capability to the task
+dosr chsr role r_users add # Create a new role
+dosr chsr role r_users grant -g users # Grant the role to the group users
+dosr chsr role r_users task t_reboot add # Create a new task
+dosr chsr role r_users task t_reboot cmd whitelist add reboot # Add the reboot command to the task
+dosr chsr role r_users task t_reboot cred caps whitelist add CAP_SYS_BOOT # Add the CAP_SYS_BOOT capability to the task
 ```
 
 Then users can restart the server with the following command:
 
 ```bash
-sr reboot
+dosr reboot
 ```
 
 ### Scenario 3 : Passing environment variables to a command
@@ -77,17 +77,17 @@ sr reboot
 You are a developer and you want to pass environment variables to a command. For example with sudo you can use the `-E` option to pass environment variables to a command. With RootAsRole, you'll need to setup a role with a task that allows the command to use environment variables. However, as you keep the default configuration, you'll have two roles that matches ANY commands, and if the first one is more restrictive than the second one, you'll need to specify the role to use. Here is an example:
   
 ```bash
-sr chsr role env add # Create a new role
-sr chsr role env task env add # Create a new task
-sr chsr role env task env cmd setpolicy allow-all # Add all command to the task
-sr chsr role env task env cred caps setpolicy allow-all # Add all capabilities to the task
-sr chsr role env task env o env setpolicy keep-all # Keep the environment variables
+dosr chsr role env add # Create a new role
+dosr chsr role env task env add # Create a new task
+dosr chsr role env task env cmd setpolicy allow-all # Add all command to the task
+dosr chsr role env task env cred caps setpolicy allow-all # Add all capabilities to the task
+dosr chsr role env task env o env setpolicy keep-all # Keep the environment variables
 ```
 
 Then you can use the following command to pass environment variables to a command:
 
 ```bash
-sr -r env [command]
+dosr -r env [command]
 ```
 
 This is because the default role do not keep the environment variables, so if you want to keep environment variables you need to specify the role to use.
@@ -97,26 +97,26 @@ This is because the default role do not keep the environment variables, so if yo
 You are an administrator that want to automatically reboot the system at 04:05 every day with cron for example. You can disable authentication by setting skip-auth in the options. Here is an example:
 
 ```bash
-sr chsr role auto add # Create a new role
-sr chsr role grant -u cron # Grant the role to the user cron
-sr chsr role auto task cron_reboot add # Create a new task
-sr chsr role auto task cron_reboot cmd whitelist add reboot # Add the reboot command to the task
-sr chsr role auto task cron_reboot cred caps whitelist add CAP_SYS_BOOT # Add the CAP_SYS_BOOT capability to the task
-sr chsr role auto task cron_reboot o authentication skip # Skip authentication
+dosr chsr role auto add # Create a new role
+dosr chsr role grant -u cron # Grant the role to the user cron
+dosr chsr role auto task cron_reboot add # Create a new task
+dosr chsr role auto task cron_reboot cmd whitelist add reboot # Add the reboot command to the task
+dosr chsr role auto task cron_reboot cred caps whitelist add CAP_SYS_BOOT # Add the CAP_SYS_BOOT capability to the task
+dosr chsr role auto task cron_reboot o authentication skip # Skip authentication
 ```
 
 Then you can configure the cron to reboot the system with the following command:
 
 ```bash
-sr crontab -u cron -e
+dosr crontab -u cron -e
 ```
 
 and add the following line to reboot the system at 04:05 every day
 
 ```cron
-5 4 * * * sr -r auto -t cron_reboot reboot
+5 4 * * * dosr -r auto -t cron_reboot reboot
 ```
 
-Note: You should consider to set the `-r auto -t cron_reboot` options to the `sr` command when you automate a task to avoid any security issue or future conflict.
+Note: You should consider to set the `-r auto -t cron_reboot` options to the `dosr` command when you automate a task to avoid any security issue or future conflict.
 
 For a more complete example, you can checkout the [Is a Linux system without root user possible ?](knowledge/no-root.md) section.
\ No newline at end of file
diff --git a/book/src/SUMMARY.md b/book/src/SUMMARY.md
index 9916d87b..3a7732e9 100644
--- a/book/src/SUMMARY.md
+++ b/book/src/SUMMARY.md
@@ -6,7 +6,7 @@
 # User Guide
 
 - [Installation](guide/installation.md)
-- [`sr` Command Line Tool](sr/README.md)
+- [`dosr` Command Line Tool](dosr/README.md)
 - [`chsr` Command Line Tool](chsr/README.md)
 - [`capable` Command Line Tool](capable/README.md)
 
diff --git a/book/src/chsr/file-config.md b/book/src/chsr/file-config.md
index b2f1860c..f9b32c3f 100644
--- a/book/src/chsr/file-config.md
+++ b/book/src/chsr/file-config.md
@@ -93,9 +93,9 @@ The following example shows a RootAsRole config without plugins when almost ever
           "cred": {
             "setuid": {
               "fallback": "thefallbackuser", // Fallback user if the -u option is not set
-              "default": "none", // The sr user cannot use -u option in general
-              "add": ["theuser"], // the sr user can use "-u theuser" option
-              "sub": ["anotheruser"] // the sr user cannot use "-u anotheruser" option (overrides add, applies only if default is all)
+              "default": "none", // The dosr user cannot use -u option in general
+              "add": ["theuser"], // the dosr user can use "-u theuser" option
+              "sub": ["anotheruser"] // the dosr user cannot use "-u anotheruser" option (overrides add, applies only if default is all)
             }, // User to setuid before executing the command
             "setgid": [ // Groups to setgid before executing the command, The first one is the primary group
               "group1",
diff --git a/book/src/sr/README.md b/book/src/dosr/README.md
similarity index 66%
rename from book/src/sr/README.md
rename to book/src/dosr/README.md
index 33fedd3a..6b5314c7 100644
--- a/book/src/sr/README.md
+++ b/book/src/dosr/README.md
@@ -1,11 +1,11 @@
-# What is sr tool
+# What is dosr tool
 
-`sr` is the abbrevation of "switch role" is a command line tool like sudo. It allows a permitted user to execute a command as another user and groups. More than sudo it allows to a permitted user to obtain some privileges. The sr command is used to switch to a role.
+`dosr` is the abbrevation of "do switch role" is a command line tool like sudo. It allows a permitted user to execute a command as another user and groups. More than sudo it allows to a permitted user to obtain some privileges. The sr command is used to switch to a role.
 
 # Usage
 
 
-Usage: sr [OPTIONS] [COMMAND]...
+Usage: dosr [OPTIONS] [COMMAND]...
 
 Arguments:
   [COMMAND]...  Command to execute
diff --git a/book/src/guide/installation.md b/book/src/guide/installation.md
index 7f914906..8b2ba93b 100644
--- a/book/src/guide/installation.md
+++ b/book/src/guide/installation.md
@@ -28,12 +28,12 @@ Install script does the following:
 - Dependency Step :
   - Installing necessary dependencies considering if compiling from source.
 - Build Step :
-  - Building sr and chsr binaries
+  - Building dosr and chsr binaries
 - Install Step : 
-  - Copying sr and chsr binaries to /usr/bin
-  - Setting all capabilities on /usr/bin/sr
-  - Setting owners and permissions on /usr/bin/sr
+  - Copying dosr and chsr binaries to /usr/bin
+  - Setting all capabilities on /usr/bin/dosr
+  - Setting owners and permissions on /usr/bin/dosr
 - Configuration Step :
-  - Deploying /etc/pam.d/sr for PAM configuration
+  - Deploying /etc/pam.d/dosr for PAM configuration
   - Deploying /etc/security/rootasrole.json for configuration
   - Setting immutable on /etc/security/rootasrole.json if filesytem supports it
\ No newline at end of file
diff --git a/book/src/knowledge/no-root.md b/book/src/knowledge/no-root.md
index 1ad549fd..6497f766 100644
--- a/book/src/knowledge/no-root.md
+++ b/book/src/knowledge/no-root.md
@@ -3,35 +3,35 @@
 To make it short, not really. But you can design your system to never have to use the root user. This is what RootAsRole aims, and the exact purpose of Linux Capabilities. Let's consider you want a system without root user and you want to setup a webserver. Firstly, let's create the apache2 user and group:
 
 ```bash
-sr adduser apache2
+dosr adduser apache2
 ```
 
 We consider that we still use the default configuration of RootAsRole. Then, let's add a task to install apache2 with the apache2 user:
 
 ```bash
-sr chsr r r_root t install_apache2 add
-sr chsr r r_root t install_apache2 cmd whitelist add apt install apache2
-sr chsr r r_root t install_apache2 cmd whitelist add "apt upgrade( -y)? apache2"
-sr chsr r r_root t install_apache2 cred set --caps CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_NET_BIND_SERVICE,CAP_SETUID --setuid apache2 --setgid apache2
+dosr chsr r r_root t install_apache2 add
+dosr chsr r r_root t install_apache2 cmd whitelist add apt install apache2
+dosr chsr r r_root t install_apache2 cmd whitelist add "apt upgrade( -y)? apache2"
+dosr chsr r r_root t install_apache2 cred set --caps CAP_CHOWN,CAP_DAC_OVERRIDE,CAP_NET_BIND_SERVICE,CAP_SETUID --setuid apache2 --setgid apache2
 ```
 
 Then, let's add a task to start apache2 with the apache2 user:
 
 ```bash
-sr chsr r r_root t start_apache2 add
-sr chsr r r_root t start_apache2 cmd whitelist add "systemctl ((re)?start|stop) apache2"
-sr chsr r r_root t start_apache2 cmd whitelist add "service apache2 ((re)?start|stop)"
-sr chsr r r_root t install_apache2 cred set --caps CAP_NET_BIND_SERVICE,CAP_SETUID --setuid apache2 --setgid apache2
+dosr chsr r r_root t start_apache2 add
+dosr chsr r r_root t start_apache2 cmd whitelist add "systemctl ((re)?start|stop) apache2"
+dosr chsr r r_root t start_apache2 cmd whitelist add "service apache2 ((re)?start|stop)"
+dosr chsr r r_root t install_apache2 cred set --caps CAP_NET_BIND_SERVICE,CAP_SETUID --setuid apache2 --setgid apache2
 ```
 
 So now you can install and start apache2 with the apache2 user:
 
 ```bash
-sr apt install apache2
+dosr apt install apache2
 ```
 
 This should install apache2 configuration files owned by apache2 user and group. Then you can start apache2 with the apache2 user:
 
 ```bash
-sr systemctl start apache2
+dosr systemctl start apache2
 ```
\ No newline at end of file
diff --git a/build.rs b/build.rs
index 87d750c2..708bf347 100644
--- a/build.rs
+++ b/build.rs
@@ -74,6 +74,10 @@ fn main() {
         eprintln!("cargo:warning={}", err);
     }
 
+    if let Err(err) = set_cargo_version(&package_version, "Cargo.toml") {
+        eprintln!("cargo:warning={}", err);
+    }
+
     if let Err(err) = set_readme_version(&package_version, "README.md") {
         eprintln!("cargo:warning={}", err);
     }
diff --git a/capable b/capable
deleted file mode 160000
index 42fff41a..00000000
--- a/capable
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 42fff41a5d814a5fe1666fd285a9d5a5646be4d7
diff --git a/rar-common/Cargo.toml b/rar-common/Cargo.toml
index a940ee4e..36a6a230 100644
--- a/rar-common/Cargo.toml
+++ b/rar-common/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "rootasrole-core"
-version = "3.1.1"
+version = "3.1.3"
 edition = "2021"
 description = "This core crate contains the RBAC and main features for the RootAsRole project."
 license = "LGPL-3.0-or-later"
@@ -12,23 +12,23 @@ semver = { version = "1.0", features = ["serde"] }
 nix = { version = "0.29", features = ["user","process", "signal", "fs", "hostname"] }
 capctl = "0.2"
 pcre2 = { version = "0.2", optional = true }
-serde = { version = "1.0.210", features=["rc", "derive"] }
-serde_json = "1.0.132"
+serde = { version = "1.0", features=["rc", "derive"] }
+serde_json = "1.0"
 glob = { version = "0.3", optional = true }
-bitflags = { version = "2.5" }
+bitflags = { version = "2.9" }
 shell-words = "1.1"
 linked_hash_set = { version = "0.1" }
 derivative = "2.2"
 sha2 = "0.10"
 chrono = "0.4"
-once_cell = "1.19"
+once_cell = "1.20"
 hex = "0.4"
 log = "0.4"
-syslog = "7.0"
+syslog = "6.0"
 env_logger = "0.11"
-bon = { version = "3.3.2", features = ["experimental-overwritable"] }
-cbor4ii = { version = "1.0.0", features = ["serde", "serde1", "use_std"] }
-konst = "0.3.16"
+bon = { version = "3", features = ["experimental-overwritable"] }
+cbor4ii = { version = "1.0", features = ["serde", "serde1", "use_std"] }
+konst = "0.3"
 
 [dev-dependencies]
 log = "0.4"
@@ -36,8 +36,8 @@ env_logger = "0.11"
 test-log = { version = "0.2" }
 
 [build-dependencies]
-serde = { version = "1.0.210", features=["rc", "derive"] }
-serde_json = "1.0.132"
+serde = { version = "1.0", features=["rc", "derive"] }
+serde_json = "1.0"
 
 [features]
 pcre2 = ["dep:pcre2"]
diff --git a/rar-common/src/database/actor.rs b/rar-common/src/database/actor.rs
index aeb9e8af..8b001f90 100644
--- a/rar-common/src/database/actor.rs
+++ b/rar-common/src/database/actor.rs
@@ -9,6 +9,8 @@ use serde::{Deserialize, Serialize};
 use serde_json::{Map, Value};
 use strum::EnumIs;
 
+use crate::util::{HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1};
+
 #[derive(Serialize, Debug, EnumIs, Clone, PartialEq, Eq, strum::Display)]
 #[serde(untagged, rename_all = "lowercase")]
 pub enum SGenericActorType {
@@ -149,9 +151,10 @@ impl Display for DUserType<'_> {
 
 #[derive(Serialize, PartialEq, Eq, Debug, Clone, EnumIs)]
 #[serde(untagged)]
+#[repr(u32)]
 pub enum SGroups {
-    Single(SGroupType),
-    Multiple(Vec),
+    Single(SGroupType) = HARDENED_ENUM_VALUE_0,
+    Multiple(Vec) = HARDENED_ENUM_VALUE_1,
 }
 
 impl Display for SGroups {
diff --git a/rar-common/src/database/de.rs b/rar-common/src/database/de.rs
index 43e3f29f..794a7399 100644
--- a/rar-common/src/database/de.rs
+++ b/rar-common/src/database/de.rs
@@ -40,13 +40,7 @@ impl<'de> Deserialize<'de> for SetBehavior {
             where
                 E: de::Error,
             {
-                if v > 1 || v < 0 {
-                    return Err(de::Error::custom(format!(
-                        "Invalid value for SetBehavior: {}",
-                        v
-                    )));
-                }
-                SetBehavior::from_repr(v as u8).ok_or(de::Error::custom(format!(
+                SetBehavior::from_repr(v as u32).ok_or(de::Error::custom(format!(
                     "Invalid value for SetBehavior: {}",
                     v
                 )))
@@ -97,7 +91,7 @@ impl<'de> Deserialize<'de> for SetBehavior {
             where
                 E: de::Error,
             {
-                if v > i32::MAX as i64 || v < i32::MIN as i64 {
+                if v > i32::MAX as i64 {
                     return Err(de::Error::custom(format!(
                         "Invalid value for SetBehavior: {}",
                         v
@@ -319,6 +313,8 @@ impl<'de> Deserialize<'de> for SCommands {
 
 #[cfg(test)]
 mod tests {
+    use crate::util::{HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1, HARDENED_ENUM_VALUE_2};
+
     use super::*;
     use capctl::Cap;
     use serde_json::json;
@@ -333,15 +329,21 @@ mod tests {
         let behavior: SetBehavior = serde_json::from_value(json_data).unwrap();
         assert_eq!(behavior, SetBehavior::All);
 
-        let json_data = json!(0);
+        let json_data = json!(HARDENED_ENUM_VALUE_0);
         let behavior: SetBehavior = serde_json::from_value(json_data).unwrap();
-        assert_eq!(behavior, SetBehavior::from_repr(0).unwrap());
+        assert_eq!(
+            behavior,
+            SetBehavior::from_repr(HARDENED_ENUM_VALUE_0).unwrap()
+        );
 
-        let json_data = json!(1);
+        let json_data = json!(HARDENED_ENUM_VALUE_1);
         let behavior: SetBehavior = serde_json::from_value(json_data).unwrap();
-        assert_eq!(behavior, SetBehavior::from_repr(1).unwrap());
+        assert_eq!(
+            behavior,
+            SetBehavior::from_repr(HARDENED_ENUM_VALUE_1).unwrap()
+        );
 
-        let invalid_data = json!(2);
+        let invalid_data = json!(HARDENED_ENUM_VALUE_2);
         assert!(serde_json::from_value::(invalid_data).is_err());
     }
 
diff --git a/rar-common/src/database/options.rs b/rar-common/src/database/options.rs
index 0d4a9014..40a0f606 100644
--- a/rar-common/src/database/options.rs
+++ b/rar-common/src/database/options.rs
@@ -17,6 +17,9 @@ use strum::{Display, EnumIs, EnumIter, EnumString, FromRepr};
 use log::debug;
 
 use crate::rc_refcell;
+use crate::util::{
+    HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1, HARDENED_ENUM_VALUE_2, HARDENED_ENUM_VALUE_3,
+};
 
 //#[cfg(feature = "finder")]
 //use super::finder::Cred;
@@ -58,13 +61,13 @@ pub enum OptType {
 #[strum(ascii_case_insensitive)]
 #[serde(rename_all = "lowercase")]
 #[derive(Default)]
-#[repr(u8)]
+#[repr(u32)]
 pub enum PathBehavior {
-    Delete,
-    KeepSafe,
-    KeepUnsafe,
+    Delete = HARDENED_ENUM_VALUE_0,
+    KeepSafe = HARDENED_ENUM_VALUE_1,
+    KeepUnsafe = HARDENED_ENUM_VALUE_2,
     #[default]
-    Inherit,
+    Inherit = HARDENED_ENUM_VALUE_3,
 }
 
 #[derive(
@@ -133,12 +136,12 @@ impl SPathOptions {}
 #[strum(ascii_case_insensitive)]
 #[serde(rename_all = "lowercase")]
 #[derive(Default)]
-#[repr(u8)]
+#[repr(u32)]
 pub enum EnvBehavior {
-    Delete,
-    Keep,
+    Delete = HARDENED_ENUM_VALUE_0,
+    Keep = HARDENED_ENUM_VALUE_1,
     #[default]
-    Inherit,
+    Inherit = HARDENED_ENUM_VALUE_2,
 }
 
 #[derive(Serialize, Hash, Deserialize, PartialEq, Eq, Debug, EnumIs, Clone)]
@@ -210,12 +213,12 @@ pub struct SEnvOptions {
 #[strum(ascii_case_insensitive)]
 #[serde(rename_all = "lowercase")]
 #[derive(Default)]
-#[repr(u8)]
+#[repr(u32)]
 pub enum SBounding {
-    Strict,
+    Strict = HARDENED_ENUM_VALUE_0,
     #[default]
-    Inherit,
-    Ignore,
+    Inherit = HARDENED_ENUM_VALUE_1,
+    Ignore = HARDENED_ENUM_VALUE_2,
 }
 
 #[derive(
@@ -224,12 +227,12 @@ pub enum SBounding {
 #[strum(ascii_case_insensitive)]
 #[serde(rename_all = "kebab-case")]
 #[derive(Default)]
-#[repr(u8)]
+#[repr(u32)]
 pub enum SPrivileged {
     #[default]
-    User,
-    Inherit,
-    Privileged,
+    User = HARDENED_ENUM_VALUE_0,
+    Inherit = HARDENED_ENUM_VALUE_1,
+    Privileged = HARDENED_ENUM_VALUE_2,
 }
 
 #[derive(
@@ -238,12 +241,12 @@ pub enum SPrivileged {
 #[strum(ascii_case_insensitive)]
 #[serde(rename_all = "kebab-case")]
 #[derive(Default)]
-#[repr(u8)]
+#[repr(u32)]
 pub enum SAuthentication {
     #[default]
-    Perform,
-    Inherit,
-    Skip,
+    Perform = HARDENED_ENUM_VALUE_0,
+    Inherit = HARDENED_ENUM_VALUE_1,
+    Skip = HARDENED_ENUM_VALUE_2,
 }
 
 #[derive(Serialize, Deserialize, PartialEq, Eq, Debug, Clone)]
diff --git a/rar-common/src/database/score.rs b/rar-common/src/database/score.rs
index aa02b64b..22842c59 100644
--- a/rar-common/src/database/score.rs
+++ b/rar-common/src/database/score.rs
@@ -1,24 +1,39 @@
 use std::cmp::Ordering;
 
-use bon::Builder;
+use bon::{builder, Builder};
 use strum::EnumIs;
 
+use crate::util::{
+    HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1, HARDENED_ENUM_VALUE_2, HARDENED_ENUM_VALUE_3,
+    HARDENED_ENUM_VALUE_4,
+};
+
 use super::actor::{DGroupType, DGroups, DUserType, SGroupType, SGroups, SUserType};
 
 #[derive(PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Debug, EnumIs, Default)]
 #[repr(u32)]
 // Matching user groups for the role
 pub enum ActorMatchMin {
-    UserMatch,
-    GroupMatch(usize),
+    UserMatch = HARDENED_ENUM_VALUE_0,
+    GroupMatch(usize) = HARDENED_ENUM_VALUE_1,
+    #[default]
+    NoMatch = HARDENED_ENUM_VALUE_2,
+}
+
+#[derive(PartialEq, Eq, Clone, Copy, Debug, EnumIs, Default)]
+#[repr(u32)]
+pub enum HardenedBool {
     #[default]
-    NoMatch,
+    False = HARDENED_ENUM_VALUE_0,
+    True = HARDENED_ENUM_VALUE_1,
 }
 
 impl ActorMatchMin {
+    #[inline]
     pub fn better(&self, other: &Self) -> bool {
         self.cmp(other) == Ordering::Less
     }
+    #[inline]
     pub fn matching(&self) -> bool {
         *self != ActorMatchMin::NoMatch
     }
@@ -126,38 +141,65 @@ impl Ord for SetUserMin {
     }
 }
 
+#[derive(PartialEq, Eq, Clone, Copy, Debug, Default, Builder)]
+#[builder(const)]
+pub struct CmdMin {
+    #[builder(default = HardenedBool::False, with = || HardenedBool::True, name = "matching")]
+    pub status: HardenedBool,
+    #[builder(default = CmdOrder::empty())]
+    pub order: CmdOrder,
+}
+
 #[derive(PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Debug, Default)]
-pub struct CmdMin(u32);
+pub struct CmdOrder(u32);
 
 bitflags::bitflags! {
 
-    impl CmdMin: u32 {
-        const Match = 0b00001;
-        const WildcardPath = 0b00010;
-        const RegexArgs = 0b00100;
-        const FullRegexArgs = 0b01000;
-        const FullWildcardPath = 0b10000;
+    impl CmdOrder: u32 {
+        const WildcardPath = 0b0001;
+        const RegexArgs = 0b0010;
+        const FullRegexArgs = 0b0100;
+        const FullWildcardPath = 0b1000;
     }
 }
 
 impl CmdMin {
+    pub const MATCH: CmdMin = CmdMin::builder().matching().build();
+
+    pub const fn empty() -> Self {
+        CmdMin::builder().build()
+    }
+    pub fn is_empty(&self) -> bool {
+        self.status == HardenedBool::False && self.order.is_empty()
+    }
+    #[inline]
     pub fn better(&self, other: &Self) -> bool {
         (self.matching() && !other.matching())
-            || (self.matching() && self.cmp(other) == Ordering::Less)
+            || (self.matching() && self.order.cmp(&other.order) == Ordering::Less)
     }
+    #[inline]
     pub fn matching(&self) -> bool {
-        !self.is_empty()
+        self.status == HardenedBool::True
+    }
+
+    pub fn set_matching(&mut self) {
+        self.status = HardenedBool::True;
+    }
+
+    pub fn union_order(&mut self, order: CmdOrder) {
+        self.order |= order;
     }
 }
 
 #[derive(PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Debug, Default)]
+#[repr(u32)]
 pub enum CapsMin {
     #[default]
-    Undefined,
-    NoCaps,
-    CapsNoAdmin(usize),
-    CapsAdmin(usize),
-    CapsAll,
+    Undefined = HARDENED_ENUM_VALUE_0,
+    NoCaps = HARDENED_ENUM_VALUE_1,
+    CapsNoAdmin(usize) = HARDENED_ENUM_VALUE_2,
+    CapsAdmin(usize) = HARDENED_ENUM_VALUE_3,
+    CapsAll = HARDENED_ENUM_VALUE_4,
 }
 
 #[derive(PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Debug, Default)]
@@ -218,37 +260,45 @@ impl Score {
     }
 
     /// Compare the score of tasks results
+    #[inline]
     pub fn cmd_cmp(&self, other: &Score) -> Ordering {
         self.cmd_min
-            .cmp(&other.cmd_min)
+            .order
+            .cmp(&other.cmd_min.order)
             .then(self.caps_min.cmp(&other.caps_min))
             .then(self.setuser_min.cmp(&other.setuser_min))
             .then(self.security_min.cmp(&other.security_min))
     }
 
+    #[inline]
     pub fn user_matching(&self) -> bool {
         self.user_min != ActorMatchMin::NoMatch
     }
 
+    #[inline]
     pub fn command_matching(&self) -> bool {
-        !self.cmd_min.is_empty()
+        self.cmd_min.matching()
     }
 
+    #[inline]
     pub fn fully_matching(&self) -> bool {
         self.user_matching() && self.command_matching()
     }
 
     /// Return true if the score is better than the other
+    #[inline]
     pub fn better_command(&self, other: &Score) -> bool {
         (self.command_matching() && !other.command_matching())
             || (self.command_matching() && self.cmd_cmp(other) == Ordering::Less)
     }
 
+    #[inline]
     pub fn better_user(&self, other: &Score) -> bool {
         (self.user_matching() && !other.user_matching())
             || (self.user_matching() && self.user_cmp(other) == Ordering::Less)
     }
 
+    #[inline]
     pub fn better_fully(&self, other: &Score) -> bool {
         (self.fully_matching() && !other.fully_matching())
             || (self.fully_matching() && self.cmp(other) == Ordering::Less)
@@ -279,20 +329,27 @@ impl Ord for Score {
     }
 }
 
+#[inline]
 fn group_is_root(actortype: &SGroupType) -> bool {
     (*actortype).fetch_id().map_or(false, |id| id == 0)
 }
+
+#[inline]
 fn dgroup_is_root(actortype: &DGroupType<'_>) -> bool {
     (*actortype).fetch_id().map_or(false, |id| id == 0)
 }
 
+#[inline]
 fn user_is_root(actortype: &SUserType) -> bool {
     (*actortype).fetch_id().map_or(false, |id| id == 0)
 }
+
+#[inline]
 fn duser_is_root(actortype: &DUserType<'_>) -> bool {
     (*actortype).fetch_id().map_or(false, |id| id == 0)
 }
 
+#[inline]
 fn groups_contains_root(list: Option<&SGroups>) -> bool {
     if let Some(list) = list {
         match list {
@@ -304,6 +361,7 @@ fn groups_contains_root(list: Option<&SGroups>) -> bool {
     }
 }
 
+#[inline]
 fn dgroups_contains_root(list: Option<&DGroups<'_>>) -> bool {
     if let Some(list) = list {
         match list {
@@ -315,6 +373,7 @@ fn dgroups_contains_root(list: Option<&DGroups<'_>>) -> bool {
     }
 }
 
+#[inline]
 fn groups_len(groups: Option<&SGroups>) -> usize {
     match groups {
         Some(groups) => groups.len(),
@@ -322,6 +381,7 @@ fn groups_len(groups: Option<&SGroups>) -> usize {
     }
 }
 
+#[inline]
 fn dgroups_len(groups: Option<&DGroups<'_>>) -> usize {
     match groups {
         Some(groups) => groups.len(),
@@ -477,8 +537,11 @@ mod tests {
     fn test_score_ordering() {
         let mut score1 = Score::default();
         let mut score2 = Score::default();
-        score1.cmd_min = CmdMin::from_bits_truncate(0b00001);
-        score2.cmd_min = CmdMin::from_bits_truncate(0b00010);
+        score1.cmd_min = CmdMin::builder().matching().build();
+        score2.cmd_min = CmdMin::builder()
+            .matching()
+            .order(CmdOrder::WildcardPath)
+            .build();
         assert!(score1 < score2 || score1 == score2 || score1 > score2);
     }
 
@@ -491,8 +554,8 @@ mod tests {
 
     #[test]
     fn test_cmdmin_better_and_matching() {
-        let a = CmdMin::from_bits_truncate(0b00001);
-        let b = CmdMin::from_bits_truncate(0b00000);
+        let a = CmdMin::builder().matching().build();
+        let b = CmdMin::builder().build();
         assert!(a.matching());
         assert!(!b.matching());
         assert!(!b.better(&a));
@@ -503,8 +566,8 @@ mod tests {
     fn test_score_better_methods() {
         let mut score1 = Score::default();
         let mut score2 = Score::default();
-        score1.cmd_min = CmdMin::from_bits_truncate(0b00001);
-        score2.cmd_min = CmdMin::from_bits_truncate(0b00000);
+        score1.cmd_min = CmdMin::builder().matching().build();
+        score2.cmd_min = CmdMin::builder().build();
         assert!(score1.better_command(&score2));
         assert!(!score2.better_command(&score1));
     }
@@ -590,23 +653,34 @@ mod tests {
     fn test_set_score() {
         let mut score = Score::default();
         let task_score = TaskScore {
-            cmd_min: CmdMin::from_bits_truncate(0b00001),
+            cmd_min: CmdMin::builder().matching().build(),
             caps_min: CapsMin::NoCaps,
             setuser_min: SetUserMin::default(),
         };
         score.set_task_score(&task_score);
-        assert_eq!(score.cmd_min, CmdMin::from_bits_truncate(0b00001));
+        assert_eq!(score.cmd_min, CmdMin::builder().matching().build());
         assert_eq!(score.caps_min, CapsMin::NoCaps);
         assert_eq!(score.setuser_min, SetUserMin::default());
         let role_score = ActorMatchMin::UserMatch;
         score.set_role_score(&role_score);
         assert_eq!(score.user_min, ActorMatchMin::UserMatch);
-        assert_eq!(score.cmd_min, CmdMin::from_bits_truncate(0b00001));
+        assert_eq!(score.cmd_min, CmdMin::builder().matching().build());
         assert_eq!(score.caps_min, CapsMin::NoCaps);
         assert_eq!(score.setuser_min, SetUserMin::default());
         assert_eq!(score.security_min, SecurityMin::empty());
-        score.set_cmd_score(CmdMin::from_bits_truncate(0b00010));
-        assert_eq!(score.cmd_min, CmdMin::from_bits_truncate(0b00010));
+        score.set_cmd_score(
+            CmdMin::builder()
+                .matching()
+                .order(CmdOrder::WildcardPath)
+                .build(),
+        );
+        assert_eq!(
+            score.cmd_min,
+            CmdMin::builder()
+                .matching()
+                .order(CmdOrder::WildcardPath)
+                .build()
+        );
         assert_eq!(score.caps_min, CapsMin::NoCaps);
         assert_eq!(score.setuser_min, SetUserMin::default());
         assert_eq!(score.user_min, ActorMatchMin::UserMatch);
@@ -623,7 +697,7 @@ mod tests {
         assert!(score.user_matching());
         assert!(!score.command_matching());
         assert!(!score.fully_matching());
-        score.cmd_min = CmdMin::from_bits_truncate(0b00001);
+        score.cmd_min = CmdMin::builder().matching().build();
         assert!(score.user_matching());
         assert!(score.command_matching());
         assert!(score.fully_matching());
@@ -637,8 +711,11 @@ mod tests {
     fn test_score_better() {
         let mut score1 = Score::default();
         let mut score2 = Score::default();
-        score1.cmd_min = CmdMin::from_bits_truncate(0b00001);
-        score2.cmd_min = CmdMin::from_bits_truncate(0b00010);
+        score1.cmd_min = CmdMin::builder().matching().build();
+        score2.cmd_min = CmdMin::builder()
+            .matching()
+            .order(CmdOrder::WildcardPath)
+            .build();
         assert!(!score2.better_command(&score1));
         assert!(score1.better_command(&score2));
         assert!(!score1.better_user(&score2));
@@ -657,14 +734,20 @@ mod tests {
     fn test_score_max_min_clamp() {
         let mut score1 = Score::default();
         let mut score2 = Score::default();
-        score1.cmd_min = CmdMin::from_bits_truncate(0b00001);
-        score2.cmd_min = CmdMin::from_bits_truncate(0b00010);
+        score1.cmd_min = CmdMin::builder().matching().build();
+        score2.cmd_min = CmdMin::builder()
+            .matching()
+            .order(CmdOrder::WildcardPath)
+            .build();
         assert_eq!(score1.max(score2), score2);
         assert_eq!(score2.max(score1), score2);
         assert_eq!(score1.min(score2), score1);
         assert_eq!(score2.min(score1), score1);
         let mut score3 = Score::default();
-        score3.cmd_min = CmdMin::from_bits_truncate(0b00011);
+        score3.cmd_min = CmdMin::builder()
+            .matching()
+            .order(CmdOrder::RegexArgs)
+            .build();
         assert_eq!(score1.clamp(score2, score3), score2);
         assert_eq!(score2.clamp(score1, score3), score2);
     }
diff --git a/rar-common/src/database/ser.rs b/rar-common/src/database/ser.rs
index ddb89e4d..62346488 100644
--- a/rar-common/src/database/ser.rs
+++ b/rar-common/src/database/ser.rs
@@ -87,7 +87,7 @@ impl Serialize for SetBehavior {
         if serializer.is_human_readable() {
             return serializer.serialize_str(&self.to_string());
         } else {
-            return serializer.serialize_u8(*self as u8);
+            return serializer.serialize_u32(*self as u32);
         }
     }
 }
@@ -114,7 +114,7 @@ impl Serialize for SSetuidSet {
             map.end()
         } else {
             let mut map = serializer.serialize_map(None)?;
-            map.serialize_entry("d", &(self.default as u8))?;
+            map.serialize_entry("d", &(self.default as u32))?;
             if let Some(fallback) = &self.fallback {
                 map.serialize_entry("f", fallback)?;
             }
@@ -155,7 +155,7 @@ impl Serialize for SSetgidSet {
             map.end()
         } else {
             let mut map = serializer.serialize_map(None)?;
-            map.serialize_entry("d", &(self.default as u8))?;
+            map.serialize_entry("d", &(self.default as u32))?;
             if !self.fallback.is_empty() {
                 map.serialize_entry("f", &self.fallback)?;
             }
@@ -198,7 +198,7 @@ impl Serialize for SCapabilities {
             } else {
                 let mut map = serializer.serialize_map(Some(3))?;
                 if self.default_behavior.is_all() {
-                    map.serialize_entry("d", &(self.default_behavior as u8))?;
+                    map.serialize_entry("d", &(self.default_behavior as u32))?;
                 }
                 if !self.add.is_empty() {
                     let v: Vec = self.add.iter().map(|cap| cap.to_string()).collect();
@@ -343,7 +343,7 @@ impl Serialize for SCommands {
         } else {
             let mut map = serializer.serialize_map(Some(3))?;
             if let Some(behavior) = &self.default_behavior {
-                map.serialize_entry("d", &(*behavior as u8))?;
+                map.serialize_entry("d", &(*behavior as u32))?;
             }
             if !self.add.is_empty() {
                 map.serialize_entry("a", &self.add)?;
@@ -424,7 +424,25 @@ mod tests {
         let mut serializer = cbor4ii::serde::Serializer::new(&mut writer);
         b.serialize(&mut serializer).unwrap();
         assert!(!writer.buffer().is_empty());
-        assert!(writer.buffer() == [0x00]);
+        // split HARDENED_ENUM_VALUE_0 to an array of bytes
+        // cbor4ii add 0x1A prefix to the value
+        let splitted = [0x1A, 0x05, 0x2A, 0x29, 0x25];
+        println!("splitted: {:?}", splitted);
+        println!("buffer: {:?}", writer.buffer());
+        assert!(writer.buffer() == splitted);
+        // test serialization of SetBehavior::All
+        let b = SetBehavior::All;
+        let bin: Vec = Vec::new();
+        let mut writer = cbor4ii::core::utils::BufWriter::new(bin);
+        let mut serializer = cbor4ii::serde::Serializer::new(&mut writer);
+        b.serialize(&mut serializer).unwrap();
+        assert!(!writer.buffer().is_empty());
+        // split HARDENED_ENUM_VALUE_0 to an array of bytes
+        // cbor4ii add 0x1A prefix to the value
+        let splitted = [0x1A, 0x0A, 0xD5, 0xD6, 0xDA];
+        println!("splitted: {:?}", splitted);
+        println!("buffer: {:?}", writer.buffer());
+        assert!(writer.buffer() == splitted);
     }
 
     #[test]
diff --git a/rar-common/src/database/structs.rs b/rar-common/src/database/structs.rs
index 5f14ac4d..12c0b322 100644
--- a/rar-common/src/database/structs.rs
+++ b/rar-common/src/database/structs.rs
@@ -13,7 +13,10 @@ use std::{
     rc::{Rc, Weak},
 };
 
-use crate::rc_refcell;
+use crate::{
+    rc_refcell,
+    util::{HARDENED_ENUM_VALUE_0, HARDENED_ENUM_VALUE_1},
+};
 
 use super::{
     actor::{SActor, SGroupType, SGroups, SUserType},
@@ -219,11 +222,11 @@ pub struct SSetuidSet {
 #[derive(PartialEq, Eq, Display, Debug, EnumIs, Clone, Copy, FromRepr, EnumString)]
 #[strum(serialize_all = "lowercase")]
 #[derive(Default)]
-#[repr(u8)]
+#[repr(u32)]
 pub enum SetBehavior {
     #[default]
-    None,
-    All,
+    None = HARDENED_ENUM_VALUE_0,
+    All = HARDENED_ENUM_VALUE_1,
 }
 #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
 #[serde(untagged)]
diff --git a/rar-common/src/util.rs b/rar-common/src/util.rs
index f1a784c0..dbd29295 100644
--- a/rar-common/src/util.rs
+++ b/rar-common/src/util.rs
@@ -22,6 +22,15 @@ pub const BOLD: &str = "\x1B[1m";
 pub const UNDERLINE: &str = "\x1B[4m";
 pub const RED: &str = "\x1B[31m";
 
+// Hardened enum values used for critical enums to mitigate attacks like Rowhammer.
+// See for example https://arxiv.org/pdf/2309.02545.pdf
+// The values are copied from https://github.com/sudo-project/sudo/commit/7873f8334c8d31031f8cfa83bd97ac6029309e4f#diff-b8ac7ab4c3c4a75aed0bb5f7c5fd38b9ea6c81b7557f775e46c6f8aa115e02cd
+pub const HARDENED_ENUM_VALUE_0: u32 = 0x052a2925; // 0101001010100010100100100101
+pub const HARDENED_ENUM_VALUE_1: u32 = 0x0ad5d6da; // 1010110101011101011011011010
+pub const HARDENED_ENUM_VALUE_2: u32 = 0x69d61fc8; // 1101001110101100001111111001000
+pub const HARDENED_ENUM_VALUE_3: u32 = 0x1629e037; // 0010110001010011110000000110111
+pub const HARDENED_ENUM_VALUE_4: u32 = 0x1fc8d3ac; // 11111110010001101001110101100
+
 #[macro_export]
 macro_rules! upweak {
     ($e:expr) => {
@@ -201,18 +210,20 @@ pub fn match_single_path(cmd_path: &PathBuf, role_path: &str) -> CmdMin {
     use glob::Pattern;
     if !role_path.ends_with(cmd_path.to_str().unwrap()) || !role_path.starts_with("/") {
         // the files could not be the same
-        return CmdMin::empty();
+        return CmdMin::default();
     }
-    let mut match_status = CmdMin::empty();
+    let mut match_status = CmdMin::default();
     debug!("Matching path {:?} with {:?}", cmd_path, role_path);
     if cmd_path == Path::new(role_path) {
-        match_status |= CmdMin::Match;
+        match_status.set_matching();
     } else if let Ok(pattern) = Pattern::new(role_path) {
         if pattern.matches_path(&cmd_path) {
-            match_status |= CmdMin::WildcardPath;
+            use crate::database::score::CmdOrder;
+
+            match_status.union_order(CmdOrder::WildcardPath);
         }
     }
-    if match_status.is_empty() {
+    if !match_status.matching() {
         debug!(
             "No match for path ``{:?}`` for evaluated path : ``{:?}``",
             cmd_path, role_path
diff --git a/resources/arch/PKGBUILD b/resources/arch/PKGBUILD
index e488694b..0ff43d30 100644
--- a/resources/arch/PKGBUILD
+++ b/resources/arch/PKGBUILD
@@ -35,9 +35,9 @@ check() {
 
 package() {
     cd $pkgname-$pkgver
-    install -Dm755 'target/release/sr' -t "$pkgdir/usr/bin"
+    install -Dm755 'target/release/dosr' -t "$pkgdir/usr/bin"
     install -Dm755 'target/release/chsr' -t "$pkgdir/usr/bin"
-    install -Dm644 'resources/arch/arch_sr_pam.conf' -t "$pkgdir/etc/pam.d/sr"
+    install -Dm644 'resources/arch/arch_sr_pam.conf' -t "$pkgdir/etc/pam.d/dosr"
     install -Dm644 'resources/rootasrole.json' -t "$pkgdir/usr/share/rootasrole/default.json"
-    setcap '=p' "$pkgdir/usr/bin/sr"
+    setcap '=p' "$pkgdir/usr/bin/dosr"
 }
diff --git a/resources/arch/rootasrole.install b/resources/arch/rootasrole.install
index b628359f..816785f3 100644
--- a/resources/arch/rootasrole.install
+++ b/resources/arch/rootasrole.install
@@ -21,7 +21,7 @@ post_install() {
         ext2|ext3|ext4|xfs|btrfs|ocfs2|jfs|reiserfs)
             if ! grep -q '"immutable": true' "$TARGET_PATH"; then
                 sed -i 's/"immutable": false/"immutable": true/' "$TARGET_PATH"
-                log "The file $TARGET_PATH is now immutable, and sr will check that immutable is enforced before executing."
+                log "The file $TARGET_PATH is now immutable, and dosr will check that immutable is enforced before executing."
             fi
             # Attempt to set the immutable flag
             if ! chattr +i "$TARGET_PATH"; then
@@ -31,7 +31,7 @@ post_install() {
             fi
             ;;
         *)
-            log "The file system $FS_TYPE does not support the immutable flag. Avoid checking the immutable flag during sr execution."
+            log "The file system $FS_TYPE does not support the immutable flag. Avoid checking the immutable flag during dosr execution."
             sed -i "s/\"immutable\": true/\"immutable\": false/g" "$TARGET_PATH"
             sed -i "s;\"CAP_LINUX_IMMUTABLE\";;g" "$TARGET_PATH"
             return 1
diff --git a/resources/man/en_US.md b/resources/man/en_US.md
index 29725c87..0f9b7324 100644
--- a/resources/man/en_US.md
+++ b/resources/man/en_US.md
@@ -6,7 +6,7 @@
 RootAsRole - An alternative to sudo/su commands that adheres to the principle of least privilege and provides more secure memory management.
 
 # SYNOPSIS
-- **sr** [__OPTIONS__] [__COMMAND__]...
+- **dosr** [__OPTIONS__] [__COMMAND__]...
 - **chsr** [__ARGUMENTS__]
 
     **chsr**'s arguments follow a grammar available at 
@@ -16,11 +16,11 @@ RootAsRole - An alternative to sudo/su commands that adheres to the principle of
 
 The Role-Based Access Control (RBAC) model is based on sets of permissions assigned to users or groups. In RootAsRole, a role is a set of administrative tasks assigned to users. Tasks are commands with specific rights. Rights can include changing the user, changing the group, or/and using Linux capabilities.
 
-The **sr** command allows the execution of commands using a role. It requires a command to be executed as a mandatory parameter. It is also possible to specify a role and a task to select.
+The **dosr** command allows the execution of commands using a role. It requires a command to be executed as a mandatory parameter. It is also possible to specify a role and a task to select.
 
 There are cases where several tasks correspond to a user's command input. In such cases, sr will select the most precise and least privileged task. The notion of precision is based on how closely the RootAsRole policy matches the user's command. The more the user's profile matches the policy, the higher the level of precision. The same applies to the precision of the user's command compared to its specification in the policy. Similarly, the task with fewer privileges will be prioritized over a task with higher privileges, but only if the tasks are equally precise. Despite this intelligent selection, confusion can still arise, and an error message will be returned.
 
-Example of a confusion case: Two roles are assigned in the same way to a user, and among these roles, two tasks are entirely equivalent, but the configured environment variable are different for these two tasks. In this case, sr will display the error message "Permission denied" and log a warning that configuration must be fixed. This case should not happen if administrators are using **chsr**, the configuration tool.
+Example of a confusion case: Two roles are assigned in the same way to a user, and among these roles, two tasks are entirely equivalent, but the configured environment variable are different for these two tasks. In this case, dosr will display the error message "Permission denied" and log a warning that configuration must be fixed. This case should not happen if administrators are using **chsr**, the configuration tool.
 
 It is possible to change the user's prompt using the **-p** option. It is also possible to view the executor's rights using the **-i** option. The displayed information is very limited for the user. Otherwise, administrator can use **chsr** to obtain the complete policy.
 
@@ -28,7 +28,7 @@ The **chsr** command is used to configure RootAsRole and its access control poli
 
 The storage mode of the access control policy can be configured. By default, RootAsRole uses a JSON file. It is possible to change the storage mode by manually modifying the **/etc/security/rootasrole.json** file.
 
-Regarding authentication, RootAsRole uses Pluggable Authentication Module (PAM). The **/etc/pam.d/sr** file can be configured to change authentication behavior.
+Regarding authentication, RootAsRole uses Pluggable Authentication Module (PAM). The **/etc/pam.d/dosr** file can be configured to change authentication behavior.
 
 The core of RootAsRole implements RBAC-0, a simplified version of RBAC. By default, it adds features in the form of plugins to implement certain RBAC-1 functionalities. RBAC-0 simply implements roles, tasks, and permissions. Plugins add role hierarchy and separation of duties. Plugins can only be implemented directly in the project. Another plugin allows testing the checksum of executed files.
 
@@ -60,10 +60,10 @@ The core of RootAsRole implements RBAC-0, a simplified version of RBAC. By defau
 
 # EXAMPLES
 
-**sr reboot**  
+**dosr reboot**  
   Execute the reboot command (if the policy allows it).
 
-**sr -r dac chmod 644 /etc/foo/bar**  
+**dosr -r dac chmod 644 /etc/foo/bar**  
   Execute the command chmod 644 /etc/foo/bar with the role dac (if the policy has a dac role and a task that allows the chmod command).
 
 # HISTORY
diff --git a/resources/man/fr_FR.md b/resources/man/fr_FR.md
index 61a71f6f..da1c8dbf 100644
--- a/resources/man/fr_FR.md
+++ b/resources/man/fr_FR.md
@@ -6,7 +6,7 @@
 RootAsRole - Une alternative pour les commandes sudo/su respectant le principe du moindre privilège et une gestion de la mémoire plus sécurisée.
 
 # SYNOPSIS
-- **sr** [__OPTIONS__] [__COMMAND__]...
+- **dosr** [__OPTIONS__] [__COMMAND__]...
 - **chsr** [__ARGUMENTS__]
     Les arguments suivent une grammaire disponible dans le code source à l'adresse 
 
@@ -15,11 +15,11 @@ RootAsRole - Une alternative pour les commandes sudo/su respectant le principe d
 
 Le modèle de Roles Based Access Control (RBAC) est basé sur des ensembles de permissions assignées à des utilisateurs ou des groupes. Pour RootAsRole, un rôle est un ensemble de tâches administratives assignées à des utilisateurs. Les tâches sont des commandes avec des droits à utiliser. Les droits peuvent être un changement d'utilisateur, un changement de groupe ou/et des Linux capabilities.
 
-La commande **sr** permet d'exécuter des commandes en utilisant un rôle. Il prends en paramètre obligatoire une commande à exécuter. Il est également possible de spécifier un rôle et une tâche à sélectionner.
+La commande **dosr** permet d'exécuter des commandes en utilisant un rôle. Il prends en paramètre obligatoire une commande à exécuter. Il est également possible de spécifier un rôle et une tâche à sélectionner.
 
-Il existe des cas où deux tâches correspondent à la commande d'un utilisateur. Dans ce cas, sr va sélectionner la tâche la plus précise et la moins privilégiée. La notion de précision est basée sur la précision de la politique RootAsRole comparée à l'occurence de la commande utilisateur. Plus le profil utilisateur et correspond à la politique, plus le niveau de précision est élevé. Il en est de même pour la précision de la commande de l'utilisateur vis-à-vis de sa spécification dans la politique. Pareillement, moins les droits sont élevés pour une tâche, plus la tâche sera prioritaire par rapport à une autre tâche. Le cas de la tâche moins privilégié n'est qu'uniquement si les tâches sont déjà avec le même niveau de précision. Malgré cette sélection intelligente, il reste des cas de confusion, ceux-ci renvoient un message d'erreur.
+Il existe des cas où deux tâches correspondent à la commande d'un utilisateur. Dans ce cas, dosr va sélectionner la tâche la plus précise et la moins privilégiée. La notion de précision est basée sur la précision de la politique RootAsRole comparée à l'occurence de la commande utilisateur. Plus le profil utilisateur et correspond à la politique, plus le niveau de précision est élevé. Il en est de même pour la précision de la commande de l'utilisateur vis-à-vis de sa spécification dans la politique. Pareillement, moins les droits sont élevés pour une tâche, plus la tâche sera prioritaire par rapport à une autre tâche. Le cas de la tâche moins privilégié n'est qu'uniquement si les tâches sont déjà avec le même niveau de précision. Malgré cette sélection intelligente, il reste des cas de confusion, ceux-ci renvoient un message d'erreur.
 
-Exemple d'un cas de confusion : Deux rôles sont assignés de la même manière à un utilisateur, parmi ces rôles, deux tâches sont totalement équivalentes mais les variables d'environment sont différents. Dans ce cas, sr affiche le message d'erreur "Permission denied" et fais un message warning dans les logs.
+Exemple d'un cas de confusion : Deux rôles sont assignés de la même manière à un utilisateur, parmi ces rôles, deux tâches sont totalement équivalentes mais les variables d'environment sont différents. Dans ce cas, dosr affiche le message d'erreur "Permission denied" et fais un message warning dans les logs.
 
 Il est possible de changer le prompt de l'utilisateur en utilisant l'option **-p**. Il est également possible de voir les droits de l'exécuteur en utilisant l'option **-i**. Les informations affichées sont très limitées.
 
@@ -27,7 +27,7 @@ La commande **chsr** sert à configurer RootAsRole et sa politique de contrôle
 
 Il est possible de configurer le mode de stockage de la politique de contrôle d'accès. Par défaut, RootAsRole utilise un fichier JSON. Il est possible de changer le mode de stockage en modifiant manuellement le fichier **/etc/security/rootasrole.json**.
 
-Concernant l'authentification, RootAsRole utilise PAM. Il est possible de configurer le fichier **/etc/pam.d/sr** pour changer le comportement de l'authentification.
+Concernant l'authentification, RootAsRole utilise PAM. Il est possible de configurer le fichier **/etc/pam.d/dosr** pour changer le comportement de l'authentification.
 
 Le coeur de RootAsRole implémente RBAC-0, une version simplifiée de RBAC. Par défaut il ajoute des fonctionnalités sous forme de plugins pour implémenter certaines fonctionnalités de RBAC-1. RBAC-0 implémente simplement les rôles, les tâches et les permissions. Les plugins ajoutent la hiérarchie de rôles et séparation des devoirs. Les plugins sont uniquement implémentable directement dans le projet. Il y a également un autre plugin qui permet de tester la somme de contrôle des fichiers exécutés.
 
@@ -50,10 +50,10 @@ Le coeur de RootAsRole implémente RBAC-0, une version simplifiée de RBAC. Par
 
 # EXAMPLES
 
-- **sr reboot**  
+- **dosr reboot**  
   Execute the command reboot. (If the policy is defined and allowed as well)
 
-- **sr -r dac chmod 644 /etc/foo/bar**
+- **dosr -r dac chmod 644 /etc/foo/bar**
   Execute the command chmod 644 /etc/foo/bar with the role dac (If the policy has a role dac and a task that allows chmod command)
 
 # HISTORIQUE
diff --git a/src/sr/finder/api/hashchecker.rs b/src/sr/finder/api/hashchecker.rs
index bbb03bbe..cd73a293 100644
--- a/src/sr/finder/api/hashchecker.rs
+++ b/src/sr/finder/api/hashchecker.rs
@@ -5,7 +5,7 @@ use libc::FS_IOC_GETFLAGS;
 use log::{debug, warn};
 use nix::unistd::{access, AccessFlags};
 use rar_common::{
-    database::score::CmdMin,
+    database::score::{CmdMin, CmdOrder},
     util::{all_paths_from_env, match_single_path, open_with_privileges},
 };
 use serde_json::to_value;
@@ -114,7 +114,10 @@ fn match_path(
     final_path: &mut Option,
 ) -> CmdMin {
     if role_path == "**" {
-        return CmdMin::FullWildcardPath;
+        return CmdMin::builder()
+            .matching()
+            .order(CmdOrder::FullWildcardPath)
+            .build();
     } else if cmd_path.is_absolute() {
         let min = match_single_path(cmd_path, role_path);
         verify_executable_conditions(checker, final_path, cmd_path, min).unwrap_or_default()
@@ -188,12 +191,12 @@ fn match_command_line(
     final_path: &mut Option,
 ) {
     let mut result = match_path(checker, env_path, cmd_path, &role_command[0], final_path);
-    if result.is_empty() || role_command.len() == 1 {
+    if !result.matching() || role_command.len() == 1 {
         *cmd_min = result;
         return;
     }
     match match_args(cmd_args, &shell_words::join(&role_command[1..])) {
-        Ok(args_result) => result |= args_result,
+        Ok(args_result) => result = args_result,
         Err(err) => {
             debug!("Error: {}", err);
             return;
@@ -321,7 +324,7 @@ mod tests {
         let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
         assert!(result.is_ok());
         assert_eq!(final_path, Some(PathBuf::from(&filename)));
-        assert_eq!(cmd_min, CmdMin::Match);
+        assert_eq!(cmd_min, CmdMin::MATCH);
 
         let mut sha256hasher = Sha256::new();
         sha256hasher.update(&buffer);
@@ -344,7 +347,7 @@ mod tests {
         let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
         assert!(result.is_ok());
         assert_eq!(final_path, Some(PathBuf::from(&filename)));
-        assert_eq!(cmd_min, CmdMin::Match);
+        assert_eq!(cmd_min, CmdMin::MATCH);
 
         let mut sha384hasher = Sha384::new();
         sha384hasher.update(&buffer);
@@ -367,7 +370,7 @@ mod tests {
         let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
         assert!(result.is_ok());
         assert_eq!(final_path, Some(PathBuf::from(&filename)));
-        assert_eq!(cmd_min, CmdMin::Match);
+        assert_eq!(cmd_min, CmdMin::MATCH);
 
         let mut sha512hasher = Sha512::new();
         sha512hasher.update(&buffer);
@@ -389,7 +392,7 @@ mod tests {
         let result = deserializer.deserialize(&mut serde_json::Deserializer::from_str(&json));
         assert!(result.is_ok());
         assert_eq!(final_path, Some(PathBuf::from(&filename)));
-        assert_eq!(cmd_min, CmdMin::Match);
+        assert_eq!(cmd_min, CmdMin::MATCH);
     }
 
     #[test]
@@ -503,7 +506,7 @@ mod tests {
         }
         assert!(result.is_ok());
         assert_eq!(final_path, PathBuf::from(&filename).canonicalize().ok());
-        assert_eq!(cmd_min, CmdMin::Match);
+        assert_eq!(cmd_min, CmdMin::MATCH);
 
         set_read_only(filename.as_path()).unwrap();
 
@@ -527,7 +530,7 @@ mod tests {
         }
         assert!(result.is_ok());
         assert_eq!(final_path, PathBuf::from(&filename).canonicalize().ok());
-        assert_eq!(cmd_min, CmdMin::Match);
+        assert_eq!(cmd_min, CmdMin::MATCH);
 
         let json = format!(
             r#"{{"read-only": true, "immutable": true, "command": "{}"}}"#,
@@ -600,7 +603,7 @@ mod tests {
         }
         assert!(result.is_ok());
         assert_eq!(final_path, PathBuf::from(&filename).canonicalize().ok());
-        assert_eq!(cmd_min, CmdMin::Match);
+        assert_eq!(cmd_min, CmdMin::MATCH);
 
         let json = format!(
             r#"{{"read-only": true, "immutable": true, "command": "{}"}}"#,
@@ -623,7 +626,7 @@ mod tests {
         assert!(result.is_ok());
         if immutable {
             assert_eq!(final_path, PathBuf::from(&filename).canonicalize().ok());
-            assert_eq!(cmd_min, CmdMin::Match);
+            assert_eq!(cmd_min, CmdMin::MATCH);
         } else {
             assert_eq!(final_path, None);
             assert_eq!(cmd_min, CmdMin::empty());
diff --git a/src/sr/finder/cmd.rs b/src/sr/finder/cmd.rs
index d5e747c7..79524356 100644
--- a/src/sr/finder/cmd.rs
+++ b/src/sr/finder/cmd.rs
@@ -1,6 +1,6 @@
 use log::{debug, warn};
 use rar_common::{
-    database::score::CmdMin,
+    database::score::{CmdMin, CmdOrder},
     util::{all_paths_from_env, match_single_path},
 };
 use std::path::PathBuf;
@@ -13,7 +13,10 @@ fn match_path(
     final_path: &mut Option,
 ) -> CmdMin {
     if role_path == "**" {
-        return CmdMin::FullWildcardPath;
+        return CmdMin::builder()
+            .matching()
+            .order(CmdOrder::FullWildcardPath)
+            .build();
     } else if cmd_path.is_absolute() {
         let min = match_single_path(cmd_path, role_path);
         if min.better(&previous_min) {
@@ -44,7 +47,10 @@ pub(super) fn match_args(
     role_args: &str,
 ) -> Result> {
     if role_args == "'^.*$'" {
-        return Ok(CmdMin::FullRegexArgs);
+        return Ok(CmdMin::builder()
+            .matching()
+            .order(CmdOrder::FullRegexArgs)
+            .build());
     }
     let commandline = shell_words::join(input_args);
     if role_args.starts_with("\'^") && role_args.ends_with("$\'") {
@@ -52,9 +58,9 @@ pub(super) fn match_args(
             debug!("{:?},No match for args {:?}", e, input_args);
         })
     } else if commandline == role_args {
-        Ok(CmdMin::Match)
+        Ok(CmdMin::builder().matching().build())
     } else {
-        Ok(CmdMin::empty())
+        Ok(CmdMin::builder().build())
     }
 }
 
@@ -67,9 +73,12 @@ fn evaluate_regex_cmd(
 
     let regex = RegexBuilder::new().build(&role_args)?;
     if regex.is_match(commandline.as_bytes())? {
-        Ok(CmdMin::RegexArgs)
+        Ok(CmdMin::builder()
+            .matching()
+            .order(CmdOrder::RegexArgs)
+            .build())
     } else {
-        Ok(CmdMin::empty())
+        Ok(CmdMin::builder().build())
     }
 }
 
@@ -109,7 +118,7 @@ fn match_command_line(
             if args_result.is_empty() {
                 return CmdMin::empty();
             }
-            result |= args_result;
+            result.union_order(args_result.order);
         }
         Err(err) => {
             debug!("Error: {}", err);
@@ -164,7 +173,13 @@ mod tests {
             &previous_min,
             &mut final_path,
         );
-        assert_eq!(result, CmdMin::FullWildcardPath);
+        assert_eq!(
+            result,
+            CmdMin::builder()
+                .matching()
+                .order(CmdOrder::FullWildcardPath)
+                .build()
+        );
         assert_eq!(final_path, None);
     }
 
@@ -246,7 +261,7 @@ mod tests {
         let role_args = "-l /tmp";
         let result = match_args(&input_args, &role_args);
         assert!(result.is_ok());
-        assert_eq!(result.unwrap(), CmdMin::Match);
+        assert_eq!(result.unwrap(), CmdMin::MATCH);
     }
 
     #[cfg(feature = "pcre2")]
@@ -256,7 +271,13 @@ mod tests {
         let role_args = "'^.*$'";
         let result = match_args(&input_args, &role_args);
         assert!(result.is_ok());
-        assert_eq!(result.unwrap(), CmdMin::FullRegexArgs);
+        assert_eq!(
+            result.unwrap(),
+            CmdMin::builder()
+                .matching()
+                .order(CmdOrder::FullRegexArgs)
+                .build()
+        );
     }
 
     #[cfg(feature = "pcre2")]
@@ -266,7 +287,13 @@ mod tests {
         let role_args = "'^.*$'";
         let result = match_args(&input_args, &role_args);
         assert!(result.is_ok());
-        assert_eq!(result.unwrap(), CmdMin::FullRegexArgs);
+        assert_eq!(
+            result.unwrap(),
+            CmdMin::builder()
+                .matching()
+                .order(CmdOrder::FullRegexArgs)
+                .build()
+        );
     }
 
     #[cfg(feature = "pcre2")]
@@ -276,7 +303,13 @@ mod tests {
         let role_args = "'^[Aa ]*$'";
         let result = match_args(&input_args, &role_args);
         assert!(result.is_ok());
-        assert_eq!(result.unwrap(), CmdMin::RegexArgs);
+        assert_eq!(
+            result.unwrap(),
+            CmdMin::builder()
+                .matching()
+                .order(CmdOrder::RegexArgs)
+                .build()
+        );
         let role_args = "'^[Aa]*$'";
         let result = match_args(&input_args, &role_args);
         assert!(result.is_ok());
@@ -448,7 +481,13 @@ mod tests {
             &previous_min,
             &mut final_path,
         );
-        assert_eq!(result, CmdMin::FullWildcardPath);
+        assert_eq!(
+            result,
+            CmdMin::builder()
+                .matching()
+                .order(CmdOrder::FullWildcardPath)
+                .build()
+        );
         assert_eq!(final_path, None);
     }
 
@@ -458,7 +497,7 @@ mod tests {
         let cmd_path = PathBuf::from("ls");
         let cmd_args: Vec = vec!["-l".to_string()];
         let role_command = vec!["/bin/l*".to_string(), "^.*$".to_string()];
-        let previous_min = CmdMin::Match; // better than regex
+        let previous_min = CmdMin::MATCH; // better than regex
         let mut final_path = Some("/usr/bin/ls".into());
         let result = match_command_line(
             &env_path,
@@ -528,7 +567,13 @@ mod tests {
             &previous_min,
             &mut final_path,
         );
-        assert_eq!(result, CmdMin::FullWildcardPath);
+        assert_eq!(
+            result,
+            CmdMin::builder()
+                .matching()
+                .order(CmdOrder::FullWildcardPath)
+                .build()
+        );
         assert_eq!(final_path, None);
     }
 }
diff --git a/src/sr/finder/de.rs b/src/sr/finder/de.rs
index 574d7671..2f8453bb 100644
--- a/src/sr/finder/de.rs
+++ b/src/sr/finder/de.rs
@@ -2348,7 +2348,7 @@ mod tests {
                 .tasks()
                 .next()
                 .unwrap()
-                .score(CmdMin::Match, SecurityMin::empty()),
+                .score(CmdMin::MATCH, SecurityMin::empty()),
             Score::builder()
                 .user_min(ActorMatchMin::UserMatch)
                 .setuser_min(SetUserMin {
@@ -2357,7 +2357,7 @@ mod tests {
                 })
                 .caps_min(CapsMin::NoCaps)
                 .security_min(SecurityMin::empty())
-                .cmd_min(CmdMin::Match)
+                .cmd_min(CmdMin::MATCH)
                 .build()
         );
     }
@@ -2656,7 +2656,7 @@ mod tests {
         assert!(config.roles[0].options.is_some());
         assert!(config.roles[0].tasks[0].options.is_some());
         assert_eq!(config.roles[0].user_min, ActorMatchMin::GroupMatch(1));
-        assert_eq!(config.roles[0].tasks[0].score.cmd_min, CmdMin::Match);
+        assert_eq!(config.roles[0].tasks[0].score.cmd_min, CmdMin::MATCH);
         assert_eq!(
             config.roles[0].tasks[0].score.setuser_min.uid,
             Some(SetuidMin::from(&0.into()))
@@ -2697,7 +2697,7 @@ mod tests {
         assert!(result.is_ok(), "Failed to deserialize: {:?}", result);
         let config = result.unwrap();
         assert_eq!(config.roles[0].user_min, ActorMatchMin::UserMatch);
-        assert_eq!(config.roles[0].tasks[0].score.cmd_min, CmdMin::Match);
+        assert_eq!(config.roles[0].tasks[0].score.cmd_min, CmdMin::MATCH);
         assert_eq!(
             config.roles[0].tasks[0].score.setuser_min.uid,
             Some(SetuidMin::from(&0.into()))
@@ -2754,7 +2754,7 @@ mod tests {
         assert!(result.is_err(), "Expected error, got: {:?}", result);
         assert!(serde_json::from_str::(int).is_err());
         let mut var_name = None;
-        let mut cmd_min = CmdMin::Match;
+        let mut cmd_min = CmdMin::MATCH;
         let dcommand = DCommandDeserializer {
             env_path: &[],
             cmd_path: &cli.cmd_path,
diff --git a/src/sr/finder/mod.rs b/src/sr/finder/mod.rs
index 939e9758..ff713d25 100644
--- a/src/sr/finder/mod.rs
+++ b/src/sr/finder/mod.rs
@@ -16,7 +16,7 @@ use rar_common::{
     database::{
         actor::DGroups,
         options::{SAuthentication, SBounding, SPrivileged, STimeout},
-        score::{CmdMin, Score},
+        score::{CmdMin, CmdOrder, Score},
     },
     util::{all_paths_from_env, open_with_privileges},
     Cred, StorageMethod,
@@ -200,7 +200,10 @@ impl BestExecSettings {
                     })?
                     .to_path_buf();
                 }
-                self.score.cmd_min = CmdMin::FullWildcardPath | CmdMin::RegexArgs;
+                self.score.cmd_min = CmdMin::builder()
+                    .matching()
+                    .order(CmdOrder::FullWildcardPath | CmdOrder::RegexArgs)
+                    .build();
             } else {
                 for command in commands.add() {
                     found = self.command_settings(
@@ -479,13 +482,16 @@ mod tests {
     fn test_update_command_score_better() {
         let mut settings = BestExecSettings {
             score: Score {
-                cmd_min: CmdMin::RegexArgs,
+                cmd_min: CmdMin::builder()
+                    .matching()
+                    .order(CmdOrder::RegexArgs)
+                    .build(),
                 ..Default::default()
             },
             final_path: PathBuf::from("/old/path"),
             ..Default::default()
         };
-        let new_cmd_min = CmdMin::Match;
+        let new_cmd_min = CmdMin::MATCH;
         let new_path = PathBuf::from("/new/path");
         let updated = settings.update_command_score(new_path.clone(), new_cmd_min.clone());
         assert!(updated);
@@ -497,13 +503,16 @@ mod tests {
     fn test_update_command_score_not_better() {
         let mut settings = BestExecSettings {
             score: Score {
-                cmd_min: CmdMin::Match,
+                cmd_min: CmdMin::MATCH,
                 ..Default::default()
             },
             final_path: PathBuf::from("/old/path"),
             ..Default::default()
         };
-        let worse_cmd_min = CmdMin::RegexArgs;
+        let worse_cmd_min = CmdMin::builder()
+            .matching()
+            .order(CmdOrder::RegexArgs)
+            .build();
         let new_path = PathBuf::from("/new/path");
         let updated = settings.update_command_score(new_path, worse_cmd_min);
         assert!(!updated);
diff --git a/src/sr/pam/mod.rs b/src/sr/pam/mod.rs
index 1a914d50..00dcd7aa 100644
--- a/src/sr/pam/mod.rs
+++ b/src/sr/pam/mod.rs
@@ -1,11 +1,10 @@
-use std::{
-    error::Error,
-    ffi::{CStr, CString},
-    ops::Deref,
-};
+use std::{error::Error, ffi::CStr, ops::Deref};
 
 use log::{debug, error, info, warn};
-use pam_client2::{Context, ConversationHandler, ErrorCode, Flag};
+use nonstick::{
+    AuthnFlags, ConversationAdapter, ErrorCode, Result as PamResult, Transaction,
+    TransactionBuilder,
+};
 use pcre2::bytes::RegexBuilder;
 
 use crate::timeout;
@@ -24,9 +23,9 @@ mod rpassword;
 mod securemem;
 
 #[cfg(not(test))]
-const PAM_SERVICE: &str = "sr";
+const PAM_SERVICE: &str = "dosr";
 #[cfg(test)]
-const PAM_SERVICE: &str = "sr_test";
+const PAM_SERVICE: &str = "dosr_test";
 
 pub(crate) const PAM_PROMPT: &str = "Password: ";
 
@@ -53,8 +52,8 @@ impl SrConversationHandler {
             Terminal::open_tty()
         }
     }
-    fn is_pam_password_prompt(&self, prompt: &CStr) -> bool {
-        let pam_prompt = prompt.to_string_lossy();
+    fn is_pam_password_prompt(&self, prompt: &impl AsRef) -> bool {
+        let pam_prompt = prompt.as_ref();
         RegexBuilder::new()
             .build("^Password: ?$")
             .unwrap()
@@ -81,41 +80,46 @@ impl Default for SrConversationHandler {
     }
 }
 
-impl ConversationHandler for SrConversationHandler {
-    fn prompt_echo_on(&mut self, prompt: &CStr) -> Result {
+impl ConversationAdapter for SrConversationHandler {
+    fn prompt(&self, prompt: impl AsRef) -> PamResult {
         if self.no_interact {
-            return Err(ErrorCode::CONV_ERR);
+            return Err(ErrorCode::ConversationError);
         }
-        let mut term = self.open().map_err(|_| ErrorCode::CONV_ERR)?;
-        term.prompt(prompt.to_string_lossy().as_ref())
-            .map_err(|_| ErrorCode::CONV_ERR)?;
-        let read = term.read_cleartext().map_err(|_| ErrorCode::BUF_ERR)?;
-        Ok(unsafe { CString::from_vec_unchecked(read.deref().to_vec()) })
+        let mut term = self.open().map_err(|_| ErrorCode::ConversationError)?;
+        term.prompt(&prompt.as_ref().to_string_lossy().to_string())
+            .map_err(|_| ErrorCode::ConversationError)?;
+        let read = term.read_cleartext().map_err(|_| ErrorCode::BufferError)?;
+        Ok(std::ffi::OsString::from(
+            String::from_utf8_lossy(read.deref()).to_string(),
+        ))
     }
 
-    fn prompt_echo_off(&mut self, prompt: &CStr) -> Result {
+    fn masked_prompt(&self, prompt: impl AsRef) -> PamResult {
         if self.no_interact {
-            return Err(ErrorCode::CONV_ERR);
+            return Err(ErrorCode::ConversationError);
         }
-        let pam_prompt = prompt.to_string_lossy();
-        if self.prompt == Self::default().prompt && !self.is_pam_password_prompt(prompt) {
-            self.prompt = pam_prompt.to_string()
-        }
-        let mut term = self.open().map_err(|_| ErrorCode::CONV_ERR)?;
-        term.prompt(pam_prompt.as_ref())
-            .map_err(|_| ErrorCode::CONV_ERR)?;
-        let read = term.read_password().map_err(|_| ErrorCode::BUF_ERR)?;
-        Ok(unsafe { CString::from_vec_unchecked(read.deref().to_vec()) })
+        //if the prompted message is the password prompt, we replace to self.prompt
+        let pam_prompt = if self.is_pam_password_prompt(&prompt.as_ref().to_string_lossy()) {
+            self.prompt.clone()
+        } else {
+            prompt.as_ref().to_string_lossy().to_string()
+        };
+        let mut term = self.open().map_err(|_| ErrorCode::ConversationError)?;
+        term.prompt(&pam_prompt)
+            .map_err(|_| ErrorCode::ConversationError)?;
+        let read = term.read_password().map_err(|_| ErrorCode::BufferError)?;
+        let os_str = CStr::from_bytes_until_nul(&read.deref()).unwrap();
+        Ok(std::ffi::OsString::from(os_str.to_str().unwrap()))
     }
 
-    fn text_info(&mut self, msg: &CStr) {
-        info!("{}", msg.to_string_lossy());
-        println!("{}", msg.to_string_lossy());
+    fn error_msg(&self, message: impl AsRef) {
+        error!("{}", message.as_ref().to_string_lossy());
+        eprintln!("{}", message.as_ref().to_string_lossy());
     }
 
-    fn error_msg(&mut self, msg: &CStr) {
-        error!("{}", msg.to_string_lossy());
-        eprintln!("{}", msg.to_string_lossy());
+    fn info_msg(&self, message: impl AsRef) {
+        info!("{}", message.as_ref().to_string_lossy());
+        println!("{}", message.as_ref().to_string_lossy());
     }
 }
 
@@ -133,10 +137,11 @@ pub(super) fn check_auth(
     debug!("need to re-authenticate : {}", !is_valid);
     if !is_valid {
         let conv = SrConversationHandler::new(prompt);
-        let mut context = Context::new(PAM_SERVICE, Some(&user.user.name), conv)
-            .expect("Failed to initialize PAM");
-        context.authenticate(Flag::SILENT)?;
-        context.acct_mgmt(Flag::SILENT)?;
+        let mut txn = TransactionBuilder::new_with_service(PAM_SERVICE)
+            .username(&user.user.name)
+            .build(conv.into_conversation())?;
+        txn.authenticate(AuthnFlags::SILENT)?;
+        txn.account_management(AuthnFlags::SILENT)?;
     }
     timeout::update_cookie(user, user, &timeout)?;
     Ok(())
diff --git a/src/sr/pam/rpassword.rs b/src/sr/pam/rpassword.rs
index deebdccb..cdb158ed 100644
--- a/src/sr/pam/rpassword.rs
+++ b/src/sr/pam/rpassword.rs
@@ -136,8 +136,8 @@ impl Terminal<'_> {
     }
 
     /// Display information
-    pub fn prompt(&mut self, text: &str) -> io::Result<()> {
-        write_unbuffered(&mut self.sink(), text)
+    pub fn prompt(&mut self, text: &impl AsRef) -> io::Result<()> {
+        write_unbuffered(&mut self.sink(), text.as_ref())
     }
 
     // boilerplate reduction functions
diff --git a/src/sr/pam/securemem.rs b/src/sr/pam/securemem.rs
index 992fadca..55dc37c0 100644
--- a/src/sr/pam/securemem.rs
+++ b/src/sr/pam/securemem.rs
@@ -11,9 +11,7 @@ use std::{
     slice,
 };
 
-use pam_client2::pam_sys::PAM_MAX_RESP_SIZE;
-
-pub(super) const SIZE: usize = PAM_MAX_RESP_SIZE as usize;
+pub(super) const SIZE: usize = libpam_sys::PAM_MAX_RESP_SIZE as usize;
 const ALIGN: usize = mem::align_of::();
 
 pub struct PamBuffer(NonNull<[u8; SIZE]>);
diff --git a/tests/capable/centos8/Vagrantfile b/tests/capable/centos8/Vagrantfile
index 765b2017..ce42ed96 100644
--- a/tests/capable/centos8/Vagrantfile
+++ b/tests/capable/centos8/Vagrantfile
@@ -11,7 +11,7 @@ Vagrant.configure("2") do |config|
       ./dependencies.sh -y;
       sudo ./configure.sh -y;
       make install;
-      sr capable -j cat /etc/shadow;'
+      dosr capable -j cat /etc/shadow;'
     SHELL
   end
   
\ No newline at end of file
diff --git a/tests/capable/debian11/Vagrantfile b/tests/capable/debian11/Vagrantfile
index eb0ae509..fb34c0d9 100644
--- a/tests/capable/debian11/Vagrantfile
+++ b/tests/capable/debian11/Vagrantfile
@@ -22,7 +22,7 @@ Vagrant.configure("2") do |config|
       ./dependencies.sh -y;
       sudo ./configure.sh -y;
       PROFILE=debug make install;
-      sr capable -j cat /etc/shadow;'
+      dosr capable -j cat /etc/shadow;'
     SHELL
 end
   
\ No newline at end of file
diff --git a/tests/capable/fedora37/Vagrantfile b/tests/capable/fedora37/Vagrantfile
index 29cd44f8..48b5d7d6 100644
--- a/tests/capable/fedora37/Vagrantfile
+++ b/tests/capable/fedora37/Vagrantfile
@@ -8,7 +8,7 @@ Vagrant.configure("2") do |config|
       ./dependencies.sh -y;
       sudo ./configure.sh -y;
       PROFILE=debug make install;
-      sr capable -j cat /etc/shadow;'
+      dosr capable -j cat /etc/shadow;'
     SHELL
   end
   
\ No newline at end of file
diff --git a/tests/capable/rh9/Vagrantfile b/tests/capable/rh9/Vagrantfile
index f63fdfa2..d4d0f08f 100644
--- a/tests/capable/rh9/Vagrantfile
+++ b/tests/capable/rh9/Vagrantfile
@@ -38,7 +38,7 @@ Vagrant.configure("2") do |config|
       ./dependencies.sh -y;
       sudo ./configure.sh -y;
       PROFILE=debug make install;
-      sr capable -j cat /etc/shadow;'
+      dosr capable -j cat /etc/shadow;'
     SHELL
 
   config.trigger.before :destroy do |trigger|
diff --git a/tests/capable/ubuntu2204/Vagrantfile b/tests/capable/ubuntu2204/Vagrantfile
index 21dfb5a3..78e25ace 100644
--- a/tests/capable/ubuntu2204/Vagrantfile
+++ b/tests/capable/ubuntu2204/Vagrantfile
@@ -9,8 +9,8 @@ Vagrant.configure("2") do |config|
       ./dependencies.sh -y;
       sudo ./configure.sh -y;
       PROFILE=debug make install;
-      sr chsr role r_root task t_capable o env set RUST_LOG=debug
-      sr capable -j cat /etc/shadow;'
+      dosr chsr role r_root task t_capable o env set RUST_LOG=debug
+      dosr capable -j cat /etc/shadow;'
     SHELL
   end
   
\ No newline at end of file
diff --git a/xtask/Cargo.toml b/xtask/Cargo.toml
index 793637e2..48fcb3f6 100644
--- a/xtask/Cargo.toml
+++ b/xtask/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "xtask"
 # The project version is managed on json file in resources/rootasrole.json
-version = "3.1.1"
+version = "3.1.3"
 edition = "2021"
 publish = false
 
diff --git a/xtask/src/configure.rs b/xtask/src/configure.rs
index 652e75c8..02dd8258 100644
--- a/xtask/src/configure.rs
+++ b/xtask/src/configure.rs
@@ -17,7 +17,7 @@ use crate::util::{
 };
 
 const TEMPLATE: &str = include_str!("../../resources/rootasrole.json");
-pub const PAM_CONFIG_PATH: &str = "/etc/pam.d/sr";
+pub const PAM_CONFIG_PATH: &str = "/etc/pam.d/dosr";
 
 fn is_running_in_container() -> bool {
     // Check for environment files that might indicate a container
diff --git a/xtask/src/installer/build.rs b/xtask/src/installer/build.rs
index ada5e619..e3d58a9c 100644
--- a/xtask/src/installer/build.rs
+++ b/xtask/src/installer/build.rs
@@ -35,7 +35,7 @@ pub fn build(options: &BuildOptions) -> Result<(), anyhow::Error> {
             .status()
             .expect("failed to clean");
     }
-    build_binary("sr", options, vec![])?;
+    build_binary("dosr", options, vec![])?;
     build_binary("chsr", options, vec!["--no-default-features"])?;
 
     build_manpages()?;
@@ -54,7 +54,7 @@ fn build_manpages() -> Result<(), anyhow::Error> {
             "man",
             "resources/man/en_US.md",
             "-o",
-            "target/man/sr.8",
+            "target/man/dosr.8",
         ])
         .status()?;
     fs::create_dir_all("target/man/fr")?;
@@ -65,16 +65,17 @@ fn build_manpages() -> Result<(), anyhow::Error> {
             "man",
             "resources/man/fr_FR.md",
             "-o",
-            "target/man/fr/sr.8",
+            "target/man/fr/dosr.8",
         ])
         .status()?;
     debug!("Compressing manpages");
     Command::new("gzip")
-        .args(["target/man/sr.8", "target/man/fr/sr.8"])
+        .args(["target/man/dosr.8", "target/man/fr/dosr.8"])
         .status()?;
     debug!("Making symlinks");
-    unix::fs::symlink("sr.8.gz", "target/man/chsr.8.gz").context("Failed to create symlink")?;
-    unix::fs::symlink("sr.8.gz", "target/man/fr/chsr.8.gz").context("Failed to create symlink")?;
+    unix::fs::symlink("dosr.8.gz", "target/man/chsr.8.gz").context("Failed to create symlink")?;
+    unix::fs::symlink("dosr.8.gz", "target/man/fr/chsr.8.gz")
+        .context("Failed to create symlink")?;
 
     debug!("Manpages built");
     Ok(())
diff --git a/xtask/src/installer/install.rs b/xtask/src/installer/install.rs
index 64e28a7b..ca6a8297 100644
--- a/xtask/src/installer/install.rs
+++ b/xtask/src/installer/install.rs
@@ -28,13 +28,13 @@ fn copy_executables(profile: &Profile) -> Result<(), anyhow::Error> {
         .context("unable to get current dir as string")?;
     info!("Current working directory: {}", cwd);
     info!(
-        "Copying files {}/target/{}/sr to {} and {}",
+        "Copying files {}/target/{}/dosr to {} and {}",
         cwd,
         profile,
         sr_dest.to_str().unwrap(),
         chsr_dest.to_str().unwrap()
     );
-    let s_sr = format!("{}/target/{}/sr", cwd, profile);
+    let s_sr = format!("{}/target/{}/dosr", cwd, profile);
     let sr = Path::new(&s_sr);
     let s_chsr = format!("{}/target/{}/chsr", cwd, profile);
     let chsr = Path::new(&s_chsr);
@@ -48,7 +48,7 @@ fn copy_executables(profile: &Profile) -> Result<(), anyhow::Error> {
     fs::copy(sr, format!("{}.tmp", s_sr))?;
     debug!("Copying chsr to chsr.tmp");
     fs::copy(chsr, format!("{}.tmp", s_chsr))?;
-    debug!("Renaming sr to /usr/bin/sr");
+    debug!("Renaming sr to /usr/bin/dosr");
     fs::rename(sr, sr_dest)?;
     debug!("Renaming chsr to /usr/bin/chsr");
     fs::rename(chsr, chsr_dest)?;
@@ -238,7 +238,7 @@ pub fn install(
         //raise dac_override to copy files
         cap_effective(&mut state, Cap::DAC_OVERRIDE).context("Failed to raise DAC_OVERRIDE")?;
 
-        // cp target/{release}/sr,chsr,capable /usr/bin
+        // cp target/{release}/dosr,chsr,capable /usr/bin
         copy_executables(&profile).context("Failed to copy sr and chsr files")?;
 
         copy_docs().context("Failed to copy documentation files")?;
@@ -262,7 +262,7 @@ pub fn install(
     cap_effective(&mut state, Cap::SETFCAP).context("Failed to raise SETFCAP")?;
 
     // set file capabilities for sr only
-    setfcap().context("Failed to set file capabilities on /usr/bin/sr")?;
+    setfcap().context("Failed to set file capabilities on /usr/bin/dosr")?;
 
     // drop all capabilities
     cap_clear(&mut state).context("Failed to drop effective capabilities")?;
diff --git a/xtask/src/installer/mod.rs b/xtask/src/installer/mod.rs
index df24954b..fbecb5dd 100644
--- a/xtask/src/installer/mod.rs
+++ b/xtask/src/installer/mod.rs
@@ -19,7 +19,7 @@ use crate::{
     util::{detect_priv_bin, get_os, OsTarget},
 };
 pub const RAR_BIN_PATH: &str = env!("RAR_BIN_PATH");
-pub const SR_DEST: &str = "sr";
+pub const SR_DEST: &str = "dosr";
 pub const CHSR_DEST: &str = "chsr";
 
 #[derive(Debug, Parser, Clone)]
diff --git a/xtask/src/util.rs b/xtask/src/util.rs
index 0c2086a8..4f281a6d 100644
--- a/xtask/src/util.rs
+++ b/xtask/src/util.rs
@@ -424,9 +424,9 @@ pub fn get_os(os: Option) -> Result {
 }
 
 pub fn detect_priv_bin() -> Option {
-    // is /usr/bin/sr exist ?
-    if std::fs::metadata("/usr/bin/sr").is_ok() {
-        Some("/usr/bin/sr".to_string())
+    // is /usr/bin/dosr exist ?
+    if std::fs::metadata("/usr/bin/dosr").is_ok() {
+        Some("/usr/bin/dosr".to_string())
     } else if std::fs::metadata("/usr/bin/sudo").is_ok() {
         Some("/usr/bin/sudo".to_string())
     } else if std::fs::metadata("/usr/bin/doas").is_ok() {