Skip to content

Commit d7e0c44

Browse files
committed
feat: enhance validation for user inputs and improve error handling across scripts
1 parent c57e562 commit d7e0c44

9 files changed

Lines changed: 83 additions & 6 deletions

File tree

README.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ Scripts use environment variables for configuration. Common variables include:
8080
| `GITHUB_TOKEN` | GitHub personal access token | Yes |
8181
| `ORG` | Organization name | Yes (most scripts) |
8282
| `API_URL_PREFIX` | GitHub API base URL (default: `https://api.github.com`) | No |
83-
| `GIT_URL_PREFIX` | GitHub base URL (default: `https://github.com`) | No |
83+
| `GIT_URL_PREFIX` | GitHub base URL (default: `https://github.com`). Must be a GitHub-family host (`github.com`, `*.github.com`, `*.ghe.com`, `*.githubenterprise.com`). | No |
8484

8585
> [!NOTE]
8686
> The `*_PREFIX` variables support GitHub Enterprise Server. Set them to your enterprise domain to use these scripts with GHES.
@@ -200,6 +200,9 @@ cd org-admin/github-repo-from-template
200200
- Assigns write permissions to teams in `REPO_WRITE`
201201
- Invites CD user as collaborator and auto-accepts the invitation
202202

203+
> [!NOTE]
204+
> `REPO_NAME` (CLI argument), `TEMPLATE_REPO`, `CD_USERNAME`, and all team slugs in `REPO_ADMIN` and `REPO_WRITE` are validated as slugs before use. Only alphanumeric characters, hyphens, and underscores are accepted.
205+
203206
---
204207

205208
### Import Repository
@@ -231,6 +234,9 @@ cd org-admin/github-import-repo
231234
> [!WARNING]
232235
> This creates a complete copy with full git history. Ensure you have sufficient disk space and network bandwidth for large repositories.
233236
237+
> [!NOTE]
238+
> `GIT_URL_PREFIX` is validated against a GitHub-host allowlist before any git operations run. Non-GitHub URLs are rejected to prevent credential leakage. Repository names and `OWNER_USERNAME` must be valid slugs (alphanumeric, hyphen, and underscore only).
239+
234240
---
235241

236242
### Add Repository Collaborators by Pattern
@@ -331,6 +337,9 @@ cd org-admin/github-auto-repo-creation
331337
> [!NOTE]
332338
> Requires `base64` in addition to `curl` and `jq` (used to encode the CODEOWNERS file content for the API).
333339
340+
> [!NOTE]
341+
> All values in `REPO_NAMES`, `ADMIN_TEAMS`, and `REPO_OWNERS` are validated as slugs — only alphanumeric characters, hyphens, and underscores are allowed. The script exits with an error if any value fails this check.
342+
334343
---
335344

336345
### Close Archived Repository Security Alerts
@@ -691,6 +700,9 @@ cd enterprise/github-dockerfile-discovery
691700
> [!NOTE]
692701
> Code search is heavily rate-limited. Increase `SEARCH_SLEEP` and `CONTENT_SLEEP` if you encounter `403` responses.
693702
703+
> [!NOTE]
704+
> `ORG_FILTER` and `ORG_EXCLUDE` are validated as syntactically correct ERE patterns before use. An invalid regex causes the script to exit immediately with an error rather than silently bypassing the filter.
705+
694706
---
695707

696708
### Get Public Repositories
@@ -724,6 +736,9 @@ cd enterprise/github-get-public-repos
724736
| `ORG_FILTER` | ERE regex to keep only matching org names ||
725737
| `ORG_EXCLUDE` | ERE regex to drop matching org names ||
726738

739+
> [!NOTE]
740+
> `ORG_FILTER` and `ORG_EXCLUDE` are validated as syntactically correct ERE patterns before use. An invalid regex causes the script to exit immediately with an error rather than silently bypassing the filter.
741+
727742
---
728743

729744
### Organize Starred Repositories

enterprise/github-dockerfile-discovery/github-dockerfile-discovery.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,6 +592,12 @@ main() {
592592

593593
# ── Apply ORG_FILTER (inclusion) if set ────────────────────────────────
594594
if [ -n "${ORG_FILTER}" ]; then
595+
local _rc=0
596+
echo "" | grep -qE "${ORG_FILTER}" 2>/dev/null || _rc=$?
597+
if [ "${_rc}" -eq 2 ]; then
598+
print_error "ORG_FILTER is not a valid ERE regex: ${ORG_FILTER}"
599+
exit 1
600+
fi
595601
local filtered=()
596602
for org in "${orgs[@]}"; do
597603
if echo "${org}" | grep -qE "${ORG_FILTER}"; then
@@ -605,6 +611,12 @@ main() {
605611

606612
# ── Apply ORG_EXCLUDE (exclusion) if set ────────────────────────────────
607613
if [ -n "${ORG_EXCLUDE}" ]; then
614+
local _rc=0
615+
echo "" | grep -qE "${ORG_EXCLUDE}" 2>/dev/null || _rc=$?
616+
if [ "${_rc}" -eq 2 ]; then
617+
print_error "ORG_EXCLUDE is not a valid ERE regex: ${ORG_EXCLUDE}"
618+
exit 1
619+
fi
608620
local kept=()
609621
for org in "${orgs[@]}"; do
610622
if ! echo "${org}" | grep -qE "${ORG_EXCLUDE}"; then

enterprise/github-get-public-repos/github-get-public-repos.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,11 +256,23 @@ resolve_orgs() {
256256

257257
# Apply ORG_FILTER include regex
258258
if [ -n "${ORG_FILTER}" ]; then
259+
local _rc=0
260+
echo "" | grep -qE "${ORG_FILTER}" 2>/dev/null || _rc=$?
261+
if [ "${_rc}" -eq 2 ]; then
262+
print_error "ORG_FILTER is not a valid ERE regex: ${ORG_FILTER}"
263+
exit 1
264+
fi
259265
raw_orgs=$(echo "${raw_orgs}" | grep -E "${ORG_FILTER}" || true)
260266
fi
261267

262268
# Apply ORG_EXCLUDE regex
263269
if [ -n "${ORG_EXCLUDE}" ]; then
270+
local _rc=0
271+
echo "" | grep -qE "${ORG_EXCLUDE}" 2>/dev/null || _rc=$?
272+
if [ "${_rc}" -eq 2 ]; then
273+
print_error "ORG_EXCLUDE is not a valid ERE regex: ${ORG_EXCLUDE}"
274+
exit 1
275+
fi
264276
raw_orgs=$(echo "${raw_orgs}" | grep -Ev "${ORG_EXCLUDE}" || true)
265277
fi
266278

org-admin/github-auto-repo-creation/github-auto-repo-creation.sh

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,17 @@ require_command base64
5252
require_command jq
5353
validate_github_token
5454

55+
# Validate all user-supplied names before use in API URL paths
56+
while IFS= read -r _name; do
57+
[ -n "${_name}" ] && validate_slug "${_name}" "REPO_NAMES value"
58+
done < <(tr ',' '\n' <<< "${REPO_NAMES}")
59+
while IFS= read -r _name; do
60+
[ -n "${_name}" ] && validate_slug "${_name}" "ADMIN_TEAMS value"
61+
done < <(tr ',' '\n' <<< "${ADMIN_TEAMS:-}")
62+
while IFS= read -r _name; do
63+
[ -n "${_name}" ] && validate_slug "${_name}" "REPO_OWNERS value"
64+
done < <(tr ',' '\n' <<< "${REPO_OWNERS}")
65+
5566
# Define the content of the CODEOWNERS file
5667
CODEOWNERS_CONTENT=$(cat << EOF
5768
# Lines starting with '#' are comments.
@@ -115,6 +126,8 @@ create_codeowners_file() {
115126
add_teams_to_repo() {
116127
local repo_name=$1
117128
local team_name=$2
129+
validate_slug "${repo_name}" "repository name"
130+
validate_slug "${team_name}" "team name"
118131
curl -s -X PUT -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" "${API_URL_PREFIX}/orgs/${ORG}/teams/${team_name}/repos/${ORG}/${repo_name}" -d "{
119132
\"permission\": \"admin\"
120133
}"
@@ -124,6 +137,8 @@ add_teams_to_repo() {
124137
add_repo_owners_to_repo() {
125138
local repo_name=$1
126139
local team_name=$2
140+
validate_slug "${repo_name}" "repository name"
141+
validate_slug "${team_name}" "team name"
127142
curl -s -X PUT -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" "${API_URL_PREFIX}/orgs/${ORG}/teams/${team_name}/repos/${ORG}/${repo_name}" -d "{
128143
\"permission\": \"push\"
129144
}"

org-admin/github-get-repo-list/github-get-repo-list.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ require_command jq
4040
validate_github_token
4141

4242
get_repo_pagination () {
43-
repo_pages=$(curl -H "Authorization: token ${GITHUB_TOKEN}" -I "${API_URL_PREFIX}/orgs/${ORG}/repos?per_page=100" | grep -Eo '&page=[0-9]+' | grep -Eo '[0-9]+' | tail -1;)
43+
repo_pages=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" -I "${API_URL_PREFIX}/orgs/${ORG}/repos?per_page=100" | grep -Eo '&page=[0-9]+' | grep -Eo '[0-9]+' | tail -1;)
4444
echo "${repo_pages:-1}"
4545
}
4646

org-admin/github-import-repo/github-import-repo.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,22 @@ require_command git
7979
require_command jq
8080
validate_github_token
8181

82+
# Validate GIT_URL_PREFIX is a recognised GitHub host to prevent credential leakage
83+
# via GIT_ASKPASS, which provides GITHUB_TOKEN to any host git connects to
84+
case "${GIT_URL_PREFIX}" in
85+
https://github.com|https://*.github.com|https://*.ghe.com|https://*.githubenterprise.com)
86+
;;
87+
*)
88+
print_error "GIT_URL_PREFIX '${GIT_URL_PREFIX}' is not a recognised GitHub host. \
89+
Refusing to run git operations that would expose GITHUB_TOKEN to an unverified host."
90+
exit 1
91+
;;
92+
esac
93+
94+
validate_slug "${SRC_REPO}" "source repository name"
95+
validate_slug "${DEST_REPO}" "destination repository name"
96+
validate_slug "${OWNER_USERNAME}" "owner username"
97+
8298
# Create repo
8399
_payload=$(jq -n --arg name "${DEST_REPO}" '{"name":$name,"visibility":"internal"}')
84100
CREATE_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X POST -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github+json" "${API_URL_PREFIX}/orgs/${ORG}/repos" -d "${_payload}")

org-admin/github-repo-from-template/github-repo-from-template.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ validate_github_token
7171
require_env_var CD_GITHUB_TOKEN "CD GitHub token"
7272
validate_token CD_GITHUB_TOKEN
7373

74+
validate_slug "${REPO_NAME}" "repository name"
75+
validate_slug "${TEMPLATE_REPO}" "template repository name"
76+
validate_slug "${CD_USERNAME}" "CD username"
77+
7478
# Create repo from template
7579
_payload=$(jq -n --arg name "${REPO_NAME}" --arg owner "${ORG}" \
7680
'{"name":$name,"owner":$owner,"private":true,"include_all_branches":true}')
@@ -83,6 +87,7 @@ fi
8387

8488
# Give internal teams permissions on the new repo
8589
for ADMIN_TEAM in ${REPO_ADMIN}; do
90+
validate_slug "${ADMIN_TEAM}" "REPO_ADMIN team"
8691
ADMIN_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X PUT -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github.v3+json" "${API_URL_PREFIX}/orgs/${ORG}/teams/${ADMIN_TEAM}/repos/${ORG}/${REPO_NAME}" -d '{"permission":"admin"}')
8792

8893
if [ "${ADMIN_RESPONSE}" -ne 204 ]; then
@@ -94,6 +99,7 @@ for ADMIN_TEAM in ${REPO_ADMIN}; do
9499
done
95100

96101
for WRITE_TEAM in ${REPO_WRITE}; do
102+
validate_slug "${WRITE_TEAM}" "REPO_WRITE team"
97103
WRITE_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X PUT -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github.v3+json" "${API_URL_PREFIX}/orgs/${ORG}/teams/${WRITE_TEAM}/repos/${ORG}/${REPO_NAME}" -d '{"permission":"push"}')
98104

99105
if [ "${WRITE_RESPONSE}" -ne 204 ]; then

personal/github-organize-stars/github-organize-stars.sh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,9 @@ if [[ -f "$CACHE_FILE" ]]; then
242242
else
243243
echo "Fetching your starred repos..."
244244
mkdir -p "$(dirname "$CACHE_FILE")"
245+
chmod 700 "$(dirname "$CACHE_FILE")"
245246
fetch_stars > "$CACHE_FILE"
247+
chmod 600 "$CACHE_FILE"
246248
fi
247249

248250
TOTAL=$(jq 'length' "$CACHE_FILE")

reporting/github-monthly-issues-report/github-monthly-issues-report.sh

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,8 @@ limit_issue_pagination () {
7878

7979
repo_issues () {
8080
for PAGE in $(limit_issue_pagination); do
81-
for i in $(curl -s -H "Authorization: token ${GITHUB_TOKEN}" "${API_URL_PREFIX}/repos/${ORG}/${REPO}/issues?state=all&labels=Linked%20[AC]&page=${PAGE}&per_page=100" | jq -r --arg s "${MONTH_START}" --arg e "${MONTH_END}" 'map(select(.created_at | . >= ($s+"T00:00") and . <= ($e+"T23:59"))) | sort_by(.number) | .[].number'); do
81+
while IFS= read -r i; do
82+
[ -z "${i}" ] && continue
8283
ISSUE_PAYLOAD=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" "${API_URL_PREFIX}/repos/${ORG}/${REPO}/issues/${i}" -H "Accept: application/vnd.github.mercy-preview+json")
8384
ISSUE_TIMELINE_PAYLOAD=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" "${API_URL_PREFIX}/repos/${ORG}/${REPO}/issues/${i}/timeline" -H "Accept: application/vnd.github.mockingbird-preview+json" | jq -r '.[] | select(.label.name=="Linked [AC]" or .label.name=="linked")')
8485

@@ -95,7 +96,7 @@ repo_issues () {
9596
--arg contrib "${ISSUE_TIMELINE_LABELED_BY}" \
9697
'{"author":$author,"title":$title,"issue_url":$url,"contributor":$contrib}' \
9798
>> "${ISSUES_TEMP}"
98-
done
99+
done < <(curl -s -H "Authorization: token ${GITHUB_TOKEN}" "${API_URL_PREFIX}/repos/${ORG}/${REPO}/issues?state=all&labels=Linked%20[AC]&page=${PAGE}&per_page=100" | jq -r --arg s "${MONTH_START}" --arg e "${MONTH_END}" 'map(select(.created_at | . >= ($s+"T00:00") and . <= ($e+"T23:59"))) | sort_by(.number) | .[].number')
99100
done
100101
}
101102

@@ -105,7 +106,6 @@ author_json () {
105106
TEST_PAYLOAD=$(cat "${ISSUES_TEMP}" | jq -r '.author' | sort | uniq -c | awk -F " " '{print "{\"author\":""\""\ $2"\""",\"count\":" $1"}"}' | jq -r .)
106107
TEST_PAYLOAD_AUTHOR=$(echo "${TEST_PAYLOAD}" | jq -r --arg AUTHOR "${AUTHOR}" 'select(.author==$AUTHOR) | .author')
107108
TEST_PAYLOAD_AUTHOR_COUNT=$(echo "${TEST_PAYLOAD}" | jq -r --arg AUTHOR "${AUTHOR}" 'select(.author==$AUTHOR) | .count')
108-
TEST_PAYLOAD_AUTHOR_ISSUE_URL=$(cat "${ISSUES_TEMP}" | jq -r --arg AUTHOR "${AUTHOR}" 'select(.author==$AUTHOR) | .title, .issue_url')
109109
echo -e "<a href=\"https://github.com/${TEST_PAYLOAD_AUTHOR}\">${TEST_PAYLOAD_AUTHOR}</a> - ${TEST_PAYLOAD_AUTHOR_COUNT}"
110110
done | sort -n -k 4,4 -r >> "${OUTPUT_FILE}"
111111
}
@@ -116,7 +116,6 @@ contributor_json () {
116116
TEST_PAYLOAD=$(cat "${ISSUES_TEMP}" | jq -r '.contributor' | sort | uniq -c | awk -F " " '{print "{\"contributor\":""\""\ $2"\""",\"count\":" $1"}"}' | jq -r .)
117117
TEST_PAYLOAD_CONTRIBUTOR=$(echo "${TEST_PAYLOAD}" | jq -r --arg CONTRIBUTOR "${CONTRIBUTOR}" 'select(.contributor==$CONTRIBUTOR) | .contributor')
118118
TEST_PAYLOAD_CONTRIBUTOR_COUNT=$(echo "${TEST_PAYLOAD}" | jq -r --arg CONTRIBUTOR "${CONTRIBUTOR}" 'select(.contributor==$CONTRIBUTOR) | .count')
119-
TEST_PAYLOAD_CONTRIBUTOR_ISSUE_URL=$(cat "${ISSUES_TEMP}" | jq -r --arg CONTRIBUTOR "${CONTRIBUTOR}" 'select(.contributor==$CONTRIBUTOR) | .issue_url')
120119
echo -e "<a href=\"https://github.com/${TEST_PAYLOAD_CONTRIBUTOR}\">${TEST_PAYLOAD_CONTRIBUTOR}</a> - ${TEST_PAYLOAD_CONTRIBUTOR_COUNT}"
121120
done | sort -n -k 4,4 -r >> "${OUTPUT_FILE}"
122121
}

0 commit comments

Comments
 (0)