Study guide
2.1 Webhook & Code Events: push, pull_request, and pull_request_target
GitHub Actions Certification GH-200
Track your progress as you read
Finished sections get a check mark.
1 Chapter 1: Introduction & Exam Overview
1.1 Exam Structure, Format & Scoring 1.2 GitHub Actions Architecture & Core Mental Model 1.3 Hands-On Preparation & Exam Day Strategy
2 Chapter 2: Workflow Triggers & Event Architecture
2.1 Webhook & Code Events: push, pull_request, and pull_request_target 2.2 Scheduled & Manual Triggers: schedule, workflow_dispatch, and repository_dispatch 2.3 Release, Issue, and Dynamic Lifecycle Events 2.4 Branch, Tag, and Path Filtering Patterns
3 Chapter 3: Jobs, Matrix Strategies & Execution Control
3.1 Job Anatomy, Step Execution & Job Dependencies (needs) 3.2 Contexts, Expressions & Functions 3.3 Environment Variables & Workflow Commands 3.4 Matrix Strategies & Multi-Platform Builds 3.5 YAML Anchors, Aliases & Merge Keys
4 Chapter 4: Reusable Workflows & Troubleshooting
4.1 Authoring & Calling Reusable Workflows (workflow_call) 4.2 Comparing Reusable Workflows, Composite Actions & Starter Workflows 4.3 Troubleshooting Workflow Runs & Debug Logging 4.4 Concurrency Controls & Status Check Functions
5 Chapter 5: Authoring Custom Actions
5.1 Action Architecture & action.yml Specification 5.2 Authoring Composite Actions 5.3 Building JavaScript Actions 5.4 Building Docker Container Actions
6 Chapter 6: Action Distribution, Marketplace & Advanced Action Types
6.1 Action Versioning, Semantic Releases & Tagging 6.2 Publishing Actions to GitHub Marketplace 6.3 Service Containers & Network Topology 6.4 actions/github-script & Workflow Tooling
7 Chapter 7: Runner Infrastructure & Self-Hosted Runners
7.1 GitHub-Hosted Runners & Larger Runners 7.2 Self-Hosted Runner Setup, Architecture & Communication 7.3 Self-Hosted Runner Security & Ephemeral Runners 7.4 Runner Groups, Labels & Organization Routing
8 Chapter 8: Enterprise Governance, Policies & Environments
8.1 Enterprise & Organization Action Policies 8.2 Environments, Protection Rules & Deployment Approvals 8.3 Repository Rulesets, Required Status Checks & Governance 8.4 Usage Limits, Billing Multipliers & Usage Metrics
9 Chapter 9: Secrets, Permissions & OIDC Cloud Authentication
9.1 Encrypted Secrets Architecture & Scopes 9.2 GITHUB_TOKEN & Least Privilege Configuration 9.3 OpenID Connect (OIDC) & Workload Identity Federation 9.4 Script Injection Prevention & Secure Workflow Patterns
10 Chapter 10: Supply Chain Hardening & Performance Optimization
10.1 Supply Chain Hardening: SHA Pinning, Dependabot & Artifact Attestations 10.2 Caching Dependencies with `actions/cache` 10.3 Artifacts Management & Job Handoff 10.4 Workflow Performance, Cost Optimization & Architecture Review
2.1 Webhook & Code Events: push, pull_request, and pull_request_target
Key Takeaways Explain This Topic AI
The push event triggers on Git ref updates (branches, tags) and can be scoped using branches, tags, and paths filters.
The pull_request event triggers by default only on opened, synchronize, and reopened activity types; other activity types like closed, labeled, or ready_for_review require explicit configuration.
Workflows triggered by pull_request execute against the ephemeral merge commit ref refs/pull/NUMBER/merge, running in the fork's context with a read-only GITHUB_TOKEN and zero access to base repository secrets.
The pull_request_target event executes in the context of the base repository (target branch) with access to repository secrets and write permissions, evaluating the workflow file from the base branch rather than the PR head branch.
Combining pull_request_target with an explicit checkout of untrusted PR code (ref: ${{ github.event.pull_request.head.sha }}) introduces severe security vulnerabilities (pwn-requests) if untrusted build scripts or actions are executed.
Last updated: August 2026
Webhook & Code Events: push, pull_request, and pull_request_target
Every automated pipeline in GitHub Actions begins with an event trigger . GitHub Actions is event-driven by design: an event is a specific activity that triggers a workflow run. In software development lifecycles, the most frequent triggers are code lifecycle events—specifically when commits are pushed directly to a repository branch or submitted through a pull request.
Understanding the subtle execution contexts, Git reference structures, and security boundaries between push , pull_request , and pull_request_target is one of the highest-weight competency areas in the GitHub Actions Certification (GH-200) examination.
1. Deep Mechanics of the push Event
The push event occurs whenever one or more commits are pushed to a repository branch or when a Git tag is pushed. It represents direct code changes landing in the repository. on:
push:
branches:
- main
- 'releases/**'
tags:
- 'v[0-9]+.[0-9]+.[0-9]+'
paths:
- 'src/**'
- 'package.json'
- Key Characteristics and Context Values
Triggering Ref ( github.ref ): For branch pushes, github.ref is formatted as refs/heads/<branch_name> (e.g., refs/heads/main ). For tag pushes, it resolves to refs/tags/<tag_name> (e.g., refs/tags/v1.0.0 ).
Target Commit SHA ( github.sha ): For branch pushes, github.sha is the commit SHA of the tip of the pushed branch. For tag pushes, it is the commit SHA to which the tag points.
Commit Comparison Payload: The event payload provides github.event.before (the commit SHA prior to the push) and github.event.after (the commit SHA after the push), allowing workflows to compute precise diff ranges.
Default Checkout Behavior: When using actions/checkout@v4 , the action automatically checks out github.ref at github.sha , representing the exact state of the pushed branch or tag.
2. The pull_request Event Architecture
The pull_request event triggers on activity related to pull requests. Unlike push , a pull request represents proposed changes from a head branch (which may reside in the same repository or in an external fork) against a base branch.
Default Activity Types vs. Explicit Types
By default, if you specify on: pull_request without defining types , GitHub Actions triggers the workflow for only three activity types:
opened : A new pull request is created.
synchronize : New commits are pushed to the head branch of the pull request.
reopened : A previously closed pull request is reopened.
If you need a workflow to respond to other pull request lifecycle events, you must explicitly declare all desired activity types in the types array. Specifying types overrides the defaults completely:
- Activity Type Description Common CI/CD Use Case
- opened Pull request opened Initial lint, unit tests, security scanning
- synchronize New commit pushed to PR branch Re-running test suites on updated code
- reopened Closed PR reopened Re-validating baseline checks
- closed PR merged or closed without merging Triggering post-merge cleanup, closing test environments
- labeled / unlabeled Label added or removed Automated routing, triage, triggering stage deployments
- ready_for_review Draft PR marked ready for review Running resource-heavy integration test suites
- converted_to_draft PR converted back to draft status Pausing continuous review automations
- assigned / unassigned Reviewer or assignee updated Team notification webhooks
- edited PR title or description edited Enforcing Conventional Commits / PR title linters
Important
When handling the closed activity type, always inspect the boolean context variable ${{ github.event.pull_request.merged }} . If github.event.pull_request.merged == true , the PR was merged into the base branch; if false , the PR was closed without being merged. on:
pull_request:
types: [opened, synchronize, reopened, closed, labeled]
branches:
- main
jobs:
deploy-cleanup:
- if: github.event.action == 'closed' && github.event.pull_request.merged == true
- runs-on: ubuntu-latest
steps:
- run: echo "PR #${{ github.event.pull_request.number }} merged into main! Cleaning up preview env..."
- Ref Resolution: The Ephemeral Merge Commit
When a pull_request event triggers, GitHub creates a synthetic, ephemeral test merge commit.
Merge Ref: github.ref points to refs/pull/<pr_number>/merge .
Head Ref: refs/pull/<pr_number>/head points to the tip of the PR branch.
When actions/checkout@v4 runs in a pull_request workflow, it defaults to checking out refs/pull/<pr_number>/merge . This ensures that your CI pipeline tests the proposed changes as if they were already merged into the current base branch , catching integration regressions and merge conflicts before the pull request is approved.
Warning
If merge conflicts exist between the PR head branch and the base branch, GitHub cannot generate the synthetic refs/pull/<pr_number>/merge ref. In such cases, the workflow run will fail at checkout or may fail to trigger until conflicts are resolved.
Loading diagram... Execution Context and Security Boundary: pull_request vs pull_request_target
3. The Security Boundary: pull_request vs pull_request_target
In enterprise and open-source workflows, managing pull requests from external forks requires a strict balance between automation convenience and security isolation.
The pull_request Fork Security Model
When a workflow is triggered by pull_request from a forked repository :
Workflow Definition Source: The workflow file is read from the head branch (the fork) .
GITHUB_TOKEN Permissions: The automatic token is scoped to read-only ( contents: read ), regardless of repository default settings.
Secrets Access: The workflow has zero access to repository secrets . ${{ secrets.* }} resolves to empty strings.
Runner Security: Protects the upstream repository from malicious code execution and credential exfiltration.
However, this isolation creates challenges for legitimate automations that need to label PRs, post bot comments, or execute SonarQube / external service analysis requiring API keys.
The pull_request_target Event
To solve the credential isolation problem, GitHub introduced pull_request_target . This event triggers on pull request activities but alters the execution environment fundamentally:
Workflow Definition Source: The workflow file is loaded strictly from the base repository target branch (e.g., main ). Changes made to .github/workflows/ inside the PR are completely ignored.
Execution Context: github.ref resolves to the base branch (e.g., refs/heads/main ).
Token & Secrets: The workflow receives the base repository GITHUB_TOKEN (with standard write permissions) and has full access to repository and organization secrets .
The "Pwn-Request" Attack Vector and Safe Usage Rules
Because pull_request_target runs with write permissions and secret access, checking out untrusted code from the fork PR head branch is extremely dangerous. # ❌ INSECURE ANTI-PATTERN: PWN-REQUEST VULNERABILITY
on:
pull_request_target:
jobs:
unsafe-build:
runs-on: ubuntu-latest
steps:
- name: Checkout untrusted fork code into privileged runner
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # DANGER!
- name: Run build script
- run: |
- npm install
npm test # Attacker injects malicious postinstall script to steal secrets!
env:
- DEPLOY_KEY: ${{ secrets.DEPLOY_KEY }}
- AWS_KEY: ${{ secrets.AWS_ACCESS_KEY_ID }}
Caution
Pwn-Request Exploitation: If you checkout the PR head commit ( github.event.pull_request.head.sha ) in a pull_request_target workflow and execute any build step, test command, linter, or action from that checked-out code, an external attacker can submit a pull request containing malicious scripts (e.g., inside package.json scripts or custom code) that execute inside your privileged environment and exfiltrate all secrets.
Recommended Safe Pattern for pull_request_target
Only use pull_request_target for lightweight administrative tasks (such as labeling, checking PR metadata, or posting comments) where untrusted code is never checked out or executed : # ✅ SECURE PATTERN: Metadata handling without checking out untrusted code
name: Label Pull Request
on:
pull_request_target:
types: [opened, labeled]
permissions:
- pull-requests: write
- contents: read
jobs:
auto-label:
runs-on: ubuntu-latest
steps:
- name: Add Triage Label
- uses: actions/github-script@v7
with:
- script: |
- github.rest.issues.addLabels({
- issue_number: context.payload.pull_request.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- labels: ['needs-review']
- });
- Feature pull_request pull_request_target
- Workflow Code Source PR Head / Fork Branch Base Branch ( main )
- Checked-out Ref (Default) refs/pull/:id/merge refs/heads/<base>
- Fork Secrets Access None (Disabled) Full Target Repo Secrets
- GITHUB_TOKEN Scope Read-Only (on forks) Read/Write (configurable)
- Primary Use Case Building & Testing Code PR Labeling, Triage, Commenting