Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions .github/scripts/check-e2e-authorization-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/usr/bin/env bash
# check-e2e-authorization-test.sh — Tests for check-e2e-authorization.sh
#
# Uses a mock gh command to avoid hitting GitHub.
# Run from the repo root: bash .github/scripts/check-e2e-authorization-test.sh

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
AUTH_SCRIPT="${SCRIPT_DIR}/check-e2e-authorization.sh"
FAILURES=0

TMPDIR="$(mktemp -d)"
trap 'rm -rf "${TMPDIR}"' EXIT

MOCK_BIN="${TMPDIR}/bin"
mkdir -p "${MOCK_BIN}"

# Mock gh: default behavior returns CONTRIBUTOR association and no ok-to-test label.
setup_mock_gh() {
Comment thread
ralphbean marked this conversation as resolved.
Resolved
cat > "${MOCK_BIN}/gh" <<'MOCKEOF'
#!/usr/bin/env bash
# Default: PR endpoint returns CONTRIBUTOR with no ok-to-test label
if [[ "$1" == "api" ]] && [[ "$2" == *"/pulls/"* ]] && [[ "$2" != *"/files"* ]]; then
echo '{"author_association": "CONTRIBUTOR", "labels": [], "updated_at": "2026-01-01T00:00:00Z"}'
exit 0
fi
# Collaborator permission endpoint: default to read (not write)
if [[ "$1" == "api" ]] && [[ "$2" == *"/collaborators/"*"/permission"* ]]; then
echo '{"role_name": "read"}'
exit 0
fi
echo "mock-gh: unhandled call: $*" >&2
exit 1
MOCKEOF
chmod +x "${MOCK_BIN}/gh"
}

run_auth() {
# Run the script, capture stdout, suppress stderr
local output
output=$(
export PATH="${MOCK_BIN}:${PATH}"
export GH_TOKEN="fake-token"
export GITHUB_OUTPUT="${TMPDIR}/github-output"
: > "${GITHUB_OUTPUT}"
bash "${AUTH_SCRIPT}" "$@" 2>/dev/null
)
echo "${output}"
}

get_github_output() {
cat "${TMPDIR}/github-output"
}

Comment thread
ralphbean marked this conversation as resolved.
Resolved
Comment thread
ralphbean marked this conversation as resolved.
Resolved
assert_authorized() {
local test_name="$1"
local output="$2"
if echo "${output}" | grep -q "authorized=true"; then
echo "PASS: ${test_name}"
else
echo "FAIL: ${test_name} — expected authorized=true, got: ${output}"
FAILURES=$((FAILURES + 1))
fi
}

assert_unauthorized() {
local test_name="$1"
local output="$2"
if echo "${output}" | grep -q "authorized=false"; then
Comment thread
ralphbean marked this conversation as resolved.
Resolved
echo "PASS: ${test_name}"
else
echo "FAIL: ${test_name} — expected authorized=false, got: ${output}"
FAILURES=$((FAILURES + 1))
fi
}

# --- Tests ---

# Test: trusted bot (fullsend-ai-coder[bot]) is authorized
setup_mock_gh
output=$(
export PR_AUTHOR_ASSOCIATION="CONTRIBUTOR"
export PR_AUTHOR_LOGIN="fullsend-ai-coder[bot]"
run_auth 1 "test-org/test-repo"
)
assert_authorized "trusted bot fullsend-ai-coder[bot] is authorized" "${output}"

# Test: MEMBER association is authorized
setup_mock_gh
output=$(
export PR_AUTHOR_ASSOCIATION="MEMBER"
export PR_AUTHOR_LOGIN="some-human"
run_auth 1 "test-org/test-repo"
)
assert_authorized "MEMBER association is authorized" "${output}"

# Test: OWNER association is authorized
setup_mock_gh
output=$(
export PR_AUTHOR_ASSOCIATION="OWNER"
export PR_AUTHOR_LOGIN="some-human"
run_auth 1 "test-org/test-repo"
)
assert_authorized "OWNER association is authorized" "${output}"

# Test: COLLABORATOR association is authorized
setup_mock_gh
output=$(
export PR_AUTHOR_ASSOCIATION="COLLABORATOR"
export PR_AUTHOR_LOGIN="some-human"
run_auth 1 "test-org/test-repo"
)
assert_authorized "COLLABORATOR association is authorized" "${output}"

# Test: CONTRIBUTOR association without bot login is unauthorized
setup_mock_gh
output=$(
export PR_AUTHOR_ASSOCIATION="CONTRIBUTOR"
export PR_AUTHOR_LOGIN="random-contributor"
run_auth 1 "test-org/test-repo"
)
assert_unauthorized "CONTRIBUTOR without bot login is unauthorized" "${output}"

# Test: unknown bot is not trusted
setup_mock_gh
output=$(
export PR_AUTHOR_ASSOCIATION="CONTRIBUTOR"
export PR_AUTHOR_LOGIN="some-other-bot[bot]"
run_auth 1 "test-org/test-repo"
)
assert_unauthorized "unknown bot is not trusted" "${output}"

# --- Summary ---
echo ""
if [[ "${FAILURES}" -gt 0 ]]; then
echo "${FAILURES} test(s) failed"
exit 1
else
echo "All tests passed"
fi
14 changes: 13 additions & 1 deletion .github/scripts/check-e2e-authorization.sh
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ PR_NUMBER="${1:?PR number required}"
REPOSITORY="${2:?repository (owner/repo) required}"

TRUSTED_ASSOCIATIONS="OWNER MEMBER COLLABORATOR"
TRUSTED_BOTS="fullsend-ai-coder[bot]"
Comment thread
ralphbean marked this conversation as resolved.
Resolved
OK_TO_TEST_LABEL="ok-to-test"

write_error_output() {
Expand All @@ -46,6 +47,14 @@ is_trusted_author() {
esac
}

is_trusted_bot() {
Comment thread
ralphbean marked this conversation as resolved.
Resolved
Comment thread
ralphbean marked this conversation as resolved.
Resolved
local login="$1"
case " ${TRUSTED_BOTS} " in
*" ${login} "*) return 0 ;;
*) return 1 ;;
esac
}
Comment thread
ralphbean marked this conversation as resolved.
Resolved

# Fallback: check actor has write+ permission via the collaborator permission
# API, which correctly resolves org membership regardless of visibility.
has_write_permission() {
Expand Down Expand Up @@ -76,7 +85,10 @@ else
author_association="$(jq -r '.author_association' <<<"${pr_json}")"
fi

if is_trusted_author "${author_association}"; then
if is_trusted_bot "${PR_AUTHOR_LOGIN:-}"; then
Comment thread
ralphbean marked this conversation as resolved.
Resolved
authorized=true
reason="trusted_bot"
elif is_trusted_author "${author_association}"; then
authorized=true
reason="trusted_author"
elif has_write_permission "${PR_AUTHOR_LOGIN:-}" 2>/dev/null; then
Comment thread
ralphbean marked this conversation as resolved.
Resolved
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ script-test:
$(call run-timed,bash scripts/post-retro-test.sh)
$(call run-timed,bash scripts/post-scribe-test.sh)
$(call run-timed,bash scripts/validate-output-schema-test.sh)
$(call run-timed,bash .github/scripts/check-e2e-authorization-test.sh)

test: script-test
Loading