pashage

Yet Another Opinionated Re-engineering of the Unix Password Store
git clone https://git.instinctive.eu/pashage.git
Log | Files | Refs | README | LICENSE

commit 0e51afc5f407be08ffcfa8c7eaceb7f588e16e04
parent 7b10725057a26c39d21b7c1bd4269d1ae469fc5a
Author: Natasha Kerensikova <natgh@instinctive.eu>
Date:   Tue, 10 Sep 2024 16:13:24 +0000

Positional arguments for external commands are escaped

Thanks to semarie on #gcu
Diffstat:
Mspec/action_spec.sh | 72++++++++++++++++++++++++++++++++++++------------------------------------
Mspec/command_spec.sh | 7++++---
Mspec/usage_spec.sh | 2+-
Msrc/pashage.sh | 41+++++++++++++++++++++--------------------
Msrc/platform-freebsd.sh | 6+++---
5 files changed, 65 insertions(+), 63 deletions(-)

diff --git a/spec/action_spec.sh b/spec/action_spec.sh @@ -86,7 +86,7 @@ Describe 'Action Functions' It 'renames a file without re-encrypting' result() { %text:expand - #|$ mkdir -p ${PREFIX}/sub + #|$ mkdir -p -- ${PREFIX}/sub #|$ scm_begin #|$ scm_mv sub/secret.age sub/renamed.age #|$ scm_commit Move sub/secret.age to sub/renamed.age @@ -101,7 +101,7 @@ Describe 'Action Functions' SCM_ACTION=scm_cp result() { %text:expand - #|$ mkdir -p ${PREFIX}/sub/ + #|$ mkdir -p -- ${PREFIX}/sub/ #|$ scm_begin #|$ do_decrypt ${PREFIX}/root.age #|$ do_encrypt sub/root.age @@ -118,7 +118,7 @@ Describe 'Action Functions' SCM_ACTION=scm_cp result() { %text:expand - #|$ mkdir -p ${PREFIX}/sub + #|$ mkdir -p -- ${PREFIX}/sub #|$ scm_begin #|$ do_decrypt ${PREFIX}/root.age #|$ do_encrypt sub/moved.age @@ -136,7 +136,7 @@ Describe 'Action Functions' SCM_ACTION=scm_cp result() { %text:expand - #|$ mkdir -p ${PREFIX}/sub/ + #|$ mkdir -p -- ${PREFIX}/sub/ #|$ scm_begin #|$ scm_cp root.age sub/root.age #|$ scm_commit Copy root.age to sub/root.age @@ -149,7 +149,7 @@ Describe 'Action Functions' It 'does not re-encrypt a non-encrypted file' result() { %text:expand - #|$ mkdir -p ${PREFIX}/sub/ + #|$ mkdir -p -- ${PREFIX}/sub/ #|$ scm_begin #|$ scm_mv notes.txt sub/notes.txt #|$ scm_commit Move notes.txt to sub/notes.txt @@ -163,7 +163,7 @@ Describe 'Action Functions' DECISION=force result() { %text:expand - #|$ mkdir -p ${PREFIX}/sub/ + #|$ mkdir -p -- ${PREFIX}/sub/ #|$ scm_begin #|$ scm_mv notes.txt sub/notes.txt #|$ scm_commit Move notes.txt to sub/notes.txt @@ -194,10 +194,10 @@ Describe 'Action Functions' } result() { %text:expand - #|$ mkdir -p ${PREFIX}/sub + #|$ mkdir -p -- ${PREFIX}/sub #|$ scm_begin #|$ yesno sub/secret.age already exists. Overwrite? - #|$ rm -f ${PREFIX}/sub/secret.age + #|$ rm -f -- ${PREFIX}/sub/secret.age #|$ do_decrypt ${PREFIX}/root.age #|$ do_encrypt sub/secret.age #|$ scm_rm root.age @@ -212,7 +212,7 @@ Describe 'Action Functions' It 'moves a whole directory with identity' result() { %text:expand - #|$ mkdir -p ${PREFIX}/subdir/sub/ + #|$ mkdir -p -- ${PREFIX}/subdir/sub/ #|$ scm_begin #|$ scm_mv sub/ subdir/sub/ #|$ scm_commit Move sub/ to subdir/sub/ @@ -225,13 +225,13 @@ Describe 'Action Functions' It 'recursively re-enecrypts a directory' result() { %text:expand - #|$ mkdir -p ${PREFIX}/subdir/new-bare/ + #|$ mkdir -p -- ${PREFIX}/subdir/new-bare/ #|$ scm_begin #|$ do_decrypt ${PREFIX}/sub/bare/deep.age #|$ do_encrypt subdir/new-bare/deep.age #|$ scm_rm sub/bare/deep.age #|$ scm_add subdir/new-bare/deep.age - #|$ mkdir -p ${PREFIX}/subdir/new-bare/sub + #|$ mkdir -p -- ${PREFIX}/subdir/new-bare/sub #|$ do_decrypt ${PREFIX}/sub/bare/sub/deepest.age #|$ do_encrypt subdir/new-bare/sub/deepest.age #|$ scm_rm sub/bare/sub/deepest.age @@ -255,11 +255,11 @@ Describe 'Action Functions' } result() { %text:expand - #|$ mkdir -p ${PREFIX}/subdir/new-bare/ + #|$ mkdir -p -- ${PREFIX}/subdir/new-bare/ #|$ scm_begin #|$ yesno Reencrypt sub/bare/deep into subdir/new-bare/deep? #|$ scm_cp sub/bare/deep.age subdir/new-bare/deep.age - #|$ mkdir -p ${PREFIX}/subdir/new-bare/sub + #|$ mkdir -p -- ${PREFIX}/subdir/new-bare/sub #|$ yesno Reencrypt sub/bare/sub/deepest into subdir/new-bare/sub/deepest? #|$ do_decrypt ${PREFIX}/sub/bare/sub/deepest.age #|$ do_encrypt subdir/new-bare/sub/deepest.age @@ -346,7 +346,7 @@ Describe 'Action Functions' When call do_decrypt '/path/to/encrypted/file.age' The output should equal 'cleartext' The error should equal \ - '$ age -d -i /path/to/identity /path/to/encrypted/file.age' + '$ age -d -i /path/to/identity -- /path/to/encrypted/file.age' End Describe 'do_decrypt_gpg' @@ -356,7 +356,7 @@ Describe 'Action Functions' unset GPG When call do_decrypt_gpg /path/to/encrypted/file.gpg The error should equal \ - '$ gpg -d --quiet --yes --compress-algo=none --no-encrypt-to /path/to/encrypted/file.gpg' + '$ gpg -d --quiet --yes --compress-algo=none --no-encrypt-to -- /path/to/encrypted/file.gpg' End It 'uses gpg when agent is available' @@ -365,7 +365,7 @@ Describe 'Action Functions' unset GPG When call do_decrypt_gpg /path/to/encrypted/file.gpg The error should equal \ - '$ gpg -d --quiet --yes --compress-algo=none --no-encrypt-to --batch --use-agent /path/to/encrypted/file.gpg' + '$ gpg -d --quiet --yes --compress-algo=none --no-encrypt-to --batch --use-agent -- /path/to/encrypted/file.gpg' End It 'uses gpg2' @@ -374,7 +374,7 @@ Describe 'Action Functions' unset GPG When call do_decrypt_gpg /path/to/encrypted/file.gpg The error should equal \ - '$ gpg2 -d --quiet --yes --compress-algo=none --no-encrypt-to --batch --use-agent /path/to/encrypted/file.gpg' + '$ gpg2 -d --quiet --yes --compress-algo=none --no-encrypt-to --batch --use-agent -- /path/to/encrypted/file.gpg' End It 'uses user-provided command' @@ -383,7 +383,7 @@ Describe 'Action Functions' GPG=user_cmd When call do_decrypt_gpg /path/to/encrypted/file.gpg The error should equal \ - '$ user_cmd -d --quiet --yes --compress-algo=none --no-encrypt-to /path/to/encrypted/file.gpg' + '$ user_cmd -d --quiet --yes --compress-algo=none --no-encrypt-to -- /path/to/encrypted/file.gpg' End It 'bails out when command cannot be guessed' @@ -913,7 +913,7 @@ Describe 'Action Functions' result(){ %text:expand #|$ scm_begin - #|$ mkdir -p ${PREFIX} + #|$ mkdir -p -- ${PREFIX} #|Cannot replace directory suspicious.age } When run do_generate suspicious 10 '[:alnum:]' @@ -926,7 +926,7 @@ Describe 'Action Functions' result(){ %text:expand #|$ scm_begin - #|$ mkdir -p ${PREFIX}/sub + #|$ mkdir -p -- ${PREFIX}/sub #|$ do_encrypt sub/new.age #|> 0123456789 #|$ scm_add ${PREFIX}/sub/new.age @@ -945,7 +945,7 @@ Describe 'Action Functions' result(){ %text:expand #|$ scm_begin - #|$ mkdir -p ${PREFIX} + #|$ mkdir -p -- ${PREFIX} #|$ do_encrypt existing.age #|> 0123456789 #|$ scm_add ${PREFIX}/existing.age @@ -968,7 +968,7 @@ Describe 'Action Functions' result(){ %text:expand #|$ scm_begin - #|$ mkdir -p ${PREFIX} + #|$ mkdir -p -- ${PREFIX} #|$ yesno An entry already exists for existing. Overwrite it? #|$ do_encrypt existing.age #|> 0123456789 @@ -993,7 +993,7 @@ Describe 'Action Functions' result(){ %text:expand #|$ scm_begin - #|$ mkdir -p ${PREFIX} + #|$ mkdir -p -- ${PREFIX} #|$ yesno An entry already exists for existing. Overwrite it? } When call do_generate existing 10 '[alnum:]' @@ -1016,7 +1016,7 @@ Describe 'Action Functions' result(){ %text:expand #|$ scm_begin - #|$ mkdir -p ${PREFIX} + #|$ mkdir -p -- ${PREFIX} #|$ do_decrypt ${PREFIX}/existing.age #|$ do_encrypt existing-XXXXXXXXX.age #|> 0123456789 @@ -1093,7 +1093,7 @@ Describe 'Action Functions' It 'initializes the store' result() { %text:expand - #|$ mkdir -p ${PREFIX} + #|$ mkdir -p -- ${PREFIX} #|$ scm_begin #|$ scm_add .age-recipients #|$ do_reencrypt_dir ${PREFIX} @@ -1110,7 +1110,7 @@ Describe 'Action Functions' It 'initializes a subdirectory' result() { %text:expand - #|$ mkdir -p ${PREFIX}/sub + #|$ mkdir -p -- ${PREFIX}/sub #|$ scm_begin #|$ scm_add sub/.age-recipients #|$ do_reencrypt_dir ${PREFIX}/sub @@ -1133,7 +1133,7 @@ Describe 'Action Functions' DECISION=keep result() { %text:expand - #|$ mkdir -p ${PREFIX} + #|$ mkdir -p -- ${PREFIX} #|$ scm_begin #|$ scm_add .age-recipients #|$ scm_commit Set age recipients at store root @@ -1182,7 +1182,7 @@ Describe 'Action Functions' result() { %text:expand #|$ scm_begin - #|$ mkdir -p ${PREFIX}/subdir + #|$ mkdir -p -- ${PREFIX}/subdir #|$ do_encrypt subdir/new.age #|> line 1 #|$ scm_add subdir/new.age @@ -1205,7 +1205,7 @@ Describe 'Action Functions' result() { %text:expand #|$ scm_begin - #|$ mkdir -p ${PREFIX}/subdir + #|$ mkdir -p -- ${PREFIX}/subdir #|$ do_encrypt subdir/new.age #|> line 1 #|> line 2 @@ -1241,7 +1241,7 @@ Describe 'Action Functions' e_result() { %text:expand #|$ scm_begin - #|$ mkdir -p ${PREFIX}/subdir + #|$ mkdir -p -- ${PREFIX}/subdir #|$ do_encrypt subdir/new.age #|> line 3 #|$ scm_add subdir/new.age @@ -1271,7 +1271,7 @@ Describe 'Action Functions' %text:expand #|$ yesno An entry already exists for existing. Overwrite it? #|$ scm_begin - #|$ mkdir -p ${PREFIX} + #|$ mkdir -p -- ${PREFIX} #|$ do_encrypt existing.age #|> password #|$ scm_add existing.age @@ -1314,7 +1314,7 @@ Describe 'Action Functions' result() { %text:expand #|$ scm_begin - #|$ mkdir -p ${PREFIX} + #|$ mkdir -p -- ${PREFIX} #|$ do_encrypt existing.age #|> password #|$ scm_add existing.age @@ -1442,7 +1442,7 @@ Describe 'Action Functions' %text:expand #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age - #|$ mv -f ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age + #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age #|$ scm_add subdir/subsub/deep.age } When call do_reencrypt subdir/subsub/deep @@ -1455,11 +1455,11 @@ Describe 'Action Functions' %text:expand #|$ do_decrypt ${PREFIX}/subdir/middle.age #|$ do_encrypt subdir/middle-XXXXXXXXX.age - #|$ mv -f ${PREFIX}/subdir/middle-XXXXXXXXX.age ${PREFIX}/subdir/middle.age + #|$ mv -f -- ${PREFIX}/subdir/middle-XXXXXXXXX.age ${PREFIX}/subdir/middle.age #|$ scm_add subdir/middle.age #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age - #|$ mv -f ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age + #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age #|$ scm_add subdir/subsub/deep.age } When call do_reencrypt subdir/ @@ -1481,7 +1481,7 @@ Describe 'Action Functions' #|$ yesno Re-encrypt subdir/subsub/deep? #|$ do_decrypt ${PREFIX}/subdir/subsub/deep.age #|$ do_encrypt subdir/subsub/deep-XXXXXXXXX.age - #|$ mv -f ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age + #|$ mv -f -- ${PREFIX}/subdir/subsub/deep-XXXXXXXXX.age ${PREFIX}/subdir/subsub/deep.age #|$ scm_add subdir/subsub/deep.age } When call do_reencrypt subdir diff --git a/spec/command_spec.sh b/spec/command_spec.sh @@ -29,10 +29,11 @@ Describe 'Command Functions' ;; -d) [ "$2" = '-i' ] || echo "Unexpected age -d \$2: \"$2\"" >&2 - @grep -v '^age' "$4" >&2 && echo "Bad encrypted file \"$4\"" >&2 - @grep -qFx "ageRecipient:$(@cat "$3")" "$4" \ + [ "$4" = '--' ] || echo "Unexpected age -d \$4: \"$4\"" >&2 + @grep -v '^age' "$5" >&2 && echo "Bad encrypted file \"$4\"" >&2 + @grep -qFx "ageRecipient:$(@cat "$3")" "$5" \ || echo "Bad identity \"$3\": $(@cat "$3")" >&2 - @sed -n 's/^age://p' "$4" + @sed -n 's/^age://p' "$5" ;; *) echo "Unexpected age \$1: \"$1\"" >&2 diff --git a/spec/usage_spec.sh b/spec/usage_spec.sh @@ -638,7 +638,7 @@ Describe 'Command-Line Parsing' PREFIX="${SHELLSPEC_WORKDIR}/repo/sub" result() { %text:expand - #|$ mkdir -p ${PREFIX} + #|$ mkdir -p -- ${PREFIX} #|$ git -C ${PREFIX} init #|$ scm_begin #|$ scm_add . diff --git a/src/pashage.sh b/src/pashage.sh @@ -151,7 +151,7 @@ yesno() { # $1: path scm_add() { [ -d "${PREFIX}/.git" ] || return 0 - git -C "${PREFIX}" add "$1" + git -C "${PREFIX}" add -- "$1" } # Start a sequence of changes, asserting nothing is pending @@ -183,7 +183,7 @@ scm_cp() { # $1: path scm_del() { [ -d "${PREFIX}/.git" ] || return 0 - git -C "${PREFIX}" rm -qr "$1" + git -C "${PREFIX}" rm -qr -- "$1" } # Move a file or directory in the filesystem and put it in pending changes @@ -191,15 +191,15 @@ scm_del() { # $2: destination scm_mv() { if [ -d "${PREFIX}/.git" ]; then - git -C "${PREFIX}" mv "$1" "$2" + git -C "${PREFIX}" mv -- "$1" "$2" else - mv "${PREFIX}/$1" "${PREFIX}/$2" + mv -- "${PREFIX}/$1" "${PREFIX}/$2" fi } # Delete a file or directory from filesystem and put it in pending chnages scm_rm() { - rm -rf "${PREFIX:?}/$1" + rm -rf -- "${PREFIX:?}/$1" scm_del "$1" } @@ -245,10 +245,10 @@ do_copy_move() { die "Error: ${DEST%/} is not a directory" fi fi - mkdir -p "${PREFIX}/${DEST}" + mkdir -p -- "${PREFIX}/${DEST}" elif [ "$2" = "${2%/}/" ]; then - mkdir -p "${PREFIX}/$2" + mkdir -p -- "${PREFIX}/$2" [ -d "${PREFIX}/$2" ] || die "Error: $2 is not a directory" DEST="$2$(basename "${SRC}")" LOCAL_ACTION=do_copy_move_file @@ -269,7 +269,7 @@ do_copy_move() { DEST="$2" fi - mkdir -p "$(dirname "${PREFIX}/${DEST}")" + mkdir -p -- "$(dirname "${PREFIX}/${DEST}")" LOCAL_ACTION=do_copy_move_file fi @@ -350,7 +350,7 @@ do_copy_move_dir() { do_copy_move_file "${SRC}" "${DEST}" elif [ -d "${ARG}" ] && [ "${ARG}" = "${ARG%/.*}" ] then - mkdir -p "${PREFIX}/${DEST}" + mkdir -p -- "${PREFIX}/${DEST}" do_copy_move_dir "${SRC}/" "${DEST}/" fi done @@ -374,7 +374,7 @@ do_copy_move_file() { unset ANSWER fi - rm -f "${PREFIX}/$2" + rm -f -- "${PREFIX}/$2" fi if [ "$1" = "${1%.age}.age" ]; then @@ -413,7 +413,7 @@ do_copy_move_file() { # $1: full path of the encrypted file # IDENTITIES_FILE: full path of age identity do_decrypt() { - checked "${AGE}" -d -i "${IDENTITIES_FILE}" "$1" + checked "${AGE}" -d -i "${IDENTITIES_FILE}" -- "$1" } # Decrypt a GPG secret file into standard output @@ -430,6 +430,7 @@ do_decrypt_gpg() { fi fi + set -- -- "$@" if [ -n "${GPG_AGENT_INFO-}" ] || [ "${GPG}" = "gpg2" ]; then set -- "--batch" "--use-agent" "$@" fi @@ -459,7 +460,7 @@ do_deinit() { do_reencrypt_dir "${PREFIX}/$1" fi scm_commit "Deinitialize ${LOC}" - rmdir -p "${PREFIX}/$1" 2>/dev/null || true + rmdir -p -- "${PREFIX}/$1" 2>/dev/null || true unset LOC unset TARGET @@ -501,7 +502,7 @@ do_delete() { scm_begin scm_rm "${TARGET}" scm_commit "Remove ${NAME} from store." - rmdir -p "$(dirname "${PREFIX}/${TARGET}")" 2>/dev/null || true + rmdir -p -- "$(dirname "${PREFIX}/${TARGET}")" 2>/dev/null || true } # Edit a secret interactively @@ -545,7 +546,7 @@ do_edit() { printf '%s\n' "New password for ${NAME} not saved." elif [ -n "${OLD_VALUE}" ] \ && printf '%s\n' "${OLD_VALUE}" \ - | diff - "${TMPFILE}" >/dev/null 2>&1 + | diff -- - "${TMPFILE}" >/dev/null 2>&1 then printf '%s\n' "Password for ${NAME} unchanged." rm "${TMPFILE}" @@ -621,7 +622,7 @@ do_encrypt() { # DECISION: whether to ask before overwrite # OVERWRITE: whether to re-use existing secret data do_generate() { - NEW_PASS="$(LC_ALL=C tr -dc "$3" </dev/urandom \ + NEW_PASS="$(LC_ALL=C tr -dc -- "$3" </dev/urandom \ | LC_ALL=C dd ibs=1 obs=1 count="$2" 2>/dev/null || true)" NEW_PASS_LEN="$(strlen "${NEW_PASS}")" @@ -632,7 +633,7 @@ do_generate() { unset NEW_PASS_LEN scm_begin - mkdir -p "$(dirname "${PREFIX}/$1.age")" + mkdir -p -- "$(dirname "${PREFIX}/$1.age")" if [ -d "${PREFIX}/$1.age" ]; then die "Cannot replace directory $1.age" @@ -716,7 +717,7 @@ do_init() { TARGET="${SUBDIR}/.age-recipients" shift - mkdir -p "${SUBDIR}" + mkdir -p -- "${SUBDIR}" if ! [ -f "${TARGET}" ] || [ "${OVERWRITE}" = yes ]; then : >|"${TARGET}" @@ -750,7 +751,7 @@ do_insert() { fi scm_begin - mkdir -p "$(dirname "${PREFIX}/$1.age")" + mkdir -p -- "$(dirname "${PREFIX}/$1.age")" if [ "${MULTILINE}" = yes ]; then printf '%s\n' \ @@ -858,7 +859,7 @@ do_reencrypt_file() { WIP_FILE="$(mktemp "${PREFIX}/$1-XXXXXXXXX.age")" do_decrypt "${PREFIX}/$1.age" \ | do_encrypt "${WIP_FILE#"${PREFIX}"/}" - mv -f "${WIP_FILE}" "${PREFIX}/$1.age" + mv -f -- "${WIP_FILE}" "${PREFIX}/$1.age" unset WIP_FILE scm_add "$1.age" } @@ -1161,7 +1162,7 @@ cmd_git() { platform_tmpdir TMPDIR="${SECURE_TMPDIR}" git -C "${PREFIX}" "$@" elif [ "$1" = init ]; then - mkdir -p "${PREFIX}" + mkdir -p -- "${PREFIX}" git -C "${PREFIX}" "$@" scm_begin scm_add '.' diff --git a/src/platform-freebsd.sh b/src/platform-freebsd.sh @@ -116,14 +116,14 @@ platform_tmpdir() { # Remove a ramdisk-based tmpdir platform_tmpdir_rm() { [ -z "${SECURE_TMPDIR-}" ] && return 0 - rm -rf "${SECURE_TMPDIR}" + rm -rf -- "${SECURE_TMPDIR}" unset SECURE_TMPDIR } # Remove a presumed disk-based tmpdir platform_tmpdir_shred() { [ -z "${SECURE_TMPDIR-}" ] && return 0 - find "${SECURE_TMPDIR}" -type f -exec rm -P -f '{}' + - rm -rf "${SECURE_TMPDIR}" + find -f "${SECURE_TMPDIR}" -- -type f -exec rm -P -f '{}' + + rm -rf -- "${SECURE_TMPDIR}" unset SECURE_TMPDIR }