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 8e18b025e10d540468bcd3c06d2ca75169f6cb95
parent cefe552791b1e9ebb41f3836cd491490cdb04a0a
Author: Natasha Kerensikova <natgh@instinctive.eu>
Date:   Sun,  3 Nov 2024 17:11:00 +0000

Copy and move commands are as covered as possible by integrated suites
Diffstat:
MREADME.md | 7+++----
Mspec/pashage_extra_spec.sh | 139+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Mspec/pass_spec.sh | 55+++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 184 insertions(+), 17 deletions(-)

diff --git a/README.md b/README.md @@ -48,6 +48,9 @@ a confirming `y` on a standard input line. - The commands `copy`, `edit`, `insert`, `list`, `move`, and `show` accept multiple arguments to operate on many secrets at once. +- The commands `copy` and `move` also operate on unencrypted files in the +password store. + - The `edit` command does not warn a about using `/tmp` rather than `/dev/shm`, because the warning does not seem actionable and quickly becomes ignored noise. @@ -65,8 +68,6 @@ how different it is; but now it installs `.age-recipients` and re-encrypts. - The `insert` command makes the user try again when entering mismatching passwords. -- TODO - ### New Features and Extensions - The new `gitconfig` command configures an existing store repository to @@ -75,8 +76,6 @@ decrypt before `diff`. - The new `random` command leverages password generation without touching the password store. -- TODO - ## Manual TODO diff --git a/spec/pashage_extra_spec.sh b/spec/pashage_extra_spec.sh @@ -66,7 +66,8 @@ Describe 'Integrated Command Functions' #| shared/.age-recipients | 2 ++ #| stale.age | 3 +++ #| subdir/file.age | 2 ++ - #| 9 files changed, 26 insertions(+) + #| y.txt | 3 +++ + #| 10 files changed, 29 insertions(+) } setup_log_bin() { %text @@ -81,7 +82,8 @@ Describe 'Integrated Command Functions' #| shared/.age-recipients | 2 ++ #| stale.age | Bin 0 -> 55 bytes #| subdir/file.age | Bin 0 -> 33 bytes - #| 9 files changed, 7 insertions(+) + #| y.txt | 3 +++ + #| 10 files changed, 10 insertions(+) } expected_log() { setup_log; } # Default log to override as needed @@ -140,6 +142,10 @@ Describe 'Integrated Command Functions' #|gpgRecipient:myOldSelf #|gpg:very-old-password #|gpg:Username: previous-life + %text >"${PREFIX}/y.txt" + #|# Title + #|Line of text + #|End of note @git -C "${PREFIX}" add . @git -C "${PREFIX}" commit -m 'Initial setup' >/dev/null @@ -158,16 +164,18 @@ Describe 'Integrated Command Functions' BeforeEach setup AfterEach cleanup - cat() { @cat "$@"; } - dd() { @dd "$@"; } - diff() { @diff "$@"; } - dirname() { @dirname "$@"; } - git() { @git "$@"; } - mkdir() { @mkdir "$@"; } - mktemp() { @mktemp "$@"; } - mv() { @mv "$@"; } - rm() { @rm "$@"; } - tr() { @tr "$@"; } + basename() { @basename "$@"; } + cat() { @cat "$@"; } + cp() { @cp "$@"; } + dd() { @dd "$@"; } + diff() { @diff "$@"; } + dirname() { @dirname "$@"; } + git() { @git "$@"; } + mkdir() { @mkdir "$@"; } + mktemp() { @mktemp "$@"; } + mv() { @mv "$@"; } + rm() { @rm "$@"; } + tr() { @tr "$@"; } platform_tmpdir() { SECURE_TMPDIR="${SHELLSPEC_WORKDIR}/secure" @@ -175,7 +183,104 @@ Describe 'Integrated Command Functions' } # Describe 'cmd_copy' is not needed (covered by 'cmd_copy_move') -# Describe 'cmd_copy_move' + + Describe 'cmd_copy_move' + DECISION=default + OVERWRITE=no + + It 'processes several files and directories into a directory' + When call cmd_move extra stale subdir + The status should be success + The error should be blank + The output should be blank + expected_log() { %text + #|Move stale.age to subdir/stale.age + #| + #| stale.age => subdir/stale.age | 0 + #| 1 file changed, 0 insertions(+), 0 deletions(-) + #|Move extra/ to subdir/extra/ + #| + #| {extra => subdir/extra}/subdir/file.age | 0 + #| 1 file changed, 0 insertions(+), 0 deletions(-) + setup_log + } + The result of function check_git_log should be successful + End + + It 'processes unencrypted files' + When run cmd_move y.txt shared/yy + The status should be success + The error should be blank + The output should be blank + expected_log() { %text + #|Move y.txt to shared/yy + #| + #| y.txt => shared/yy | 0 + #| 1 file changed, 0 insertions(+), 0 deletions(-) + setup_log + } + The result of function check_git_log should be successful + End + + It 'does not overwrite a file without confirmation' + Data 'n' + When call cmd_copy subdir/file stale + The status should be success + The error should be blank + The output should equal 'stale.age already exists. Overwrite? [y/n]' + The result of function check_git_log should be successful + End + + It 'overwrites a file after confirmation' + Data 'y' + When call cmd_copy subdir/file stale + The status should be success + The error should be blank + The output should equal 'stale.age already exists. Overwrite? [y/n]' + expected_log() { %text + #|Copy subdir/file.age to stale.age + #| + #| stale.age | 3 +-- + #| 1 file changed, 1 insertion(+), 2 deletions(-) + setup_log + } + The result of function check_git_log should be successful + End + + It 'display copy usage with `c*` commands' + PROGRAM=prg + COMMAND=curious + When run cmd_copy_move single + The status should equal 1 + The output should be blank + The error should equal 'Usage: prg copy [--force,-f] old-path new-path' + The result of function check_git_log should be successful + End + + It 'display move usage with `m*` commands' + PROGRAM=prg + COMMAND=memory + When run cmd_copy_move single + The status should equal 1 + The output should be blank + The error should equal 'Usage: prg move [--force,-f] old-path new-path' + The result of function check_git_log should be successful + End + + It 'displays both usages when in doubt' + PROGRAM=prg + COMMAND=bad + When run cmd_copy_move single + The status should equal 1 + The output should be blank + expected_err() { %text + #|Usage: prg copy [--force,-f] old-path new-path + #| prg move [--force,-f] old-path new-path + } + The error should equal "$(expected_err)" + The result of function check_git_log should be successful + End + End Describe 'cmd_delete' DECISION=default @@ -833,6 +938,14 @@ Describe 'Integrated Command Functions' # to reach full coverage, by precisely identifying unreachable lines # written for defensive programming against internal inconsistencies. + It 'includes invalid values of DECISION in do_copy_move_file' + DECISION='invalid' + When run do_copy_move_file subdir/file.age extra/file.age + The status should equal 1 + The output should be blank + The error should equal 'Unexpected DECISION value "invalid"' + End + It 'includes invalid values of SHOW in do_show' SHOW='invalid' When run do_show diff --git a/spec/pass_spec.sh b/spec/pass_spec.sh @@ -2296,6 +2296,16 @@ Describe 'Pass-like command' The contents of file "${GITLOG}" should equal "$(setup_log)" End + It 'fails to move a directory into a file masquerading as a directory' + Skip if 'pass(age) needs bash' check_skip $2 + When run script $1 mv subdir y.txt/ + The status should equal 1 + The error should include 'y.txt' + The error should include 'ot a directory' + The result of function git_log should be successful + The contents of file "${GITLOG}" should equal "$(setup_log)" + End + It 'rejects a source path containing ..' Skip if 'pass(age) needs bash' check_skip $2 When run script $1 mv fluff/../stale subdir/ @@ -2323,6 +2333,24 @@ Describe 'Pass-like command' The result of function git_log should be successful The contents of file "${GITLOG}" should equal "$(setup_log)" End + + extra_setup() { + @mkdir "${PREFIX}/extra/stale.age" "${PREFIX}/extra/stale.gpg" + } + BeforeEach extra_setup + + It 'fails to move a file where a directory already exists' + Skip if 'pass(age) needs bash' check_skip $2 + When run script $1 mv -f stale extra/ + The status should equal 1 + if [ "$2" = pashage ]; then + The error should equal 'Error: extra/ already contains stale.age/' + else + The error should match pattern 'mv: *directory*' + fi + The result of function git_log should be successful + The contents of file "${GITLOG}" should equal "$(setup_log)" + End End Describe 'cp' @@ -2729,6 +2757,15 @@ Describe 'Pass-like command' The contents of file "${GITLOG}" should equal "$(setup_log)" End + It 'fails to copy a directory into a file masquerading as a directory' + Skip if 'pass(age) needs bash' check_skip $2 + When run script $1 cp subdir y.txt/ + The status should equal 1 + The error should include 'y.txt is not a directory' + The result of function git_log should be successful + The contents of file "${GITLOG}" should equal "$(setup_log)" + End + It 'rejects a source path containing ..' Skip if 'pass(age) needs bash' check_skip $2 When run script $1 cp fluff/../stale subdir/ @@ -2752,6 +2789,24 @@ Describe 'Pass-like command' The result of function git_log should be successful The contents of file "${GITLOG}" should equal "$(setup_log)" End + + extra_setup() { + @mkdir "${PREFIX}/extra/stale.age" "${PREFIX}/extra/stale.gpg" + } + BeforeEach extra_setup + + It 'fails to copy a file where a directory already exists' + Skip if 'pass(age) needs bash' check_skip $2 + When run script $1 cp -f stale extra/ + The status should equal 1 + if [ "$2" = pashage ]; then + The error should equal 'Error: extra/ already contains stale.age/' + else + The error should match pattern 'cp: *directory*' + fi + The result of function git_log should be successful + The contents of file "${GITLOG}" should equal "$(setup_log)" + End End Describe 'git'