From a24130d680916996598032bf12fc7b1717b41c5f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Mon, 9 Dec 2024 12:34:05 +0100 Subject: [PATCH] Explicitly tell visudo which editor to use On FreeBSD sudo doesn't use /usr/bin/editor as default editor. --- .../sudo-compliance-tests/src/visudo.rs | 199 +++++++++--------- .../src/visudo/flag_file.rs | 147 ++++++------- .../src/visudo/flag_no_includes.rs | 50 +++-- .../src/visudo/flag_owner.rs | 24 +-- .../src/visudo/flag_perms.rs | 22 +- .../src/visudo/flag_quiet.rs | 9 +- .../src/visudo/flag_strict.rs | 21 +- .../src/visudo/include.rs | 124 +++++------ .../src/visudo/sudoers/editor.rs | 17 +- .../src/visudo/what_now_prompt.rs | 25 +-- test-framework/sudo-test/src/docker.rs | 46 ++-- test-framework/sudo-test/src/lib.rs | 42 ++-- 12 files changed, 357 insertions(+), 369 deletions(-) diff --git a/test-framework/sudo-compliance-tests/src/visudo.rs b/test-framework/sudo-compliance-tests/src/visudo.rs index dc9b3a5d8..d80280f43 100644 --- a/test-framework/sudo-compliance-tests/src/visudo.rs +++ b/test-framework/sudo-compliance-tests/src/visudo.rs @@ -1,5 +1,6 @@ use std::{thread, time::Duration}; +use sudo_test::EnvBuilder; use sudo_test::{ helpers::assert_ls_output, Command, Env, TextFile, ETC_DIR, ETC_SUDOERS, ROOT_GROUP, }; @@ -38,20 +39,26 @@ const CHMOD_EXEC: &str = "100"; const EDITOR_TRUE: &str = "#!/bin/sh true"; +fn visudo_env(sudoers: impl Into, editor: impl Into) -> EnvBuilder { + let mut env = Env(sudoers); + env.file(DEFAULT_EDITOR, editor) + .env("EDITOR", DEFAULT_EDITOR); + env +} + #[test] fn default_editor_is_usr_bin_editor() -> Result<()> { let expected = "default editor was called"; - let env = Env("") - .file( - DEFAULT_EDITOR, - TextFile(format!( - "#!/bin/sh + let env = visudo_env( + "", + TextFile(format!( + "#!/bin/sh echo '{expected}' > {LOGS_PATH}" - )) - .chmod(CHMOD_EXEC), - ) - .build()?; + )) + .chmod(CHMOD_EXEC), + ) + .build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -64,9 +71,7 @@ echo '{expected}' > {LOGS_PATH}" #[test] fn creates_sudoers_file_with_default_ownership_and_perms_if_it_doesnt_exist() -> Result<()> { - let env = Env("") - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env("", TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)).build()?; Command::new("rm") .args(["-f", ETC_SUDOERS]) @@ -87,16 +92,15 @@ fn creates_sudoers_file_with_default_ownership_and_perms_if_it_doesnt_exist() -> #[test] fn errors_if_currently_being_edited() -> Result<()> { - let env = Env("") - .file( - DEFAULT_EDITOR, - TextFile( - "#!/bin/sh + let env = visudo_env( + "", + TextFile( + "#!/bin/sh sleep 3", - ) - .chmod(CHMOD_EXEC), ) - .build()?; + .chmod(CHMOD_EXEC), + ) + .build()?; let child = Command::new("visudo").spawn(&env)?; @@ -119,16 +123,15 @@ sleep 3", #[test] fn passes_temporary_file_to_editor() -> Result<()> { - let env = Env("") - .file( - DEFAULT_EDITOR, - TextFile(format!( - r#"#!/bin/sh + let env = visudo_env( + "", + TextFile(format!( + r#"#!/bin/sh echo "$@" > {LOGS_PATH}"# - )) - .chmod(CHMOD_EXEC), - ) - .build()?; + )) + .chmod(CHMOD_EXEC), + ) + .build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -157,9 +160,7 @@ ls -l /tmp/sudoers-*/sudoers > {LOGS_PATH}"# ) }; - let env = Env("") - .file(DEFAULT_EDITOR, TextFile(editor_script).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env("", TextFile(editor_script).chmod(CHMOD_EXEC)).build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -173,16 +174,15 @@ ls -l /tmp/sudoers-*/sudoers > {LOGS_PATH}"# #[test] fn saves_file_if_no_syntax_errors() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; - let env = Env("") - .file( - DEFAULT_EDITOR, - TextFile(format!( - r#"#!/bin/sh + let env = visudo_env( + "", + TextFile(format!( + r#"#!/bin/sh echo '{expected}' >> $2"# - )) - .chmod("100"), - ) - .build()?; + )) + .chmod("100"), + ) + .build()?; Command::new("rm") .args(["-f", ETC_SUDOERS]) @@ -204,9 +204,7 @@ echo '{expected}' >> $2"# #[test] fn stderr_message_when_file_is_not_modified() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; - let env = Env(expected) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env(expected, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)).build()?; let output = Command::new("visudo").output(&env)?; @@ -234,17 +232,16 @@ fn stderr_message_when_file_is_not_modified() -> Result<()> { #[test] fn does_not_save_the_file_if_there_are_syntax_errors() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; - let env = Env(expected) - .file( - DEFAULT_EDITOR, - TextFile( - "#!/bin/sh + let env = visudo_env( + expected, + TextFile( + "#!/bin/sh echo 'this is fine' > $2", - ) - .chmod(CHMOD_EXEC), ) - .build()?; + .chmod(CHMOD_EXEC), + ) + .build()?; let output = Command::new("visudo").output(&env)?; @@ -264,16 +261,15 @@ echo 'this is fine' > $2", #[test] fn editor_exits_with_a_nonzero_code() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; - let env = Env(SUDOERS_ALL_ALL_NOPASSWD) - .file( - DEFAULT_EDITOR, - TextFile( - "#!/bin/sh + let env = visudo_env( + SUDOERS_ALL_ALL_NOPASSWD, + TextFile( + "#!/bin/sh exit 11", - ) - .chmod(CHMOD_EXEC), ) - .build()?; + .chmod(CHMOD_EXEC), + ) + .build()?; let output = Command::new("visudo").output(&env)?; @@ -291,16 +287,15 @@ exit 11", #[test] fn temporary_file_is_deleted_during_edition() -> Result<()> { - let env = Env("") - .file( - DEFAULT_EDITOR, - TextFile( - "#!/bin/sh + let env = visudo_env( + "", + TextFile( + "#!/bin/sh rm $2", - ) - .chmod(CHMOD_EXEC), ) - .build()?; + .chmod(CHMOD_EXEC), + ) + .build()?; let output = Command::new("visudo").output(&env)?; @@ -322,16 +317,15 @@ rm $2", #[test] fn temp_file_initial_contents() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; - let env = Env(expected) - .file( - DEFAULT_EDITOR, - TextFile(format!( - "#!/bin/sh + let env = visudo_env( + expected, + TextFile(format!( + "#!/bin/sh cp $2 {LOGS_PATH}" - )) - .chmod(CHMOD_EXEC), - ) - .build()?; + )) + .chmod(CHMOD_EXEC), + ) + .build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -345,9 +339,7 @@ cp $2 {LOGS_PATH}" #[test] fn temporary_file_is_deleted_when_done() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; - let env = Env(expected) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env(expected, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)).build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -365,18 +357,17 @@ fn temporary_file_is_deleted_when_done() -> Result<()> { fn temporary_file_is_deleted_when_terminated_by_signal() -> Result<()> { let kill_visudo = "/root/kill-visudo.sh"; let expected = SUDOERS_ALL_ALL_NOPASSWD; - let env = Env(expected) - .file( - DEFAULT_EDITOR, - TextFile( - "#!/bin/sh + let env = visudo_env( + expected, + TextFile( + "#!/bin/sh touch /tmp/barrier sleep 2", - ) - .chmod(CHMOD_EXEC), ) - .file(kill_visudo, include_str!("visudo/kill-visudo.sh")) - .build()?; + .chmod(CHMOD_EXEC), + ) + .file(kill_visudo, include_str!("visudo/kill-visudo.sh")) + .build()?; let child = Command::new("visudo").spawn(&env)?; @@ -399,17 +390,16 @@ sleep 2", #[test] fn does_not_panic_on_io_errors_parse_ok() -> Result<()> { - let env = Env("") - .file( - DEFAULT_EDITOR, - TextFile( - "#!/bin/sh + let env = visudo_env( + "", + TextFile( + "#!/bin/sh echo ' ' >> $2", - ) - .chmod(CHMOD_EXEC), ) - .build()?; + .chmod(CHMOD_EXEC), + ) + .build()?; let output = Command::new("bash") .args(["-c", "visudo | true; echo \"${PIPESTATUS[0]}\""]) @@ -427,17 +417,16 @@ echo ' ' >> $2", #[test] fn does_not_panic_on_io_errors_parse_error() -> Result<()> { - let env = Env("") - .file( - DEFAULT_EDITOR, - TextFile( - "#!/bin/sh + let env = visudo_env( + "", + TextFile( + "#!/bin/sh echo 'bad syntax' >> $2", - ) - .chmod(CHMOD_EXEC), ) - .build()?; + .chmod(CHMOD_EXEC), + ) + .build()?; let output = Command::new("bash") .args(["-c", "visudo | true; echo \"${PIPESTATUS[0]}\""]) diff --git a/test-framework/sudo-compliance-tests/src/visudo/flag_file.rs b/test-framework/sudo-compliance-tests/src/visudo/flag_file.rs index 526184937..9dec26c44 100644 --- a/test-framework/sudo-compliance-tests/src/visudo/flag_file.rs +++ b/test-framework/sudo-compliance-tests/src/visudo/flag_file.rs @@ -1,7 +1,8 @@ -use sudo_test::{helpers::assert_ls_output, Command, Env, TextFile, ROOT_GROUP}; +use sudo_test::{helpers::assert_ls_output, Command, TextFile, ROOT_GROUP}; +use crate::visudo::visudo_env; use crate::{ - visudo::{CHMOD_EXEC, DEFAULT_EDITOR, EDITOR_TRUE, ETC_SUDOERS, LOGS_PATH, TMP_SUDOERS}, + visudo::{CHMOD_EXEC, EDITOR_TRUE, ETC_SUDOERS, LOGS_PATH, TMP_SUDOERS}, Result, SUDOERS_ALL_ALL_NOPASSWD, SUDOERS_ROOT_ALL, USERNAME, }; @@ -19,9 +20,7 @@ macro_rules! assert_snapshot { #[test] fn creates_sudoers_file_with_default_ownership_and_perms_if_it_doesnt_exist() -> Result<()> { - let env = Env("") - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env("", TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)).build()?; let file_path = TMP_SUDOERS; Command::new("visudo") @@ -44,17 +43,16 @@ fn saves_file_if_no_syntax_errors() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; let unexpected = SUDOERS_ROOT_ALL; let file_path = TMP_SUDOERS; - let env = Env("") - .file(file_path, unexpected) - .file( - DEFAULT_EDITOR, - TextFile(format!( - r#"#!/bin/sh + let env = visudo_env( + "", + TextFile(format!( + r#"#!/bin/sh echo '{expected}' > $2"# - )) - .chmod(CHMOD_EXEC), - ) - .build()?; + )) + .chmod(CHMOD_EXEC), + ) + .file(file_path, unexpected) + .build()?; Command::new("visudo") .args(["-f", file_path]) @@ -72,17 +70,16 @@ fn positional_argument() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; let unexpected = SUDOERS_ROOT_ALL; let file_path = TMP_SUDOERS; - let env = Env("") - .file(file_path, unexpected) - .file( - DEFAULT_EDITOR, - TextFile(format!( - r#"#!/bin/sh + let env = visudo_env( + "", + TextFile(format!( + r#"#!/bin/sh echo '{expected}' > $2"# - )) - .chmod(CHMOD_EXEC), - ) - .build()?; + )) + .chmod(CHMOD_EXEC), + ) + .file(file_path, unexpected) + .build()?; Command::new("visudo") .arg(file_path) @@ -101,18 +98,17 @@ fn flag_has_precedence_over_positional_argument() -> Result<()> { let original = SUDOERS_ROOT_ALL; let file_path = "/tmp/sudoers"; let file_path2 = "/tmp/sudoers2"; - let env = Env("") - .file(file_path, original) - .file(file_path2, original) - .file( - DEFAULT_EDITOR, - TextFile(format!( - r#"#!/bin/sh + let env = visudo_env( + "", + TextFile(format!( + r#"#!/bin/sh echo '{expected}' > $2"# - )) - .chmod(CHMOD_EXEC), - ) - .build()?; + )) + .chmod(CHMOD_EXEC), + ) + .file(file_path, original) + .file(file_path2, original) + .build()?; Command::new("visudo") .args(["-f", file_path]) @@ -133,16 +129,15 @@ echo '{expected}' > $2"# fn etc_sudoers_is_not_modified() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; let unexpected = SUDOERS_ROOT_ALL; - let env = Env(expected) - .file( - DEFAULT_EDITOR, - TextFile(format!( - "#!/bin/sh + let env = visudo_env( + expected, + TextFile(format!( + "#!/bin/sh echo '{unexpected}' > $2" - )) - .chmod(CHMOD_EXEC), - ) - .build()?; + )) + .chmod(CHMOD_EXEC), + ) + .build()?; Command::new("visudo") .args(["--file", TMP_SUDOERS]) @@ -161,16 +156,15 @@ echo '{unexpected}' > $2" #[test] fn passes_temporary_file_to_editor() -> Result<()> { - let env = Env("") - .file( - DEFAULT_EDITOR, - TextFile(format!( - r#"#!/bin/sh + let env = visudo_env( + "", + TextFile(format!( + r#"#!/bin/sh echo "$@" > {LOGS_PATH}"# - )) - .chmod(CHMOD_EXEC), - ) - .build()?; + )) + .chmod(CHMOD_EXEC), + ) + .build()?; let file_path = TMP_SUDOERS; Command::new("visudo") @@ -191,8 +185,7 @@ echo "$@" > {LOGS_PATH}"# #[test] fn regular_user_can_create_file() -> Result<()> { - let env = Env("") - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod("111")) + let env = visudo_env("", TextFile(EDITOR_TRUE).chmod("111")) .user(USERNAME) .build()?; @@ -227,18 +220,17 @@ fn regular_user_can_update_a_file_they_own() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; let unexpected = SUDOERS_ROOT_ALL; let file_path = TMP_SUDOERS; - let env = Env("") - .file(file_path, TextFile(unexpected).chown(USERNAME).chmod("666")) - .file( - DEFAULT_EDITOR, - TextFile(format!( - r#"#!/bin/sh + let env = visudo_env( + "", + TextFile(format!( + r#"#!/bin/sh echo '{expected}' > $2"# - )) - .chmod("777"), - ) - .user(USERNAME) - .build()?; + )) + .chmod("777"), + ) + .file(file_path, TextFile(unexpected).chown(USERNAME).chmod("666")) + .user(USERNAME) + .build()?; Command::new("visudo") .args(["-f", file_path]) @@ -259,18 +251,17 @@ fn regular_user_cannot_update_a_file_they_dont_own() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; let unexpected = SUDOERS_ROOT_ALL; let file_path = TMP_SUDOERS; - let env = Env("") - .file(file_path, TextFile(unexpected).chmod("666")) - .file( - DEFAULT_EDITOR, - TextFile(format!( - r#"#!/bin/sh + let env = visudo_env( + "", + TextFile(format!( + r#"#!/bin/sh echo '{expected}' > $2"# - )) - .chmod("777"), - ) - .user(USERNAME) - .build()?; + )) + .chmod("777"), + ) + .file(file_path, TextFile(unexpected).chmod("666")) + .user(USERNAME) + .build()?; let output = Command::new("visudo") .args(["-f", file_path]) diff --git a/test-framework/sudo-compliance-tests/src/visudo/flag_no_includes.rs b/test-framework/sudo-compliance-tests/src/visudo/flag_no_includes.rs index 3dae62f2e..1c22fe5ac 100644 --- a/test-framework/sudo-compliance-tests/src/visudo/flag_no_includes.rs +++ b/test-framework/sudo-compliance-tests/src/visudo/flag_no_includes.rs @@ -1,23 +1,23 @@ -use sudo_test::{Command, Env, TextFile, ETC_DIR}; +use sudo_test::{Command, TextFile, ETC_DIR}; +use crate::visudo::visudo_env; use crate::{ - visudo::{CHMOD_EXEC, DEFAULT_EDITOR, LOGS_PATH}, + visudo::{CHMOD_EXEC, LOGS_PATH}, Result, }; #[test] fn does_not_edit_at_include_files_that_dont_contain_syntax_errors() -> Result<()> { - let env = Env("# 1 -@include sudoers2") - .file(format!("{ETC_DIR}/sudoers2"), "# 2") - .file( - DEFAULT_EDITOR, + let env = visudo_env( + "# 1 +@include sudoers2", TextFile(format!( "#!/bin/sh cat $2 >> {LOGS_PATH}" )) .chmod(CHMOD_EXEC), ) + .file(format!("{ETC_DIR}/sudoers2"), "# 2") .build()?; Command::new("visudo") @@ -38,21 +38,20 @@ cat $2 >> {LOGS_PATH}" #[test] fn does_edit_at_include_files_that_contain_syntax_errors() -> Result<()> { - let env = Env("# 1 -@include sudoers2") - .file( - format!("{ETC_DIR}/sudoers2"), - "# 2 -this is fine", - ) - .file( - DEFAULT_EDITOR, + let env = visudo_env( + "# 1 +@include sudoers2", TextFile(format!( "#!/bin/sh cat $2 >> {LOGS_PATH}" )) .chmod(CHMOD_EXEC), ) + .file( + format!("{ETC_DIR}/sudoers2"), + "# 2 +this is fine", + ) .build()?; Command::new("visudo") @@ -73,8 +72,15 @@ cat $2 >> {LOGS_PATH}" #[test] fn does_not_edit_deep_at_include_files_that_contain_syntax_errors() -> Result<()> { - let env = Env("# 1 -@include sudoers2") + let env = visudo_env( + "# 1 +@include sudoers2", + TextFile(format!( + "#!/bin/sh +cat $2 >> {LOGS_PATH}" + )) + .chmod(CHMOD_EXEC), + ) .file( format!("{ETC_DIR}/sudoers2"), "# 2 @@ -85,14 +91,6 @@ fn does_not_edit_deep_at_include_files_that_contain_syntax_errors() -> Result<() "# 3 this is fine", ) - .file( - DEFAULT_EDITOR, - TextFile(format!( - "#!/bin/sh -cat $2 >> {LOGS_PATH}" - )) - .chmod(CHMOD_EXEC), - ) .build()?; Command::new("visudo") diff --git a/test-framework/sudo-compliance-tests/src/visudo/flag_owner.rs b/test-framework/sudo-compliance-tests/src/visudo/flag_owner.rs index aa83340c8..52f4efad1 100644 --- a/test-framework/sudo-compliance-tests/src/visudo/flag_owner.rs +++ b/test-framework/sudo-compliance-tests/src/visudo/flag_owner.rs @@ -1,16 +1,16 @@ -use sudo_test::{Command, Env, TextFile, ROOT_GROUP}; +use sudo_test::{Command, TextFile, ROOT_GROUP}; +use crate::visudo::visudo_env; use crate::{ - visudo::{CHMOD_EXEC, DEFAULT_EDITOR, EDITOR_TRUE, ETC_SUDOERS, TMP_SUDOERS}, + visudo::{CHMOD_EXEC, EDITOR_TRUE, ETC_SUDOERS, TMP_SUDOERS}, Result, USERNAME, }; #[test] fn when_present_changes_ownership_of_existing_file() -> Result<()> { let file_path = TMP_SUDOERS; - let env = Env("") + let env = visudo_env("", TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .file(file_path, TextFile("").chown("root:users").chmod("777")) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .build()?; Command::new("visudo") @@ -31,9 +31,8 @@ fn when_present_changes_ownership_of_existing_file() -> Result<()> { #[test] fn when_absent_ownership_is_preserved() -> Result<()> { let file_path = TMP_SUDOERS; - let env = Env("") + let env = visudo_env("", TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .file(file_path, TextFile("").chown("root:users").chmod("777")) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .build()?; Command::new("visudo") @@ -54,10 +53,12 @@ fn when_absent_ownership_is_preserved() -> Result<()> { #[test] fn etc_sudoers_ownership_is_always_changed() -> Result<()> { let file_path = ETC_SUDOERS; - let env = Env(TextFile("").chown(format!("{USERNAME}:users")).chmod("777")) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) - .user(USERNAME) - .build()?; + let env = visudo_env( + TextFile("").chown(format!("{USERNAME}:users")).chmod("777"), + TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC), + ) + .user(USERNAME) + .build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -74,12 +75,11 @@ fn etc_sudoers_ownership_is_always_changed() -> Result<()> { #[test] fn flag_check() -> Result<()> { let file_path = TMP_SUDOERS; - let env = Env("") + let env = visudo_env("", TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .file( file_path, TextFile("").chown(format!("{USERNAME}:users")).chmod("777"), ) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .user(USERNAME) .build()?; diff --git a/test-framework/sudo-compliance-tests/src/visudo/flag_perms.rs b/test-framework/sudo-compliance-tests/src/visudo/flag_perms.rs index 30a7cfe25..5cab2c058 100644 --- a/test-framework/sudo-compliance-tests/src/visudo/flag_perms.rs +++ b/test-framework/sudo-compliance-tests/src/visudo/flag_perms.rs @@ -1,16 +1,16 @@ -use sudo_test::{Command, Env, TextFile}; +use sudo_test::{Command, TextFile}; +use crate::visudo::visudo_env; use crate::{ - visudo::{CHMOD_EXEC, DEFAULT_EDITOR, EDITOR_TRUE, ETC_SUDOERS, TMP_SUDOERS}, + visudo::{CHMOD_EXEC, EDITOR_TRUE, ETC_SUDOERS, TMP_SUDOERS}, Result, USERNAME, }; #[test] fn when_present_changes_perms_of_existing_file() -> Result<()> { let file_path = TMP_SUDOERS; - let env = Env("") + let env = visudo_env("", TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .file(file_path, TextFile("").chmod("777")) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .build()?; Command::new("visudo") @@ -31,9 +31,8 @@ fn when_present_changes_perms_of_existing_file() -> Result<()> { #[test] fn when_absent_perms_are_preserved() -> Result<()> { let file_path = TMP_SUDOERS; - let env = Env("") + let env = visudo_env("", TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .file(file_path, TextFile("").chmod("777")) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .build()?; Command::new("visudo") @@ -54,9 +53,11 @@ fn when_absent_perms_are_preserved() -> Result<()> { #[test] fn etc_sudoers_perms_are_always_changed() -> Result<()> { let file_path = ETC_SUDOERS; - let env = Env(TextFile("").chmod("777")) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env( + TextFile("").chmod("777"), + TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC), + ) + .build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -73,12 +74,11 @@ fn etc_sudoers_perms_are_always_changed() -> Result<()> { #[test] fn flag_check() -> Result<()> { let file_path = TMP_SUDOERS; - let env = Env("") + let env = visudo_env("", TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .file( file_path, TextFile("").chown(format!("{USERNAME}:users")).chmod("777"), ) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .user(USERNAME) .build()?; diff --git a/test-framework/sudo-compliance-tests/src/visudo/flag_quiet.rs b/test-framework/sudo-compliance-tests/src/visudo/flag_quiet.rs index 59b39bfbf..dc477b502 100644 --- a/test-framework/sudo-compliance-tests/src/visudo/flag_quiet.rs +++ b/test-framework/sudo-compliance-tests/src/visudo/flag_quiet.rs @@ -1,15 +1,14 @@ -use sudo_test::{Command, Env, TextFile}; +use sudo_test::{Command, TextFile}; +use crate::visudo::visudo_env; use crate::Result; -use super::{CHMOD_EXEC, DEFAULT_EDITOR, EDITOR_TRUE}; +use super::{CHMOD_EXEC, EDITOR_TRUE}; #[test] #[ignore = "gh657"] fn supresses_syntax_error_messages() -> Result<()> { - let env = Env("this is fine") - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env("this is fine", TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)).build()?; let output = Command::new("visudo").arg("-q").output(&env)?; diff --git a/test-framework/sudo-compliance-tests/src/visudo/flag_strict.rs b/test-framework/sudo-compliance-tests/src/visudo/flag_strict.rs index 94e6395b4..890e727c3 100644 --- a/test-framework/sudo-compliance-tests/src/visudo/flag_strict.rs +++ b/test-framework/sudo-compliance-tests/src/visudo/flag_strict.rs @@ -1,16 +1,19 @@ -use sudo_test::{Command, Env, TextFile}; +use sudo_test::{Command, TextFile}; +use crate::visudo::visudo_env; use crate::{ - visudo::{CHMOD_EXEC, DEFAULT_EDITOR, EDITOR_TRUE}, + visudo::{CHMOD_EXEC, EDITOR_TRUE}, Result, }; #[test] #[ignore = "gh657"] fn undefined_alias() -> Result<()> { - let env = Env(["# User_Alias ADMINS = root", "ADMINS ALL=(ALL:ALL) ALL"]) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env( + ["# User_Alias ADMINS = root", "ADMINS ALL=(ALL:ALL) ALL"], + TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC), + ) + .build()?; let output = Command::new("visudo").arg("--strict").output(&env)?; @@ -33,9 +36,11 @@ fn undefined_alias() -> Result<()> { #[test] fn alias_cycle() -> Result<()> { - let env = Env(["User_Alias FOO = FOO", "FOO ALL=(ALL:ALL) ALL"]) - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env( + ["User_Alias FOO = FOO", "FOO ALL=(ALL:ALL) ALL"], + TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC), + ) + .build()?; let output = Command::new("visudo").arg("--strict").output(&env)?; diff --git a/test-framework/sudo-compliance-tests/src/visudo/include.rs b/test-framework/sudo-compliance-tests/src/visudo/include.rs index 0141a820d..523d06d03 100644 --- a/test-framework/sudo-compliance-tests/src/visudo/include.rs +++ b/test-framework/sudo-compliance-tests/src/visudo/include.rs @@ -1,16 +1,16 @@ -use sudo_test::{Command, Env, TextFile, ETC_DIR}; +use sudo_test::{Command, TextFile, ETC_DIR}; +use crate::visudo::visudo_env; use crate::{ - visudo::{CHMOD_EXEC, DEFAULT_EDITOR, EDITOR_TRUE, LOGS_PATH}, + visudo::{CHMOD_EXEC, EDITOR_TRUE, LOGS_PATH}, Result, }; #[test] #[ignore = "gh657"] fn prompt() -> Result<()> { - let env = Env("@include sudoers2") + let env = visudo_env("@include sudoers2", TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .file(format!("{ETC_DIR}/sudoers2"), "") - .file(DEFAULT_EDITOR, TextFile(EDITOR_TRUE).chmod(CHMOD_EXEC)) .build()?; let output = Command::new("visudo").output(&env)?; @@ -26,17 +26,16 @@ fn prompt() -> Result<()> { #[test] #[ignore = "gh657"] fn calls_editor_on_included_files() -> Result<()> { - let env = Env("@include sudoers2") - .file(format!("{ETC_DIR}/sudoers2"), "") - .file( - DEFAULT_EDITOR, - TextFile(format!( - "#!/bin/sh + let env = visudo_env( + "@include sudoers2", + TextFile(format!( + "#!/bin/sh echo $@ >> {LOGS_PATH}" - )) - .chmod(CHMOD_EXEC), - ) - .build()?; + )) + .chmod(CHMOD_EXEC), + ) + .file(format!("{ETC_DIR}/sudoers2"), "") + .build()?; Command::new("visudo") .stdin("\n") @@ -54,18 +53,17 @@ echo $@ >> {LOGS_PATH}" #[test] #[ignore = "gh657"] fn closing_stdin_is_understood_as_yes_to_all() -> Result<()> { - let env = Env("@include sudoers2 -@include sudoers3") - .file(format!("{ETC_DIR}/sudoers2"), "") - .file(format!("{ETC_DIR}/sudoers3"), "") - .file( - DEFAULT_EDITOR, + let env = visudo_env( + "@include sudoers2 +@include sudoers3", TextFile(format!( "#!/bin/sh echo $@ >> {LOGS_PATH}" )) .chmod(CHMOD_EXEC), ) + .file(format!("{ETC_DIR}/sudoers2"), "") + .file(format!("{ETC_DIR}/sudoers3"), "") .build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -81,9 +79,16 @@ echo $@ >> {LOGS_PATH}" #[test] #[ignore = "gh657"] fn edit_order_follows_include_order() -> Result<()> { - let env = Env("# 1 + let env = visudo_env( + "# 1 @include sudoers2 -@include sudoers4") +@include sudoers4", + TextFile(format!( + "#!/bin/sh +cat $2 >> {LOGS_PATH}" + )) + .chmod(CHMOD_EXEC), + ) .file( format!("{ETC_DIR}/sudoers2"), "# 2 @@ -91,14 +96,6 @@ fn edit_order_follows_include_order() -> Result<()> { ) .file(format!("{ETC_DIR}/sudoers3"), "# 3") .file(format!("{ETC_DIR}/sudoers4"), "# 4") - .file( - DEFAULT_EDITOR, - TextFile(format!( - "#!/bin/sh -cat $2 >> {LOGS_PATH}" - )) - .chmod(CHMOD_EXEC), - ) .build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -117,21 +114,20 @@ cat $2 >> {LOGS_PATH}" #[test] #[ignore = "gh657"] fn include_cycle_does_not_edit_the_same_files_many_times() -> Result<()> { - let env = Env("# 1 -@include sudoers2") - .file( - format!("{ETC_DIR}/sudoers2"), - "# 2 -@include sudoers", - ) - .file( - DEFAULT_EDITOR, + let env = visudo_env( + "# 1 +@include sudoers2", TextFile(format!( "#!/bin/sh cat $2 >> {LOGS_PATH}" )) .chmod(CHMOD_EXEC), ) + .file( + format!("{ETC_DIR}/sudoers2"), + "# 2 +@include sudoers", + ) .build()?; let output = Command::new("visudo").output(&env)?; @@ -158,19 +154,18 @@ cat $2 >> {LOGS_PATH}" #[test] #[ignore = "gh657"] fn does_edit_at_include_added_in_last_edit() -> Result<()> { - let env = Env("# 1") - .file(format!("{ETC_DIR}/sudoers2"), "# 2") - .file( - DEFAULT_EDITOR, - TextFile(format!( - "#!/bin/sh + let env = visudo_env( + "# 1", + TextFile(format!( + "#!/bin/sh cp $2 /tmp/scratchpad [ -f {LOGS_PATH} ] || echo '@include sudoers2' >> $2 cat /tmp/scratchpad >> {LOGS_PATH}" - )) - .chmod(CHMOD_EXEC), - ) - .build()?; + )) + .chmod(CHMOD_EXEC), + ) + .file(format!("{ETC_DIR}/sudoers2"), "# 2") + .build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -189,11 +184,9 @@ cat /tmp/scratchpad >> {LOGS_PATH}" #[test] #[ignore = "gh657"] fn does_edit_at_include_removed_in_last_edit() -> Result<()> { - let env = Env("# 1 -@include sudoers2") - .file(format!("{ETC_DIR}/sudoers2"), "# 2") - .file( - DEFAULT_EDITOR, + let env = visudo_env( + "# 1 +@include sudoers2", TextFile(format!( "#!/bin/sh cp $2 /tmp/scratchpad @@ -202,6 +195,7 @@ cat /tmp/scratchpad >> {LOGS_PATH}" )) .chmod(CHMOD_EXEC), ) + .file(format!("{ETC_DIR}/sudoers2"), "# 2") .build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -221,12 +215,9 @@ cat /tmp/scratchpad >> {LOGS_PATH}" #[test] #[ignore = "gh657"] fn edits_existing_at_includes_first_then_newly_added_at_includes() -> Result<()> { - let env = Env("# 1 -@include sudoers2") - .file(format!("{ETC_DIR}/sudoers2"), "# 2") - .file(format!("{ETC_DIR}/sudoers3"), "# 3") - .file( - DEFAULT_EDITOR, + let env = visudo_env( + "# 1 +@include sudoers2", TextFile(format!( "#!/bin/sh cp $2 /tmp/scratchpad @@ -235,6 +226,8 @@ cat /tmp/scratchpad >> {LOGS_PATH}" )) .chmod(CHMOD_EXEC), ) + .file(format!("{ETC_DIR}/sudoers2"), "# 2") + .file(format!("{ETC_DIR}/sudoers3"), "# 3") .build()?; Command::new("visudo").output(&env)?.assert_success()?; @@ -253,19 +246,18 @@ cat /tmp/scratchpad >> {LOGS_PATH}" #[test] fn does_not_edit_files_in_includedir_directories() -> Result<()> { - let env = Env(format!( - "# 1 + let env = visudo_env( + format!( + "# 1 @includedir {ETC_DIR}/sudoers.d" - )) - .file(format!("{ETC_DIR}/sudoers.d/a"), "# 2") - .file( - DEFAULT_EDITOR, + ), TextFile(format!( "#!/bin/sh cat $2 >> {LOGS_PATH}" )) .chmod(CHMOD_EXEC), ) + .file(format!("{ETC_DIR}/sudoers.d/a"), "# 2") .build()?; Command::new("visudo").output(&env)?.assert_success()?; diff --git a/test-framework/sudo-compliance-tests/src/visudo/sudoers/editor.rs b/test-framework/sudo-compliance-tests/src/visudo/sudoers/editor.rs index 0c400b42b..a725d502d 100644 --- a/test-framework/sudo-compliance-tests/src/visudo/sudoers/editor.rs +++ b/test-framework/sudo-compliance-tests/src/visudo/sudoers/editor.rs @@ -2,7 +2,7 @@ use sudo_test::{Command, Env, TextFile}; use crate::{visudo::CHMOD_EXEC, Result}; -use crate::visudo::{DEFAULT_EDITOR, LOGS_PATH}; +use crate::visudo::{visudo_env, LOGS_PATH}; #[test] #[ignore = "gh657"] @@ -99,16 +99,15 @@ fn editors_must_be_specified_by_absolute_path() -> Result<()> { #[test] fn on_invalid_editor_does_not_falls_back_to_configured_default_value() -> Result<()> { - let env = Env("Defaults editor=true") - .file( - DEFAULT_EDITOR, - TextFile( - "#!/bin/sh + let env = visudo_env( + "Defaults editor=true", + TextFile( + "#!/bin/sh rm -f {LOGS_PATH}", - ) - .chmod(CHMOD_EXEC), ) - .build()?; + .chmod(CHMOD_EXEC), + ) + .build()?; Command::new("touch") .arg(LOGS_PATH) diff --git a/test-framework/sudo-compliance-tests/src/visudo/what_now_prompt.rs b/test-framework/sudo-compliance-tests/src/visudo/what_now_prompt.rs index 012869f92..5a6b08cfd 100644 --- a/test-framework/sudo-compliance-tests/src/visudo/what_now_prompt.rs +++ b/test-framework/sudo-compliance-tests/src/visudo/what_now_prompt.rs @@ -1,7 +1,8 @@ -use sudo_test::{Command, Env, TextFile}; +use sudo_test::{Command, TextFile}; +use crate::visudo::visudo_env; use crate::{ - visudo::{CHMOD_EXEC, DEFAULT_EDITOR, ETC_SUDOERS, LOGS_PATH}, + visudo::{CHMOD_EXEC, ETC_SUDOERS, LOGS_PATH}, Result, SUDOERS_ALL_ALL_NOPASSWD, }; @@ -17,9 +18,7 @@ echo '{BAD_SUDOERS}' > $2"# #[test] fn prompt_is_printed_to_stdout() -> Result<()> { - let env = Env("") - .file(DEFAULT_EDITOR, TextFile(editor()).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env("", TextFile(editor()).chmod(CHMOD_EXEC)).build()?; let output = Command::new("visudo").output(&env)?; @@ -31,9 +30,7 @@ fn prompt_is_printed_to_stdout() -> Result<()> { #[test] fn on_e_re_edits() -> Result<()> { - let env = Env("") - .file(DEFAULT_EDITOR, TextFile(editor()).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env("", TextFile(editor()).chmod(CHMOD_EXEC)).build()?; Command::new("visudo") .stdin("e") @@ -54,9 +51,7 @@ fn on_e_re_edits() -> Result<()> { #[test] fn on_x_closes_without_saving_changes() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; - let env = Env(expected) - .file(DEFAULT_EDITOR, TextFile(editor()).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env(expected, TextFile(editor()).chmod(CHMOD_EXEC)).build()?; Command::new("visudo") .stdin("x") @@ -84,9 +79,7 @@ fn on_x_closes_without_saving_changes() -> Result<()> { #[ignore = "gh657"] fn on_uppercase_q_closes_while_saving_changes() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; - let env = Env(expected) - .file(DEFAULT_EDITOR, TextFile(editor()).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env(expected, TextFile(editor()).chmod(CHMOD_EXEC)).build()?; Command::new("visudo") .stdin("Q") @@ -114,9 +107,7 @@ fn on_uppercase_q_closes_while_saving_changes() -> Result<()> { #[ignore = "gh657"] fn on_invalid_option_prompts_again() -> Result<()> { let expected = SUDOERS_ALL_ALL_NOPASSWD; - let env = Env(expected) - .file(DEFAULT_EDITOR, TextFile(editor()).chmod(CHMOD_EXEC)) - .build()?; + let env = visudo_env(expected, TextFile(editor()).chmod(CHMOD_EXEC)).build()?; let cases = [ (2, "?"), diff --git a/test-framework/sudo-test/src/docker.rs b/test-framework/sudo-test/src/docker.rs index 474218d1e..71c3f3ebc 100644 --- a/test-framework/sudo-test/src/docker.rs +++ b/test-framework/sudo-test/src/docker.rs @@ -70,12 +70,12 @@ impl Container { Ok(Container { id }) } - pub fn output(&self, cmd: &Command) -> Result { - run(&mut self.docker_exec(cmd), cmd.get_stdin()) + pub fn output(&self, cmd: &Command, env: &[(String, String)]) -> Result { + run(&mut self.docker_exec(cmd, env), cmd.get_stdin()) } - pub fn spawn(&self, cmd: &Command) -> Result { - let mut docker_exec = self.docker_exec(cmd); + pub fn spawn(&self, cmd: &Command, env: &[(String, String)]) -> Result { + let mut docker_exec = self.docker_exec(cmd, env); docker_exec.stdout(Stdio::piped()).stderr(Stdio::piped()); @@ -89,7 +89,7 @@ impl Container { Ok(Child::new(docker_exec.spawn()?)) } - fn docker_exec(&self, cmd: &Command) -> process::Command { + fn docker_exec(&self, cmd: &Command, env: &[(String, String)]) -> process::Command { let mut docker_exec = docker_command(); docker_exec.arg("exec"); if cmd.get_stdin().is_some() { @@ -102,6 +102,10 @@ impl Container { docker_exec.arg("--user"); docker_exec.arg(as_.to_string()); } + for (env_var, env_val) in env { + docker_exec.arg("--env"); + docker_exec.arg(format!("{env_var}={env_val}")); + } docker_exec.arg(&self.id); docker_exec.args(cmd.get_args()); docker_exec @@ -126,7 +130,7 @@ impl Container { self.output(Command::new("sh").args([ "-c", "mkdir /tmp/profraw; find / -name '*.profraw' -exec cp {} /tmp/profraw/ \\; || true", - ]))? + ]), &[])? .assert_success()?; let src_path = format!("{}:/tmp/profraw", self.id); @@ -282,9 +286,11 @@ mod tests { fn exec_as_root_works() -> Result<()> { let docker = Container::new(IMAGE)?; - docker.output(&Command::new("true"))?.assert_success()?; + docker + .output(&Command::new("true"), &[])? + .assert_success()?; - let output = docker.output(&Command::new("false"))?; + let output = docker.output(&Command::new("false"), &[])?; assert_eq!(Some(1), output.status.code()); Ok(()) @@ -295,7 +301,7 @@ mod tests { let docker = Container::new(IMAGE)?; docker - .output(Command::new("true").as_user("root"))? + .output(Command::new("true").as_user("root"), &[])? .assert_success() } @@ -307,18 +313,18 @@ mod tests { if cfg!(target_os = "linux") { docker - .output(Command::new("useradd").arg(username))? + .output(Command::new("useradd").arg(username), &[])? .assert_success()?; } else if cfg!(target_os = "freebsd") { docker - .output(Command::new("pw").args(["useradd", username]))? + .output(Command::new("pw").args(["useradd", username]), &[])? .assert_success()?; } else { todo!() } docker - .output(Command::new("true").as_user(username))? + .output(Command::new("true").as_user(username), &[])? .assert_success() } @@ -331,7 +337,9 @@ mod tests { docker.cp(path, expected)?; - let actual = docker.output(Command::new("cat").arg(path))?.stdout()?; + let actual = docker + .output(Command::new("cat").arg(path), &[])? + .stdout()?; assert_eq!(expected, actual); Ok(()) @@ -345,10 +353,12 @@ mod tests { let docker = Container::new(IMAGE)?; docker - .output(Command::new("tee").arg(filename).stdin(expected))? + .output(Command::new("tee").arg(filename).stdin(expected), &[])? .assert_success()?; - let actual = docker.output(Command::new("cat").arg(filename))?.stdout()?; + let actual = docker + .output(Command::new("cat").arg(filename), &[])? + .stdout()?; assert_eq!(expected, actual); Ok(()) @@ -358,18 +368,18 @@ mod tests { fn spawn_works() -> Result<()> { let docker = Container::new(IMAGE)?; - let child = docker.spawn(Command::new("sh").args(["-c", "sleep 2"]))?; + let child = docker.spawn(Command::new("sh").args(["-c", "sleep 2"]), &[])?; // `sh` process may not be immediately visible to `pidof` since it was spawned so wait a bit thread::sleep(Duration::from_millis(500)); docker - .output(Command::new("pidof").arg("sh"))? + .output(Command::new("pidof").arg("sh"), &[])? .assert_success()?; child.wait()?.assert_success()?; - let output = docker.output(Command::new("pidof").arg("sh"))?; + let output = docker.output(Command::new("pidof").arg("sh"), &[])?; assert!(!output.status().success()); assert_eq!(Some(1), output.status().code()); diff --git a/test-framework/sudo-test/src/lib.rs b/test-framework/sudo-test/src/lib.rs index 2b41e0c23..ba4a43dec 100644 --- a/test-framework/sudo-test/src/lib.rs +++ b/test-framework/sudo-test/src/lib.rs @@ -64,6 +64,7 @@ type Username = String; pub struct Env { container: Container, users: HashSet, + env: Vec<(String, String)>, } /// creates a new test environment builder that contains the specified `/etc/sudoers` file @@ -95,7 +96,7 @@ impl Command { ); } - env.container.output(self) + env.container.output(self, &env.env) } /// spawns the command in the specified test environment @@ -107,7 +108,7 @@ impl Command { ); } - env.container.spawn(self) + env.container.spawn(self, &env.env) } } @@ -119,6 +120,7 @@ pub struct EnvBuilder { groups: HashMap, hostname: Option, users: HashMap, + env: Vec<(String, String)>, } impl EnvBuilder { @@ -200,6 +202,13 @@ impl EnvBuilder { self } + /// Sets an env var for all commands executed in the container + pub fn env(&mut self, key: impl AsRef, val: impl AsRef) -> &mut Self { + self.env + .push((key.as_ref().to_owned(), val.as_ref().to_owned())); + self + } + /// builds the test environment /// /// # Panics @@ -283,6 +292,7 @@ impl EnvBuilder { let env = Env { container, users: usernames, + env: self.env.clone(), }; if cfg!(target_os = "freebsd") { @@ -426,7 +436,7 @@ impl User { let group_list = self.groups.iter().cloned().collect::>().join(","); useradd.arg("-G").arg(group_list); } - container.output(&useradd)?.assert_success()?; + container.output(&useradd, &[])?.assert_success()?; if let Some(password) = &self.password { container @@ -434,6 +444,7 @@ impl User { Command::new("pw") .args(["usermod", "-n", &self.name, "-h", "0"]) .stdin(password), + &[], )? .assert_success()?; } @@ -454,11 +465,14 @@ impl User { useradd.arg("--groups").arg(group_list); } useradd.arg(&self.name); - container.output(&useradd)?.assert_success()?; + container.output(&useradd, &[])?.assert_success()?; if let Some(password) = &self.password { container - .output(Command::new("chpasswd").stdin(format!("{}:{password}", self.name)))? + .output( + Command::new("chpasswd").stdin(format!("{}:{password}", self.name)), + &[], + )? .assert_success()?; } } else { @@ -527,7 +541,7 @@ impl Group { groupadd.arg("-g"); groupadd.arg(id.to_string()); } - container.output(&groupadd)?.assert_success() + container.output(&groupadd, &[])?.assert_success() } else if cfg!(target_os = "linux") { let mut groupadd = Command::new("groupadd"); if let Some(id) = self.id { @@ -535,7 +549,7 @@ impl Group { groupadd.arg(id.to_string()); } groupadd.arg(&self.name); - container.output(&groupadd)?.assert_success() + container.output(&groupadd, &[])?.assert_success() } else { todo!(); } @@ -613,10 +627,10 @@ impl TextFile { container.cp(path, &contents)?; container - .output(Command::new("chown").args([&self.chown, path]))? + .output(Command::new("chown").args([&self.chown, path]), &[])? .assert_success()?; container - .output(Command::new("chmod").args([&self.chmod, path]))? + .output(Command::new("chmod").args([&self.chmod, path]), &[])? .assert_success() } } @@ -689,13 +703,13 @@ impl Directory { fn create(&self, container: &Container) -> Result<()> { let path = &self.path; container - .output(Command::new("mkdir").args([path]))? + .output(Command::new("mkdir").args([path]), &[])? .assert_success()?; container - .output(Command::new("chown").args([&self.chown, path]))? + .output(Command::new("chown").args([&self.chown, path]), &[])? .assert_success()?; container - .output(Command::new("chmod").args([&self.chmod, path]))? + .output(Command::new("chmod").args([&self.chmod, path]), &[])? .assert_success() } } @@ -718,7 +732,7 @@ impl From<&'_ str> for Directory { fn getent_group(container: &Container) -> Result<(HashSet, HashSet)> { let stdout = container - .output(Command::new("getent").arg("group"))? + .output(Command::new("getent").arg("group"), &[])? .stdout()?; let mut groupnames = HashSet::new(); let mut group_ids = HashSet::new(); @@ -740,7 +754,7 @@ fn getent_group(container: &Container) -> Result<(HashSet, HashSet Result<(HashSet, HashSet)> { let stdout = container - .output(Command::new("getent").arg("passwd"))? + .output(Command::new("getent").arg("passwd"), &[])? .stdout()?; let mut usernames = HashSet::new(); let mut user_ids = HashSet::new();