GHSL-2025-111: Privilege Escalation via pull_request_target

GHSL-2025-111: Privilege Escalation via pull_request_target #

Summary #

ItemValue
Advisory IDGHSL-2025-111
SeverityHigh
Affected Componentnrwl/nx
CVEN/A
CWECWE-269 (Improper Privilege Management)
Referencehttps://securitylab.github.com/advisories/GHSL-2025-111_nrwl_nx/

Vulnerability Description #

GHSL-2025-111 is a privilege escalation vulnerability in the pr-title-validation.yml workflow. The vulnerability exists because:

  1. The default branch was fixed, but vulnerable workflow remained on other branches (21.4.x, website-21)
  2. The workflow uses pull_request_target without branch filters
  3. An attacker can target the vulnerable branches to execute code with elevated privileges

Attack Vector #

sequenceDiagram
    participant Attacker
    participant VulnerableBranch
    participant Workflow
    participant Secrets

    Attacker->>VulnerableBranch: Create PR targeting 21.4.x or website-21
    Note right of Attacker: Default branch is fixed, but other branches are not
    VulnerableBranch->>Workflow: Trigger pull_request_target
    Workflow->>Workflow: Checkout PR code with elevated permissions
    Workflow->>Secrets: Access repository secrets
    Workflow-->>Attacker: Code execution with write permissions

Vulnerable Code Pattern #

name: PR Title Validation (Vulnerable)

on:
  pull_request_target:
    types: [opened, edited, synchronize]
    # VULNERABLE: No branch filter - all branches affected
    # Fixed on main but vulnerable on 21.4.x and website-21 branches

permissions:
  contents: write
  pull-requests: write

jobs:
  validate-title:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      # VULNERABLE: PR title directly interpolated
      - name: Validate PR title
        run: |
          echo "Validating PR: ${{ github.event.pull_request.title }}"

      # VULNERABLE: PR branch checkout in pull_request_target
      - uses: actions/checkout@v4
        with:
          ref: ${{ github.event.pull_request.head.sha }}

      - name: Run validation script
        run: |
          node scripts/validate-pr-title.js

sisakulint Detection #

sisakulint detects this vulnerability with multiple rules:

1. untrusted-checkout #

[untrusted-checkout] checking out untrusted code from pull request in workflow
with privileged trigger 'pull_request_target'. This allows potentially malicious
code from external contributors to execute with access to repository secrets.

2. code-injection-critical #

[code-injection-critical] "github.event.pull_request.title" is potentially
untrusted and used in a workflow with privileged triggers.

Branch-Specific Vulnerability #

This case demonstrates an important security lesson:

BranchStatus
main/masterFixed
21.4.xVulnerable
website-21Vulnerable

Lesson: Security fixes must be applied to ALL branches, not just the default branch.

Remediation #

Option 1: Add Branch Filter #

on:
  pull_request_target:
    types: [opened, edited, synchronize]
    branches:
      - main
      - master

Option 2: Use pull_request Instead #

on:
  pull_request:
    types: [opened, edited, synchronize]
# Note: pull_request trigger doesn't have secrets access from forks

Option 3: Remove Vulnerable Workflow from All Branches #

# Delete workflow from all branches
git checkout 21.4.x
git rm .github/workflows/pr-title-validation.yml
git commit -m "fix: remove vulnerable workflow"
git push

git checkout website-21
git rm .github/workflows/pr-title-validation.yml
git commit -m "fix: remove vulnerable workflow"
git push

Option 4: Use Environment Variables #

- name: Validate PR title
  env:
    PR_TITLE: ${{ github.event.pull_request.title }}
  run: |
    echo "Validating PR: $PR_TITLE"

Fix Commits #

Impact Mitigation #

The potential impact was limited because:

  • NPM publishing requires 2FA (prevents package compromise)
  • The attacker would still have repository write access

Auto-Fix Support #

sisakulint provides auto-fix for this vulnerability:

sisakulint -fix on script/actions/ghsl/ghsl-2025-111.yaml

Test Files #

  • Vulnerable pattern: script/actions/ghsl/ghsl-2025-111.yaml

References #