From 53f07ba1c9bd064c3acac8064faa888aeeef43c4 Mon Sep 17 00:00:00 2001 From: Shane Jaroch Date: Fri, 9 Jan 2026 11:12:56 -0500 Subject: [PATCH] wip --- git-remote-gcrypt | 7 +- tests/test-clean-command.sh | 133 +++++++++++++++++++++++++++++++----- 2 files changed, 121 insertions(+), 19 deletions(-) diff --git a/git-remote-gcrypt b/git-remote-gcrypt index 4e29096..011fddd 100755 --- a/git-remote-gcrypt +++ b/git-remote-gcrypt @@ -760,7 +760,7 @@ ensure_connected() echo_info "ERROR: Remote repository contains unencrypted or unknown files!" echo_info "To protect your privacy, git-remote-gcrypt will NOT push to this remote." echo_info "Found unexpected files: $(echo "$early_bad_files" | head -n 3 | tr '\n' ' ')" - echo_info "To see full list of unexpected files, use: git-remote-gcrypt clean --force $URL" + echo_info "To see unencrypted files, use: git-remote-gcrypt clean $URL" echo_info "To fix and remove these files, use: git-remote-gcrypt clean --force $URL" exit 1 fi @@ -1356,6 +1356,11 @@ elif [ "$NAME" = "gcrypt-clean" ]; then echo_die "Could not connect to $URL." fi + if [ "$Did_find_repo" != "yes" ]; then + echo_die "Error: No gcrypt manifest found on remote '$URL'." \ + "Aborting clean to prevent accidental data loss." + fi + # Get all files in the remote # For rsync backends, list files directly via rsync --list-only (awk extracts filename). # For rclone backends, list files via rclone lsf. diff --git a/tests/test-clean-command.sh b/tests/test-clean-command.sh index f39d511..a2f3e87 100755 --- a/tests/test-clean-command.sh +++ b/tests/test-clean-command.sh @@ -19,8 +19,13 @@ print_err() { echo -e "${RED}✗ $*${NC}"; } SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)" export PATH="$SCRIPT_DIR:$PATH" +# Isolate git config from user environment +export GIT_CONFIG_SYSTEM=/dev/null +export GIT_CONFIG_GLOBAL=/dev/null + # Suppress git advice messages -GIT="git -c advice.defaultBranchName=false" +# Note: git-remote-gcrypt reads actual config files, not just CLI -c options +GIT="git -c advice.defaultBranchName=false -c commit.gpgSign=false" # -------------------------------------------------- # Set up test environment @@ -31,11 +36,44 @@ trap 'rm -rf "$tempdir"' EXIT print_info "Setting up test environment..." +# -------------------------------------------------- +# GPG Setup (Derived from system-test.sh) +# -------------------------------------------------- +export GNUPGHOME="${tempdir}/gpg" +mkdir "${GNUPGHOME}" + +# Wrapper to suppress obsolete warnings +cat <<'EOF' >"${GNUPGHOME}/gpg" +#!/usr/bin/env bash +set -efuC -o pipefail; shopt -s inherit_errexit +args=( "${@}" ) +for ((i = 0; i < ${#}; ++i)); do + if [[ ${args[${i}]} = "--secret-keyring" ]]; then + unset "args[${i}]" "args[$(( i + 1 ))]" + break + fi +done +exec gpg "${args[@]}" +EOF +chmod +x "${GNUPGHOME}/gpg" + +# Generate key +( + gpg --batch --passphrase "" --quick-generate-key "Test " +) + +# -------------------------------------------------- +# Git Setup +# -------------------------------------------------- + # Create a bare repo with dirty files $GIT init --bare "$tempdir/remote.git" >/dev/null cd "$tempdir/remote.git" $GIT config user.email "test@test.com" $GIT config user.name "Test" +$GIT config gpg.program "${GNUPGHOME}/gpg" +# Needed for encryption to work during setup +$GIT config gcrypt.participants "test@test.com" # Add multiple unencrypted files echo "SECRET=abc" >"$tempdir/secret1.txt" @@ -71,29 +109,29 @@ output=$("$SCRIPT_DIR/git-remote-gcrypt" clean 2>&1 || :) assert_grep "Usage: git-remote-gcrypt clean" "$output" "clean shows usage when no URL/remote found" # -------------------------------------------------- -# Test 2: Default scan-only mode +# Test 2: Safety Check (Abort on non-gcrypt) # -------------------------------------------------- -print_info "Test 2: Default scan-only mode..." +print_info "Test 2: Safety Check (Abort on non-gcrypt)..." cd "$tempdir/remote.git" -output=$("$SCRIPT_DIR/git-remote-gcrypt" clean "$tempdir/remote.git" 2>&1) -assert_grep "secret1.txt" "$output" "clean identifies unencrypted files" -assert_grep "NOTE: This is a scan" "$output" "clean defaults to scan-only mode" +output=$("$SCRIPT_DIR/git-remote-gcrypt" clean "$tempdir/remote.git" 2>&1 || :) +assert_grep "Error: No gcrypt manifest found" "$output" "clean aborts on non-gcrypt repo" if $GIT ls-tree HEAD | grep -q "secret1.txt"; then - print_success "Files still exist after default scan" + print_success "Files preserved (Safety check passed)" else - print_err "Default scan incorrectly deleted files!" + print_err "Files deleted despite safety check!" exit 1 fi # -------------------------------------------------- -# Test 3: Remote resolution +# Test 3: Remote resolution (Abort on non-gcrypt) # -------------------------------------------------- print_info "Test 3: Remote resolution..." mkdir -p "$tempdir/client" && cd "$tempdir/client" && $GIT init >/dev/null +$GIT config gpg.program "${GNUPGHOME}/gpg" $GIT remote add origin "$tempdir/remote.git" -output=$("$SCRIPT_DIR/git-remote-gcrypt" clean origin 2>&1) -assert_grep "Checking remote: $tempdir/remote.git" "$output" "clean resolved 'origin' to URL" +output=$("$SCRIPT_DIR/git-remote-gcrypt" clean origin 2>&1 || :) +assert_grep "Error: Remote 'origin' is not a gcrypt:: remote" "$output" "clean aborts on resolved non-gcrypt remote" # -------------------------------------------------- # Test 4: Remote listing @@ -101,19 +139,78 @@ assert_grep "Checking remote: $tempdir/remote.git" "$output" "clean resolved 'or print_info "Test 4: Remote listing..." $GIT remote add gcrypt-origin "gcrypt::$tempdir/remote.git" output=$("$SCRIPT_DIR/git-remote-gcrypt" clean 2>&1 || :) -assert_grep "Available gcrypt remotes:" "$output" "clean lists remotes" +assert_grep "Available remotes:" "$output" "clean lists remotes" assert_grep "gcrypt-origin" "$output" "clean listed 'gcrypt-origin'" # -------------------------------------------------- -# Test 5: Force cleanup +# Test 5: Clean Valid Gcrypt Repo # -------------------------------------------------- -print_info "Test 5: Force cleanup..." -"$SCRIPT_DIR/git-remote-gcrypt" clean "$tempdir/remote.git" --force >/dev/null 2>&1 -if $GIT -C "$tempdir/remote.git" ls-tree HEAD 2>/dev/null | grep -q "secret"; then - print_err "Files still exist after force cleanup!" +print_info "Test 5: Clean Valid Gcrypt Repo..." + +# 1. Initialize a valid gcrypt repo +mkdir "$tempdir/valid.git" && cd "$tempdir/valid.git" && $GIT init --bare >/dev/null +cd "$tempdir/client" +$GIT config user.name "Test" +$GIT config user.email "test@test.com" +$GIT config user.signingkey "test@test.com" +# Create content to push +echo "valid content" >content.txt +$GIT add content.txt +$GIT commit -m "init valid" +# Push to intialize +set -x +$GIT push -f "gcrypt::$tempdir/valid.git" master:master || { + set +x + print_err "Git push failed" + exit 1 +} +set +x + +print_info "Initialized valid gcrypt repo" + +# 2. Inject garbage file into the remote git index/tree +cd "$tempdir/valid.git" +GREF="refs/gcrypt/gitception" +if ! $GIT rev-parse --verify "$GREF" >/dev/null 2>&1; then + print_err "Gref $GREF not found in remote!" + exit 1 +fi + +GARBAGE_BLOB=$(echo "GARBAGE DATA" | $GIT hash-object -w --stdin) +CURRENT_TREE=$($GIT rev-parse "$GREF^{tree}") +export GIT_INDEX_FILE=index.garbage +$GIT read-tree "$CURRENT_TREE" +$GIT update-index --add --cacheinfo 100644 "$GARBAGE_BLOB" "garbage_file" +NEW_TREE=$($GIT write-tree) +rm index.garbage +PARENT=$($GIT rev-parse "$GREF") +NEW_COMMIT=$(echo "Inject garbage" | $GIT commit-tree "$NEW_TREE" -p "$PARENT") +$GIT update-ref "$GREF" "$NEW_COMMIT" + +# Verify injection +if ! $GIT ls-tree -r "$GREF" | grep -q "garbage_file"; then + print_err "Failed to inject garbage_file into $GREF" + exit 1 +fi +print_info "Injected garbage_file into remote $GREF" + +# 3. Scan (expect to find garbage_file) +set -x +output=$("$SCRIPT_DIR/git-remote-gcrypt" clean "gcrypt::$tempdir/valid.git" 2>&1) +set +x +assert_grep "garbage_file" "$output" "clean identified unencrypted file in valid repo" +assert_grep "NOTE: This is a scan" "$output" "clean scan-only mode confirmed" + +# 4. Clean Force +"$SCRIPT_DIR/git-remote-gcrypt" clean "gcrypt::$tempdir/valid.git" --force >/dev/null 2>&1 + +# Verify garbage_file is GONE from the GREF tree +UPDATED_TREE=$($GIT rev-parse "$GREF^{tree}") +if $GIT ls-tree -r "$UPDATED_TREE" | grep -q "garbage_file"; then + print_err "Garbage file still exists in remote git tree after CLEAN FORCE!" exit 1 else - print_success "Files removed after clean --force" + print_success "Garbage file removed successfully." fi # -------------------------------------------------- -- 2.52.0