-
Notifications
You must be signed in to change notification settings - Fork 250
Recursively clean git credentials from all checkouts in workspace and /tmp/ #17086
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. Weβll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
45e4f0e
8c424cb
3625e93
6f1232c
64ba040
97f7d63
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| Smoke test run 22211157844 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,10 @@ | ||
| #!/usr/bin/env bash | ||
| # | ||
| # clean_git_credentials.sh - Remove git credentials from .git/config | ||
| # clean_git_credentials.sh - Remove git credentials from all git checkouts | ||
| # | ||
| # This script removes any git credentials that may have been left on disk | ||
| # accidentally by an injected step. It specifically targets the credentials | ||
| # in $GITHUB_WORKSPACE/.git/config to prevent credential leakage. | ||
| # accidentally by an injected step. It recursively finds all .git/config | ||
| # files in $GITHUB_WORKSPACE and /tmp/ and cleans credentials from each. | ||
| # | ||
| # This is a security measure to ensure that git credentials configured by | ||
| # custom steps or other workflow steps are removed before the agentic engine | ||
|
|
@@ -16,65 +16,75 @@ | |
|
|
||
| set -euo pipefail | ||
|
|
||
| # Get the workspace directory (defaults to current GITHUB_WORKSPACE) | ||
| WORKSPACE="${GITHUB_WORKSPACE:-.}" | ||
| GIT_CONFIG_PATH="${WORKSPACE}/.git/config" | ||
| # clean_git_config removes credentials from a single .git/config file | ||
| clean_git_config() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice refactor extracting this into a reusable |
||
| local GIT_CONFIG_PATH="$1" | ||
|
|
||
| echo "Cleaning git credentials from ${GIT_CONFIG_PATH@Q}" | ||
| echo "Cleaning git credentials from ${GIT_CONFIG_PATH@Q}" | ||
|
|
||
| # Check if .git/config exists | ||
| if [ ! -f "${GIT_CONFIG_PATH}" ]; then | ||
| echo "No .git/config found at ${GIT_CONFIG_PATH@Q}, nothing to clean" | ||
| exit 0 | ||
| fi | ||
| # Remove credential helper configuration | ||
| # This removes lines like: | ||
| # [credential] | ||
| # helper = ... | ||
| # And any credential URL-specific configs like: | ||
| # [credential "https://github.com"] | ||
| # helper = ... | ||
| if git config --file "${GIT_CONFIG_PATH}" --remove-section credential 2>/dev/null; then | ||
| echo "Removed [credential] section from git config" | ||
| fi | ||
|
|
||
| # Remove credential helper configuration | ||
| # This removes lines like: | ||
| # [credential] | ||
| # helper = ... | ||
| # And any credential URL-specific configs like: | ||
| # [credential "https://github.com"] | ||
| # helper = ... | ||
| if git config --file "${GIT_CONFIG_PATH}" --remove-section credential 2>/dev/null; then | ||
| echo "Removed [credential] section from git config" | ||
| fi | ||
| # Remove credential URL-specific sections using sed | ||
| # Pattern: match lines from "[credential ..." to the next section header, | ||
| # deleting the credential header line and all lines until the next "[" section. | ||
| sed -i '/^\[credential /,/^\[/{ /^\[credential /d; /^\[/!d; }' "${GIT_CONFIG_PATH}" 2>/dev/null || true | ||
|
|
||
| # Remove credential URL-specific sections using grep | ||
| # This handles multi-line credential sections with URLs | ||
| sed -i '/^\[credential /,/^\[/{ /^\[credential /d; /^\[/!d; }' "${GIT_CONFIG_PATH}" 2>/dev/null || true | ||
| # Remove http extraheader (used by GitHub Actions for authentication) | ||
| # This is used by actions/checkout to authenticate | ||
| if git config --file "${GIT_CONFIG_PATH}" --unset-all http.extraheader 2>/dev/null; then | ||
| echo "Removed http.extraheader from git config" | ||
| fi | ||
|
|
||
| # Remove http extraheader (used by GitHub Actions for authentication) | ||
| # This is used by actions/checkout to authenticate | ||
| if git config --file "${GIT_CONFIG_PATH}" --unset-all http.extraheader 2>/dev/null; then | ||
| echo "Removed http.extraheader from git config" | ||
| fi | ||
| # Remove any http.<url>.extraheader configurations | ||
| git config --file "${GIT_CONFIG_PATH}" --get-regexp '^http\..*\.extraheader$' 2>/dev/null | while read -r key _; do | ||
| git config --file "${GIT_CONFIG_PATH}" --unset-all "$key" || true | ||
| echo "Removed $key from git config" | ||
| done || true | ||
|
|
||
| # Remove any http.<url>.extraheader configurations | ||
| git config --file "${GIT_CONFIG_PATH}" --get-regexp '^http\..*\.extraheader$' 2>/dev/null | while read -r key _; do | ||
| git config --file "${GIT_CONFIG_PATH}" --unset-all "$key" || true | ||
| echo "Removed $key from git config" | ||
| done || true | ||
| # Remove any credentials from remote URLs (https://username:password@github.com format) | ||
| # Replace authenticated URLs with unauthenticated ones | ||
| if git config --file "${GIT_CONFIG_PATH}" --get-regexp '^remote\..*\.url$' 2>/dev/null | grep -q '@'; then | ||
| echo "Found authenticated remote URLs, cleaning..." | ||
| git config --file "${GIT_CONFIG_PATH}" --get-regexp '^remote\..*\.url$' 2>/dev/null | while read -r key url; do | ||
| # Remove credentials from URL: https://user:pass@host -> https://host | ||
| clean_url=$(echo "$url" | sed -E 's|(https?://)([^@]+@)?(.*)|\1\3|') | ||
| if [ "$url" != "$clean_url" ]; then | ||
| git config --file "${GIT_CONFIG_PATH}" "$key" "$clean_url" | ||
| echo "Cleaned credentials from $key" | ||
| fi | ||
| done || true | ||
| fi | ||
|
|
||
| # Remove any credentials from remote URLs (https://username:password@github.com format) | ||
| # Replace authenticated URLs with unauthenticated ones | ||
| if git config --file "${GIT_CONFIG_PATH}" --get-regexp '^remote\..*\.url$' 2>/dev/null | grep -q '@'; then | ||
| echo "Found authenticated remote URLs, cleaning..." | ||
| git config --file "${GIT_CONFIG_PATH}" --get-regexp '^remote\..*\.url$' 2>/dev/null | while read -r key url; do | ||
| # Remove credentials from URL: https://user:pass@host -> https://host | ||
| clean_url=$(echo "$url" | sed -E 's|(https?://)([^@]+@)?(.*)|\1\3|') | ||
| if [ "$url" != "$clean_url" ]; then | ||
| git config --file "${GIT_CONFIG_PATH}" "$key" "$clean_url" | ||
| echo "Cleaned credentials from $key" | ||
| fi | ||
| done || true | ||
| fi | ||
| echo "β Git credentials cleaned from ${GIT_CONFIG_PATH@Q}" | ||
|
|
||
| # Verify the file is still valid git config | ||
| if ! git config --file "${GIT_CONFIG_PATH}" --list >/dev/null 2>&1; then | ||
| echo "ERROR: Git config file is corrupted after cleaning: ${GIT_CONFIG_PATH@Q}" | ||
| exit 1 | ||
| fi | ||
| } | ||
|
|
||
| # Get the workspace directory (defaults to current GITHUB_WORKSPACE) | ||
| WORKSPACE="${GITHUB_WORKSPACE:-.}" | ||
|
|
||
| echo "β Git credentials cleaned successfully" | ||
| # Collect all .git/config files to clean from workspace and /tmp/ | ||
| CLEANED=0 | ||
| while IFS= read -r git_config; do | ||
| clean_git_config "${git_config}" | ||
| CLEANED=$((CLEANED + 1)) | ||
| done < <(find "${WORKSPACE}" /tmp -maxdepth 10 -name "config" -path "*/.git/config" 2>/dev/null | sort -u) | ||
|
Comment on lines
+79
to
+84
|
||
|
|
||
| # Verify the file is still valid git config | ||
| if ! git config --file "${GIT_CONFIG_PATH}" --list >/dev/null 2>&1; then | ||
| echo "ERROR: Git config file is corrupted after cleaning" | ||
| exit 1 | ||
| if [ "${CLEANED}" -eq 0 ]; then | ||
| echo "No .git/config files found, nothing to clean" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| fi | ||
|
|
||
| exit 0 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| #!/usr/bin/env bash | ||
| # Test script for clean_git_credentials.sh | ||
| # Run: bash clean_git_credentials_test.sh | ||
|
|
||
| set -e | ||
|
|
||
| SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" | ||
| CLEAN_SCRIPT="${SCRIPT_DIR}/clean_git_credentials.sh" | ||
|
|
||
| # Test counters | ||
| TESTS_PASSED=0 | ||
| TESTS_FAILED=0 | ||
|
|
||
| # Temporary workspace for tests | ||
| TEST_WORKSPACE=$(mktemp -d) | ||
|
|
||
| cleanup() { | ||
| rm -rf "${TEST_WORKSPACE}" | ||
| } | ||
| trap cleanup EXIT | ||
|
|
||
| # Helper: create a minimal git repo with a .git/config file | ||
| make_git_config() { | ||
| local dir="$1" | ||
| local config="$2" | ||
| mkdir -p "${dir}/.git" | ||
| echo "${config}" >"${dir}/.git/config" | ||
| } | ||
|
|
||
| # Helper: assert a condition | ||
| assert() { | ||
| local name="$1" | ||
| local condition="$2" | ||
| if eval "${condition}"; then | ||
| echo "β ${name}" | ||
| TESTS_PASSED=$((TESTS_PASSED + 1)) | ||
| else | ||
| echo "β ${name}" | ||
| TESTS_FAILED=$((TESTS_FAILED + 1)) | ||
| fi | ||
| } | ||
|
|
||
| echo "Testing clean_git_credentials.sh..." | ||
| echo "" | ||
|
|
||
| # ββ Test 1: No .git/config anywhere (no-op, exit 0) βββββββββββββββββββββββββ | ||
| echo "Test 1: No .git/config files β exit 0 with informational message" | ||
| EMPTY_WS=$(mktemp -d) | ||
| OUTPUT=$(GITHUB_WORKSPACE="${EMPTY_WS}" bash "${CLEAN_SCRIPT}" 2>&1) | ||
| EXIT_CODE=$? | ||
| rmdir "${EMPTY_WS}" | ||
| assert "exits 0 when no .git/config found" "[ ${EXIT_CODE} -eq 0 ]" | ||
| assert "prints informational message" "echo '${OUTPUT}' | grep -q 'No .git/config'" | ||
| echo "" | ||
|
|
||
| # ββ Test 2: Removes [credential] section ββββββββββββββββββββββββββββββββββββ | ||
| echo "Test 2: Removes [credential] section from workspace .git/config" | ||
| REPO="${TEST_WORKSPACE}/repo2" | ||
| make_git_config "${REPO}" "[core] | ||
| repositoryformatversion = 0 | ||
| [credential] | ||
| helper = store | ||
| [remote \"origin\"] | ||
| url = https://github.com/org/repo.git" | ||
| GITHUB_WORKSPACE="${REPO}" bash "${CLEAN_SCRIPT}" >/dev/null 2>&1 | ||
| assert "credential section removed" "! grep -q '\[credential\]' '${REPO}/.git/config'" | ||
| assert "core section preserved" "grep -q '\[core\]' '${REPO}/.git/config'" | ||
| assert "remote section preserved" "grep -q '\[remote' '${REPO}/.git/config'" | ||
| echo "" | ||
|
|
||
| # ββ Test 3: Removes http.extraheader ββββββββββββββββββββββββββββββββββββββββ | ||
| echo "Test 3: Removes http.extraheader from git config" | ||
| REPO="${TEST_WORKSPACE}/repo3" | ||
| make_git_config "${REPO}" "[core] | ||
| repositoryformatversion = 0 | ||
| [http] | ||
| extraheader = Authorization: Basic dXNlcjpwYXNz" | ||
| GITHUB_WORKSPACE="${REPO}" bash "${CLEAN_SCRIPT}" >/dev/null 2>&1 | ||
| assert "http.extraheader removed" "! git config --file '${REPO}/.git/config' http.extraheader 2>/dev/null" | ||
| assert "config still valid" "git config --file '${REPO}/.git/config' --list >/dev/null 2>&1" | ||
| echo "" | ||
|
|
||
| # ββ Test 4: Strips credentials from remote URL ββββββββββββββββββββββββββββββ | ||
| echo "Test 4: Strips credentials from authenticated remote URL" | ||
| REPO="${TEST_WORKSPACE}/repo4" | ||
| make_git_config "${REPO}" "[core] | ||
| repositoryformatversion = 0 | ||
| [remote \"origin\"] | ||
| url = https://x-access-token:ghs_abc123@github.com/org/repo.git | ||
| fetch = +refs/heads/*:refs/remotes/origin/*" | ||
| GITHUB_WORKSPACE="${REPO}" bash "${CLEAN_SCRIPT}" >/dev/null 2>&1 | ||
| CLEANED_URL=$(git config --file "${REPO}/.git/config" remote.origin.url) | ||
| assert "credentials stripped from URL" "[ '${CLEANED_URL}' = 'https://github.com/org/repo.git' ]" | ||
| echo "" | ||
|
|
||
| # ββ Test 5: Recursively finds repo nested inside workspace ββββββββββββββββββ | ||
| echo "Test 5: Recursively cleans nested git repo inside workspace" | ||
| OUTER="${TEST_WORKSPACE}/outer5" | ||
| INNER="${OUTER}/vendor/dep" | ||
| make_git_config "${OUTER}" "[core] | ||
| repositoryformatversion = 0 | ||
| [credential] | ||
| helper = store" | ||
| make_git_config "${INNER}" "[core] | ||
| repositoryformatversion = 0 | ||
| [http] | ||
| extraheader = Authorization: Basic dXNlcjpwYXNz" | ||
| GITHUB_WORKSPACE="${OUTER}" bash "${CLEAN_SCRIPT}" >/dev/null 2>&1 | ||
| assert "outer credential section cleaned" "! grep -q '\[credential\]' '${OUTER}/.git/config'" | ||
| assert "inner extraheader cleaned" "! git config --file '${INNER}/.git/config' http.extraheader 2>/dev/null" | ||
| echo "" | ||
|
|
||
| # ββ Test 6: Finds git repo in /tmp ββββββββββββββββββββββββββββββββββββββββββ | ||
| echo "Test 6: Cleans git repo located in /tmp" | ||
| TMP_REPO=$(mktemp -d) | ||
| make_git_config "${TMP_REPO}" "[core] | ||
| repositoryformatversion = 0 | ||
| [credential] | ||
| helper = store" | ||
| # Use a workspace that does NOT contain the repo so it is found only via /tmp | ||
| GITHUB_WORKSPACE="${TEST_WORKSPACE}/workspace6" | ||
| mkdir -p "${GITHUB_WORKSPACE}" | ||
| GITHUB_WORKSPACE="${GITHUB_WORKSPACE}" bash "${CLEAN_SCRIPT}" >/dev/null 2>&1 | ||
| assert "tmp repo credential section cleaned" "! grep -q '\[credential\]' '${TMP_REPO}/.git/config'" | ||
| rm -rf "${TMP_REPO}" | ||
|
Comment on lines
+113
to
+125
|
||
| echo "" | ||
|
|
||
| # ββ Test 7: Config file remains valid after all cleanups ββββββββββββββββββββ | ||
| echo "Test 7: Config file is still valid after cleaning" | ||
| REPO="${TEST_WORKSPACE}/repo7" | ||
| make_git_config "${REPO}" "[core] | ||
| repositoryformatversion = 0 | ||
| [credential] | ||
| helper = /usr/lib/git-credential-gnome-keyring | ||
| [http] | ||
| extraheader = Authorization: Bearer sometoken | ||
| [remote \"origin\"] | ||
| url = https://oauth2:tok@github.com/org/repo.git | ||
| fetch = +refs/heads/*:refs/remotes/origin/*" | ||
| GITHUB_WORKSPACE="${REPO}" bash "${CLEAN_SCRIPT}" >/dev/null 2>&1 | ||
| assert "config still valid" "git config --file '${REPO}/.git/config' --list >/dev/null 2>&1" | ||
| assert "core settings intact" "git config --file '${REPO}/.git/config' core.repositoryformatversion >/dev/null 2>&1" | ||
| echo "" | ||
|
|
||
| # ββ Summary ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | ||
| echo "Tests passed: ${TESTS_PASSED}" | ||
| echo "Tests failed: ${TESTS_FAILED}" | ||
|
|
||
| if [ "${TESTS_FAILED}" -gt 0 ]; then | ||
| exit 1 | ||
| fi | ||
|
|
||
| echo "β All tests passed!" | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file appears to be an ephemeral artifact from a specific smoke test run and isnβt referenced elsewhere in the repo. It will accumulate over time and add noise to the repository history; please remove it from the PR (or add an explicit convention +
.gitignoreentry if these files are intended to be tracked).