diff --git a/CHANGELOG.md b/CHANGELOG.md index 52d86f96..93385ed0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,12 @@ # Changelog -## {Next Version} +## {{Next Version}} ### Bugfixes -- Don't let reveal clobber secret files (#579) +- In 'tell', warn about disabled, revoked, expired, or invalid keys (#552, #508, #317, #290, #283, #238) +- Error if 'tell' is used on an email address with multiple keys (#552) +- Don't let 'reveal' clobber secret files (#579) ### Misc @@ -34,7 +36,7 @@ ### Features - Support SECRETS_PINENTRY env var for gnupg --pinentry-mode parameter (#221) -- Show output from gnupg if 'hide' fails (#516) +- Show output from gnupg if 'hide' fails (#516, #202, #317) - Add support for Busybox (#478) ### Bugfixes diff --git a/man/man1/git-secret-add.1 b/man/man1/git-secret-add.1 index 96f93441..fe29932a 100644 --- a/man/man1/git-secret-add.1 +++ b/man/man1/git-secret-add.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-ADD" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-ADD" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-add\fR \- starts to track added files\. diff --git a/man/man1/git-secret-cat.1 b/man/man1/git-secret-cat.1 index 41fbe2c1..fcec3c13 100644 --- a/man/man1/git-secret-cat.1 +++ b/man/man1/git-secret-cat.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-CAT" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-CAT" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-cat\fR \- decrypts files passed on command line to stdout diff --git a/man/man1/git-secret-changes.1 b/man/man1/git-secret-changes.1 index feb4c6f2..0bfa3a8e 100644 --- a/man/man1/git-secret-changes.1 +++ b/man/man1/git-secret-changes.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-CHANGES" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-CHANGES" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-changes\fR \- view diff of the hidden files\. diff --git a/man/man1/git-secret-clean.1 b/man/man1/git-secret-clean.1 index 59903dfd..51d43323 100644 --- a/man/man1/git-secret-clean.1 +++ b/man/man1/git-secret-clean.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-CLEAN" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-CLEAN" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-clean\fR \- removes all the hidden files\. diff --git a/man/man1/git-secret-hide.1 b/man/man1/git-secret-hide.1 index 8c2b9bea..c3bc7617 100644 --- a/man/man1/git-secret-hide.1 +++ b/man/man1/git-secret-hide.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-HIDE" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-HIDE" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-hide\fR \- encrypts all added files with the inner keyring\. diff --git a/man/man1/git-secret-init.1 b/man/man1/git-secret-init.1 index 50e0341d..75bc5643 100644 --- a/man/man1/git-secret-init.1 +++ b/man/man1/git-secret-init.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-INIT" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-INIT" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-init\fR \- initializes git\-secret repository\. diff --git a/man/man1/git-secret-killperson.1 b/man/man1/git-secret-killperson.1 index 4d76da4d..0292f93a 100644 --- a/man/man1/git-secret-killperson.1 +++ b/man/man1/git-secret-killperson.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-KILLPERSON" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-KILLPERSON" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-killperson\fR \- deletes key identified by an email from the inner keyring\. diff --git a/man/man1/git-secret-list.1 b/man/man1/git-secret-list.1 index 26bda7d0..7e9c5f5c 100644 --- a/man/man1/git-secret-list.1 +++ b/man/man1/git-secret-list.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-LIST" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-LIST" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-list\fR \- prints all the added files\. diff --git a/man/man1/git-secret-remove.1 b/man/man1/git-secret-remove.1 index 067cfa28..03b9458c 100644 --- a/man/man1/git-secret-remove.1 +++ b/man/man1/git-secret-remove.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-REMOVE" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-REMOVE" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-remove\fR \- removes files from index\. diff --git a/man/man1/git-secret-reveal.1 b/man/man1/git-secret-reveal.1 index c65f5a25..1e5948c5 100644 --- a/man/man1/git-secret-reveal.1 +++ b/man/man1/git-secret-reveal.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-REVEAL" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-REVEAL" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-reveal\fR \- decrypts all added files\. diff --git a/man/man1/git-secret-tell.1 b/man/man1/git-secret-tell.1 index 05f56a6c..d18ce63b 100644 --- a/man/man1/git-secret-tell.1 +++ b/man/man1/git-secret-tell.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-TELL" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-TELL" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-tell\fR \- adds a person, who can access private data\. @@ -15,10 +15,13 @@ git secret tell [\-m] [\-d dir] [emails]\.\.\. .fi . .SH "DESCRIPTION" -\fBgit\-secret\-tell\fR receives an email addresses as an input, searches for the \fBgpg\fR\-key in the \fBgpg\fR\'s \fBhomedir\fR by these emails, then imports a person\'s public key into the \fBgit\-secret\fR\'s inner keychain\. From this moment this person can encrypt new files with the keyring which contains their key, but they cannot decrypt the old files, which were already encrypted without their key\. The files should be re\-encrypted with the new keyring by someone who has the unencrypted files\. +\fBgit\-secret\-tell\fR receives one or more email addresses as an input, searches for the \fBgpg\fR\-key in the \fBgpg\fR \fBhomedir\fR by these emails, then imports the corresponding public key into \fBgit\-secret\fR\'s inner keychain\. From this moment this person can encrypt new files with the keyring which contains their key, but they cannot decrypt the old files, which were already encrypted without their key\. The files should be re\-encrypted with the new keyring by someone who has the unencrypted files\. . .P -\fBDo not manually import secret key into \fBgit\-secret\fR\fR\. Anyways, it won\'t work with any of the secret\-keys imported\. +Versions of \fBgit\-secret tell\fR after 0\.3\.2 will warn about keys that are expired, revoked, or otherwise invalid, and also if multiple keys are found for a single email address\. +. +.P +\fBDo not manually import secret keys into \fBgit\-secret\fR\fR\. It won\'t work with imported secret keys anyway\. . .SH "OPTIONS" . diff --git a/man/man1/git-secret-tell.1.ronn b/man/man1/git-secret-tell.1.ronn index 352c7f3d..a86134d3 100644 --- a/man/man1/git-secret-tell.1.ronn +++ b/man/man1/git-secret-tell.1.ronn @@ -7,14 +7,16 @@ git-secret-tell - adds a person, who can access private data. ## DESCRIPTION -`git-secret-tell` receives an email addresses as an input, searches for the `gpg`-key in the `gpg`'s -`homedir` by these emails, then imports a person's public key into the `git-secret`'s inner keychain. +`git-secret-tell` receives one or more email addresses as an input, searches for the `gpg`-key in the `gpg` +`homedir` by these emails, then imports the corresponding public key into `git-secret`'s inner keychain. From this moment this person can encrypt new files with the keyring which contains their key, but they cannot decrypt the old files, which were already encrypted without their key. The files should be re-encrypted with the new keyring by someone who has the unencrypted files. -**Do not manually import secret key into `git-secret`**. Anyways, it won't work with any of the secret-keys imported. +Versions of `git-secret tell` after 0.3.2 will warn about keys that are expired, revoked, or otherwise invalid, +and also if multiple keys are found for a single email address. +**Do not manually import secret keys into `git-secret`**. It won't work with imported secret keys anyway. ## OPTIONS diff --git a/man/man1/git-secret-usage.1 b/man/man1/git-secret-usage.1 index 7f98210d..e7110a87 100644 --- a/man/man1/git-secret-usage.1 +++ b/man/man1/git-secret-usage.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-USAGE" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-USAGE" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-usage\fR \- prints all the available commands\. diff --git a/man/man1/git-secret-whoknows.1 b/man/man1/git-secret-whoknows.1 index dcf3ecb2..28c890db 100644 --- a/man/man1/git-secret-whoknows.1 +++ b/man/man1/git-secret-whoknows.1 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET\-WHOKNOWS" "1" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET\-WHOKNOWS" "1" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\-whoknows\fR \- prints email\-labels for each key in the keyring\. diff --git a/man/man7/git-secret.7 b/man/man7/git-secret.7 index 2fa49713..34bd9694 100644 --- a/man/man7/git-secret.7 +++ b/man/man7/git-secret.7 @@ -1,7 +1,7 @@ .\" generated with Ronn/v0.7.3 .\" http://github.com/rtomayko/ronn/tree/0.7.3 . -.TH "GIT\-SECRET" "7" "January 2020" "sobolevn" "git-secret 0.3.2" +.TH "GIT\-SECRET" "7" "March 2020" "sobolevn" "git-secret 0.3.2" . .SH "NAME" \fBgit\-secret\fR \- bash tool to store private data inside a git repo\. diff --git a/src/_utils/_git_secret_tools.sh b/src/_utils/_git_secret_tools.sh index 5c72c625..302bbf7d 100644 --- a/src/_utils/_git_secret_tools.sh +++ b/src/_utils/_git_secret_tools.sh @@ -539,7 +539,7 @@ function _exe_is_busybox { echo "$is_busybox" } - +# this is used by just about every command function _user_required { # This function does a bunch of validations: # 1. It calls `_secrets_dir_exists` to verify that "$_SECRETS_DIR" exists. @@ -606,18 +606,19 @@ function _assert_keychain_contains_emails { gpg_uids=$(_get_users_in_gpg_keyring "$homedir") for email in "${emails[@]}"; do if [[ $email != *"@"* ]]; then - _abort "does not appear to be an email: $email" + _abort "does not appear to be an email: $email" fi - local email_ok=0 + local emails_found=0 for uid in $gpg_uids; do - if [[ "$uid" == "$email" ]]; then - email_ok=1 - break - fi + if [[ "$uid" == "$email" ]]; then + emails_found=$((emails_found+1)) + fi done - if [[ $email_ok -eq 0 ]]; then - _abort "email not found in gpg keyring: $email" - fi + if [[ $emails_found -eq 0 ]]; then + _abort "no key found in gpg keyring for: $email" + elif [[ $emails_found -gt 1 ]]; then + _abort "$emails_found keys found in gpg keyring for: $email" + fi done } @@ -630,7 +631,7 @@ function _get_encrypted_filename { echo "${filename}${SECRETS_EXTENSION}" | sed -e 's#^\./##' } - +# this is used throughout this file, and in 'whoknows' function _get_users_in_gpg_keyring { # show the users in the gpg keyring. # `whoknows` command uses it internally. @@ -642,19 +643,41 @@ function _get_users_in_gpg_keyring { args+=( "--homedir" "$homedir" ) fi - # we use --fixed-list-mode so older versions of gpg emit 'uid:' lines. - # here gawk splits on colon as --with-colon, exact matches field 1 as 'uid', and selects field 10 "User-ID" - # the gensub regex extracts email from <> within field 10. (If there's no <>, then field is just an email address - # (and maybe a comment) and the regex just passes it through.) - # sed at the end removes any 'comment' that appears in parentheses, for #530 - # 3>&- closes fd 3 for bats, see https://github.com/bats-core/bats-core#file-descriptor-3-read-this-if-bats-hangs + ## We use --fixed-list-mode so older versions of gpg emit 'uid:' lines. + ## Gawk splits on colon as --with-colon, matches field 1 as 'uid', result=$($SECRETS_GPG_COMMAND "${args[@]}" --no-permission-warning --list-public-keys --with-colon --fixed-list-mode | \ - gawk -F: '$1~/uid/{print gensub(/.*<(.*)>.*/, "\\1", "g", $10); }' | \ - sed 's/([^)]*)//g' 3>&-) + gawk -F: '$1=="uid"' ) - echo "$result" + # For #508 / #552: warn user if gpg indicates keys are one of: + # i=invalid, d=disabled, r=revoked, e=expired, n=not valid + # See https://github.com/gpg/gnupg/blob/master/doc/DETAILS#field-2---validity # for more on gpg 'validity codes'. + local invalid_lines + invalid_lines=$(echo "$result" | gawk -F: '$2=="i" || $2=="d" || $2=="r" || $2=="e" || $2=="n"') + + local emails + emails=$(_extract_emails_from_gpg_output "$result") + + local emails_with_invalid_keys + emails_with_invalid_keys=$(_extract_emails_from_gpg_output "$invalid_lines") + + if [[ -n "$emails_with_invalid_keys" ]]; then + _warn "at least one key for email(s) is revoked, expired, or otherwise invalid: $emails_with_invalid_keys" + fi + + echo "$emails" } +function _extract_emails_from_gpg_output { + local result=$1 + + # gensub() outputs email from <> within field 10, "User-ID". If there's no <>, then field is just an email address + # (and maybe a comment) and we pass it through. + # Sed at the end removes any 'comment' that appears in parentheses, for #530 + # 3>&- closes fd 3 for bats, see https://github.com/bats-core/bats-core#file-descriptor-3-read-this-if-bats-hangs + local emails + emails=$(echo "$result" | gawk -F: '{print gensub(/.*<(.*)>.*/, "\\1", "g", $10); }' | sed 's/([^)]*)//g' 3>&-) + echo "$emails" +} function _get_users_in_gitsecret_keyring { # show the users in the gitsecret keyring. diff --git a/src/commands/git_secret_tell.sh b/src/commands/git_secret_tell.sh index af22db24..768b818d 100644 --- a/src/commands/git_secret_tell.sh +++ b/src/commands/git_secret_tell.sh @@ -64,7 +64,7 @@ function tell { if [[ "${#emails[@]}" -eq 0 ]]; then # If after possible addition of git_email, emails are still empty, # we should raise an exception. - _abort "you must provide at least one email address." + _abort "you must use -m or provide at least one email address." fi _assert_keychain_contains_emails "$homedir" "${emails[@]}" diff --git a/tests/test_add.bats b/tests/test_add.bats index a063d2d0..e2995bee 100644 --- a/tests/test_add.bats +++ b/tests/test_add.bats @@ -105,7 +105,8 @@ function teardown { run git secret add -i "$test_file" [ "$status" -eq 0 ] - run _file_has_line "$test_file" "../.gitignore" + [[ -f "$current_dir/.gitignore" ]] + run _file_has_line "$test_file" "$current_dir/.gitignore" [ "$status" -eq 0 ] # .gitignore was not created: diff --git a/tests/test_expiration.bats b/tests/test_expiration.bats index fcee482a..8a387cbe 100644 --- a/tests/test_expiration.bats +++ b/tests/test_expiration.bats @@ -18,7 +18,7 @@ function teardown { unset_current_state } -@test "test 'hide' using expired key" { +@test "run 'hide' using expired key" { FILE_TO_HIDE="$TEST_DEFAULT_FILENAME" FILE_CONTENTS="hidden content юникод" set_state_secret_add "$FILE_TO_HIDE" "$FILE_CONTENTS" @@ -34,6 +34,11 @@ function teardown { } +@test "run 'whoknows' using expired key" { + run git secret whoknows + [ $status -eq 0 ] +} + @test "run 'whoknows -l' on only expired user" { run git secret whoknows -l [ "$status" -eq 0 ] @@ -50,7 +55,7 @@ function teardown { -@test "run 'whoknows -l' on expired and normal user" { +@test "run 'whoknows -l' on normal key and expired key" { install_fixture_key "$TEST_DEFAULT_USER" set_state_secret_tell "$TEST_DEFAULT_USER"