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 supplied for a token that exists in repository settings
  • Teams are tempted to switch everything to pull_request_target without 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_target unsafely by checking out and running fork code with secret access

Step-by-Step Fix

  1. 1.Separate fork-safe validation from privileged jobs
  2. 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. 1.**Use pull_request_target only for metadata or explicitly reviewed workflows**
  2. 2.Do not check out and execute fork code with repository secrets just because pull_request_target has higher privileges. Use it for labels, comments, or gated approvals unless you have a hardened review flow.
yaml
on:
  pull_request_target:
    types: [opened, synchronize, reopened]
  1. 1.Move privileged execution behind environments or maintainer approval
  2. 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.
yaml
environment:
  name: fork-preview
  1. 1.Design fork workflows to succeed without private secrets whenever possible
  2. 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_target without a security review
  • Document which workflows maintainers must approve manually for external contributors