commit 08ed7dd12a284c4a05bcc1f8c345d1ffcc799d3a
parent 483fc8febac23f8015aaaf8850d399403a5d1459
Author: Natasha Kerensikova <natgh@instinctive.eu>
Date: Sat, 9 Nov 2024 13:18:56 +0000
Success of piped expressions is corrected and tested
Diffstat:
4 files changed, 207 insertions(+), 21 deletions(-)
diff --git a/spec/action_spec.sh b/spec/action_spec.sh
@@ -1139,7 +1139,6 @@ Describe 'Action Functions'
It 'updates the first line of an existing file'
OVERWRITE=yes
mktemp() { %= "$1"; }
- tail() { @tail "$@"; }
do_decrypt() {
mocklog do_decrypt "$@"
%text
@@ -1168,6 +1167,34 @@ Describe 'Action Functions'
The output should equal 'Decrypting previous secret for existing'
The error should equal "$(result)"
End
+
+ It 'updates the only line of an existing one-line file'
+ OVERWRITE=yes
+ mktemp() { %= "$1"; }
+ do_decrypt() {
+ mocklog do_decrypt "$@"
+ %text
+ #|old password
+ }
+ mv() { mocklog mv "$@"; }
+ result(){
+ %text:expand
+ #|$ scm_begin
+ #|$ mkdir -p -- ${PREFIX}
+ #|$ do_decrypt ${PREFIX}/existing.age
+ #|$ do_encrypt existing-XXXXXXXXX.age
+ #|> 0123456789
+ #|$ mv ${PREFIX}/existing-XXXXXXXXX.age ${PREFIX}/existing.age
+ #|$ scm_add ${PREFIX}/existing.age
+ #|$ scm_commit Replace generated password for existing.
+ #|$ do_show existing
+ #|> 0123456789
+ }
+ When call do_generate existing 10 '[alnum:]'
+ The status should be success
+ The output should equal 'Decrypting previous secret for existing'
+ The error should equal "$(result)"
+ End
End
Describe 'do_grep'
@@ -1208,6 +1235,21 @@ Describe 'Action Functions'
The status should be success
The output should equal "$(result)"
End
+
+ It 'outputs all the matching lines'
+ result(){
+ %text
+ #|(B)subdir/(G)match(N):
+ #|other
+ #|suffix
+ }
+ start_do_grep(){
+ ( cd "${PREFIX}" && do_grep '' "$@" )
+ }
+ When call start_do_grep -vea
+ The status should be success
+ The output should equal "$(result)"
+ End
End
Describe 'do_init'
@@ -1609,7 +1651,7 @@ Describe 'Action Functions'
mocklog do_encrypt "$@"
}
- mktemp() { %putsn "$1"; }
+ mktemp() { %putsn "${2-$1}"; }
mv() { mocklog mv "$@"; }
scm_add() { mocklog scm_add "$@"; }
scm_begin() { mocklog scm_begin "$@"; }
diff --git a/spec/pashage_extra_spec.sh b/spec/pashage_extra_spec.sh
@@ -344,6 +344,17 @@ Describe 'Integrated Command Functions'
The result of function check_git_log should be successful
End
+ It 'aborts on decryption failure even without pipefail'
+ Set 'pipefail:off'
+ mock-age() { false; }
+ When run cmd_move --reencrypt stale renamed
+ The status should equal 1
+ The error should equal \
+ "Fatal(1): mock-age -d -i ${IDENTITIES_FILE} -- ${PREFIX}/stale.age"
+ The output should be blank
+ The result of function check_git_log should be successful
+ End
+
It 'displays usage when called with incompatible reencryption arguments'
PROGRAM=prg
COMMAND=copy
@@ -544,6 +555,18 @@ Describe 'Integrated Command Functions'
The file "${PREFIX}/subdir/new.gpg" should not be exist
The result of function check_git_log should be successful
End
+
+ It 'aborts on decryption failure even without pipefail'
+ Set 'pipefail:off'
+ mock-age() { false; }
+ tail() { @tail "$@"; }
+ When run cmd_edit stale
+ The status should equal 1
+ The error should equal \
+ "Fatal(1): mock-age -d -i ${IDENTITIES_FILE} -- ${PREFIX}/stale.age"
+ The output should be blank
+ The result of function check_git_log should be successful
+ End
End
Describe 'cmd_find'
@@ -623,6 +646,18 @@ Describe 'Integrated Command Functions'
The result of function git_log should be successful
The contents of file "${GITLOG}" should equal "$(setup_log)"
End
+
+ It 'aborts on decryption failure even without pipefail'
+ Set 'pipefail:off'
+ mock-age() { false; }
+ tail() { @tail "$@"; }
+ When run cmd_generate --inplace stale
+ The status should equal 1
+ The error should equal \
+ "Fatal(1): mock-age -d -i ${IDENTITIES_FILE} -- ${PREFIX}/stale.age"
+ The output should equal 'Decrypting previous secret for stale'
+ The result of function check_git_log should be successful
+ End
End
Describe 'cmd_git'
@@ -652,7 +687,19 @@ Describe 'Integrated Command Functions'
End
End
-# Describe 'cmd_grep' is not needed (fully covered in pass_spec.sh)
+ Describe 'cmd_grep'
+ It 'aborts on decryption failure even without pipefail'
+ Set 'pipefail:off'
+ grep() { @grep "$@"; }
+ mock-age() { false; }
+ When run cmd_grep foo
+ The status should equal 1
+ The error should equal \
+ "Fatal(1): mock-age -d -i ${IDENTITIES_FILE} -- file.age"
+ The output should be blank
+ The result of function check_git_log should be successful
+ End
+ End
Describe 'cmd_gitconfig'
grep() { @grep "$@"; }
@@ -1050,6 +1097,27 @@ Describe 'Integrated Command Functions'
}
The error should equal "$(expected_err)"
End
+
+ It 'aborts on age decryption failure even without pipefail'
+ Set 'pipefail:off'
+ mock-age() { false; }
+ When run cmd_list_or_show stale
+ The status should equal 1
+ The error should equal \
+ "Fatal(1): mock-age -d -i ${IDENTITIES_FILE} -- ${PREFIX}/stale.age"
+ The output should be blank
+ The result of function check_git_log should be successful
+ End
+
+ It 'aborts on gpg decryption failure even without pipefail'
+ Set 'pipefail:off'
+ GPG=false
+ When run cmd_list_or_show old
+ The status should equal 1
+ The error should equal "Fatal(1): false -d --quiet --yes --compress-algo=none --no-encrypt-to -- ${PREFIX}/old.gpg"
+ The output should be blank
+ The result of function check_git_log should be successful
+ End
End
# Describe 'cmd_move' is not needed (covered by 'cmd_copy_move')
@@ -1237,6 +1305,17 @@ Describe 'Integrated Command Functions'
The error should include 'sneaky'
The result of function check_git_log should be successful
End
+
+ It 'aborts on age decryption failure even without pipefail'
+ Set 'pipefail:off'
+ mock-age() { false; }
+ When run cmd_reencrypt stale
+ The status should equal 1
+ The error should equal \
+ "Fatal(1): mock-age -d -i ${IDENTITIES_FILE} -- ${PREFIX}/stale.age"
+ The output should be blank
+ The result of function check_git_log should be successful
+ End
End
Describe 'cmd_usage'
diff --git a/spec/pass_spec.sh b/spec/pass_spec.sh
@@ -1554,6 +1554,27 @@ Describe 'Pass-like command'
The contents of file "${GITLOG}" should equal "$(expected_log $3)"
End
+ It 'replaces the line of an existing one-line file'
+ Skip if 'pass(age) needs bash' check_skip $2
+ When run script $1 generate -ni fluff/one 4
+ The status should be success
+ The output should include 'The generated password for'
+ The lines of contents of file "${PREFIX}/fluff/one.$3" should equal 3
+ The line 3 of contents of file "${PREFIX}/fluff/one.$3" should \
+ match pattern "$3:[0-9a-zA-z][0-9a-zA-z][0-9a-zA-z][0-9a-zA-z]"
+ The output should \
+ include "$(@sed -n "3s/$3://p" "${PREFIX}/fluff/one.$3")"
+ expected_log() { %text:expand
+ #|Replace generated password for fluff/one.
+ #|
+ #| fluff/one.$1 | 2 +-
+ #| 1 file changed, 1 insertion(+), 1 deletion(-)
+ setup_log
+ }
+ The result of function git_log should be successful
+ The contents of file "${GITLOG}" should equal "$(expected_log $3)"
+ End
+
It 'pastes the generated password into the clipboard'
DISPLAY=mock
Skip if 'pass(age) needs bash' check_skip $2
diff --git a/src/pashage.sh b/src/pashage.sh
@@ -69,6 +69,12 @@ glob_exists() {
fi
}
+# Always-successful grep filter
+# ... grep arguments
+grep_filter() {
+ grep "$@" || true
+}
+
# Generate random characters
# $1: number of characters
# $2: allowed character set
@@ -626,14 +632,23 @@ do_generate() {
elif [ -e "${PREFIX}/$1.age" ] && [ "${OVERWRITE}" = yes ]; then
printf '%s\n' "Decrypting previous secret for $1"
- OLD_SECRET="$(do_decrypt "${PREFIX}/$1.age" | tail -n +2)"
+ OLD_SECRET_FULL="$(do_decrypt "${PREFIX}/$1.age")"
+ OLD_SECRET="${OLD_SECRET_FULL#*
+}"
WIP_FILE="$(mktemp "${PREFIX}/$1-XXXXXXXXX.age")"
- do_encrypt "${WIP_FILE#"${PREFIX}"/}" <<-EOF
- ${NEW_PASS}
- ${OLD_SECRET}
- EOF
+ if [ "${OLD_SECRET}" = "${OLD_SECRET_FULL}" ]; then
+ do_encrypt "${WIP_FILE#"${PREFIX}"/}" <<-EOF
+ ${NEW_PASS}
+ EOF
+ else
+ do_encrypt "${WIP_FILE#"${PREFIX}"/}" <<-EOF
+ ${NEW_PASS}
+ ${OLD_SECRET}
+ EOF
+ fi
mv "${WIP_FILE}" "${PREFIX}/$1.age"
VERB="Replace"
+ unset OLD_SECRET_FULL
unset OLD_SECRET
unset WIP_FILE
@@ -691,15 +706,30 @@ do_grep() {
if [ -d "${ARG}" ]; then
( cd "${ARG}" && do_grep "${SUBDIR}${ARG}/" "$@" )
elif [ "${ARG}" = "${ARG%.age}.age" ]; then
- FOUND="$(do_decrypt "${ARG}" | (grep "$@" || true))"
- if [ -n "${FOUND}" ]; then
- printf '%s%s\n%s\n' \
- "${BLUE_TEXT}${SUBDIR}" \
- "${BOLD_TEXT}${ARG%.age}${NORMAL_TEXT}:" \
- "${FOUND}"
- fi
+ HEADER="${BLUE_TEXT}${SUBDIR}${BOLD_TEXT}"
+ HEADER="${HEADER}${ARG%.age}${NORMAL_TEXT}:"
+ SECRET="$(do_decrypt "${ARG}")"
+ do_grep_filter "$@" <<-EOF
+ ${SECRET}
+ EOF
fi
done
+
+ unset ARG
+ unset HEADER
+}
+
+# Wrapper around grep filter to added a header when a match is found
+# ... grep arguments
+# HEADER header to print before matches, if any
+do_grep_filter() {
+ unset SECRET
+
+ grep_filter "$@" | while IFS= read -r LINE; do
+ [ -n "${HEADER}" ] && printf '%s\n' "${HEADER}"
+ printf '%s\n' "${LINE}"
+ HEADER=''
+ done
}
# Add identities to a subdirectory
@@ -790,7 +820,9 @@ do_insert() {
fi
done
- printf '%s\n' "${LINE1}" | do_encrypt "$1.age"
+ do_encrypt "$1.age" <<-EOF
+ ${LINE1}
+ EOF
unset LINE1 LINE2
fi
@@ -804,11 +836,19 @@ do_list_or_show() {
if [ -z "$1" ]; then
do_tree "${PREFIX}" "Password Store"
elif [ -f "${PREFIX}/$1.age" ]; then
- do_decrypt "${PREFIX}/$1.age" | do_show "$1"
+ SECRET="$(do_decrypt "${PREFIX}/$1.age")"
+ do_show "$1" <<-EOF
+ ${SECRET}
+ EOF
+ unset SECRET
elif [ -d "${PREFIX}/$1" ]; then
do_tree "${PREFIX}/$1" "$1"
elif [ -f "${PREFIX}/$1.gpg" ]; then
- do_decrypt_gpg "${PREFIX}/$1.gpg" | do_show "$1"
+ SECRET="$(do_decrypt_gpg "${PREFIX}/$1.gpg")"
+ do_show "$1" <<-EOF
+ ${SECRET}
+ EOF
+ unset SECRET
else
die "Error: $1 is not in the password store."
fi
@@ -872,9 +912,11 @@ do_reencrypt_file() {
fi
OVERWRITE=once
- WIP_FILE="$(mktemp "${PREFIX}/$1-XXXXXXXXX.age")"
- do_decrypt "${PREFIX}/$1.age" \
- | do_encrypt "${WIP_FILE#"${PREFIX}"/}"
+ WIP_FILE="$(mktemp -u "${PREFIX}/$1-XXXXXXXXX.age")"
+ SECRET="$(do_decrypt "${PREFIX}/$1.age")"
+ do_encrypt "${WIP_FILE#"${PREFIX}"/}" <<-EOF
+ ${SECRET}
+ EOF
mv -f -- "${WIP_FILE}" "${PREFIX}/$1.age"
unset WIP_FILE
scm_add "$1.age"
@@ -885,6 +927,8 @@ do_reencrypt_file() {
# SELECTED_LINE: which line to paste or diplay as qr-code
# SHOW: how to show the secret
do_show() {
+ unset SECRET
+
case "${SHOW}" in
text)
cat