diff --git a/config b/config index e7179150..d3086b49 100644 --- a/config +++ b/config @@ -82,6 +82,9 @@ # Wait for the post-update scripts to complete (boolean value) #wait_for_scripts= +# Sets an automatic response to all prompts and run non-interactively (string value) +#assume= + # # The sections below contain the options that apply specifically to diff --git a/docs/swupd.1.rst b/docs/swupd.1.rst index 51f37ce6..535334bc 100644 --- a/docs/swupd.1.rst +++ b/docs/swupd.1.rst @@ -144,6 +144,10 @@ used to modify the core behavior and resources that swupd uses. Prints the swupd output as a machine readable JSON stream. +- ``-y, --yes`` + + Assume yes as answer to all prompts and run non-interactively. + - ``--allow-insecure-http`` For security reasons, swupd only allows system updates using @@ -175,6 +179,10 @@ used to modify the core behavior and resources that swupd uses. Wait for the post-update scripts to complete. +- ``--assume={yes|no}`` + + Sets an automatic response to all prompts and run non-interactively. + SUBCOMMANDS =========== diff --git a/src/3rd_party_bundle_add.c b/src/3rd_party_bundle_add.c index e09f9b29..c71cae36 100644 --- a/src/3rd_party_bundle_add.c +++ b/src/3rd_party_bundle_add.c @@ -166,8 +166,8 @@ static enum swupd_code validate_file_permissions(struct list *files_to_be_instal if (ret_code == SWUPD_NO) { /* the bundle has files with dangerous permissions, * ask the user wether to continue or not */ - info("\n"); - if (confirm_action("The 3rd-party bundle you are about to install contains files with dangerous permission", " with the installation")) { + warn("\nThe 3rd-party bundle you are about to install contains files with dangerous permission\n"); + if (confirm_action()) { ret_code = SWUPD_OK; } else { ret_code = SWUPD_INVALID_FILE; diff --git a/src/3rd_party_update.c b/src/3rd_party_update.c index ac50028c..a5de9ac8 100644 --- a/src/3rd_party_update.c +++ b/src/3rd_party_update.c @@ -224,8 +224,8 @@ static enum swupd_code validate_file_permissions(struct list *files_to_be_update if (ret_code == SWUPD_NO) { /* the bundle has files with dangerous permissions, * ask the user wether to continue or not */ - info("\n"); - if (confirm_action("The 3rd-party update you are about to install contains files with dangerous permission", " with the update")) { + warn("\nThe 3rd-party update you are about to install contains files with dangerous permission\n"); + if (confirm_action()) { ret_code = SWUPD_OK; } else { ret_code = SWUPD_INVALID_FILE; diff --git a/src/globals.c b/src/globals.c index d830491c..d281c983 100644 --- a/src/globals.c +++ b/src/globals.c @@ -42,10 +42,12 @@ #define FLAG_DEBUG 1003 #define FLAG_ALLOW_INSECURE_HTTP 1004 #define FLAG_VERBOSE 1005 +#define FLAG_ASSUME 1006 struct globals globals = { .sigcheck = true, .timecheck = true, + .user_interaction = INTERACTIVE, .max_retries = DEFAULT_MAX_RETRIES, .retry_delay = DEFAULT_RETRY_DELAY, .update_server_port = -1, @@ -412,6 +414,34 @@ bool set_default_urls() return true; } +bool set_assume_option(char *option) +{ + bool ret; + char *option_lower = NULL; + + if (!option) { + error("Option shouldn't be NULL, please choose either 'yes' or 'no' as your option\n"); + return false; + } + + option_lower = str_tolower(option); + + if (strcmp(option_lower, "y") == 0 || strcmp(option_lower, "yes") == 0) { + globals.user_interaction = NON_INTERACTIVE_ASSUME_YES; + ret = true; + } else if (strcmp(option_lower, "n") == 0 || strcmp(option_lower, "no") == 0) { + globals.user_interaction = NON_INTERACTIVE_ASSUME_NO; + ret = true; + } else { + error("Please choose either 'yes' or 'no' as your non interactive option\n"); + ret = false; + } + + free_string(&option_lower); + + return ret; +} + bool globals_init(void) { if (!globals.state_dir) { @@ -524,6 +554,8 @@ static const struct option global_opts[] = { { "allow-insecure-http", no_argument, 0, FLAG_ALLOW_INSECURE_HTTP }, { "wait-for-scripts", no_argument, 0, FLAG_WAIT_FOR_SCRIPTS }, { "verbose", no_argument, 0, FLAG_VERBOSE }, + { "assume", required_argument, 0, FLAG_ASSUME }, + { "yes", no_argument, 0, 'y' }, { 0, 0, 0, 0 } }; @@ -613,6 +645,8 @@ static bool global_parse_opt(int opt, char *optarg) case 'j': json_format = optarg_to_bool(optarg); return true; + case 'y': + return set_assume_option("y"); case FLAG_NO_PROGRESS: progress_set_enabled(!optarg_to_bool(optarg)); return true; @@ -631,6 +665,8 @@ static bool global_parse_opt(int opt, char *optarg) case FLAG_VERBOSE: verbose = true; return true; + case FLAG_ASSUME: + return set_assume_option(optarg); default: return false; } @@ -683,12 +719,14 @@ void global_print_help(void) print(" -N, --no-scripts Do not run the post-update scripts and boot update tool\n"); print(" -b, --no-boot-update Do not install boot files to the boot partition (containers)\n"); print(" -j, --json-output Print all output as a JSON stream\n"); + print(" -y, --yes Assume yes as answer to all prompts and run non-interactively\n"); print(" --allow-insecure-http Allow updates over insecure connections\n"); print(" --quiet Quiet output. Print only important information and errors\n"); print(" --verbose Enable verbosity for commands\n"); print(" --debug Print extra information to help debugging problems\n"); print(" --no-progress Don't print progress report\n"); print(" --wait-for-scripts Wait for the post-update scripts to complete\n"); + print(" --assume=[yes|no] Sets an automatic response to all prompts and run non-interactively\n"); print("\n"); } diff --git a/src/globals.h b/src/globals.h index 4ec618f9..d69df476 100644 --- a/src/globals.h +++ b/src/globals.h @@ -18,6 +18,12 @@ extern "C" { #define optarg_to_bool(_optarg) (_optarg ? strtobool(_optarg) : true) +enum user_interaction { + INTERACTIVE = 0, + NON_INTERACTIVE_ASSUME_YES, + NON_INTERACTIVE_ASSUME_NO +}; + /* * Global variables */ @@ -50,6 +56,7 @@ extern struct globals { int update_count; int update_server_port; int update_skip; + int user_interaction; timelist *global_times; } globals; diff --git a/src/helpers.c b/src/helpers.c index fa5ce3e1..20a86f52 100644 --- a/src/helpers.c +++ b/src/helpers.c @@ -1201,16 +1201,19 @@ char *get_tracking_dir(void) return sys_path_join(globals.state_dir, "bundles"); } -bool confirm_action(const char *warning_msg, const char *action) +bool confirm_action(void) { int response; - if (warning_msg) { - warn("%s\n", warning_msg); + info("Do you want to continue? (y/N): "); + if (globals.user_interaction == INTERACTIVE) { + response = tolower(getchar()); + info("\n"); + } else { + info("%s\n", globals.user_interaction == NON_INTERACTIVE_ASSUME_YES ? "y" : "N"); + info("The \"--assume=%s\" option was used\n", globals.user_interaction == NON_INTERACTIVE_ASSUME_YES ? "yes" : "no"); + response = globals.user_interaction == NON_INTERACTIVE_ASSUME_YES ? 'y' : 'n'; } - info("Do you want to continue%s? (y/N): ", action ? action : ""); - response = tolower(getchar()); - info("%s\n", response == 'y' ? "y" : "N"); return response == 'y'; } diff --git a/src/swupd.h b/src/swupd.h index b08f307b..23b9a7d7 100644 --- a/src/swupd.h +++ b/src/swupd.h @@ -319,7 +319,7 @@ extern void print_header(const char *header); extern void prettify_size(long size_in_bytes, char **pretty_size); extern int link_or_rename(const char *orig, const char *dest); extern int create_state_dirs(const char *state_dir_path); -extern bool confirm_action(const char *warning_msg, const char *action); +extern bool confirm_action(void); /* subscription.c */ typedef bool (*subs_fn_t)(struct list **subs, const char *component, int recursion, bool is_optional); diff --git a/swupd.bash b/swupd.bash index d6ef47c3..56bee3ac 100644 --- a/swupd.bash +++ b/swupd.bash @@ -23,7 +23,7 @@ _swupd() # $1 is the command being completed, $2 is the current word being expanded local opts IFS=$' \t\n' local -i i installed - local global="--help --url --contenturl --versionurl --port --path --format --nosigcheck --ignore-time --statedir --certpath --time --no-scripts --no-boot-update --max-parallel-downloads --max-retries --retry-delay --json-output --allow-insecure-http --debug --verbose --quiet --no-progress --wait-for-scripts" + local global="--help --url --contenturl --versionurl --port --path --format --nosigcheck --ignore-time --statedir --certpath --time --no-scripts --no-boot-update --max-parallel-downloads --max-retries --retry-delay --json-output --allow-insecure-http --debug --verbose --quiet --no-progress --wait-for-scripts --assume --yes" COMPREPLY=() for ((i=COMP_CWORD-1;i>=0;i--)) do case "${COMP_WORDS[$i]}" in diff --git a/swupd.zsh b/swupd.zsh index 1711c2cd..14281db8 100644 --- a/swupd.zsh +++ b/swupd.zsh @@ -143,8 +143,10 @@ local -a global_opts; global_opts=( '(help status -t --time)'{-t,--time}'[Show verbose time output for swupd operations]' '(help status -N --no-scripts)'{-N,--no-scripts}'[Do not run the post-update scripts and boot update tool]' '(help status -b --no-boot-update)'{-b,--no-boot-update}'[Do not install boot files to the boot partition (containers)]' + '(help status -y --yes)'{-y,--yes}'[Assume yes as answer to all prompts and run non-interactively]' '(help status)--no-progress[Don`t print progress report]' '(help status)--wait-for-scripts[Wait for the post-update scripts to complete]' + '(help status)--assume[Sets an automatic response to all prompts and run non-interactively]' ) # Level-1 completion for sub-command and options to swupd diff --git a/test/functional/3rd-party/3rd-party-bundle-add-dangerous-flags.bats b/test/functional/3rd-party/3rd-party-bundle-add-dangerous-flags.bats index a818867d..c29bd9af 100755 --- a/test/functional/3rd-party/3rd-party-bundle-add-dangerous-flags.bats +++ b/test/functional/3rd-party/3rd-party-bundle-add-dangerous-flags.bats @@ -43,7 +43,7 @@ test_setup() { Warning: File /bar/file_2 has dangerous permissions Warning: File /bar/file_3 has dangerous permissions Warning: The 3rd-party bundle you are about to install contains files with dangerous permission - Do you want to continue with the installation? (y/N): y + Do you want to continue? (y/N):$SPACE Installing files... Warning: post-update helper scripts skipped due to --no-scripts argument Exporting 3rd-party bundle binaries... @@ -76,7 +76,7 @@ test_setup() { Warning: File /bar/file_2 has dangerous permissions Warning: File /bar/file_3 has dangerous permissions Warning: The 3rd-party bundle you are about to install contains files with dangerous permission - Do you want to continue with the installation? (y/N): N + Do you want to continue? (y/N):$SPACE Aborting bundle installation... Failed to install 1 of 1 bundles EOM @@ -84,3 +84,61 @@ test_setup() { assert_is_output "$expected_output" } + +@test "TPR065: Trying to add a bundle that has dangerous flags from a 3rd-party repository using --assume to run non interactively" { + + # If a bundle from a 3rd-party repository has the setuid, setgid or sticky bit flags + # set, then it needs to use the --force flag to be installed since those flags are + # dangerous + + run sudo sh -c "$SWUPD 3rd-party bundle-add $SWUPD_OPTS test-bundle1 --assume=no" + + assert_status_is "$SWUPD_INVALID_FILE" + expected_output=$(cat <<-EOM + Searching for bundle test-bundle1 in the 3rd-party repositories... + Bundle test-bundle1 found in 3rd-party repository repo1 + Bundles added from a 3rd-party repository are forced to run with the --no-scripts flag for security reasons + Loading required manifests... + Validating 3rd-party bundle binaries... + No packs need to be downloaded + Validate downloaded files + Starting download of remaining update content. This may take a while... + Validating 3rd-party bundle file permissions... + Warning: File /bar/file_2 has dangerous permissions + Warning: File /bar/file_3 has dangerous permissions + Warning: The 3rd-party bundle you are about to install contains files with dangerous permission + Do you want to continue? (y/N): N + The "--assume=no" option was used + Aborting bundle installation... + Failed to install 1 of 1 bundles + EOM + ) + assert_is_output "$expected_output" + + run sudo sh -c "$SWUPD 3rd-party bundle-add $SWUPD_OPTS test-bundle1 --assume=yes" + + assert_status_is "$SWUPD_OK" + expected_output=$(cat <<-EOM + Searching for bundle test-bundle1 in the 3rd-party repositories... + Bundle test-bundle1 found in 3rd-party repository repo1 + Bundles added from a 3rd-party repository are forced to run with the --no-scripts flag for security reasons + Loading required manifests... + Validating 3rd-party bundle binaries... + No packs need to be downloaded + Validate downloaded files + No extra files need to be downloaded + Validating 3rd-party bundle file permissions... + Warning: File /bar/file_2 has dangerous permissions + Warning: File /bar/file_3 has dangerous permissions + Warning: The 3rd-party bundle you are about to install contains files with dangerous permission + Do you want to continue? (y/N): y + The "--assume=yes" option was used + Installing files... + Warning: post-update helper scripts skipped due to --no-scripts argument + Exporting 3rd-party bundle binaries... + Successfully installed 1 bundle + EOM + ) + assert_is_output "$expected_output" + +} diff --git a/test/functional/3rd-party/3rd-party-update-dangerous-flags.bats b/test/functional/3rd-party/3rd-party-update-dangerous-flags.bats index 03589756..d77cf920 100755 --- a/test/functional/3rd-party/3rd-party-update-dangerous-flags.bats +++ b/test/functional/3rd-party/3rd-party-update-dangerous-flags.bats @@ -68,7 +68,7 @@ test_setup() { Warning: The update has a new file /bar/file_4 with dangerous permissions Warning: The update sets dangerous permissions to file /foo/file_1 Warning: The 3rd-party update you are about to install contains files with dangerous permission - Do you want to continue with the update? (y/N): y + Do you want to continue? (y/N):$SPACE Installing files... Update was applied Updating 3rd-party bundle binaries... @@ -110,7 +110,7 @@ test_setup() { Warning: The update has a new file /bar/file_4 with dangerous permissions Warning: The update sets dangerous permissions to file /foo/file_1 Warning: The 3rd-party update you are about to install contains files with dangerous permission - Do you want to continue with the update? (y/N): N + Do you want to continue? (y/N):$SPACE Aborting update... Update failed EOM @@ -118,3 +118,81 @@ test_setup() { assert_is_output "$expected_output" } + +@test "TPR064: Try updating a system from a 3rd-party repo when the update has files with dangerous flags using --assume to run non interactively" { + + # the --non-interactive flag can be used to avoid getting prompots + # from swupd + + run sudo sh -c "$SWUPD 3rd-party update $SWUPD_OPTS --assume=no" + + assert_status_is "$SWUPD_INVALID_FILE" + expected_output=$(cat <<-EOM + _______________________ + 3rd-Party Repo: repo1 + _______________________ + Updates from a 3rd-party repository are forced to run with the --no-scripts flag for security reasons + Update started + Preparing to update from 10 to 20 + Downloading packs for: + - test-bundle1 + Finishing packs extraction... + Statistics for going from version 10 to version 20: + changed bundles : 1 + new bundles : 0 + deleted bundles : 0 + changed files : 3 + new files : 2 + deleted files : 0 + Validate downloaded files + No extra files need to be downloaded + Validating 3rd-party bundle file permissions... + Warning: The update has a new file /bar/file_4 with dangerous permissions + Warning: The update sets dangerous permissions to file /foo/file_1 + Warning: The 3rd-party update you are about to install contains files with dangerous permission + Do you want to continue? (y/N): N + The "--assume=no" option was used + Aborting update... + Update failed + EOM + ) + assert_is_output "$expected_output" + + run sudo sh -c "$SWUPD 3rd-party update $SWUPD_OPTS --yes" + + assert_status_is "$SWUPD_OK" + expected_output=$(cat <<-EOM + _______________________ + 3rd-Party Repo: repo1 + _______________________ + Updates from a 3rd-party repository are forced to run with the --no-scripts flag for security reasons + Update started + Preparing to update from 10 to 20 + Downloading packs for: + - test-bundle1 + Finishing packs extraction... + Statistics for going from version 10 to version 20: + changed bundles : 1 + new bundles : 0 + deleted bundles : 0 + changed files : 3 + new files : 2 + deleted files : 0 + Validate downloaded files + No extra files need to be downloaded + Validating 3rd-party bundle file permissions... + Warning: The update has a new file /bar/file_4 with dangerous permissions + Warning: The update sets dangerous permissions to file /foo/file_1 + Warning: The 3rd-party update you are about to install contains files with dangerous permission + Do you want to continue? (y/N): y + The "--assume=yes" option was used + Installing files... + Update was applied + Updating 3rd-party bundle binaries... + Warning: post-update helper scripts skipped due to --no-scripts argument + Update successful - System updated from version 10 to version 20 + EOM + ) + assert_is_output "$expected_output" + +} diff --git a/test/functional/testlib.bash b/test/functional/testlib.bash index 15b5b142..d3147e07 100644 --- a/test/functional/testlib.bash +++ b/test/functional/testlib.bash @@ -1,7 +1,5 @@ #!/usr/bin/bash -set -e - FUNC_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" TEST_ROOT_DIR="$(pwd)" TEST_FILENAME=$(basename "$BATS_TEST_FILENAME") @@ -9,6 +7,8 @@ TEST_NAME=${TEST_FILENAME%.bats} THEME_DIRNAME="$BATS_TEST_DIRNAME" THIRD_PARTY_BUNDLES_DIR="opt/3rd-party/bundles" THIRD_PARTY_BIN_DIR="opt/3rd-party/bin" +SPACE=" " +TAB=" " export TEST_NAME export TEST_NAME_SHORT="$TEST_NAME" @@ -17,6 +17,8 @@ export FUNC_DIR export SWUPD_DIR="$FUNC_DIR/../.." export THIRD_PARTY_BUNDLES_DIR export THIRD_PARTY_BIN_DIR +export SPACE +export TAB # detect where the swupd binary is if [ -e "$SWUPD" ]; then