fix(ci): make the credential scrub failure-safe, and stop the lists drifting
Round 5, and the best findings are again about the fix from round 4. The scrub only ran on success. `set -e` aborts the step when `go mod download` fails, so the cleanup line after it never executed — leaving a push-capable credential on a long-lived self-hosted runner for whatever job landed there next. It is now a `trap ... EXIT`, verified against a simulated failure. It also scrubbed the wrong file in principle: `git config --global` writes to GIT_CONFIG_GLOBAL, else $XDG_CONFIG_HOME/git/config when that exists, else ~/.gitconfig — so deleting ~/.gitconfig can scrub a path the credential was never in. The step now names GIT_CONFIG_GLOBAL itself, leaving exactly one file to remove. And the verification failed open in the case that matters most: `grep -F ""` matches every file, so a run WITHOUT the secret — a fork PR, the threat model — failed the check with a message accusing it of leaking a credential it never had. Guarded on a non-empty secret. Credentials move to an Authorization header instead of being embedded in the URL, so a password containing @ : / or # can no longer break URL parsing in a way that reads as a bad password. Two list-drift holes closed with one test that reads across languages: TestOpenAICompatProvidersAreFullyWired asserts every openAICompatProviders entry is both advertised in endpointProviderNames and has a credential arm in scripts/preflight.sh. Adding a compat provider touches three places in two languages and nothing connected them. Break-checked in both directions. Finally, a whitespace-only GADFLY_BASE_URL disagreed across the boundary: Go TrimSpaces it and takes the registry path, bash called it "set" and skipped the pre-flight, so the missing key arrived as a bare 401 with no notice. Both now agree on what unset means. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
@@ -70,15 +70,39 @@ jobs:
|
||||
REGISTRY_USER: ${{ secrets.REGISTRY_USER }}
|
||||
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Own the config path outright. `git config --global` writes to
|
||||
# GIT_CONFIG_GLOBAL, else $XDG_CONFIG_HOME/git/config when that
|
||||
# directory exists, else ~/.gitconfig — so "delete ~/.gitconfig"
|
||||
# scrubs a file the credential may never have been in. Naming the
|
||||
# path leaves exactly one file to remove.
|
||||
export GIT_CONFIG_GLOBAL="$(mktemp)"
|
||||
|
||||
# Scrub on ANY exit, not just success. `set -e` means a failed
|
||||
# `go mod download` aborts this step, and a cleanup written as the
|
||||
# next line would never run — leaving a push-capable credential on a
|
||||
# long-lived self-hosted runner for whatever job lands there next.
|
||||
trap 'rm -f "$GIT_CONFIG_GLOBAL"' EXIT
|
||||
|
||||
go env -w GOPRIVATE=gitea.stevedudenhoeffer.com/*
|
||||
git config --global url."https://${REGISTRY_USER}:${REGISTRY_PASSWORD}@gitea.stevedudenhoeffer.com/".insteadOf "https://gitea.stevedudenhoeffer.com/"
|
||||
# Basic-auth header rather than credentials inside the URL: a
|
||||
# password containing @ : / or # breaks URL parsing, and the failure
|
||||
# would look like a bad password rather than a quoting bug.
|
||||
git config --global \
|
||||
"http.https://gitea.stevedudenhoeffer.com/.extraheader" \
|
||||
"Authorization: Basic $(printf '%s:%s' "$REGISTRY_USER" "$REGISTRY_PASSWORD" | base64 | tr -d '\n')"
|
||||
go mod download
|
||||
rm -f "$HOME/.gitconfig"
|
||||
# Prove the scrub worked, and prove it against the whole home dir —
|
||||
# checking only the file just deleted would pass no matter what, and
|
||||
# the credential can also reach ~/.netrc or ~/.config/go/env.
|
||||
test ! -e "$HOME/.gitconfig"
|
||||
if grep -rqF "${REGISTRY_PASSWORD}" "$HOME" 2>/dev/null; then
|
||||
|
||||
rm -f "$GIT_CONFIG_GLOBAL"
|
||||
test ! -e "$GIT_CONFIG_GLOBAL"
|
||||
|
||||
# Prove the scrub across the whole home dir, not just the file we
|
||||
# deleted — that check would pass no matter what, and git/go can also
|
||||
# write ~/.netrc or ~/.config/go/env. Guarded on a non-empty secret:
|
||||
# `grep -F ""` matches every file, so a secretless run (fork PR) would
|
||||
# fail here with a message accusing it of leaking nothing.
|
||||
if [ -n "${REGISTRY_PASSWORD:-}" ] && grep -rqF "$REGISTRY_PASSWORD" "$HOME" 2>/dev/null; then
|
||||
echo "::error::registry credential still present under \$HOME after scrub"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user