Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions .semgrep/rules/github-actions-pull-request-target.test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Mapping form with sub-config
# ruleid: github-actions-pull-request-target
on:
pull_request_target:
types: [opened, synchronize]

name: pull_request_target with types

jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo hi
---
# Mapping form with empty value
# ruleid: github-actions-pull-request-target
on:
pull_request_target:

name: pull_request_target empty

jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo hi
---
# Block list form
# ruleid: github-actions-pull-request-target
on:
- pull_request_target

name: pull_request_target list

jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo hi
---
# Scalar form
# ruleid: github-actions-pull-request-target
on: pull_request_target

name: pull_request_target scalar

jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo hi
---
# Combined with other triggers (mapping)
# ruleid: github-actions-pull-request-target
on:
push:
branches: [main]
pull_request_target:
types: [opened]

name: combined triggers

jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo hi
---
# Combined with other triggers (mapping)
# ruleid: github-actions-pull-request-target
on:
pull_request_target:
types: [opened]
push:
branches: [main]

name: combined triggers

jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo hi
---
# Negative: regular pull_request
# ok: github-actions-pull-request-target
on:
pull_request:
types: [opened]

name: pull_request only (safe)

jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo hi
---
# Negative: workflow_run (different trigger, also potentially dangerous but not this rule)
# ok: github-actions-pull-request-target
on:
workflow_run:
workflows: [CI]
types: [completed]

name: workflow_run only

jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo hi
---
# Negative: word `pull_request_target` appearing in a non-trigger context should not match
# ok: github-actions-pull-request-target
on:
pull_request:

name: pull_request_target as a step env value (not a trigger)

jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo "$EVENT"
env:
EVENT: pull_request_target
77 changes: 77 additions & 0 deletions .semgrep/rules/github-actions-pull-request-target.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Flags use of the `pull_request_target` trigger in GitHub Actions workflows.
#
# `pull_request_target` runs in the context of the BASE repository with full
# write permissions and access to secrets, but `github.event.pull_request.*`
# carries attacker-controlled values from the PR head. Any workflow that
# subsequently checks out, builds, tests, or executes code referenced from
# the PR head ref is vulnerable to "pwn requests" — arbitrary remote code
# execution by anyone who can open a PR against the repo.
#
# Legitimate uses (labeling, commenting, status checks on fork PRs) exist
# but are rare and high-risk. Prefer `pull_request`. If `pull_request_target`
# is required, never check out `github.event.pull_request.head.*`, scope
# `permissions:` to the minimum needed, and add a
# `# nosemgrep: github-actions-pull-request-target` line immediately above
# the trigger with a justification reviewed by the security team.
rules:
- id: github-actions-pull-request-target
languages:
- yaml
severity: ERROR
message: >-
Workflow uses the `pull_request_target` trigger. This runs with the base repository's
write permissions and secret access while exposing attacker-controlled PR metadata, and
is a well-known source of remote code execution ("pwn requests") when combined with a
checkout or build of the PR head. Prefer `pull_request`. If `pull_request_target` is
genuinely required (e.g. labeling, commenting, posting status to fork PRs), do NOT check
out `github.event.pull_request.head.*`, scope `permissions:` to the minimum needed, and
add a `# nosemgrep: github-actions-pull-request-target` line above with a justification
reviewed by the security team.
metadata:
category: security
cwe:
- 'CWE-269: Improper Privilege Management'
owasp:
- A01:2021 - Broken Access Control
references:
- https://securitylab.github.com/research/github-actions-preventing-pwn-requests/
- https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
- https://www.legitsecurity.com/blog/github-actions-that-open-the-door-to-cicd-pipeline-attacks
technology:
- github-actions
subcategory:
- vuln
likelihood: HIGH
impact: HIGH
confidence: HIGH
paths:
include:
- '*.github/workflows/*.yml'
- '*.github/workflows/*.yaml'
patterns:
- pattern-either:
# Mapping form (with or without sibling triggers, with or without sub-config):
# on:
# push:
# branches: [main]
# pull_request_target:
# types: [opened]
- pattern: |
on:
...
pull_request_target: ...
- pattern: |
on:
...
pull_request_target:
# Block list form (with or without sibling triggers):
# on:
# - push
# - pull_request_target
- pattern: |
on:
- ...
- pull_request_target
# Scalar form:
# on: pull_request_target
- pattern: 'on: pull_request_target'
Loading