Skip to content

Commit 72686f6

Browse files
committed
Merge branch '173-handle-quotes-in-filenames'
* 173-handle-quotes-in-filenames: Fix formatting Avoid unusual file-quoting issues breaking check for encrypted files by not checking file name directly Fix listing of encrypted files to respect non-default contexts Update changelog Correct tests' expected values for ls-crypt commands Move uninstall work that relies on `ls-crypt` earlier to before removal of transcript from Git repo Fix handling of encrypted files with double-quotes in name. #173 # Conflicts: # CHANGELOG.md
2 parents d64bba4 + 4310c29 commit 72686f6

File tree

4 files changed

+52
-24
lines changed

4 files changed

+52
-24
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ system, you must also run the `--upgrade` command in each repository:
3636

3737
### Fixed
3838

39+
- Fix handling of double-quotes in encrypted file names (#173)
3940
- Make --upgrade safer by failing fast if transcrypt config cannot be read
4041
(#189)
4142
- Fail with error when an empty password is provided to the -p or --password

tests/test_contexts.bats

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ function teardown {
7676
[[ $(git config --get diff.crypt.binary) = "true" ]]
7777
[[ $(git config --get merge.renormalize) = "true" ]]
7878

79-
[[ "$(git config --get alias.ls-crypt)" = "!git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = \":\" }; / crypt/{ print \$1 }'" ]]
79+
[[ "$(git config --get alias.ls-crypt)" = '!"$(git config transcrypt.crypt-dir 2>/dev/null || printf %s/crypt ""$(git rev-parse --git-dir)"")"/transcrypt --list' ]]
8080
}
8181

8282
@test "init: show extra context details in --display" {
@@ -216,7 +216,7 @@ function teardown {
216216
[ "${lines[1]}" = "$SUPER_SECRET_CONTENT_ENC" ]
217217
}
218218

219-
@test "contexts: git ls-crypt lists encrypted file for all contexts" {
219+
@test "contexts: git ls-crypt lists encrypted files for all contexts" {
220220
encrypt_named_file sensitive_file "$SECRET_CONTENT"
221221
encrypt_named_file super_sensitive_file "$SECRET_CONTENT" "super-secret"
222222

@@ -226,14 +226,14 @@ function teardown {
226226
[ "${lines[1]}" = "super_sensitive_file" ]
227227
}
228228

229-
@test "contexts: git ls-crypt-default lists encrypted file for only 'default' context" {
229+
@test "contexts: git ls-crypt-default lists encrypted files for all contexts" {
230230
encrypt_named_file sensitive_file "$SECRET_CONTENT"
231231
encrypt_named_file super_sensitive_file "$SECRET_CONTENT" "super-secret"
232232

233233
run git ls-crypt-default
234234
[ "$status" -eq 0 ]
235235
[ "${lines[0]}" = "sensitive_file" ]
236-
[ "${lines[1]}" = "" ]
236+
[ "${lines[1]}" = "super_sensitive_file" ]
237237
}
238238

239239
@test "contexts: git ls-crypt-super-secret lists encrypted file for only 'super-secret' context" {

tests/test_init.bats

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ SETUP_SKIP_INIT_TRANSCRYPT=1
4949
[ "$(git config --get merge.renormalize)" = "true" ]
5050
[ "$(git config --get merge.crypt.name)" = "Merge transcrypt secret files" ]
5151

52-
[ "$(git config --get alias.ls-crypt)" = "!git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = \":\" }; / crypt/{ print \$1 }'" ]
52+
[ "$(git config --get alias.ls-crypt)" = '!"$(git config transcrypt.crypt-dir 2>/dev/null || printf %s/crypt ""$(git rev-parse --git-dir)"")"/transcrypt --list' ]
5353
}
5454

5555
@test "init: show details for --display" {

transcrypt

Lines changed: 46 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,34 @@ derive_context_config_group() {
161161
fi
162162
}
163163

164+
# Internal function that returns a list of filenames for encrypted files in the
165+
# repo, where the filenames are verbatim and not quoted in any way even if they
166+
# contain unusual characters like double-quotes, backslash and control
167+
# characters. We must avoid quoting of filenames to support names containing
168+
# double quotes. #173
169+
_list_encrypted_files() {
170+
local strict_context=${1:-}
171+
172+
IFS=$'\n'
173+
# List files with -z option to disable quoting of filenames, then
174+
# immediately convert NUL-delimited filenames to be newline-delimited to be
175+
# compatibility with bash variables
176+
for file in $(git ls-files -z | tr '\0' '\n'); do
177+
# Check for the suffix ': filter: crypt' that identifies encrypted file
178+
local check
179+
check=$(git check-attr filter "$file" 2>/dev/null)
180+
181+
# Only output names of encrypted files matching the context, either
182+
# strictly (if $1 = "true") or loosely (if $1 is false or unset)
183+
if [[ "$strict_context" == "true" ]] &&
184+
[[ "$check" == *": filter: crypt${CONTEXT_CRYPT_SUFFIX:-}" ]]; then
185+
echo "$file"
186+
elif [[ "$check" == *": filter: crypt${CONTEXT_CRYPT_SUFFIX:-}"* ]]; then
187+
echo "$file"
188+
fi
189+
done
190+
}
191+
164192
# Detect OpenSSL major version 3 or later which requires a compatibility
165193
# work-around to include the prefix 'Salted__' and salt value when encrypting.
166194
#
@@ -325,7 +353,7 @@ git_pre_commit() {
325353
tmp=$(mktemp)
326354
IFS=$'\n'
327355
slow_mode_if_failed() {
328-
for secret_file in $(git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = ":" }; /crypt$/{ print $1 }'); do
356+
for secret_file in $(_list_encrypted_files); do
329357
# Skip symlinks, they contain the linked target file path not plaintext
330358
if [[ -L $secret_file ]]; then
331359
continue
@@ -373,7 +401,7 @@ git_pre_commit() {
373401
if [[ "${BASH_VERSINFO[0]}" -ge 4 ]] && [[ "${BASH_VERSINFO[1]}" -ge 4 ]]; then
374402
num_procs=$(nproc)
375403
num_jobs="\j"
376-
for secret_file in $(git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = ":" }; /crypt$/{ print $1 }'); do
404+
for secret_file in $(_list_encrypted_files); do
377405
while ((${num_jobs@P} >= num_procs)); do
378406
wait -n
379407
done
@@ -679,15 +707,15 @@ save_configuration() {
679707
git config merge.crypt.name 'Merge transcrypt secret files'
680708

681709
# add git alias for listing ALL encrypted files regardless of context
682-
git config alias.ls-crypt "!git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = \":\" }; / crypt/{ print \$1 }'"
710+
git config alias.ls-crypt "!$transcrypt_path --list"
683711

684712
# add a git alias for listing encrypted files in specific context, including 'default'
685713
if [[ "$CONTEXT" = 'default' ]]; then
686714
# List files with gitattribute 'filter=crypt'
687-
git config alias.ls-crypt-default "!git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = \":\" }; / crypt$/{ print \$1 }'"
715+
git config alias.ls-crypt-default "!$transcrypt_path --list"
688716
else
689717
# List files with gitattribute 'filter=crypt-<CONTEXT>'
690-
git config "alias.ls-crypt-${CONTEXT}" "!git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = \":\" }; / crypt-${CONTEXT}$/{ print \$1 }'"
718+
git config "alias.ls-crypt-${CONTEXT}" "!$transcrypt_path --context=${CONTEXT} --list"
691719
fi
692720
}
693721

@@ -832,6 +860,15 @@ uninstall_transcrypt() {
832860
remove_cached_plaintext
833861
fi
834862

863+
# touch all encrypted files to prevent stale stat info
864+
local encrypted_files
865+
encrypted_files=$(git ls-crypt)
866+
if [[ $encrypted_files ]] && [[ $IS_BARE == 'false' ]]; then
867+
cd "$REPO" >/dev/null || die 1 'could not change into the "%s" directory' "$REPO"
868+
# shellcheck disable=SC2086
869+
touch $encrypted_files
870+
fi
871+
835872
# remove helper scripts
836873
# Keep obsolete clean,smudge,textconv,merge refs here to remove them on upgrade
837874
for script in {transcrypt,clean,smudge,textconv,merge}; do
@@ -853,15 +890,6 @@ uninstall_transcrypt() {
853890
fi
854891
[[ -f "$pre_commit_hook_installed" ]] && rm "$pre_commit_hook_installed"
855892

856-
# touch all encrypted files to prevent stale stat info
857-
local encrypted_files
858-
encrypted_files=$(git ls-crypt)
859-
if [[ $encrypted_files ]] && [[ $IS_BARE == 'false' ]]; then
860-
cd "$REPO" >/dev/null || die 1 'could not change into the "%s" directory' "$REPO"
861-
# shellcheck disable=SC2086
862-
touch $encrypted_files
863-
fi
864-
865893
# remove context settings: cipher & password config, ls-crypt alias variant,
866894
# crypt filter/diff/merge attributes. We do it here instead of `clean_gitconfig`
867895
# to avoid interfering with flushing of credentials
@@ -1005,7 +1033,7 @@ upgrade_transcrypt() {
10051033
list_files() {
10061034
if [[ $IS_BARE == 'false' ]]; then
10071035
cd "$REPO" >/dev/null || die 1 'could not change into the "%s" directory' "$REPO"
1008-
git -c core.quotePath=false ls-files | git -c core.quotePath=false check-attr --stdin filter | awk 'BEGIN { FS = ":" }; /crypt/{ print $1 }'
1036+
_list_encrypted_files true
10091037
fi
10101038
}
10111039

@@ -1014,13 +1042,12 @@ show_raw_file() {
10141042
if [[ -f $show_file ]]; then
10151043
# ensure the file is currently being tracked
10161044
local escaped_file=${show_file//\//\\\/}
1017-
if git -c core.quotePath=false ls-files --others -- "$show_file" | awk "/${escaped_file}/{ exit 1 }"; then
1018-
file_paths=$(git -c core.quotePath=false ls-tree --name-only --full-name HEAD "$show_file")
1019-
else
1045+
file_paths=$(_list_encrypted_files | grep "$escaped_file")
1046+
if [[ -z "$file_paths" ]]; then
10201047
die 1 'the file "%s" is not currently being tracked by git' "$show_file"
10211048
fi
10221049
elif [[ $show_file == '*' ]]; then
1023-
file_paths=$(git ls-crypt)
1050+
file_paths=$(_list_encrypted_files)
10241051
else
10251052
die 1 'the file "%s" does not exist' "$show_file"
10261053
fi

0 commit comments

Comments
 (0)