Report #88868
[bug\_fix] Secrets are empty or unavailable in workflows triggered by pull requests from forks
Change the triggering event to \`pull\_request\_target\` \(with strict security checks to prevent checkout of untrusted code in privileged contexts\) or use a two-workflow pattern with \`workflow\_run\` where the privileged workflow is triggered by the completion of the unprivileged test workflow. Root cause: GitHub Actions explicitly prevents secrets from being passed to workflows triggered by \`pull\_request\` events from forks to prevent malicious actors from exfiltrating secrets by submitting a PR that prints or transmits secret values.
Journey Context:
A maintainer has a workflow that runs integration tests against a live staging API using an API key stored as \`secrets.STAGING\_API\_KEY\`. When maintainers push to branches in the repository, the workflow runs successfully and authenticates. However, when an external contributor forks the repository and submits a Pull Request, the workflow runs but the tests fail with "401 Unauthorized" or the environment variable is empty. The maintainer checks the workflow logs and sees that the secret is indeed not being set \(it shows as empty or '\*\*\*' is absent\). The maintainer initially suspects a typo in the secret name, but it works on non-fork PRs. After searching "github actions secrets not working pull request", the maintainer finds documentation explaining the security model: workflows triggered by the \`pull\_request\` event from forks run in the fork's context and do not have access to the base repository's secrets or the \`GITHUB\_TOKEN\` with write permissions. The maintainer considers switching to \`pull\_request\_target\`, which runs in the base repo context and has access to secrets. However, upon reading the security hardening guide, the maintainer realizes that naively checking out the PR code with \`pull\_request\_target\` allows arbitrary code execution with access to secrets \(the "pwn request" vulnerability\). The maintainer implements the recommended pattern: keep the \`pull\_request\` workflow for unsafe testing without secrets, and create a second workflow triggered by \`workflow\_run\` that runs after the first one completes, has access to secrets, and performs privileged operations like deployment or commenting on the PR with test results.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-22T07:45:17.868356+00:00— report_created — created