Repository navigation
Expand file tree
/
Copy pathclean_git_credentials.sh
More file actions
executable file
·127 lines (110 loc) · 5.3 KB
/
Copy pathclean_git_credentials.sh
File metadata and controls
executable file
·127 lines (110 loc) · 5.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env bash
set +o histexpand
#
# 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 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
# executes, preventing the agent from accessing or exfiltrating credentials.
#
# Exit codes:
# 0 - Success (credentials cleaned or no .git/config found)
# 1 - Error (failed to clean credentials)
set -euo pipefail
# clean_git_config removes credentials from a single .git/config file
clean_git_config() {
local GIT_CONFIG_PATH="$1"
echo "Cleaning git credentials from ${GIT_CONFIG_PATH@Q}"
# 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 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
# Checkout stores persisted credentials in a separate config reached via includeIf.
local include_iterations=0 max_include_iterations=1000
while IFS= read -r -d '' entry; do
if (( include_iterations >= max_include_iterations )); then
echo "ERROR: Checkout credential include cleanup exceeded ${max_include_iterations} iterations" >&2
exit 1
fi
include_iterations=$((include_iterations + 1))
local key="${entry%%$'\n'*}" include_path="${entry#*$'\n'}"
if [[ "$include_path" =~ (^|/)git-credentials-[0-9a-f-]+\.config$ ]] &&
git config --file "${GIT_CONFIG_PATH}" --fixed-value --get-all "$key" "$include_path" >/dev/null; then
git config --file "${GIT_CONFIG_PATH}" --fixed-value --unset-all "$key" "$include_path"
if [[ -n "${RUNNER_TEMP:-}" && "$include_path" = /* ]]; then
local temp_dir resolved_path
temp_dir=$(realpath -m -- "$RUNNER_TEMP")
resolved_path=$(realpath -m -- "$include_path")
if [[ "$resolved_path" == "$temp_dir"/* ]]; then
rm -f -- "$include_path"
fi
fi
echo "Removed checkout credential include from git config"
fi
done < <(git config --file "${GIT_CONFIG_PATH}" --null --get-regexp '^includeif\..*\.path$' 2>/dev/null)
# 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:-.}"
# Explicit checkout configs must not depend on bounded workspace discovery.
if [ "$#" -gt 0 ]; then
for git_config in "$@"; do
clean_git_config "${git_config}"
done
exit 0
fi
# Collect all git config files to clean from workspace and /tmp/.
# Includes both repository configs and submodule configs under .git/modules/**/config.
# Use maxdepth 15 as a conservative cap to cover deeply nested submodules
# (.git/modules/<a>/modules/<b>/...) without unbounded traversal.
CLEANED=0
while IFS= read -r git_config; do
clean_git_config "${git_config}"
CLEANED=$((CLEANED + 1))
done < <(find "${WORKSPACE}" /tmp -maxdepth 15 -type f -name "config" \( -path "*/.git/config" -o -path "*/.git/modules/*/config" \) 2>/dev/null | sort -u)
if [ "${CLEANED}" -eq 0 ]; then
echo "No .git/config files found, nothing to clean"
fi
exit 0