Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

**Collective memory, encrypted.**

[![tests: 428](https://img.shields.io/badge/tests-428-brightgreen?style=flat)](test/)
[![tests: 431](https://img.shields.io/badge/tests-431-brightgreen?style=flat)](test/)
![lints: 8](https://img.shields.io/badge/lints-8-blue?style=flat)
[![license: MIT](https://img.shields.io/badge/license-MIT-blue?style=flat)](LICENSE)

Expand Down
24 changes: 21 additions & 3 deletions hooks/encryption.template
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,38 @@ if ! command -v git-crypt &>/dev/null; then
exit 0
fi

staged_input=$(mktemp) || {
echo "ERROR: Failed to create encryption hook staged-path input" >&2
exit 1
}
attribute_input=$(mktemp) || {
rm -f "$staged_input"
echo "ERROR: Failed to create encryption hook attribute input" >&2
exit 1
}
trap 'rm -f "$staged_input" "$attribute_input"' EXIT

# Staged adds/copies/modifies/renames, NUL-delimited for path safety.
if ! git diff --cached --name-only --diff-filter=ACMR -z > "$staged_input"; then
echo "ERROR: Could not inspect staged paths for encryption" >&2
exit 1
fi
staged=()
while IFS= read -r -d '' path; do staged+=("$path"); done \
< <(git diff --cached --name-only --diff-filter=ACMR -z)
while IFS= read -r -d '' path; do staged+=("$path"); done < "$staged_input"
[ "${#staged[@]}" -eq 0 ] && exit 0

# Which staged paths are under an encrypted (filter=git-crypt) pattern?
# check-attr -z emits NUL-delimited <path> <attr> <value> triples.
if ! git check-attr --cached -z filter -- "${staged[@]}" > "$attribute_input"; then
echo "ERROR: Could not inspect staged encryption attributes" >&2
exit 1
fi
encrypted_staged=()
while IFS= read -r -d '' path \
&& IFS= read -r -d '' _attr \
&& IFS= read -r -d '' value; do
[ "$value" = "git-crypt" ] && encrypted_staged+=("$path")
done < <(git check-attr --cached -z filter -- "${staged[@]}")
done < "$attribute_input"
[ "${#encrypted_staged[@]}" -eq 0 ] && exit 0

verify_encrypted_paths "${encrypted_staged[@]}"
8 changes: 7 additions & 1 deletion hooks/obfuscation.template
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,21 @@ run_notes() {

build_staged_obfuscation_plan() {
local candidates="$1" plan="$2"
local staged="$candidates.staged"
local file relpath
: > "$candidates"

if ! git diff --cached --name-only --diff-filter=ACMR > "$staged"; then
rm -f "$staged"
return 1
fi
while IFS= read -r file; do
[[ "$file" != "$NOTES_ROOT/"* ]] && continue
relpath="${file#"$NOTES_ROOT/"}"
[[ "$relpath" == ".manifest" ]] && continue
printf '%s\n' "$relpath" >> "$candidates"
done < <(git diff --cached --name-only --diff-filter=ACMR)
done < "$staged"
rm -f "$staged"

classify_obfuscation_candidates \
"$NOTES_ROOT" "$NOTES_ROOT/.manifest" "$candidates" "$plan"
Expand Down
58 changes: 58 additions & 0 deletions test/integration.bats
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,64 @@ SH
[ "$(wc -l < "$calls" | tr -d ' ')" -eq 1 ]
}

@test "encryption hook fails closed when staged-path inspection fails (#49)" {
notes setup --yes

printf '%s\n' "public" > "$TARGET_DIR/public.md"
git -C "$TARGET_DIR" add public.md

local mock_bin="$BATS_TEST_TMPDIR/failing-encryption-diff-bin"
local real_git
real_git=$(command -v git)
mkdir -p "$mock_bin"
cat > "$mock_bin/git" <<'SH'
#!/usr/bin/env bash
if [ "${1:-}" = "diff" ]; then
printf '%s\n' "staged inspection failed" >&2
exit 73
fi
exec "$REAL_GIT" "$@"
SH
chmod +x "$mock_bin/git"
export PATH="$mock_bin:$PATH"
export REAL_GIT="$real_git"

run_encryption_hook
[ "$status" -eq 1 ]
[[ "$output" == *"staged inspection failed"* ]]
[[ "$output" == *"Could not inspect staged paths for encryption"* ]]
}

@test "encryption hook fails closed when staged-attribute inspection fails (#49)" {
notes setup --yes

mkdir -p "$TARGET_DIR/notes"
printf '%s\n' "secret" > "$TARGET_DIR/notes/secret.md"
git -C "$TARGET_DIR" add notes/secret.md

local mock_bin="$BATS_TEST_TMPDIR/failing-encryption-attr-bin"
local real_git
real_git=$(command -v git)
mkdir -p "$mock_bin"
cat > "$mock_bin/git" <<'SH'
#!/usr/bin/env bash
if [ "${1:-}" = "check-attr" ]; then
printf '%s\n' "attribute inspection failed" >&2
exit 74
fi
exec "$REAL_GIT" "$@"
SH
chmod +x "$mock_bin/git"
export PATH="$mock_bin:$PATH"
export REAL_GIT="$real_git"

run_encryption_hook
[ "$status" -eq 1 ]
[[ "$output" == *"attribute inspection failed"* ]]
[[ "$output" == *"Could not inspect staged encryption attributes"* ]]
}


@test "encryption hook falls back per path after a batched plaintext result (#49)" {
notes setup --yes
local fpr
Expand Down
32 changes: 32 additions & 0 deletions test/obfuscation-hooks.bats
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,38 @@ SH
}


@test "obfuscation hook fails closed when staged-path inspection fails" {
notes setup --yes
git -C "$NOTES_CALLER_PWD" add -A
git -C "$NOTES_CALLER_PWD" commit --no-verify -q -m "setup"

printf '%s\n' "# Delta" > "$NOTES_CALLER_PWD/notes/delta.md"
git -C "$NOTES_CALLER_PWD" add notes/delta.md

local mock_bin="$BATS_TEST_TMPDIR/failing-obfuscation-git-bin"
local real_git
real_git=$(command -v git)
mkdir -p "$mock_bin"
cat > "$mock_bin/git" <<'SH'
#!/usr/bin/env bash
if [ "${1:-}" = "diff" ]; then
printf '%s\n' "staged inspection failed" >&2
exit 73
fi
exec "$REAL_GIT" "$@"
SH
chmod +x "$mock_bin/git"

run env PATH="$mock_bin:$PATH" REAL_GIT="$real_git" \
bash -c 'cd "$1" && .git/hooks/pre-commit.d/obfuscation' \
_ "$NOTES_CALLER_PWD"

[ "$status" -eq 1 ]
[[ "$output" == *"staged inspection failed"* ]]
[[ "$output" == *"Could not classify staged note filenames"* ]]
}


@test "auto hook fails closed when a staged readable file is missing from disk" {
notes setup --yes
git -C "$NOTES_CALLER_PWD" add -A
Expand Down
Loading