Skip to content

Commit

Permalink
Clarify the error during OIDC exchange on PRs from forks
Browse files Browse the repository at this point in the history
This specializes the token retrieval error handling, providing an
alternative error message when the error cause is something
that we know can't possibly work due to GitHub's own restrictions
on PRs from forks.

PR #203
Closes #202
Ref python-pillow/Pillow#7616

Co-authored-by: Sviatoslav Sydorenko <[email protected]>
  • Loading branch information
woodruffw and webknjaz authored Feb 27, 2024
1 parent edfa8f3 commit e53eb8b
Showing 1 changed file with 43 additions and 1 deletion.
44 changes: 43 additions & 1 deletion oidc-exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,20 @@
Learn more at https://docs.github.com/en/actions/deployment/security-hardening-your-deployments/about-security-hardening-with-openid-connect#adding-permissions-settings.
"""

# Specialization of the token retrieval failure case, when we know that
# the failure cause is use within a third-party PR.
_TOKEN_RETRIEVAL_FAILED_FORK_PR_MESSAGE = """
OpenID Connect token retrieval failed: {identity_error}
The workflow context indicates that this action was called from a
pull request on a fork. GitHub doesn't give these workflows OIDC permissions,
even if `id-token: write` is explicitly configured.
To fix this, change your publishing workflow to use an event that
forks of your repository cannot trigger (such as tag or release
creation, or a manually triggered workflow dispatch).
"""

# Rendered if the package index refuses the given OIDC token.
_SERVER_REFUSED_TOKEN_EXCHANGE_MESSAGE = """
Token request failed: the server refused the request for the following reasons:
Expand Down Expand Up @@ -165,6 +179,29 @@ def _get(name: str) -> str: # noqa: WPS430
)


def event_is_third_party_pr() -> bool:
# Non-`pull_request` events cannot be from third-party PRs.
if os.getenv("GITHUB_EVENT_NAME") != "pull_request":
return False

event_path = os.getenv("GITHUB_EVENT_PATH")
if not event_path:
# No GITHUB_EVENT_PATH indicates a weird GitHub or runner bug.
debug("unexpected: no GITHUB_EVENT_PATH to check")
return False

try:
event = json.loads(Path(event_path).read_bytes())
except json.JSONDecodeError:
debug("unexpected: GITHUB_EVENT_PATH does not contain valid JSON")
return False

try:
return event["pull_request"]["head"]["repo"]["fork"]
except KeyError:
return False


repository_url = get_normalized_input("repository-url")
repository_domain = urlparse(repository_url).netloc
token_exchange_url = f"https://{repository_domain}/_/oidc/mint-token"
Expand All @@ -182,7 +219,12 @@ def _get(name: str) -> str: # noqa: WPS430
try:
oidc_token = id.detect_credential(audience=oidc_audience)
except id.IdentityError as identity_error:
die(_TOKEN_RETRIEVAL_FAILED_MESSAGE.format(identity_error=identity_error))
cause_msg_tmpl = (
_TOKEN_RETRIEVAL_FAILED_FORK_PR_MESSAGE if event_is_third_party_pr()
else _TOKEN_RETRIEVAL_FAILED_MESSAGE
)
for_cause_msg = cause_msg_tmpl.format(identity_error=identity_error)
die(for_cause_msg)

# Now we can do the actual token exchange.
mint_token_resp = requests.post(
Expand Down

0 comments on commit e53eb8b

Please sign in to comment.