commit ac9120660432d96504a818249a9a9b60906ef885
parent 6a286d4509cad4e0658b9e3a51c8ce2470faa777
Author: Natasha Kerensikova <natgh@instinctive.eu>
Date: Sat, 16 Nov 2024 07:30:54 +0000
Generate command optionally asks for confirmation before saving
Diffstat:
5 files changed, 169 insertions(+), 12 deletions(-)
diff --git a/README.md b/README.md
@@ -81,6 +81,10 @@ passwords.
- The `generate` command has a new command-line argument to specify
explicitly the character set.
+- The `generate` command optionally asks for confirmation before storing
+the generated secret (e.g. for iterative attempts against stupid password
+rules)
+
- The `init` command has new flags to control re-encryption (never or
ask for each file).
@@ -102,8 +106,6 @@ The following features are currently under consideration:
- partial display of secrets on standard output
- successive clipboard copy of several lines from a single decryption
(e.g. username then password)
-- optional interactive confirmation between generation and encryption
-(e.g. for iterative attempts against stupid password rules)
- OTP support
- maybe extension support?
@@ -274,7 +276,8 @@ Environment:
```
pashage generate [--no-symbols,-n] [--clip,-c | --qrcode,-q]
- [--in-place,-i | --force,-f] pass-name [pass-length]
+ [--in-place,-i | --force,-f] [--try,-t]
+ pass-name [pass-length [character-set]]
```
This subcommand generates a new secret from `/dev/urandom`, stores it in
@@ -291,6 +294,8 @@ Flags:
characters
- `-q` or `--qrcode`: display the secret as a QR-code instead of using the
standard output
+- `-t` or `--try`: display the secret and ask for confirmation before
+ storing it into the database
Environment:
- `CLICOLOR`: when set to a non-empty value, use ANSI escape sequences to
@@ -305,10 +310,10 @@ Environment:
when `PASHAGE_DIR` is unset
- `PASSAGE_IDENTITIES_FILE`: _identity_ file to use instead of
`~/.passage/identities` when `PASHAGE_IDENTITIES_FILE` is unset
-- `PASSWORD_STORE_CHARACTER_SET_NO_SYMBOLS`: character set to use with
- `tr(1)` when `-n` is specified, instead of `[:alnum:]`
-- `PASSWORD_STORE_CHARACTER_SET`: character set to use with `tr(1)` when
- `-n` is not specified, instead of `[:punct:][:alnum:]`
+- `PASSWORD_STORE_CHARACTER_SET_NO_SYMBOLS`: default character set to use
+ with `tr(1)` when `-n` is specified, instead of `[:alnum:]`
+- `PASSWORD_STORE_CHARACTER_SET`: default character set to use with `tr(1)`
+ when `-n` is not specified, instead of `[:punct:][:alnum:]`
- `PASSWORD_STORE_CLIP_TIME`: number of second before clearing the
clipboard when `-c` is used, instead of 45
- `PASSWORD_STORE_DIR`: database directory to use instead of
diff --git a/spec/action_spec.sh b/spec/action_spec.sh
@@ -1032,6 +1032,7 @@ Describe 'Action Functions'
End
Describe 'do_generate'
+ DECISION=default
PREFIX="${SHELLSPEC_WORKDIR}/prefix"
SHOW=none
@@ -1253,6 +1254,48 @@ Describe 'Action Functions'
The output should equal 'Decrypting previous secret for existing'
The error should equal "$(result)"
End
+
+ It 'saves the password after showing it and getting confirmation'
+ DECISION=interactive
+ yesno() {
+ mocklog yesno "$@"
+ ANSWER=y
+ }
+ result(){
+ %text:expand
+ #|$ do_show sub/new
+ #|> 0123456789
+ #|$ yesno Save generated password for sub/new?
+ #|$ scm_begin
+ #|$ mkdir -p -- ${PREFIX}/sub
+ #|$ do_encrypt sub/new.age
+ #|> 0123456789
+ #|$ scm_add ${PREFIX}/sub/new.age
+ #|$ scm_commit Add generated password for sub/new.
+ }
+ When call do_generate sub/new 10 '[alnum:]'
+ The status should be success
+ The output should be blank
+ The error should equal "$(result)"
+ End
+
+ It 'does not save the password after showing it and getting cancellation'
+ DECISION=interactive
+ yesno() {
+ mocklog yesno "$@"
+ ANSWER=n
+ }
+ result(){
+ %text:expand
+ #|$ do_show sub/new
+ #|> 0123456789
+ #|$ yesno Save generated password for sub/new?
+ }
+ When call do_generate sub/new 10 '[alnum:]'
+ The status should be success
+ The output should be blank
+ The error should equal "$(result)"
+ End
End
Describe 'do_grep'
diff --git a/spec/pashage_extra_spec.sh b/spec/pashage_extra_spec.sh
@@ -612,6 +612,7 @@ Describe 'Integrated Command Functions'
End
Describe 'cmd_generate'
+ DECISION=default
MULTILINE=no
OVERWRITE=no
SHOW=text
@@ -688,6 +689,42 @@ Describe 'Integrated Command Functions'
The output should equal 'Decrypting previous secret for stale'
The result of function check_git_log should be successful
End
+
+ It 'saves after showing and getting confirmation'
+ Data 'y'
+ When call cmd_generate --try new
+ The status should be success
+ The error should be blank
+ The file "${PREFIX}/new.age" should be exist
+ expected_out() {
+ %putsn '(B)The generated password for (U)new(!U) is:(N)'
+ @sed '$s/^age://p;d' "${PREFIX}/new.age"
+ %putsn 'Save generated password for new? [y/n]'
+ }
+ The output should equal "$(expected_out)"
+ expected_log() { %text
+ #|Add generated password for new.
+ #|
+ #| new.age | 2 ++
+ #| 1 file changed, 2 insertions(+)
+ setup_log
+ }
+ The result of function check_git_log should be successful
+ End
+
+ It 'does not save after showing and getting cancellation'
+ Data 'n'
+ When call cmd_generate --try new 5 '[:lower:]'
+ The status should be success
+ The error should be blank
+ The lines of output should equal 3
+ The line 1 of output should \
+ equal '(B)The generated password for (U)new(!U) is:(N)'
+ The line 2 of output should match pattern '[a-z][a-z][a-z][a-z][a-z]'
+ The line 3 of output should \
+ equal 'Save generated password for new? [y/n]'
+ The result of function check_git_log should be successful
+ End
End
Describe 'cmd_git'
@@ -1509,6 +1546,7 @@ Describe 'Integrated Command Functions'
# 'find' does not change the repository
Example 'generate'
+ DECISION=default
MULTILINE=no
OVERWRITE=no
When run cmd_generate new-pass
diff --git a/spec/usage_spec.sh b/spec/usage_spec.sh
@@ -96,6 +96,7 @@ Describe 'Command-Line Parsing'
do_generate() {
mocklog do_generate "$@"
%text:expand >&2
+ #|DECISION=${DECISION}
#|MULTILINE=${MULTILINE}
#|OVERWRITE=${OVERWRITE}
#|SELECTED_LINE=${SELECTED_LINE}
@@ -626,7 +627,7 @@ Describe 'Command-Line Parsing'
usage_text() { %text
#|Usage: prg generate [--no-symbols,-n] [--clip,-c | --qrcode,-q]
- #| [--in-place,-i | --force,-f]
+ #| [--in-place,-i | --force,-f] [--try,-t]
#| pass-name [pass-length [character-set]]
}
@@ -635,6 +636,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:punct:][:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -651,6 +653,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 12 [:punct:][:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -667,6 +670,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 12 [A-Z]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -683,6 +687,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path -f
#|$ do_generate -f 25 [:punct:][:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -699,6 +704,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:punct:][:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -715,6 +721,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:punct:][:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -731,6 +738,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:punct:][:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -747,6 +755,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:punct:][:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -763,6 +772,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -779,6 +789,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -795,6 +806,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -811,6 +823,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -827,6 +840,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:punct:][:alnum:]
+ #|DECISION=default
#|MULTILINE=yes
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -843,6 +857,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:punct:][:alnum:]
+ #|DECISION=default
#|MULTILINE=yes
#|OVERWRITE=no
#|SELECTED_LINE=1
@@ -859,6 +874,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:punct:][:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=yes
#|SELECTED_LINE=1
@@ -875,6 +891,7 @@ Describe 'Command-Line Parsing'
%text
#|$ check_sneaky_path secret
#|$ do_generate secret 25 [:punct:][:alnum:]
+ #|DECISION=default
#|MULTILINE=no
#|OVERWRITE=yes
#|SELECTED_LINE=1
@@ -886,6 +903,40 @@ Describe 'Command-Line Parsing'
The error should equal "$(result)"
End
+ It 'asks for confirmation before saving the generated password (long)'
+ result() {
+ %text
+ #|$ check_sneaky_path secret
+ #|$ do_generate secret 25 [:punct:][:alnum:]
+ #|DECISION=interactive
+ #|MULTILINE=no
+ #|OVERWRITE=no
+ #|SELECTED_LINE=1
+ #|SHOW=text
+ }
+ When call cmd_generate --try secret
+ The status should be success
+ The output should be blank
+ The error should equal "$(result)"
+ End
+
+ It 'asks for confirmation before saving the generated password (short)'
+ result() {
+ %text
+ #|$ check_sneaky_path secret
+ #|$ do_generate secret 25 [:punct:][:alnum:]
+ #|DECISION=interactive
+ #|MULTILINE=no
+ #|OVERWRITE=no
+ #|SELECTED_LINE=1
+ #|SHOW=text
+ }
+ When call cmd_generate -t secret
+ The status should be success
+ The output should be blank
+ The error should equal "$(result)"
+ End
+
It 'reports incompatible generation long options'
cat() { @cat; }
When run cmd_generate --in-place --force secret
diff --git a/src/pashage.sh b/src/pashage.sh
@@ -610,6 +610,7 @@ do_encrypt() {
# $1: secret name
# $2: new password length
# $3: new password charset
+# DECISION: when interactive, show-ask-commit instead of commit-show
# MULTILINE: whether to re-use existing secret data
# OVERWRITE: whether to overwrite without confirmation
do_generate() {
@@ -622,6 +623,20 @@ do_generate() {
fi
unset NEW_PASS_LEN
+ if [ "${DECISION}" = interactive ]; then
+ do_generate_show "$@"
+ yesno "Save generated password for $1?"
+ [ "${ANSWER}" = y ] && do_generate_commit "$@"
+ else
+ do_generate_commit "$@"
+ [ "${ANSWER-y}" = y ] && do_generate_show "$@"
+ fi
+
+ unset NEW_PASS
+}
+
+# SCM-committing part of do_generate
+do_generate_commit() {
scm_begin
mkdir -p -- "$(dirname "${PREFIX}/$1.age")"
@@ -670,7 +685,10 @@ do_generate() {
scm_commit "${VERB} generated password for $1."
unset VERB
+}
+# Showing part of do_generate
+do_generate_show() {
if [ "${SHOW}" = text ]; then
printf '%sThe generated password for %s%s%s is:%s\n' \
"${BOLD_TEXT}" \
@@ -683,8 +701,6 @@ do_generate() {
do_show "$1" <<-EOF
${NEW_PASS}
EOF
-
- unset NEW_PASS
}
# Recursively grep decrypted secrets in current directory
@@ -1222,7 +1238,10 @@ cmd_generate() {
fi
SHOW=qrcode
shift ;;
- -[cfinq]?*)
+ -t|--try)
+ DECISION=interactive
+ shift ;;
+ -[cfinqt]?*)
REST="${1#-?}"
ARG="${1%"${REST}"}"
shift
@@ -1664,7 +1683,7 @@ EOF
generate)
cat <<EOF
${F}${PROGRAM} generate [--no-symbols,-n] [--clip,-c | --qrcode,-q]
-${I}${BLANKPG} [--in-place,-i | --force,-f]
+${I}${BLANKPG} [--in-place,-i | --force,-f] [--try,-t]
${I}${BLANKPG} pass-name [pass-length [character-set]]
EOF
[ "${VERBOSE}" = yes ] && cat <<EOF
@@ -1675,6 +1694,7 @@ ${I} or display it as a QR-code.
${I} Prompt before overwriting existing password unless forced.
${I} Optionally replace only the first line of an existing file
${I} with a new password.
+${I} Optionally prompt for confirmation between generation and saving.
EOF
;;
git)