Skip to content

Commit 6726730

Browse files
committed
parse_encrypt: Don't let e.g. "*.ext" match files in subdirs
This matches the behavior before 3.4.0. Silent errors from ls-files to avoid warnings about e.g. directories that aren't readable and also list files that would have been encrypted had they not been tracked in git (#521). Fix the patterns written to info/exclude so that they match the same files as are encrypted (e.g. *.key should only match .key files in the topdir, not in subdirs).
1 parent 5648f8b commit 6726730

5 files changed

Lines changed: 87 additions & 36 deletions

File tree

test/conftest.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -609,14 +609,14 @@ def gnupg(tmpdir_factory, runner):
609609
env["GNUPGHOME"] = home
610610

611611
# this pre-populates std files in the GNUPGHOME
612-
runner(["gpg", "-k"], env=env)
612+
runner(["gpg", "-k"], env=env, report=False)
613613

614614
def register_gpg_password(password):
615615
"""Publish a new GPG mock password and flush cached passwords"""
616616
home.join("mock-password").write(password)
617-
runner(["gpgconf", "--reload", "gpg-agent"], env=env)
617+
runner(["gpgconf", "--reload", "gpg-agent"], env=env, report=False)
618618

619619
yield data(home, register_gpg_password)
620620

621-
runner(["gpgconf", "--kill", "gpg-agent"], env=env)
622-
runner(["gpgconf", "--remove-socketdir", "gpg-agent"], env=env)
621+
runner(["gpgconf", "--kill", "gpg-agent"], env=env, report=False)
622+
runner(["gpgconf", "--remove-socketdir", "gpg-agent"], env=env, report=False)

test/test_encryption.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ def encrypt_targets(yadm_cmd, paths):
9292
paths.work.join("globs dir/globs file2").write("globs file2")
9393
expected.append("globs dir/globs file2")
9494
paths.encrypt.write("globs*\n", mode="a")
95+
paths.encrypt.write("globs d*/globs*\n", mode="a")
9596

9697
# blank lines
9798
paths.encrypt.write("\n \n\t\n", mode="a")
@@ -404,8 +405,8 @@ def test_encrypt_added_to_exclude(runner, yadm_cmd, paths, gnupg):
404405

405406
run = runner(yadm_cmd("encrypt"), env=env)
406407

407-
assert "test-encrypt-data" in paths.repo.join("info/exclude").read()
408-
assert "original-data" in paths.repo.join("info/exclude").read()
408+
assert "test-encrypt-data" in exclude_file.read()
409+
assert "original-data" in exclude_file.read()
409410
assert run.success
410411
assert run.err == ""
411412

test/test_unit_exclude_encrypted.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def test_exclude_encrypted(runner, tmpdir, yadm, encrypt_exists, auto_exclude, e
2424
if exclude == "outdated":
2525
exclude_file.write(f"original-exclude\n{header}outdated\n", ensure=True)
2626
elif exclude == "up-to-date":
27-
exclude_file.write(f"original-exclude\n{header}test-encrypt-data\n", ensure=True)
27+
exclude_file.write(f"original-exclude\n{header}/test-encrypt-data\n", ensure=True)
2828

2929
script = f"""
3030
YADM_TEST=1 source {yadm}
@@ -42,9 +42,9 @@ def test_exclude_encrypted(runner, tmpdir, yadm, encrypt_exists, auto_exclude, e
4242
if encrypt_exists:
4343
assert exclude_file.exists()
4444
if exclude == "missing":
45-
assert exclude_file.read() == f"{header}test-encrypt-data\n"
45+
assert exclude_file.read() == f"{header}/test-encrypt-data\n"
4646
else:
47-
assert exclude_file.read() == ("original-exclude\n" f"{header}test-encrypt-data\n")
47+
assert exclude_file.read() == ("original-exclude\n" f"{header}/test-encrypt-data\n")
4848
if exclude != "up-to-date":
4949
assert f"Updating {exclude_file}" in run.out
5050
else:

test/test_unit_parse_encrypt.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,11 @@ def create_test_encrypt_data(paths):
100100
edata += "*card1\n" # matches same file as the one above
101101
paths.work.join("wildcard1").write("", ensure=True)
102102
paths.work.join("wildcard2").write("", ensure=True)
103+
paths.work.join("subdir/wildcard1").write("", ensure=True)
103104
expected.add("wildcard1")
104105
expected.add("wildcard2")
105106

106-
edata += "dirwild*\n"
107+
edata += "dirwild*/file*\n"
107108
paths.work.join("dirwildcard/file1").write("", ensure=True)
108109
paths.work.join("dirwildcard/file2").write("", ensure=True)
109110
expected.add("dirwildcard/file1")
@@ -125,6 +126,9 @@ def create_test_encrypt_data(paths):
125126
expected.add("ex ex/file4")
126127
expected.add("ex ex/file6.text")
127128

129+
paths.work.join("dirwildcard/file7.ex").write("", ensure=True)
130+
expected.add("dirwildcard/file7.ex")
131+
128132
# double star
129133
edata += "doublestar/**/file*\n"
130134
edata += "!**/file3\n"

yadm

Lines changed: 72 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ PROC_VERSION="/proc/version"
6161
OPERATING_SYSTEM="Unknown"
6262

6363
ENCRYPT_INCLUDE_FILES="unparsed"
64+
NO_ENCRYPT_TRACKED_FILES=()
6465

6566
LEGACY_WARNING_ISSUED=0
6667
INVALID_ALT=()
@@ -1042,6 +1043,12 @@ function encrypt() {
10421043
printf '%s\n' "${ENCRYPT_INCLUDE_FILES[@]}"
10431044
echo
10441045

1046+
if [ ${#NO_ENCRYPT_TRACKED_FILES[@]} -gt 0 ]; then
1047+
echo "Warning: The following files are tracked and will NOT be encrypted:"
1048+
printf '%s\n' "${NO_ENCRYPT_TRACKED_FILES[@]}"
1049+
echo
1050+
fi
1051+
10451052
# encrypt all files which match the globs
10461053
if tar -f - -c "${ENCRYPT_INCLUDE_FILES[@]}" | _encrypt_to "$YADM_ARCHIVE"; then
10471054
echo "Wrote new file: $YADM_ARCHIVE"
@@ -1468,38 +1475,59 @@ function version() {
14681475

14691476
function exclude_encrypted() {
14701477

1478+
local auto_exclude
14711479
auto_exclude=$(config --bool yadm.auto-exclude)
14721480
[ "$auto_exclude" == "false" ] && return 0
14731481

1474-
exclude_path="${YADM_REPO}/info/exclude"
1475-
newline=$'\n'
1476-
exclude_flag="# yadm-auto-excludes"
1477-
exclude_header="${exclude_flag}${newline}"
1482+
# do nothing if there is no YADM_ENCRYPT
1483+
[ -e "$YADM_ENCRYPT" ] || return 0
1484+
1485+
readonly exclude_path="${YADM_REPO}/info/exclude"
1486+
readonly newline=$'\n'
1487+
readonly exclude_flag="# yadm-auto-excludes"
1488+
1489+
local exclude_header="${exclude_flag}${newline}"
14781490
exclude_header="${exclude_header}# This section is managed by yadm."
14791491
exclude_header="${exclude_header}${newline}"
14801492
exclude_header="${exclude_header}# Any edits below will be lost."
14811493
exclude_header="${exclude_header}${newline}"
14821494

1483-
# do nothing if there is no YADM_ENCRYPT
1484-
[ -e "$YADM_ENCRYPT" ] || return 0
1485-
14861495
# read encrypt
1487-
encrypt_data=""
1488-
while IFS='' read -r line || [ -n "$line" ]; do
1489-
encrypt_data="${encrypt_data}${line}${newline}"
1496+
local encrypt_data=""
1497+
local pattern
1498+
while IFS='' read -r pattern || [ -n "$pattern" ]; do
1499+
case ${pattern:0:1} in
1500+
\#)
1501+
pattern=""
1502+
;;
1503+
!)
1504+
# Prepend / to the pattern so that it matches the same files as in
1505+
# parse_encrypt (i.e. only from the root)
1506+
pattern="!/${pattern:1}$newline"
1507+
;;
1508+
*)
1509+
if ! [[ $pattern =~ ^[[:blank:]]*(#|$) ]]; then
1510+
pattern="/$pattern$newline"
1511+
else
1512+
pattern=""
1513+
fi
1514+
;;
1515+
esac
1516+
encrypt_data="${encrypt_data}${pattern}"
14901517
done <"$YADM_ENCRYPT"
14911518

14921519
# read info/exclude
1493-
unmanaged=""
1494-
managed=""
1520+
local unmanaged=""
1521+
local managed=""
14951522
if [ -e "$exclude_path" ]; then
1496-
flag_seen=0
1523+
local -i flag_seen=0
1524+
local line
14971525
while IFS='' read -r line || [ -n "$line" ]; do
14981526
[ "$line" = "$exclude_flag" ] && flag_seen=1
1499-
if [ "$flag_seen" -eq 0 ]; then
1500-
unmanaged="${unmanaged}${line}${newline}"
1501-
else
1527+
if ((flag_seen)); then
15021528
managed="${managed}${line}${newline}"
1529+
else
1530+
unmanaged="${unmanaged}${line}${newline}"
15031531
fi
15041532
done <"$exclude_path"
15051533
fi
@@ -1926,26 +1954,44 @@ function parse_encrypt() {
19261954
local -a exclude
19271955
local -a include
19281956

1929-
while IFS= read -r pattern; do
1930-
case $pattern in
1931-
\#*)
1957+
local pattern
1958+
while IFS='' read -r pattern || [ -n "$pattern" ]; do
1959+
case ${pattern:0:1} in
1960+
\#)
19321961
# Ignore comments
19331962
;;
1934-
!*)
1935-
exclude+=("--exclude=${pattern:1}")
1963+
!)
1964+
exclude+=("--exclude=/${pattern:1}")
19361965
;;
19371966
*)
1938-
if ! [[ $pattern =~ ^[[:blank:]]*$ ]]; then
1967+
if ! [[ $pattern =~ ^[[:blank:]]*(#|$) ]]; then
19391968
include+=("$pattern")
19401969
fi
19411970
;;
19421971
esac
19431972
done <"$YADM_ENCRYPT"
19441973

1945-
if [[ ${#include} -gt 0 ]]; then
1946-
while IFS= read -r filename; do
1947-
ENCRYPT_INCLUDE_FILES+=("${filename%/}")
1948-
done <<<"$("$GIT_PROGRAM" ls-files --others "${exclude[@]}" -- "${include[@]}")"
1974+
if [ ${#include[@]} -gt 0 ]; then
1975+
while IFS='' read -r filename; do
1976+
if [ -n "$filename" ]; then
1977+
ENCRYPT_INCLUDE_FILES+=("${filename%/}")
1978+
fi
1979+
done <<<"$(
1980+
"$GIT_PROGRAM" --glob-pathspecs ls-files --others \
1981+
"${exclude[@]}" -- "${include[@]}" 2>/dev/null
1982+
)"
1983+
1984+
[ "$YADM_COMMAND" = "encrypt" ] || return
1985+
1986+
# List files that matches encryption pattern but is tracked
1987+
while IFS='' read -r filename; do
1988+
if [ -n "$filename" ]; then
1989+
NO_ENCRYPT_TRACKED_FILES+=("${filename%/}")
1990+
fi
1991+
done <<<"$(
1992+
"$GIT_PROGRAM" --glob-pathspecs ls-files \
1993+
"${exclude[@]}" -- "${include[@]}"
1994+
)"
19491995
fi
19501996
}
19511997

0 commit comments

Comments
 (0)