Ignore encrypted files (#69)
Append the contents of .config/yadm/encrypt to the repo's git ignore list. This is to help prevent accidentally committing unencrypted sensitive data.
This commit is contained in:
parent
f3249e00b5
commit
0c9468c9b5
6 changed files with 159 additions and 1 deletions
|
@ -101,6 +101,7 @@ def supported_configs():
|
||||||
'local.user',
|
'local.user',
|
||||||
'yadm.alt-copy',
|
'yadm.alt-copy',
|
||||||
'yadm.auto-alt',
|
'yadm.auto-alt',
|
||||||
|
'yadm.auto-exclude',
|
||||||
'yadm.auto-perms',
|
'yadm.auto-perms',
|
||||||
'yadm.auto-private-dirs',
|
'yadm.auto-private-dirs',
|
||||||
'yadm.git-program',
|
'yadm.git-program',
|
||||||
|
|
|
@ -372,6 +372,29 @@ def test_offer_to_add(runner, yadm_y, paths, encrypt_targets, untracked):
|
||||||
assert f'AM {worktree_archive.basename}' in run.out
|
assert f'AM {worktree_archive.basename}' in run.out
|
||||||
|
|
||||||
|
|
||||||
|
def test_encrypt_added_to_exclude(runner, yadm_y, paths):
|
||||||
|
"""Confirm that .config/yadm/encrypt is added to exclude"""
|
||||||
|
|
||||||
|
expect = [
|
||||||
|
('passphrase:', PASSPHRASE),
|
||||||
|
('passphrase:', PASSPHRASE),
|
||||||
|
]
|
||||||
|
|
||||||
|
exclude_file = paths.repo.join('info/exclude')
|
||||||
|
paths.encrypt.write('test-encrypt-data\n')
|
||||||
|
exclude_file.write('original-data', ensure=True)
|
||||||
|
|
||||||
|
run = runner(
|
||||||
|
yadm_y('encrypt'),
|
||||||
|
expect=expect,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert 'test-encrypt-data' in paths.repo.join('info/exclude').read()
|
||||||
|
assert 'original-data' in paths.repo.join('info/exclude').read()
|
||||||
|
assert run.success
|
||||||
|
assert run.err == ''
|
||||||
|
|
||||||
|
|
||||||
def encrypted_data_valid(runner, encrypted, expected):
|
def encrypted_data_valid(runner, encrypted, expected):
|
||||||
"""Verify encrypted data matches expectations"""
|
"""Verify encrypted data matches expectations"""
|
||||||
run = runner([
|
run = runner([
|
||||||
|
|
|
@ -27,7 +27,7 @@ def test_introspect_category(
|
||||||
expected = []
|
expected = []
|
||||||
if name == 'commands':
|
if name == 'commands':
|
||||||
expected = supported_commands
|
expected = supported_commands
|
||||||
elif name == 'config':
|
elif name == 'configs':
|
||||||
expected = supported_configs
|
expected = supported_configs
|
||||||
elif name == 'switches':
|
elif name == 'switches':
|
||||||
expected = supported_switches
|
expected = supported_switches
|
||||||
|
|
66
test/test_unit_exclude_encrypted.py
Normal file
66
test/test_unit_exclude_encrypted.py
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
"""Unit tests: exclude_encrypted"""
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'exclude', ['missing', 'outdated', 'up-to-date'])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'encrypt_exists', [True, False], ids=['encrypt', 'no-encrypt'])
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
'auto_exclude', [True, False], ids=['enabled', 'disabled'])
|
||||||
|
def test_exclude_encrypted(
|
||||||
|
runner, tmpdir, yadm, encrypt_exists, auto_exclude, exclude):
|
||||||
|
"""Test exclude_encrypted()"""
|
||||||
|
|
||||||
|
header = (
|
||||||
|
"# yadm-auto-excludes\n"
|
||||||
|
"# This section is managed by yadm.\n"
|
||||||
|
"# Any edits below will be lost.\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
config_function = 'function config() { echo "false";}'
|
||||||
|
if auto_exclude:
|
||||||
|
config_function = 'function config() { return; }'
|
||||||
|
|
||||||
|
encrypt_file = tmpdir.join('encrypt_file')
|
||||||
|
repo_dir = tmpdir.join('repodir')
|
||||||
|
exclude_file = repo_dir.join('info/exclude')
|
||||||
|
|
||||||
|
if encrypt_exists:
|
||||||
|
encrypt_file.write('test-encrypt-data\n', ensure=True)
|
||||||
|
if exclude == 'outdated':
|
||||||
|
exclude_file.write(
|
||||||
|
f'original-exclude\n{header}outdated\n', ensure=True)
|
||||||
|
elif exclude == 'up-to-date':
|
||||||
|
exclude_file.write(
|
||||||
|
f'original-exclude\n{header}test-encrypt-data\n', ensure=True)
|
||||||
|
|
||||||
|
script = f"""
|
||||||
|
YADM_TEST=1 source {yadm}
|
||||||
|
{config_function}
|
||||||
|
DEBUG=1
|
||||||
|
YADM_ENCRYPT="{encrypt_file}"
|
||||||
|
YADM_REPO="{repo_dir}"
|
||||||
|
exclude_encrypted
|
||||||
|
"""
|
||||||
|
run = runner(command=['bash'], inp=script)
|
||||||
|
assert run.success
|
||||||
|
assert run.err == ''
|
||||||
|
|
||||||
|
if auto_exclude:
|
||||||
|
if encrypt_exists:
|
||||||
|
assert exclude_file.exists()
|
||||||
|
if exclude == 'missing':
|
||||||
|
assert exclude_file.read() == f'{header}test-encrypt-data\n'
|
||||||
|
else:
|
||||||
|
assert exclude_file.read() == (
|
||||||
|
'original-exclude\n'
|
||||||
|
f'{header}test-encrypt-data\n')
|
||||||
|
if exclude != 'up-to-date':
|
||||||
|
assert f'Updating {exclude_file}' in run.out
|
||||||
|
else:
|
||||||
|
assert run.out == ''
|
||||||
|
else:
|
||||||
|
assert run.out == ''
|
||||||
|
else:
|
||||||
|
assert run.out == ''
|
51
yadm
51
yadm
|
@ -804,6 +804,7 @@ function encrypt() {
|
||||||
|
|
||||||
require_gpg
|
require_gpg
|
||||||
require_encrypt
|
require_encrypt
|
||||||
|
exclude_encrypted
|
||||||
parse_encrypt
|
parse_encrypt
|
||||||
|
|
||||||
cd_work "Encryption" || return
|
cd_work "Encryption" || return
|
||||||
|
@ -986,6 +987,7 @@ local.os
|
||||||
local.user
|
local.user
|
||||||
yadm.alt-copy
|
yadm.alt-copy
|
||||||
yadm.auto-alt
|
yadm.auto-alt
|
||||||
|
yadm.auto-exclude
|
||||||
yadm.auto-perms
|
yadm.auto-perms
|
||||||
yadm.auto-private-dirs
|
yadm.auto-private-dirs
|
||||||
yadm.git-program
|
yadm.git-program
|
||||||
|
@ -1069,6 +1071,55 @@ function version() {
|
||||||
|
|
||||||
# ****** Utility Functions ******
|
# ****** Utility Functions ******
|
||||||
|
|
||||||
|
function exclude_encrypted() {
|
||||||
|
|
||||||
|
auto_exclude=$(config --bool yadm.auto-exclude)
|
||||||
|
[ "$auto_exclude" == "false" ] && return 0
|
||||||
|
|
||||||
|
exclude_path="${YADM_REPO}/info/exclude"
|
||||||
|
newline=$'\n'
|
||||||
|
exclude_flag="# yadm-auto-excludes"
|
||||||
|
exclude_header="${exclude_flag}${newline}"
|
||||||
|
exclude_header="${exclude_header}# This section is managed by yadm."
|
||||||
|
exclude_header="${exclude_header}${newline}"
|
||||||
|
exclude_header="${exclude_header}# Any edits below will be lost."
|
||||||
|
exclude_header="${exclude_header}${newline}"
|
||||||
|
|
||||||
|
# do nothing if there is no YADM_ENCRYPT
|
||||||
|
[ -e "$YADM_ENCRYPT" ] || return 0
|
||||||
|
|
||||||
|
# read encrypt
|
||||||
|
encrypt_data=""
|
||||||
|
while IFS='' read -r line || [ -n "$line" ]; do
|
||||||
|
encrypt_data="${encrypt_data}${line}${newline}"
|
||||||
|
done < "$YADM_ENCRYPT"
|
||||||
|
|
||||||
|
# read info/exclude
|
||||||
|
unmanaged=""
|
||||||
|
managed=""
|
||||||
|
if [ -e "$exclude_path" ]; then
|
||||||
|
flag_seen=0
|
||||||
|
while IFS='' read -r line || [ -n "$line" ]; do
|
||||||
|
[ "$line" = "$exclude_flag" ] && flag_seen=1
|
||||||
|
if [ "$flag_seen" -eq 0 ]; then
|
||||||
|
unmanaged="${unmanaged}${line}${newline}"
|
||||||
|
else
|
||||||
|
managed="${managed}${line}${newline}"
|
||||||
|
fi
|
||||||
|
done < "$exclude_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "${exclude_header}${encrypt_data}" != "$managed" ]; then
|
||||||
|
basedir=${exclude_path%/*}
|
||||||
|
[ -e "$basedir" ] || mkdir -p "$basedir" # assert path
|
||||||
|
debug "Updating ${exclude_path}"
|
||||||
|
printf "%s" "${unmanaged}${exclude_header}${encrypt_data}" > "$exclude_path"
|
||||||
|
fi
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
function is_valid_branch_name() {
|
function is_valid_branch_name() {
|
||||||
# Git branches do not allow:
|
# Git branches do not allow:
|
||||||
# * path component that begins with "."
|
# * path component that begins with "."
|
||||||
|
|
17
yadm.1
17
yadm.1
|
@ -347,6 +347,11 @@ Disable the automatic linking described in the section ALTERNATES. If disabled,
|
||||||
you may still run "yadm alt" manually to create the alternate links. This
|
you may still run "yadm alt" manually to create the alternate links. This
|
||||||
feature is enabled by default.
|
feature is enabled by default.
|
||||||
.TP
|
.TP
|
||||||
|
.B yadm.auto-exclude
|
||||||
|
Disable the automatic exclusion of patterns defined in
|
||||||
|
.IR $HOME/.config/yadm/encrypt .
|
||||||
|
This feature is enabled by default.
|
||||||
|
.TP
|
||||||
.B yadm.auto-perms
|
.B yadm.auto-perms
|
||||||
Disable the automatic permission changes described in the section PERMISSIONS.
|
Disable the automatic permission changes described in the section PERMISSIONS.
|
||||||
If disabled, you may still run
|
If disabled, you may still run
|
||||||
|
@ -674,6 +679,18 @@ configuration.
|
||||||
It is recommended that you use a private repository when keeping confidential
|
It is recommended that you use a private repository when keeping confidential
|
||||||
files, even though they are encrypted.
|
files, even though they are encrypted.
|
||||||
|
|
||||||
|
Patterns found in
|
||||||
|
.I $HOME/.config/yadm/encrypt
|
||||||
|
are automatically added to the repository's
|
||||||
|
.I info/exclude
|
||||||
|
file every time
|
||||||
|
.B yadm encrypt
|
||||||
|
is run.
|
||||||
|
This is to prevent accidentally committing sensitive data to the repository.
|
||||||
|
This can be disabled using the
|
||||||
|
.I yadm.auto-exclude
|
||||||
|
configuration.
|
||||||
|
|
||||||
.SH PERMISSIONS
|
.SH PERMISSIONS
|
||||||
|
|
||||||
When files are checked out of a Git repository, their initial permissions are
|
When files are checked out of a Git repository, their initial permissions are
|
||||||
|
|
Loading…
Reference in a new issue