Introduction
GitHub does not expose repository secrets to workflows triggered from forked pull requests by default. That behavior is a security control, not a bug. The real fix is to redesign the workflow so untrusted fork code can run safe validation steps without ever receiving deployment credentials, cloud keys, or production tokens.
Symptoms
- Fork pull request workflows fail because a secret resolves to an empty string
- Deployment, preview, or integration-test jobs work on same-repo branches but fail on external contributions
- Actions logs show
Input required and not suppliedfor a token that exists in repository settings - Teams are tempted to switch everything to
pull_request_targetwithout reviewing the security impact
Common Causes
- The workflow assumes repository secrets are always available
- Build, test, and deploy logic live in the same job with no trust boundary
- A privileged action expects secrets even for untrusted fork PRs
- Maintainers use
pull_request_targetunsafely by checking out and running fork code with secret access
Step-by-Step Fix
- 1.Separate fork-safe validation from privileged jobs
- 2.Keep normal build, lint, and unit tests under
pull_request, but move deploy or secret-dependent actions behind conditions that only run for trusted branches.
```yaml jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: npm ci - run: npm test
deploy: if: github.event.pull_request.head.repo.full_name == github.repository runs-on: ubuntu-latest steps: - run: ./deploy.sh env: DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }} ```
- 1.**Use
pull_request_targetonly for metadata or explicitly reviewed workflows** - 2.Do not check out and execute fork code with repository secrets just because
pull_request_targethas higher privileges. Use it for labels, comments, or gated approvals unless you have a hardened review flow.
on:
pull_request_target:
types: [opened, synchronize, reopened]- 1.Move privileged execution behind environments or maintainer approval
- 2.If external contributors need preview deployments, run them only after a maintainer approves the workflow or reruns a trusted job in a guarded environment.
environment:
name: fork-preview- 1.Design fork workflows to succeed without private secrets whenever possible
- 2.Public package installs, linting, type checks, and unit tests should not depend on production credentials. Save the privileged path for post-review execution.
Prevention
- Treat fork pull requests as untrusted code paths by default
- Split CI into fork-safe jobs and privileged jobs with explicit trust checks
- Avoid executing checked-out fork code inside
pull_request_targetwithout a security review - Document which workflows maintainers must approve manually for external contributors