use serde_json::json;
use sysknife_daemon::executor::{build_action_spec, ExecutorError};
const SHELL_EQUIVALENT: &[&str] = &[
"/bin/bash",
"/bin/sh",
"/usr/bin/bash",
"/usr/local/bin/bash",
"/BIN/BASH",
"/usr/bin/zsh",
"/bin/busybox",
"/usr/bin/su",
"/usr/bin/runuser",
"/usr/bin/pkexec",
"/usr/sbin/chroot",
"/usr/bin/python3",
"/usr/bin/perl",
"/usr/bin/ruby",
"/usr/bin/node",
"/usr/bin/gdb",
"/usr/bin/awk",
"/bin/sed",
"/usr/bin/vim",
"/usr/bin/less",
"/usr/bin/man",
"/usr/bin/env",
"/usr/bin/find",
"/usr/bin/xargs",
"/usr/bin/tar",
"/usr/bin/socat",
"/usr/bin/tmux",
"/usr/bin/make",
"/usr/bin/git",
"/usr/bin/systemctl",
"/usr/bin/apt-get",
"/usr/bin/pip3",
"/usr/bin/docker",
"/usr/sbin/nginx,/bin/bash",
];
const NARROW: &[&str] = &[
"/usr/sbin/nginx",
"/usr/bin/uptime",
"/usr/bin/df",
"/usr/sbin/logrotate",
"/usr/sbin/nginx,/usr/bin/df",
];
fn grant(
commands: &str,
nopasswd: bool,
) -> Result<sysknife_daemon::actions::ActionSpec, ExecutorError> {
build_action_spec(
"GrantSudoAccess",
&json!({
"name": "deploy-helper",
"user": "deploy",
"commands": commands,
"nopasswd": nopasswd,
}),
)
}
#[test]
fn a_shell_equivalent_grant_is_refused_with_nopasswd() {
for commands in SHELL_EQUIVALENT {
let result = grant(commands, true);
assert!(
matches!(result, Err(ExecutorError::InvalidParam("commands"))),
"NOPASSWD {commands} is a standing root shell and must be refused, got {result:?}"
);
}
}
#[test]
fn a_narrow_grant_still_works_with_nopasswd() {
for commands in NARROW {
assert!(
grant(commands, true).is_ok(),
"{commands} is a narrow grant and must stay available"
);
}
}
#[test]
fn a_shell_equivalent_grant_is_permitted_when_the_password_prompt_stays() {
assert!(grant("/bin/bash", false).is_ok());
assert!(grant("ALL", false).is_ok());
assert!(
matches!(
grant("ALL", true),
Err(ExecutorError::InvalidParam("commands"))
),
"the original ALL refusal must still hold"
);
}
#[test]
fn the_preview_names_the_equivalence() {
use sysknife_daemon::preview::preview_action;
use sysknife_types::{CallerRole, RequestEnvelope, RequestHash};
let request = RequestEnvelope {
action_name: "GrantSudoAccess".to_string(),
request_id: "req-1".to_string(),
params: json!({
"name": "deploy-helper",
"user": "deploy",
"commands": "/usr/sbin/nginx,/bin/bash",
"nopasswd": false,
}),
caller_role: CallerRole::Admin,
request_hash: RequestHash::new("hash".to_string()),
};
let preview = preview_action(&request, json!({}), json!({}));
let named = preview
.warnings
.iter()
.any(|w| w.contains("/bin/bash") && w.contains("deploy") && w.contains("ALL"));
assert!(
named,
"the preview must say the grant is ALL-equivalent and name the command: {:?}",
preview.warnings
);
}
#[test]
fn the_preview_stays_quiet_about_a_narrow_grant() {
use sysknife_daemon::preview::preview_action;
use sysknife_types::{CallerRole, RequestEnvelope, RequestHash};
let request = RequestEnvelope {
action_name: "GrantSudoAccess".to_string(),
request_id: "req-1".to_string(),
params: json!({
"name": "deploy-helper",
"user": "deploy",
"commands": "/usr/sbin/nginx",
"nopasswd": true,
}),
caller_role: CallerRole::Admin,
request_hash: RequestHash::new("hash".to_string()),
};
let preview = preview_action(&request, json!({}), json!({}));
assert!(
!preview
.warnings
.iter()
.any(|w| w.contains("ALL-equivalent") || w.contains("same authority")),
"a narrow grant must not be labelled ALL-equivalent: {:?}",
preview.warnings
);
}