Environment Bypass Risk
Description
Workflows triggered by pull_request_target, workflow_run, or other elevated events can call protected environments without the usual approval gates. A malicious contributor can craft a PR that triggers the privileged workflow and bypasses environment reviewers or required branches, releasing unreviewed code. GitHub cautions that pull_request_target should never deploy directly to production because it runs with the base repository’s token. 1
Vulnerable Instance
- Workflow uses
pull_request_target. - Job targets a protected environment (deployments, secrets) without checking the source branch.
- No manual approval before environment deployment.
name: Deploy on PR Target
on:
pull_request_target:
branches: [main]
jobs:
deploy:
environment: production
permissions:
contents: write
deployments: write
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }}
- run: ./scripts/deploy.shA forked PR can inject arbitrary code into deploy.sh; the workflow runs with maintainer permissions and bypasses environment approvals.
Mitigation Strategies
- Avoid privileged deployments on
pull_request_target
Usepull_requestfor testing only; gate deployments onworkflow_dispatch/push. - Require environment reviewers
Configure environments with required reviewers and wait timers so automation cannot bypass them. - Validate triggering metadata
Checkgithub.event.pull_request.head_repo.full_nameand restrict to trusted repos/branches. - Split validation from deployment
Have PR workflows create artifacts; only trustedpushorworkflow_dispatchjobs deploy them. - Scope permissions
Setpermissions: read-allby default and elevate only after approval.
Secure Version
Split into two separate workflow files. The PR workflow only runs tests; production deploys only on trusted push events and require a human reviewer on the environment.
.github/workflows/pr-validation.yml — safe, read-only PR checks:
name: PR Validation
on:
pull_request: # NOT pull_request_target
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: read # read-only; no deployment access
steps:
- uses: actions/checkout@v4
- run: npm ci
- run: npm test.github/workflows/deploy.yml — production deploys only on merged commits:
name: Deploy to Production
on:
push:
branches: [main] # only runs after PR is merged and reviewed
jobs:
deploy:
runs-on: ubuntu-latest
environment:
name: production # requires a human reviewer to approve in GitHub UI
url: https://prod.example.com
permissions:
contents: read
deployments: write
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy.shImpact
| Dimension | Severity | Notes |
|---|---|---|
| Likelihood | Many teams use pull_request_target or chained workflows for convenience. | |
| Risk | Attackers can push unreviewed code to protected environments or access secrets. | |
| Blast radius | Compromised workflow affects all environments linked via workflow_run or deployments. |
References
- GitHub Docs, “Using environments for deployment,” https://docs.github.com/actions/deployment/targeting-different-environments/using-environments-for-deployment 1
- GitHub Docs, “pull_request_target event,” https://docs.github.com/actions/using-workflows/events-that-trigger-workflows#pull_request_target
GitHub Docs, “Using environments for deployment,” https://docs.github.com/actions/deployment/targeting-different-environments/using-environments-for-deployment ↩︎ ↩︎