Skip to content

Commit

Permalink
Merge pull request #175 from mbland/safe-prompt
Browse files Browse the repository at this point in the history
Add @go.prompt_for_safe_input to lib/prompt
  • Loading branch information
mbland committed Apr 5, 2017
2 parents 38e79b6 + d751a99 commit 9319b70
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 5 deletions.
39 changes: 39 additions & 0 deletions lib/prompt
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
# @go.prompt_for_input
# Prompts the user for a line of input
#
# @go.prompt_for_safe_input
# Prompts the user for a line of input, then validates it isn't dangerous
#
# @go.prompt_for_yes_or_no
# Prompts the user for a yes or no response
#
Expand Down Expand Up @@ -61,6 +64,42 @@
fi
}

# Prompts the user for a line of input, then validates it isn't dangerous
#
# With the exception of the `--or-die` option, the arguments are the same as the
# underlying `@go.prompt_for_input`. Useful if the input value may be used to
# construct a command or query.
#
# Options (must be specified before arguments):
# --or-die <desc> Print description and stack and exit with error if invalid
#
# Arguments:
# result_var Name of the caller-declared variable for the result
# prompt Text prompt for user input
# default (Optional) Default value if response is empty
# fail_msg (Optional) Failure message if empty input isn't valid
@go.prompt_for_safe_input() {
local or_die
local description

if [[ "$1" == '--or-die' ]]; then
or_die='true'
description="$2"
shift 2
fi
@go.validate_identifier_or_die 'Input prompt response variable name' "$1"

if ! @go.prompt_for_input "$@"; then
return 1
elif [[ "$or_die" == 'true' ]]; then
@go.validate_input_or_die "$description" "${!1}"
elif ! @go.validate_input "${!1}"; then
@go.printf '"%s" is an invalid response, as it contains %s.\n' \
"${!1}" 'unescaped shell metacharacters or control operators' >&2
return 1
fi
}

# Prompts the user for a yes or no response
#
# Arguments:
Expand Down
5 changes: 3 additions & 2 deletions lib/validation
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,9 @@ readonly __GO_VALID_IDENTIFIER_PATTERN='^[[:alpha:]_][[:alnum:]_]*$'
local skip_callers="${3:-2}"

if ! @go.validate_input "$value"; then
printf '%s "%s" for %s contains invalid characters at:\n' \
"$description" "$value" "${FUNCNAME[$((skip_callers - 1))]}" >&2
printf '%s "%s" for %s contains %s at:\n' \
"$description" "$value" "${FUNCNAME[$((skip_callers - 1))]}" \
'unescaped shell metacharacters or control operators' >&2
@go.print_stack_trace "$skip_callers" >&2
exit 1
fi
Expand Down
3 changes: 2 additions & 1 deletion tests/file/open-or-dup.bats
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,8 @@ create_file_open_test_go_script() {
FILE_PATH_OR_FD="$file_path_or_fd" run "$TEST_GO_SCRIPT"

local err_msg="file_path_or_fd \"$file_path_or_fd\" "
err_msg+='for @go.open_file_or_duplicate_fd contains invalid characters at:'
err_msg+='for @go.open_file_or_duplicate_fd contains '
err_msg+='unescaped shell metacharacters or control operators at:'

local expected=("$err_msg"
" $TEST_GO_SCRIPT:5 main")
Expand Down
95 changes: 95 additions & 0 deletions tests/prompt/prompt-for-safe-input.bats
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#! /usr/bin/env bats

load ../environment

setup() {
test_filter
@go.create_test_go_script '. "$_GO_USE_MODULES" "prompt"' \
'declare prompt="$1"' \
'declare default="$2"' \
'declare fail_msg="$3"' \
'declare response="initial value"' \
'declare result' \
'@go.prompt_for_safe_input 'response' "$prompt" "$default" "$fail_msg"' \
'result="$?"' \
'if [[ "$result" -eq "0" ]]; then' \
' printf -- "%s\n" "$response"' \
'fi' \
'exit "$result"'
}

teardown() {
@go.remove_test_go_rootdir
}

@test "$SUITE: error if variable not a valid identifier" {
@go.create_test_go_script '. "$_GO_USE_MODULES" "prompt"' \
'@go.prompt_for_safe_input "invalid;"'

run "$TEST_GO_SCRIPT"
assert_failure

local err_msg='Input prompt response variable name "invalid;" for '
err_msg+='@go.prompt_for_safe_input '
err_msg+='contains invalid identifier characters at:'

assert_lines_match "^${err_msg}\$" \
"^ $TEST_GO_SCRIPT:[0-9] main$"

}

@test "$SUITE: reads value with no metacharacters or control operators" {
run "$TEST_GO_SCRIPT" $'What is your quest?\n' <<<'To seek the grail!'
assert_success 'What is your quest?' \
'To seek the grail!'
}

@test "$SUITE: reads a value with an escaped metacharacter" {
run "$TEST_GO_SCRIPT" $'What is your quest?\n' <<<"To seek the grail\;"
assert_success 'What is your quest?' \
"To seek the grail\;"
}

@test "$SUITE: fails when reading a value with an unescaped metacharacter" {
run "$TEST_GO_SCRIPT" $'What is your quest?\n' <<<'To seek the grail;'

local err_msg='"To seek the grail;" is an invalid response, as it contains '
err_msg+='unescaped shell metacharacters or control operators.'

assert_failure 'What is your quest?' \
"$err_msg"
}

@test "$SUITE: fails when the default value has an unescaped metacharacter" {
run "$TEST_GO_SCRIPT" $'What is your quest?\n' 'To seek the grail;' <<<''

local err_msg='"To seek the grail;" is an invalid response, as it contains '
err_msg+='unescaped shell metacharacters or control operators.'

assert_failure 'What is your quest? [default: To seek the grail;]' \
"$err_msg"
}

@test "$SUITE: fails when no default or input provided" {
run "$TEST_GO_SCRIPT" $'What is your quest?\n' '' 'Auuuuuuuugh!' <<<''
assert_failure 'What is your quest?' \
'Auuuuuuuugh!'
}

@test "$SUITE: --or-die exits when the value has an unescaped metacharacter" {
@go.create_test_go_script '. "$_GO_USE_MODULES" "prompt"' \
'declare response="initial value"' \
"@go.prompt_for_safe_input --or-die 'Quest response' 'response' \\" \
$' "What is your quest?\n"'

run "$TEST_GO_SCRIPT" <<<'To seek the grail;'
assert_failure

local err_msg='^Quest response "To seek the grail;" for '
err_msg+='@go.prompt_for_safe_input contains '
err_msg+='unescaped shell metacharacters or control operators at:$'

assert_lines_match 'What is your quest?' \
"$err_msg" \
" $TEST_GO_SCRIPT:[0-9] main"
}
4 changes: 2 additions & 2 deletions tests/validation.bats
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ assert_success_on_valid_input() {
assert_failure

local err_msg='^input argument "foo;bar" for @go\.validate_input_or_die '
err_msg+='contains invalid characters at:$'
err_msg+='contains unescaped shell metacharacters or control operators at:$'

assert_lines_match "$err_msg" \
" $TEST_GO_SCRIPT:[0-9] main"
Expand All @@ -105,7 +105,7 @@ assert_success_on_valid_input() {
run "$TEST_GO_SCRIPT"
assert_failure
assert_lines_match \
'^input argument "foo;bar" for test_func contains invalid characters at:$' \
'^input argument "foo;bar" for test_func contains unescaped .* at:$' \
" $TEST_GO_SCRIPT:[0-9] main"
}

Expand Down

0 comments on commit 9319b70

Please sign in to comment.