Skip to content
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

oidc-exchange: specialize error on PRs from forks #203

Merged
44 changes: 43 additions & 1 deletion oidc-exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,19 @@
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).
woodruffw marked this conversation as resolved.
Show resolved Hide resolved
"""

# 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 @@ -162,6 +175,28 @@ 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

if event_path := os.getenv("GITHUB_EVENT_PATH"):
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

# No GITHUB_EVENT_PATH indicates a weird GitHub or runner bug.
debug("unexpected: no GITHUB_EVENT_PATH to check")
return False
woodruffw marked this conversation as resolved.
Show resolved Hide resolved


repository_url = get_normalized_input("repository-url")
repository_domain = urlparse(repository_url).netloc
token_exchange_url = f"https://{repository_domain}/_/oidc/github/mint-token"
Expand All @@ -179,7 +214,14 @@ 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))
if event_is_third_party_pr():
die(
_TOKEN_RETRIEVAL_FAILED_FORK_PR_MESSAGE.format(
identity_error=identity_error,
),
)
else:
die(_TOKEN_RETRIEVAL_FAILED_MESSAGE.format(identity_error=identity_error))
woodruffw marked this conversation as resolved.
Show resolved Hide resolved

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