Migrate a GitHub PR Workflow

Replace a risky GitHub PR workflow with a safer two-workflow design.

Introduction

30 Second Summary

Public projects often welcome changes from people you have never met. An automatic check can expose protected information if it trusts those changes too soon.

In this project, you will migrate a public GitHub Actions workflow away from the risky pull_request_target event before GitHub blocks it by default on December 2, 2026. The safer design keeps untrusted tests separate from the privileged workflow that adds the comment.

What You'll Build

A fresh pull request passes its tests before a separate workflow posts the automated comment with no pull_request_target warning.

By the end of this project, you'll have:

  • A controlled risk demonstration that reports whether a dummy secret was available as a safe yes or no.
  • An unprivileged test workflow triggered by pull_request that checks proposed code without privileged access.
  • A privileged follow-up workflow triggered by workflow_run that comments on successful pull requests without the blocked event.
  • Secret Mission: An optional challenge to push your workflow security skills further.

Are there any prerequisites?

A free GitHub account is enough because this project costs nothing on a public repository. Basic familiarity with YAML helps you follow the workflow files.

Before We Start

This workflow experiment lives entirely on GitHub. Every later step depends on access to the right account.

Before any public repository exists, you need a signed-in browser session with permission to create one. That account will own the GitHub Actions configuration used later.

In this step, get ready to:
  • Sign in to GitHub through your web browser.
  • Confirm that your personal account can own a public repository.
  • Keep the new repository form ready for the next step.
Sign in to GitHub

GitHub authenticates your browser before it lets you create repositories or manage their settings. Use your normal account sign-in method.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type your browser's name into the search bar.
  • Press Enter to open your browser.
  • Visit GitHub's sign-in page.

Keep Your Credentials Private

Enter your password only on GitHub's own sign-in page. This project never asks you to add it to a repository.

If your dashboard loads immediately, your browser already has an authenticated session.

  • Enter your username or email address when the sign-in form appears.
  • Enter your password when the password field appears.
  • Click Sign in when the button appears.
  • Complete any account verification step that GitHub requests.

You'll see your personal dashboard after authentication. Your profile control is visible in the top navigation.

That's the first gate cleared: your browser session can now work with resources owned by your account.

Can't Reach Your Account?

Help me troubleshoot GitHub sign-in. You can also search the GitHub Community for account-specific guidance.

Confirm repository access

The new repository form shows which account will own the project. It also confirms whether public visibility is available without creating anything.

Before you check, do you expect GitHub to list your personal account as the repository owner?

You should see your personal account selected under Owner. You should also see Public among the available choices.

That access check is complete: your account can own the public repository used in this project.

What Does This Access Prove?

The selected Owner identifies the account that controls the new repository. Your personal account can create the project because it appears here.

Repository control covers workflow files in GitHub Actions. It also covers the settings used for repository secrets.

  • Keep the new repository form open for the next step.

Your GitHub session is ready. Next, you'll create the public repository that hosts the workflow experiment.

Create the Risky Workflow

Pull request automation often needs permission to label or comment on contributions. GitHub Actions can provide that permission from a workflow.

The pull_request_target event runs in the base repository's context. That context can access repository secrets.

To make that trust boundary visible, you'll create a controlled example with a disposable secret. This gives you a safe baseline for the later migration.

In this step, get ready to:
  • Create a public repository with main as its default branch.
  • Configure a disposable repository secret for the workflow.
  • Create a workflow triggered by pull_request_target that reports only whether the secret is available.
Create the public repository

A public repository lets you reproduce the event policy that this project addresses. Adding a README initializes the repository with a default branch.

  • Select the plus menu in the upper-right corner of GitHub.
  • Click New repository.
  • Choose your personal account from the Owner dropdown.
  • Enter safe-pr-workflow-template in the Repository name field.
  • Choose Public for the repository visibility.
  • Toggle Add README to On.
  • Click Create repository.

Good start. Your public safe-pr-workflow-template repository now has a README and an initialized default branch.

  • Check the branch selector above the file list.

You should see main as the selected branch.

Default branch not named main?

Your account can use a custom name for new default branches. Rename the initialized branch before continuing.

  • Open the branch dropdown above the file list.
  • Click View all branches.
  • Open the dropdown beside the current branch.
  • Click Rename branch.
  • Enter main as the new name.
  • Click Rename branch.

If the branch controls look different on your repository, help me rename my repository's default branch to main.

Add the disposable secret

A repository secret stores a value for workflows without placing it in the repository. The workflow can reference the value by name.

For this security-sensitive field, your disposable value carries no risk outside this exercise.

  • Click Settings below the repository name.
  • Select Secrets and variables in the Security section of the sidebar.
  • Click Actions.
  • Click the Secrets tab.
  • Click New repository secret.
  • Enter DUMMY_PULL_REQUEST_TARGET_SECRET in the Name field.
  • Enter nextwork-dummy-only in the Secret field.
  • Click Add secret.

That's the security-sensitive setup handled safely. GitHub now lists DUMMY_PULL_REQUEST_TARGET_SECRET without revealing its stored value.

Can't add the repository secret?

  • Confirm that you are inside the safe-pr-workflow-template repository settings.
  • Check that you selected Actions under Secrets and variables.
  • If the option is missing, help me find the repository Actions secrets page.
Create the legacy workflow

GitHub Actions discovers YAML workflow files inside .github/workflows. This workflow deliberately uses the privileged event while keeping the demonstration controlled.

The risk sits in the event's elevated access. This example checks out only the trusted base branch and prints only a yes-or-no result.

  • Return to the repository's main page using the repository name link.
  • Click the Add file dropdown above the file list.
  • Click Create new file.
  • Enter .github/workflows/pull-request-target.yml in the file name field.
  • Define the controlled legacy workflow by pasting this code into the editor:
# Name the legacy workflow in the Actions tab.
name: Legacy pull_request_target demo

# Trigger from pull request activity in the base repository context.
on:
  pull_request_target:

# Limit the automatic token to reading repository contents.
permissions:
  contents: read

jobs:
  inspect-base-context:
    runs-on: ubuntu-latest
    env:
      DUMMY_PULL_REQUEST_TARGET_SECRET: ${{ secrets.DUMMY_PULL_REQUEST_TARGET_SECRET }}
    steps:
      # Keep checkout on the base repository's default branch.
      - name: Check out the base repository
        uses: actions/checkout@v6

      # Report secret presence without exposing its value.
      - name: Report available dummy secret
        if: ${{ env.DUMMY_PULL_REQUEST_TARGET_SECRET != '' }}
        run: echo "Dummy secret available: yes"

      - name: Report unavailable dummy secret
        if: ${{ env.DUMMY_PULL_REQUEST_TARGET_SECRET == '' }}
        run: echo "Dummy secret available: no"

What does this workflow do?

  • The pull_request_target trigger runs the workflow from the base repository's default branch.
  • The permissions section limits the automatic token to read access for repository contents.
  • The actions/checkout@v6 step has no ref. It checks out the base repository's default branch.
  • The env section maps the disposable secret into the job environment.
  • The two if checks compare that environment value with an empty string. Exactly one step prints a yes-or-no result.
  • Click Commit changes... in the upper-right corner of the editor.
  • Enter Add legacy pull_request_target workflow in the Commit message field.
  • Select Commit directly to the main branch.
  • Click Commit changes.

Your browser returns to the new workflow file. You should see .github/workflows/pull-request-target.yml on the main branch.

Workflow file not committed?

  • Confirm that the file path is exactly .github/workflows/pull-request-target.yml.
  • Compare the spaces before pull_request_target, contents, and each job key with the reference below.
  • If GitHub still rejects the file, help me troubleshoot my GitHub Actions YAML.

✔️ Awesome, I've got everything!

Great. Your legacy workflow is committed to main with the intended base-context checkout and safe secret check.

ⓧ I'd like to double check the full code

Compare your complete .github/workflows/pull-request-target.yml file with this reference.

# Name the legacy workflow in the Actions tab.
name: Legacy pull_request_target demo

# Trigger from pull request activity in the base repository context.
on:
  pull_request_target:

# Limit the automatic token to reading repository contents.
permissions:
  contents: read

jobs:
  inspect-base-context:
    runs-on: ubuntu-latest
    env:
      DUMMY_PULL_REQUEST_TARGET_SECRET: ${{ secrets.DUMMY_PULL_REQUEST_TARGET_SECRET }}
    steps:
      # Keep checkout on the base repository's default branch.
      - name: Check out the base repository
        uses: actions/checkout@v6

      # Report secret presence without exposing its value.
      - name: Report available dummy secret
        if: ${{ env.DUMMY_PULL_REQUEST_TARGET_SECRET != '' }}
        run: echo "Dummy secret available: yes"

      - name: Report unavailable dummy secret
        if: ${{ env.DUMMY_PULL_REQUEST_TARGET_SECRET == '' }}
        run: echo "Dummy secret available: no"

The comments identify the trigger, permissions, checkout behavior, and secret-presence check.

Before you check, picture the workflow path and secret name you expect GitHub to show.

  • Return to the repository's main page using the repository name link.
  • Click Settings.
  • Select Secrets and variables in the sidebar.
  • Click Actions.

You should see DUMMY_PULL_REQUEST_TARGET_SECRET under repository secrets. Its value remains hidden.

What have you proved?

The public safe-pr-workflow-template repository now uses main as its default branch. The privileged workflow and disposable secret are both ready for a pull request test.

Your controlled legacy workflow is ready. Next, you'll open a pull request to see how GitHub handles its trigger.

Run a Test Pull Request

Your legacy workflow exists. Its risk becomes visible only when a pull request activates it.

This test gives GitHub Actions a real event to evaluate. The resulting status becomes your baseline for the safer redesign.

In this step, get ready to:
  • Make a harmless change on a new branch.
  • Open the first test pull request into main.
  • Observe the current policy status for the workflow run.
Make a harmless branch change

A pull request needs a difference between two branches. One extra blank line creates that difference without changing the workflow's behavior.

  • Return to the main page for safe-pr-workflow-template in your existing GitHub browser tab.
  • Select the .github folder in the file list.
  • Select the workflows folder.
  • Select pull-request-target.yml.
  • Select the pencil icon in the upper-right corner to enter the file editor.

You should see .github/workflows/pull-request-target.yml in the editor. Its existing workflow content stays unchanged.

  • Place your cursor at the end of the final line.
  • Press Enter once to add a blank line.
  • Select the Commit changes... button.
  • Enter Create test pull request in the Commit message field.
  • Select Create a new branch for this commit and start a pull request.
  • Select Propose changes.

The branch is ready. Its only difference is a blank line that can trigger your test safely.

Don't See the Pull Request Form?

  • Confirm that you selected Create a new branch for this commit and start a pull request before proposing the change.
  • Check that the blank line still appears at the end of pull-request-target.yml.
  • Select Propose changes again after GitHub finishes creating the branch.
  • Help me reach the pull request form from GitHub's file editor.
Open the test pull request

The base branch receives the proposed change. The compare branch supplies the blank-line commit that triggers this test.

  • Confirm that the base branch dropdown shows main.
  • Confirm that the new branch appears in the compare branch dropdown.
  • Enter Test pull request target workflow as the pull request title.
  • Enter Open the first safety test for the legacy workflow. in the description.
  • Select Create pull request.

Good work. Your first test pull request is now open against main.

Inspect the workflow status

GitHub applies a time-sensitive policy to this event. The Actions area records what happened to your run.

Before you check, consider whether today's run carries a warning or a blocked status.

  • Select Actions under the repository name.
  • Select the workflow associated with .github/workflows/pull-request-target.yml in the left sidebar.
  • Locate the newest run associated with the open test pull request.
  • Select the run name to view its summary.

You should see a workflow run associated with Test pull request target workflow.

Its summary displays the warning or blocked result that GitHub applies at the time of your test. That visible status confirms the pull request activated the legacy workflow.

Why Can This Status Differ?

GitHub announced that pull_request_target events in public repositories are blocked by default from December 2, 2026.

Earlier runs can carry a warning. Active enforcement produces a blocked result.

Don't See the Workflow Run?

  • Refresh the Actions page after GitHub registers the pull request event.
  • Check that the pull request targets main.
  • Check that .github/workflows/pull-request-target.yml on main still uses pull_request_target.
  • Help me find the missing pull request workflow run.

Your test pull request now exposes how GitHub treats the legacy trigger today. Next, you will inspect the run without revealing the dummy secret.

Inspect the Workflow Run

Your first pull request has produced a visible legacy workflow run. Its status tells you whether GitHub accepted the event at this point in the rollout.

Now you will inspect the GitHub Actions log for the evidence behind that status. You are looking for the checked-out repository context plus the dummy secret's availability.

In this step, get ready to:
  • Open the run details for the first test pull request.
  • Confirm that the workflow checked out the base repository context.
  • Record the dummy secret's yes-or-no availability result.
Open the run details

The run details connect the pull request event to the job that responded. This gives you one place to inspect the status plus every step the job executed.

  • In the Actions area from earlier, select the run associated with the open test pull request.
  • Select the job listed on the run details page.
  • Confirm that the page belongs to the first test pull request targeting main.

You should now see the job's individual log steps. This is the evidence trail created by the legacy workflow.

Trace the checked-out context

A checkout log reveals which side of the pull request supplied the code. That distinction matters because pull_request_target uses the base repository context.

Before you inspect the checkout, which side of the pull request do you think supplied the code?

  • Expand the step that checks out the repository.
  • Scan its log for the checkout source.
  • Confirm that the checkout belongs to safe-pr-workflow-template in the base repository context.

You should see that the checkout comes from the base repository context for safe-pr-workflow-template. The pull request targets its main branch.

What does base repository context mean?

The pull_request_target event evaluates the pull request from the trusted repository's side. In this run, that context belongs to safe-pr-workflow-template.

The workflow can therefore hold permissions from the base repository while responding to a pull request. That trust boundary is the source of the risk.

Can't find the base context?

  • Collapse unrelated log steps so the checkout output is easier to scan.
  • Use your browser's page search for safe-pr-workflow-template.
  • Ask for help with the checkout evidence.
Check the dummy secret result

Secret availability is the second half of the risk. A presence check proves whether the job received the disposable dummy secret without printing its value.

Before you expand the availability check, do you think the base repository context can read the dummy repository secret?

  • Expand the log step that reports whether DUMMY_PULL_REQUEST_TARGET_SECRET is available.
  • Locate the single yes-or-no output in that step.

For a completed run, you should see yes. The disposable secret's actual value stays hidden.

You have now captured the crucial evidence. Base repository code ran with access to the dummy repository secret.

Why log only yes or no?

A yes-or-no check demonstrates secret availability without leaking the credential. This keeps the lesson focused on the trust boundary.

Real credentials never belong in a demonstration log. A disposable dummy value keeps the test safe.

No yes result?

  • Confirm that you are viewing the run associated with the first open test pull request.
  • Check whether GitHub blocked the event before the job started. A blocked attempt has no availability output because its steps never executed.
  • Ask for help interpreting the missing result.

You have a clear record of why the legacy workflow is risky. Next, you will separate untrusted pull request testing from the workflow that holds repository permissions.

Split Testing From Privileged Actions

Your first GitHub Actions run proved that pull_request_target uses the base repository context. That context sat beside a secret-aware job.

This step separates pull request testing from privileged commenting. The pull_request workflow gets read-only access for tests. The workflow_run workflow comments using trusted metadata.

In this step, get ready to:
  • Remove the legacy pull request target workflow.
  • Create a read-only pull request testing workflow.
  • Create a metadata-only pull request commenting workflow.
Remove the privileged trigger

The legacy file remains an active entry point until you remove it. Its historical run stays available in Actions after deletion.

Removing this file stops future runs without erasing the evidence you inspected earlier.

  • Select safe-pr-workflow-template in the page breadcrumb to return to the repository.
  • Select .github/workflows/pull-request-target.yml in the repository file tree.
  • Open the dropdown menu in the top-right corner of the file view.
  • Click Delete file.
  • Click Commit changes....
  • Enter Remove legacy pull request target workflow in the Commit message field.
  • Choose the option that commits directly to the main branch.
  • Click Commit changes.

You return to the repository files. The pull-request-target.yml file no longer appears under .github/workflows.

That closes the risky entry point while preserving the original run as historical evidence.

Unable to delete the workflow?

  • Confirm that you opened the file from your own safe-pr-workflow-template repository.
  • Check that the commit destination is the main branch.
  • help me remove a GitHub workflow file from the main branch
Create the read-only test workflow

A pull_request workflow can check proposed code with a read-only token. It does not need the repository secret.

Why split the workflows?

The test job handles untrusted pull request code with read-only access. The later comment job handles trusted event metadata with one write permission.

This boundary keeps untrusted code away from the token that can change repository content.

  • Return to the repository file list.
  • Click Add file above the file list.
  • Click Create new file.
  • Enter .github/workflows/pr-tests.yml in the filename field.
  • Build the unprivileged test workflow by pasting this YAML into the editor:
# Test pull request code without privileged access.
name: PR tests

on:
  pull_request:
    branches:
      - main

# Keep the token read-only while tests execute.
permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Check out pull request code
        uses: actions/checkout@v7
        with:
          persist-credentials: false

      - name: Confirm legacy trigger is absent
        run: |
          if grep -R "pull_request_target" .github/workflows; then
            echo "Legacy trigger found."
            exit 1
          fi
          echo "No pull_request_target trigger found."

What does this workflow do?

  • The pull_request trigger starts testing when a pull request targets main.
  • The contents: read permission allows read-only repository access. Every unspecified token scope receives no access.
  • The actions/checkout@v7 step checks out the proposed pull request code. The persist-credentials: false setting removes stored Git credentials from the workspace.
  • The final step fails if any active workflow still contains pull_request_target.
  • Click Commit changes....
  • Enter Add unprivileged PR tests in the Commit message field.
  • Choose the option that commits directly to the main branch.
  • Click Commit changes.
  • Select the Actions tab to confirm that PR tests appears in the left sidebar.

The testing half of your new design is ready. Pull request code now runs with read-only access and no repository secret reference.

PR tests missing from Actions?

  • Confirm that the file path is exactly .github/workflows/pr-tests.yml.
  • Check that name: PR tests starts at the left edge of the file.
  • help me find why my PR tests workflow is missing from GitHub Actions
Create the metadata-only comment workflow

The workflow_run event starts a separate job after the test workflow completes. This job receives the permission required to post a comment.

The safety boundary depends on trusted event metadata. This workflow never checks out pull request code or downloads pull request artifacts.

  • Return to the repository file list.
  • Click Add file above the file list.
  • Click Create new file.
  • Enter .github/workflows/pr-comment.yml in the filename field.
  • Build the privileged metadata workflow by pasting this YAML into the editor:
# Comment after the unprivileged test workflow completes.
name: PR comment

on:
  workflow_run:
    workflows: ["PR tests"]
    types: [completed]

# Grant only the permission required to create a comment.
permissions:
  issues: write

jobs:
  comment:
    if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.pull_requests[0]
    runs-on: ubuntu-latest
    steps:
      - name: Comment using trusted metadata
        uses: actions/github-script@v9
        with:
          script: |
            // Read the pull request from the trusted workflow-run payload.
            const pullRequest = context.payload.workflow_run.pull_requests[0];

            // Comment without checking out or executing pull request code.
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: pullRequest.number,
              body: 'PR tests passed.'
            });

How does the privilege boundary work?

  • The workflows: ["PR tests"] filter connects this workflow to the exact test workflow name.
  • The job condition allows commenting only after a successful run with pull request metadata.
  • The issues: write permission grants the scope used to create the pull request comment. Every other token scope receives no access.
  • The actions/github-script@v9 step reads the pull request number from the trusted workflow-run payload. No untrusted files enter this job.
  • Click Commit changes....
  • Enter Add privileged PR comment workflow in the Commit message field.
  • Choose the option that commits directly to the main branch.
  • Click Commit changes.

Before you check, which two replacement workflow names do you expect to see in Actions?

  • Select the Actions tab to inspect the workflow list.

You should see PR tests plus PR comment in the left sidebar. No active workflow should be named for the legacy pull request target file.

You have replaced one risky workflow with a clear security boundary. Untrusted code and privileged commenting now run in separate jobs.

One replacement workflow missing?

  • Confirm that both workflow files were committed to the main branch.
  • Check that the trigger references PR tests with the same capitalization as the test workflow name.
  • help me troubleshoot my split GitHub Actions workflows

✔️ Awesome, I've got everything!

Great. Your repository now contains separate workflows for unprivileged testing and privileged commenting.

ⓧ I'd like to double check the full code

Compare both workflow files with these complete versions. The legacy pull-request-target.yml file should be absent.

# Test pull request code without privileged access.
name: PR tests

on:
  pull_request:
    branches:
      - main

# Keep the token read-only while tests execute.
permissions:
  contents: read

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Check out pull request code
        uses: actions/checkout@v7
        with:
          persist-credentials: false

      - name: Confirm legacy trigger is absent
        run: |
          if grep -R "pull_request_target" .github/workflows; then
            echo "Legacy trigger found."
            exit 1
          fi
          echo "No pull_request_target trigger found."
# Comment after the unprivileged test workflow completes.
name: PR comment

on:
  workflow_run:
    workflows: ["PR tests"]
    types: [completed]

# Grant only the permission required to create a comment.
permissions:
  issues: write

jobs:
  comment:
    if: github.event.workflow_run.conclusion == 'success' && github.event.workflow_run.pull_requests[0]
    runs-on: ubuntu-latest
    steps:
      - name: Comment using trusted metadata
        uses: actions/github-script@v9
        with:
          script: |
            // Read the pull request from the trusted workflow-run payload.
            const pullRequest = context.payload.workflow_run.pull_requests[0];

            // Comment without checking out or executing pull request code.
            await github.rest.issues.createComment({
              owner: context.repo.owner,
              repo: context.repo.repo,
              issue_number: pullRequest.number,
              body: 'PR tests passed.'
            });

Your split design is ready. Next, you will open another pull request and watch the tests trigger the comment without a pull_request_target warning.

Verify the Safer Workflow

Your replacement GitHub Actions design is in place. The remaining risk is a handoff that looks safe on paper but fails when a real pull request triggers it.

This step sends a second test pull request through the full chain. The pull_request workflow handles read-only testing.

The workflow_run workflow adds the comment from trusted metadata. Together, these results prove that the safer design works.

In this step, get ready to:
  • Create a harmless test change from the current main branch.
  • Open a second pull request into main.
  • Verify the replacement workflow chain on the new pull request.
Create a fresh test change

A fresh pull request needs a change that does not exist on main. Basing the branch on the current main branch ensures it inherits the safer workflow files.

  • Return to the Code tab of safe-pr-workflow-template.
  • Open the branch dropdown above the file list.
  • Click View all branches.

The branches page lists main as the default branch. It also shows the branches created earlier.

  • Click New branch.

The branch form shows a name field plus the source branch used as its starting point.

  • Enter safer-workflow-verification in the New branch name field.
  • Confirm that Source shows main.
  • Click Create new branch.

The repository now shows safer-workflow-verification as the current branch. Its starting point contains both replacement workflows.

A small Markdown note gives the pull request a harmless change to test. It leaves .github/workflows/pr-tests.yml plus .github/workflows/pr-comment.yml untouched.

  • Click Add file above the file list.
  • Click Create new file.

The file editor opens on the safer-workflow-verification branch.

  • Enter verification-note.md in the file name field.
  • Enter Safer workflow verification run. in the file contents field.
  • Click Preview.

The preview shows a one-line note. No workflow or secret value appears in the change.

  • Click Commit changes....
  • Enter Add safer workflow verification note in the Commit message field.
  • Click Commit changes.

The new branch now contains verification-note.md. That single-file change is ready to trigger your safer workflow chain.

Can't create the test change?

  • Confirm that the branch dropdown shows safer-workflow-verification before creating the file.
  • Select the existing branch if a previous attempt already created it.
  • Make sure the file name is exactly verification-note.md.
  • Help me create the verification branch and file in GitHub.
Open the second pull request

The base branch is the destination for the proposed change. The compare branch holds the change you want the workflows to test.

  • Click Pull requests under the repository name.
  • Click New pull request.
  • Select main from the base menu.
  • Select safer-workflow-verification from the compare menu.

The comparison should show only verification-note.md as the proposed change. The workflow files remain unchanged.

  • Click Create pull request on the comparison page.
  • Enter Verify safer workflow handoff as the pull request title.
  • Enter This pull request verifies unprivileged tests and the trusted follow-up comment. as the description.
  • Click Create pull request to open the pull request.

There it is: your second pull request is now exercising the replacement design with a real repository event.

Before you open the checks, which workflow do you expect to report first?

  • Click the Checks tab on the second pull request.
  • Wait for the check associated with .github/workflows/pr-tests.yml to finish.
  • Open the completed check.

You'll see a successful result from the pull request test workflow. This proves that the unprivileged pull_request path tested the proposed change.

Is the pull request check missing or failing?

  • Confirm that the pull request targets main.
  • Return to the repository's Actions tab.
  • Open the newest run from the pull request test workflow to find the failed step.
  • Help me diagnose the missing or failed pull request test run.
Verify the trusted follow-up

The workflow_run handoff starts after the pull request test finishes. A short gap before the follow-up run appears is normal while the event is queued.

Before you inspect the follow-up, do you expect any pull_request_target warning to appear?

  • Return to the repository's Actions tab.
  • Select the replacement comment workflow from the left sidebar.
  • Open the newest run associated with Verify safer workflow handoff.
  • Wait for the follow-up run to complete.
  • Confirm that the run completed successfully.
  • Review the run summary to confirm that no warning mentions pull_request_target.

The clean result proves that the trusted follow-up ran through workflow_run. It did not need an active pull_request_target trigger.

  • Return to the second pull request.
  • Click the Conversation tab.
  • Refresh the page after the follow-up run finishes.
  • Locate the comment created by the replacement comment workflow.

You've closed the loop: the unprivileged tests pass before the trusted workflow posts its comment. No pull_request_target warning appears.

Is the workflow comment missing?

  • Confirm that the pull request test check completed successfully.
  • Return to the Actions tab.
  • Open the newest replacement comment workflow run to inspect its result.
  • Help me diagnose the missing workflow comment.

Your second pull request now has passing tests plus a trusted follow-up comment. Next, you'll inspect the repository's Actions policy settings to see which controls your plan exposes.

Review Actions Policy Options

Your safer pull request design now tests untrusted code with read-only permissions. The separate comment workflow uses trusted metadata.

Repository policies add a governance layer around GitHub Actions workflow execution. You will check the repository page so your migration records the controls your current plan actually exposes.

In this step, get ready to:
  • Find the Actions policy settings for your repository.
  • Review the policy controls available on your current plan.
  • Record any plan-dependent controls that are unavailable.
Find the repository policy page

Actions policies govern who can trigger workflows. Event rules govern which events may run, including pull_request_target.

GitHub places Policies separately from General in the repository's Actions settings. Finding or missing this page answers the first plan-availability question.

  • Switch back to the main page of safe-pr-workflow-template.
  • Click Settings beneath the repository name.
  • Locate Actions in the left sidebar.
  • Check whether Policies appears beneath Actions.

✔️ I can see Policies

Your account exposes the repository-level Actions policy page. Opening it lets you check which governance controls are available.

  • Click Policies beneath Actions.
  • Keep the policy page open for the control review.

You should now see the repository's Actions policy page. This confirms that repository-level policy access is available in your current account context.

ⓧ Policies is unavailable

Some account contexts do not expose repository policy controls. The available Actions settings page becomes your evidence for this outcome.

  • Keep the repository's Actions settings page visible.
  • Use the checkpoint below to record that Policies is unavailable.

You should see the available Actions settings without a Policies entry. That absence is a valid plan-specific result.

Compare and record the available controls

Workflow execution protections can control workflow events or actors. Your goal is a plan-specific inventory of the controls shown for this repository.

This review leaves the repository settings unchanged. Before you compare the page with the list, which controls do you expect your current plan to expose?

✔️ I can review policy controls

  • Check whether the page offers Event rules for events such as pull_request_target.
  • Check whether the page offers Actor rules.
  • Check whether workflow file targeting is available.
  • Check whether the enforcement choices include Active or Evaluate.
  • Check whether Policy insights appears in the sidebar.
  • Leave every repository setting unchanged.

You should now have an inventory of each control that appears. Any control missing from the page belongs in your unavailable list.

ⓧ My plan does not show policy controls

The missing controls are still useful evidence. They show which governance features this repository cannot rely on.

  • Open GitHub's workflow execution policy guide in a new browser tab.
  • Compare the documented control categories with the options shown in your repository.
  • Record every missing repository-level control in the checkpoint below.
  • Return to the repository's Actions settings page.

You should now have a clear record of the controls unavailable in your account context. The repository's Actions settings remain visible as confirmation.

How to read your results

  • Event rules control which events are permitted to run workflows.
  • Actor rules control who is permitted to trigger workflows.
  • Evaluate is documented for GitHub Enterprise Cloud. It shows what a policy would block without enforcing the restriction.
  • Policy insights shows runs that a policy blocked or would have blocked.

At this point, you should either be viewing Policies with an inventory of controls or be viewing the Actions settings with a record that those controls are unavailable.

That closes the final governance check. Your repository now has a plan-specific record of its Actions policy controls.

Secret mission

Audit the Workflow Trust Boundary

Passing checks show that the split workflow works. Audit the trust boundary to prove that privileged commenting stays separated from untrusted pull request code.

Clean Up Your Resources

Clean Up Your Resources

You have completed the important part. Your public GitHub Actions repository now uses the safer split workflow with no ongoing cost for this project.

Choose whether to keep the repository as a reusable template, pause your work, or delete the repository entirely.

Resources you used:

  • Your public repository named safe-pr-workflow-template with its safer pull request test workflow and privileged comment workflow.
  • The disposable repository secret named DUMMY_PULL_REQUEST_TARGET_SECRET.

Keep everything running

Keep safe-pr-workflow-template as a reusable example of the safer workflow design. The dummy value was created only for this exercise.

Removing the disposable secret leaves both safer workflows intact.

  • Return to the repository settings sidebar from the Actions policy settings.
  • Navigate to the page that manages repository secrets used by Actions.
  • Find the row named DUMMY_PULL_REQUEST_TARGET_SECRET.
  • Use the deletion control for that secret.
  • Confirm the deletion when GitHub asks.
  • Refresh the repository secrets page.

You should no longer see DUMMY_PULL_REQUEST_TARGET_SECRET in the list. Your repository remains available as a template with its passing tests and automated pull request comment.

Pause - I'll come back to this later

There is no local process from this project to stop. Remove the disposable secret before closing the browser so the repository can wait safely for your return.

  • Return to the repository settings sidebar from the Actions policy settings.
  • Navigate to the page that manages repository secrets used by Actions.
  • Find the row named DUMMY_PULL_REQUEST_TARGET_SECRET.
  • Use the deletion control for that secret.
  • Confirm the deletion when GitHub asks.
  • Close the browser tab after the secrets page refreshes.

Your repository remains available when you return. Its workflow files and historical runs stay in place.

Delete - I don't want to use this again

Deleting the repository removes the template you built. Choose this option only when you no longer need the safer workflows or their historical runs.

  • Return to the repository settings sidebar from the Actions policy settings.
  • Navigate to the page that manages repository secrets used by Actions.
  • Find the row named DUMMY_PULL_REQUEST_TARGET_SECRET.
  • Use the deletion control for that secret.
  • Confirm the secret deletion when GitHub asks.
  • Switch to the general settings page for safe-pr-workflow-template.
  • Scroll to the repository deletion area at the bottom of the page.
  • Start the permanent deletion process for safe-pr-workflow-template.
  • Enter safe-pr-workflow-template if the confirmation prompt requests the repository name.
  • Complete the final confirmation shown by GitHub.
  • Visit the former repository URL.

You should see that safe-pr-workflow-template is no longer available. The repository deletion also removes its workflow files and remaining project history.

Nice Work!

Nice Work!

You did it! Your public repository now handles pull request automation through a safer GitHub Actions design.

You've learned how to:

  • Inspected the legacy pull_request_target run to confirm its base repository context. You checked DUMMY_PULL_REQUEST_TARGET_SECRET availability without exposing the value.
  • Moved untrusted testing into .github/workflows/pr-tests.yml with read-only access. You kept commenting in .github/workflows/pr-comment.yml with limited permissions.
  • Proved the safer pull request flow with a passing test run. You also saw an automated comment with no pull_request_target warning. You reviewed the Actions policy controls available on your current GitHub plan.
  • Secret Mission: Took on an optional workflow security challenge to push your skills further.

Ready to quiz yourself?