How to Safely Label Pull Requests Without Exposing Your Repo to AttackThe `pull_request_target` trigger is necessary for labeling, but it's also a risk. Here's how to configure it correctly.

Introduction: Labeling PRs—the Security Trap Few Acknowledge

Labeling pull requests should be straightforward, but in 2025, automation brings more risk than most teams admit. The appeal is obvious: categorizing PRs from contributors automatically, keeping a clean workflow, and offering maintainers more control. But here's the brutal truth: doing this safely in a public repo isn't as simple as copying a YAML file from Stack Overflow. The same pull_request_target trigger that makes cross-fork action possible is also a foot in the door for attackers.

Too many maintainers ignore or misunderstand these risks, happily merging workflow fixes that quietly expose secrets or privileged contexts to untrusted code. The line between convenience and catastrophic vulnerability is razor thin. If you want speed and safety, you need to take a hard look at how your workflow is triggered, what code is running, and who controls it.

Why pull_request_target Is Both Powerful and Dangerous

Let's be completely honest: the pull_request_target trigger is the only viable way for Actions to label PRs created from a forked repository. Why? Because standard pull_request workflows run from the contributor's code/context, so they don't have access to secrets or the ability to write labels on the base repo. The pull_request_target solves this—at a serious price.

See, this trigger executes workflow code from your base branch, but on event payloads that could come from anyone. If that workflow isn't brutally simple—or if it does anything more than process those event details using tightly controlled, trusted code—you're opening the door to code execution attacks, data exfiltration, and privilege escalation. Think your repo is too small to matter? Automated scanners don't care; every vulnerability is a potential target.

Safe Labeling: Architecture of a Minimal, Secure Workflow

If you want to label PRs from forks safely, the workflow file must do the absolute minimum: parse the event payload and add a label using the official, trusted GitHub API or action. Custom scripts, third-party actions, or any logic that interprets PR content as commands is off-limits. The most honest answer? Don't run any contributed or unreviewed code, period.

Here's a safe labeling workflow using only the official actions/labeler or actions/github-script:

name: "Auto-label PRs securely"
on:
  pull_request_target:
    types: [opened, synchronize]

permissions:
  pull-requests: write
  contents: read

jobs:
  label:
    runs-on: ubuntu-latest
    steps:
      - name: Add label based on branch name
        uses: actions/github-script@v7
        with:
          script: |
            const branch = context.payload.pull_request.head.ref;
            if (branch.startsWith('feat/')) {
              github.rest.issues.addLabels({
                owner: context.repo.owner,
                repo: context.repo.repo,
                issue_number: context.issue.number,
                labels: ['feature'],
              });
            }

Brutal advice: Never allow custom commands, downloads, or npm/python installs in a workflow running as pull_request_target. Keep it declarative, official, and transparent.

Don't Trust Third-Party Actions—Here's Why

Even a single unauthorized or poorly reviewed action can undermine any permission controls you set. The “Marketplace” is riddled with old, vulnerable, or even outright malicious actions; many haven't had a security review in years. If you're not pinning your actions to a SHA, or if you're using anything except battle-tested, first-party options, you're in dangerous territory.

The solution is to combine strict permissions, avoid run: script blocks, and never allow outside packages or binaries to be fetched. Here's how to pin a GitHub Action to a specific hash (always do this):

- uses: actions/github-script@df1e596637b055631b3431c77b2e0738c0328b34  # Example pin

Anything less is a gamble with your contributors' trust and your organization's data.

Limiting Permissions and Avoiding Secrets—Your Real Safety Net

It's not just about what code runs, but what that code can do. For labeling, you only need pull-requests: write; that's it. Keep all other permissions “none” or “read” (and never expose more unless absolutely necessary). Never inject secrets, personal tokens, or elevated rights for labeling PRs, and always double-check the permissions block in your workflow file. Nearly every public breach through Actions comes down to giving too much access for a simple task.

Review your repo and your organization settings! The more granular your permissions, the less chance a misfire turns into a crisis. Here's the brutally honest config:

permissions:
  pull-requests: write
  contents: read
  secrets: none # Not an actual key, just don't set or use any!

If you see “secrets:” used for a labeling workflow, tear it out—there's no legitimate need.

Advanced Tips: Logging, Monitoring, and Fail-Safes

No workflow is 100% bulletproof, so you need visibility. GitHub's audit logs and branch protection aren't just for compliance—they're your alert system. Enable workflow run reviews, set branch protection to require reviews on workflow file changes, and turn on Dependabot or similar tools for auto-notifications about risky dependencies.

For extra assurance, have another workflow that checks for accidental secrets exposure on PRs, or that disables workflow runs on suspicious contributors until manually approved. Automation is valuable, but no replacement for a vigilant, security-conscious team.

Conclusion: Labeling PRs Should Never Cost You Your Repo

Auto-labeling saves time—until it lets someone own your infrastructure. The allure of fast, seamless PR workflows is powerful, but one misstep with pull_request_target can undo years of trust and hard work. Keep your workflows ruthlessly simple. Run only trusted, official code. Pin your dependencies, tighten permissions, and never, ever run untrusted scripts. Every other “shortcut” is a recipe for disaster.

The honest takeaway? Labeling PRs is safe only if you treat automation with the same respect as production code. If you cut corners to save ten minutes, you'll be paying for it for years.