From c0b01abd7c725fd480e6fd796b4ce6a365ae1add Mon Sep 17 00:00:00 2001 From: Shane Jaroch Date: Fri, 9 Jan 2026 12:17:10 -0500 Subject: [PATCH] finalize/polish --- git-remote-gcrypt | 123 +++++++++++++++++++++++------------- tests/test-clean-command.sh | 32 +++++++++- 2 files changed, 109 insertions(+), 46 deletions(-) diff --git a/git-remote-gcrypt b/git-remote-gcrypt index 011fddd..6768ab3 100755 --- a/git-remote-gcrypt +++ b/git-remote-gcrypt @@ -47,6 +47,7 @@ Options: check [URL] Check if URL is a gcrypt repository clean [URL|REMOTE] Scan/Clean unencrypted files from remote clean -f, --force Actually delete files (default is scan only) + clean -i, --init Scan even if no manifest found (DANGEROUS with --force) Git Protocol Commands (for debugging): capabilities List remote helper capabilities @@ -86,6 +87,7 @@ while [ $# -gt 0 ]; do while [ $# -gt 0 ]; do case "$1" in --force|-f) FORCE_CLEAN=yes ;; + --init|-i) FORCE_INIT=yes ;; -*) echo "Unknown option: $1" >&2; exit 1 ;; *) if [ -z "$URL" ]; then @@ -732,6 +734,50 @@ read_config() print_debug "read_config done" } +early_safety_check() +{ + local check_files="" early_bad_files="" + + # EARLY SAFETY CHECK for gitception backends: + # Before GPG validation, check if the remote has unencrypted files. + if [ "$NAME" = "gcrypt-clean" ]; then + return 0 + fi + if isurl sftp "$URL" || isurl rsync "$URL" || isurl rclone "$URL" || islocalrepo "$URL"; then + return 0 + fi + + git fetch --quiet "$URL" "refs/heads/master:refs/gcrypt/safety-check" 2>/dev/null || + git fetch --quiet "$URL" "refs/heads/main:refs/gcrypt/safety-check" 2>/dev/null || true + + if ! git rev-parse --verify "refs/gcrypt/safety-check" >/dev/null 2>&1; then + return 0 + fi + + check_files=$(git ls-tree --name-only "refs/gcrypt/safety-check" 2>/dev/null || :) + git update-ref -d "refs/gcrypt/safety-check" 2>/dev/null || true + + if isnull "$check_files"; then + return 0 + fi + + early_bad_files=$(echo "$check_files" | grep -v -E '^[a-f0-9]{56}$|^[a-f0-9]{64}$|^[a-f0-9]{96}$|^[a-f0-9]{128}$' || :) + if isnull "$early_bad_files"; then + return 0 + fi + + if [ "$(git config --bool gcrypt.allow-unencrypted-remote)" = "true" ]; then + return 0 + fi + + 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 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 +} + ensure_connected() { local manifest_="" r_repoid="" r_name="" url_frag="" r_sigmatch="" r_signers="" \ @@ -741,34 +787,9 @@ ensure_connected() then return fi - - # EARLY SAFETY CHECK for gitception backends: - # Before GPG validation, check if the remote has unencrypted files. - if [ "$NAME" != "gcrypt-clean" ] && ! isurl sftp "$URL" && ! isurl rsync "$URL" && ! isurl rclone "$URL" && ! islocalrepo "$URL"; then - local check_files="" - git fetch --quiet "$URL" "refs/heads/master:refs/gcrypt/safety-check" 2>/dev/null || - git fetch --quiet "$URL" "refs/heads/main:refs/gcrypt/safety-check" 2>/dev/null || true - - if git rev-parse --verify "refs/gcrypt/safety-check" >/dev/null 2>&1; then - check_files=$(git ls-tree --name-only "refs/gcrypt/safety-check" 2>/dev/null || :) - git update-ref -d "refs/gcrypt/safety-check" 2>/dev/null || true - - if isnonnull "$check_files"; then - early_bad_files=$(echo "$check_files" | grep -v -E '^[a-f0-9]{56}$|^[a-f0-9]{64}$|^[a-f0-9]{96}$|^[a-f0-9]{128}$' || :) - if isnonnull "$early_bad_files"; then - if [ "$(git config --bool gcrypt.allow-unencrypted-remote)" != "true" ]; then - 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 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 - fi - fi - fi - fi - + + early_safety_check + Did_find_repo=no print_debug "Calling read_config" read_config @r_sigmatch @r_signers @@ -1337,28 +1358,22 @@ gcrypt_main_loop() done } -if [ "$NAME" = "gcrypt-check" ]; then - resolve_url check - echo_info "Checking remote: $URL" - setup - ensure_connected - CLEAN_FINAL "$URL" - git remote remove "$NAME" 2>/dev/null || true - if iseq "$Did_find_repo" "no" - then - exit 100 - fi -elif [ "$NAME" = "gcrypt-clean" ]; then - resolve_url clean - echo_info "Checking remote: $URL" - setup +cmd_clean() +{ + local remote_files="" valid_files="" bad_files="" f="" + if ! ensure_connected; 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." + if [ "${FORCE_INIT:-}" = "yes" ]; then + echo_info "WARNING: No gcrypt manifest found, but --init specified." + echo_info "WARNING: Proceeding to scan/clean potential unencrypted files." + else + echo_die "Error: No gcrypt manifest found on remote '$URL'." \ + "Aborting clean to prevent accidental data loss." + fi fi # Get all files in the remote @@ -1429,6 +1444,24 @@ elif [ "$NAME" = "gcrypt-clean" ]; then git remote remove "$NAME" 2>/dev/null || true echo_info "Done. Remote cleaned." exit 0 +} + +if [ "$NAME" = "gcrypt-check" ]; then + resolve_url check + echo_info "Checking remote: $URL" + setup + ensure_connected + CLEAN_FINAL "$URL" + git remote remove "$NAME" 2>/dev/null || true + if iseq "$Did_find_repo" "no" + then + exit 100 + fi +elif [ "$NAME" = "gcrypt-clean" ]; then + resolve_url clean + echo_info "Checking remote: $URL" + setup + cmd_clean elif [ "$1" = --version ] || [ "$1" = -v ]; then echo "git-remote-gcrypt version $VERSION" exit 0 diff --git a/tests/test-clean-command.sh b/tests/test-clean-command.sh index a2f3e87..51448c3 100755 --- a/tests/test-clean-command.sh +++ b/tests/test-clean-command.sh @@ -149,6 +149,8 @@ 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 +$GIT config user.email "test@test.com" +$GIT config user.name "Test" cd "$tempdir/client" $GIT config user.name "Test" $GIT config user.email "test@test.com" @@ -170,7 +172,7 @@ print_info "Initialized valid gcrypt repo" # 2. Inject garbage file into the remote git index/tree cd "$tempdir/valid.git" -GREF="refs/gcrypt/gitception" +GREF="refs/heads/master" if ! $GIT rev-parse --verify "$GREF" >/dev/null 2>&1; then print_err "Gref $GREF not found in remote!" exit 1 @@ -221,3 +223,31 @@ output=$("$SCRIPT_DIR/git-remote-gcrypt" check "$tempdir/remote.git" 2>&1 || :) assert_grep "gcrypt: Checking remote:" "$output" "check command is recognized" print_success "All clean/check command tests passed!" + +# -------------------------------------------------- +# Test 7: clean --init (Bypass manifest check) +# -------------------------------------------------- +print_info "Test 7: clean --init (Bypass manifest check)..." + +# Reuse the dirty remote from earlier ($tempdir/remote.git) which has secret1.txt and secret2.txt + +# 1. Standard clean should fail (as tested in Test 2) +output=$("$SCRIPT_DIR/git-remote-gcrypt" clean "gcrypt::$tempdir/remote.git" 2>&1 || :) +assert_grep "Error: No gcrypt manifest found" "$output" "standard clean fails on dirty remote" + +# 2. Clean with --init should succeed (scan only) +output=$("$SCRIPT_DIR/git-remote-gcrypt" clean --init "gcrypt::$tempdir/remote.git" 2>&1 || :) +assert_grep "WARNING: No gcrypt manifest found, but --init specified" "$output" "--init warns about missing manifest" +assert_grep "Found the following files to remove" "$output" "--init scan found files" +assert_grep "secret1.txt" "$output" "--init found secret1.txt" + +# 3. Clean with --init --force should remove files +"$SCRIPT_DIR/git-remote-gcrypt" clean --init --force "gcrypt::$tempdir/remote.git" >/dev/null 2>&1 + +cd "$tempdir/remote.git" +if $GIT ls-tree HEAD | grep -q "secret1.txt"; then + print_err "--init --force FAILED to remove secret1.txt" + exit 1 +else + print_success "--init --force removed unencrypted files" +fi -- 2.52.0