Update tests & code. Add Android/Termux workflow
authorShane Jaroch <chown_tee@proton.me>
Sat, 17 Jan 2026 04:45:32 +0000 (23:45 -0500)
committerShane Jaroch <chown_tee@proton.me>
Sat, 17 Jan 2026 04:45:54 +0000 (23:45 -0500)
.github/workflows/lint.yaml
git-remote-gcrypt
tests/system-test-clean-command.sh
tests/system-test-clean-repack.sh
tests/system-test-privacy-leaks.sh

index 1cf0c1e4c2eb424ee66f494d8d2c104432b75ea0..1d651c2f2035355aa3f66020cf5efc8741265968 100644 (file)
@@ -89,6 +89,20 @@ jobs:
       - name: Verify [make check/install]
         run: make check/install
 
+  # Handles Android (Termux)
+  termux-test:
+    runs-on: ubuntu-latest
+    container:
+      image: termux/termux-docker:latest
+    steps:
+      - uses: actions/checkout@v4
+
+      - name: Install dependencies
+        run: pkg update && pkg install -y git make
+
+      - name: Run your test script
+        run: ./my-test-script.sh
+
   # Lint job
   lint:
     runs-on: ubuntu-latest
index 3e8bb9fd1139c70506aa5b6f99f86427753b1668..bc9d938dc646d5945c4b8c715095d92c29e3dc34 100755 (executable)
@@ -107,11 +107,6 @@ while [ $# -gt 0 ]; do
                                echo "Error: --hard requires --force" >&2
                                exit 1
                        fi
-                       if [ "${FORCE_INIT:-}" = "yes" ] && [ -z "$FORCE_CLEAN" ]; then
-                               echo "Error: --init requires --force" >&2
-                               echo "(To scan a repository, use 'clean' without arguments)" >&2
-                               exit 1
-                       fi
                        break # Stop parsing outer loop
                        ;;
                stat)
@@ -367,8 +362,8 @@ gitception_get()
        if [ -e "$fet_head" ]; then
                command mv -f "$fet_head" "$fet_head.$$~" || :
        fi
-       if git fetch -q -f "$1" "$Gref_rbranch:$Gref" >/dev/null; then
-               obj_id="$(git ls-tree "$Gref" | xgrep -E '\b'"$2"'$' | awk '{print $3}')"
+       if git fetch -q -f "$1" "$Gref_rbranch:$Gref-fetch" >/dev/null; then
+               obj_id="$(git ls-tree "$Gref-fetch" | xgrep -E '\b'"$2"'$' | awk '{print $3}')"
                if isnonnull "$obj_id" && git cat-file blob "$obj_id"; then
                        ret_=:
                else
@@ -460,7 +455,9 @@ gitception_new_repo()
        local commit_id="" empty_tree=4b825dc642cb6eb9a060e54bf8d69288fbee4904
        # get any file to update Gref, and if it's not updated we create empty
        git update-ref -d "$Gref" || :
-       gitception_get "$1" "x" 2>/dev/null >&2 || :
+       if gitception_get "$1" "x" 2>/dev/null >&2; then
+               git update-ref "$Gref" "$Gref-fetch"
+       fi
        git rev-parse -q --verify "$Gref" >/dev/null && return 0 ||
                commit_id=$(anon_commit "$empty_tree") &&
                git update-ref "$Gref" "$commit_id"
@@ -690,7 +687,8 @@ gpg_hash()
 rungpg()
 {
        if isnonnull "$Conf_gpg_args"; then
-               set -- "$Conf_gpg_args" "$@"
+               # shellcheck disable=SC2086
+               set -- $Conf_gpg_args "$@"
        fi
        # gpg will fail to run when there is no controlling tty,
        # due to trying to print messages to it, even if a gpg agent is set
@@ -922,6 +920,7 @@ ensure_connected()
        print_debug "Getting manifest from $URL file $Manifestfile"
        # GET "$URL" "$Manifestfile" "$tmp_manifest" 2>| "$tmp_stderr" || {
     # Debugging: don't capture stderr, let it flow to console
+       git update-ref -d "$Gref-fetch" || :
        GET "$URL" "$Manifestfile" "$tmp_manifest" || {
                if ! isnull "$Repoid"; then
                        cat >&2 "$tmp_stderr"
@@ -934,6 +933,11 @@ ensure_connected()
                fi
        }
 
+       # gitception: populate Gref from fetch
+       if git rev-parse -q --verify "$Gref-fetch" >/dev/null; then
+               git update-ref "$Gref" "$Gref-fetch"
+       fi
+
        Did_find_repo=yes
        echo_info "Decrypting manifest"
        if ! manifest_=$(PRIVDECRYPT "$r_sigmatch" "$r_signers" < "$tmp_manifest") || \
@@ -1603,6 +1607,13 @@ cmd_clean()
        fi
 
        echo_info "Removing files..."
+
+       # If we are forcing clean on uninitialized repo, Gref might be missing.
+       # Fetch it so gitception_remove has a base.
+       if [ "$Did_find_repo" != "yes" ] && isnonnull "$Gref_rbranch"; then
+               git fetch -q -f "$URL" "$Gref_rbranch:$Gref" 2>/dev/null || :
+       fi
+
        REMOVE "$URL" "$bad_files"
 
        if [ "${DO_REPACK:-}" = "yes" ]; then
index b5d9e786f7d28657912fba88569553587c46c265..43f35e5c72efe6edd807e4f7020ba3e51dda59cf 100755 (executable)
@@ -222,28 +222,28 @@ print_info "Injected garbage_file into remote $GREF"
 DOT_GARBAGE_BLOB=$(echo "HIDDEN GARBAGE" | $GIT hash-object -w --stdin)
 export GIT_INDEX_FILE=index.dotgarbage
 $GIT read-tree "$NEW_TREE"
-$GIT update-index --add --cacheinfo 100644 "$DOT_GARBAGE_BLOB" ".garbage_file"
+$GIT update-index --add --cacheinfo 100644 "$DOT_GARBAGE_BLOB" ".garbage (file)"
 NEW_TREE_DOT=$($GIT write-tree)
 rm index.dotgarbage
 NEW_COMMIT_DOT=$(echo "Inject dot garbage" | $GIT commit-tree "$NEW_TREE_DOT" -p "$NEW_COMMIT")
 $GIT update-ref "$GREF" "$NEW_COMMIT_DOT"
 
-if ! $GIT ls-tree -r "$GREF" | grep -q "\.garbage_file"; then
-       print_err "Failed to inject .garbage_file into $GREF"
+if ! $GIT ls-tree -r "$GREF" | grep -F -q ".garbage (file)"; then
+       print_err "Failed to inject .garbage (file) into $GREF"
        exit 1
 fi
-print_info "Injected .garbage_file into remote $GREF"
+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 "\.garbage_file" "$output" "clean identified unencrypted DOTFILE in valid repo"
+assert_grep "\.garbage (file)" "$output" "clean identified unencrypted DOTFILE 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
+"$SCRIPT_DIR/git-remote-gcrypt" clean "gcrypt::$tempdir/valid.git" --force
 
 # Verify garbage_file is GONE from the GREF tree
 UPDATED_TREE=$($GIT rev-parse "$GREF^{tree}")
index 29df415738eba32e4512019f23c6cdf892639c43..edeb0f78619c15e74b7bac84098a9f22ee612fc3 100755 (executable)
@@ -28,18 +28,18 @@ export GNUPGHOME="$TEST_DIR/gpg"
 mkdir -p "$GNUPGHOME"
 chmod 700 "$GNUPGHOME"
 
-cat <<EOF >"${GNUPGHOME}/gpg"
+cat <<'EOF' >"${GNUPGHOME}/gpg"
 #!/usr/bin/env bash
 export GNUPGHOME="$GNUPGHOME"
 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 ))]"
+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[@]}"
+exec gpg "${args[@]}"
 EOF
 chmod +x "${GNUPGHOME}/gpg"
 
@@ -109,10 +109,38 @@ echo "Backend SHA: $HEAD_SHA"
 # Start should have 3 commits (init, push1, push2, push3) -> wait, init is implicit.
 # Each push updates the manifested repo.
 
-# Run clean --repack
+# Inject garbage to verify cleanup AND repack
+echo "GARBAGE" >garbage.txt
+GARBAGE_BLOB=$(git hash-object -w garbage.txt)
+echo "Created garbage blob: $GARBAGE_BLOB"
+# Manually inject into backend (simulate inconsistency)
+# But here we are simulating a gitception remote.
+# To simulate "garbage" (unencrypted file), we can push one or hack the backend.
+# Using 'git-remote-gcrypt' clean mechanism relies on files existing in the remote manifest (or filesystem for other backends).
+# For git backend, "garbage" is a file in the remote repo's HEAD tree that isn't in the manifest/packed list.
+# Let's clone backend, add file, push.
+cd "$TEST_DIR/raw_backend"
+echo "Garbage Data" >".garbage (file)"
+git add ".garbage (file)"
+git commit -m "Inject unencrypted garbage"
+git push origin master
+
+# Verify garbage exists
 cd "$REPO_DIR"
-echo "Running clean --repack..."
-git-remote-gcrypt clean --repack origin
+# Run clean --repack --force (needed because we have garbage now)
+echo "Running clean --repack --force..."
+git-remote-gcrypt clean --repack --force origin
+
+# Verify garbage removal from backend
+cd "$TEST_DIR/raw_backend"
+git pull origin master
+
+if [ -f ".garbage (file)" ]; then
+       echo "Failure: .garbage (file) still exists in backend!"
+       exit 1
+else
+       echo "Success: .garbage (file) removed."
+fi
 
 # Verify result
 # Clone backend again (pull) and check structure
index 1d7929511fafa920b0e215507e6d6dd278b65281..8d553b6e2bc36a1991142db12b74b88e4258a3cd 100755 (executable)
@@ -66,8 +66,9 @@ cd "${tempdir}/dirty-setup"
 git init
 git remote add origin "${tempdir}/remote-repo"
 echo "API_KEY=12345-SUPER-SECRET" >.env
-git add .env
+git add -f .env
 git commit -m "Oops, pushed secret keys"
+LEAK_SHA=$(git rev-parse master)
 git push origin master
 
 print_info "Step 3: Switch to git-remote-gcrypt usage"
@@ -106,14 +107,17 @@ print_info "Step 4: Verify LEAKAGE"
 # But we know it persists.
 cd "${tempdir}/remote-repo"
 
-if git ls-tree -r master | grep -q ".env"; then
-       print_warn "PRIVACY LEAK DETECTED: .env file matches found in remote!"
-       print_warn "Content of .env in remote:"
-       git show master:.env
-       print_success "Test Passed: Vulnerability successfully reproduced."
+if git cat-file -e "$LEAK_SHA"; then
+       print_warn "PRIVACY LEAK DETECTED: Leaked commit $LEAK_SHA still exists in remote objects!"
+       if git cat-file -p "$LEAK_SHA:.env" >/dev/null; then
+               print_warn "Content of .env is reachable."
+               print_success "Test Passed: Vulnerability successfully reproduced."
+       else
+               print_err "Commit exists but .env unreadable?"
+               exit 1
+       fi
 else
-       print_err "Unexpected: .env file NOT found. Did gcrypt overwrite it?"
-       # detecting it is 'failure' of the vulnerability check, but 'success' for privacy
+       print_err "Unexpected: Leaked commit NOT found. Did gcrypt prune it?"
        exit 1
 fi