Skip to content

Security Vulnerability Report – Critical RCE via pull_request_target Workflow

Critical
ljharb published GHSA-43vf-c68r-43mr Jan 9, 2026

Package

No package listed

Affected versions

0

Patched versions

None

Description

Security Vulnerability Report – Critical RCE via pull_request_target Workflow

Repository

tc39/proposal-amount

Severity

Critical (CVSS 9.8)

CVSS Vector

CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

Category

CI/CD Supply Chain – GitHub Actions Remote Code Execution (RCE)

Reporter

Barak Haryati | JFrog
This research was conducted as part of JFrog’s Open Source and Supply Chain Security Contribution efforts. 🐸

Date

2026-01-08


Summary

A critical Remote Code Execution (RCE) vulnerability exists in the GitHub Actions CI workflow of tc39/proposal-amount.

Vulnerable Workflow: .github/workflows/pr-preview.yml

The workflow is triggered using pull_request_target, checks out untrusted pull request code, and executes attacker-controlled scripts with access to repository secrets.

Job Permissions: Write access confirmed including:

  • contents: write - Full repository takeover
  • pull-requests: write - PR manipulation

This allows a malicious pull request author to execute arbitrary code in the base repository security context, leading to:

  • Full repository takeover
  • TC39 specification tampering
  • JavaScript ecosystem supply chain attack
  • Credential theft via workflow manipulation

This issue is confirmed and validated, not theoretical.

⚠️ Responsible Disclosure: All PoC testing was performed exclusively on a personal fork. The same attack can be trivially executed against the production repository with identical impact. An empty PR (#76) was submitted to the original repo to confirm the workflow triggers.


About TC39

TC39 is Ecma International's Technical Committee 39 – the committee responsible for evolving JavaScript (ECMAScript), the programming language that powers:

  • Every web browser in the world
  • Node.js and server-side JavaScript
  • React, Vue, Angular and all frontend frameworks
  • Billions of websites and applications

Impact Scope:

  • TC39 proposals define the future of JavaScript
  • Changes to TC39 repositories affect every JavaScript developer
  • Compromised specifications could introduce backdoors into the language itself
  • Trust in the JavaScript standardization process would be severely damaged

This is not just another repository – this is the governing body of the world's most popular programming language.


Root Cause Analysis

The following insecure pattern causes the vulnerability:

  1. pr-preview.yml workflow is triggered by pull_request_target, which runs with base-repository privileges
  2. The workflow checks out the pull request code and executes PR-controlled commands via npm
  3. The GITHUB_TOKEN has contents:write permission enabling full repository control
  4. With contents:write, an attacker can create new workflows to access ALL repository secrets

GITHUB_TOKEN Permissions

The GITHUB_TOKEN in this workflow has the following permissions:

Permission Access Impact
Contents write Full repository takeover
Metadata read Read repository metadata
PullRequests write Approve/merge malicious code

Critical: With contents:write, an attacker can:

  • Modify any file in the repository
  • Create new GitHub Actions workflows
  • Access ALL repository secrets via new workflows
  • Push malicious commits directly to main branch
  • Modify TC39 specifications

Workflow Manipulation for Secret Theft

With contents: write permission, an attacker can create new workflows or modify existing ones to access ALL repository secrets.

Attack Flow:

  1. Submit malicious PR triggering pull_request_target
  2. Exfiltrate GITHUB_TOKEN with contents:write
  3. Use token to push a new workflow file (e.g., .github/workflows/exfil.yml)
  4. New workflow runs and exfiltrates ALL secrets
  5. Full repository and secret access achieved

This means any secret in the repository can be stolen, even if not directly used in the vulnerable workflow.


Critical Attack Vectors

1. 📜 TC39 Specification Tampering

With contents:write, an attacker can:

  • Modify ECMAScript proposal documents
  • Inject malicious code examples into specifications
  • Alter the "Amount" proposal that could become part of JavaScript
  • Undermine trust in the TC39 standardization process

2. 🔗 Supply Chain Attack on JavaScript Ecosystem

  • Compromised specifications could influence JavaScript implementations
  • Browser vendors and Node.js could implement backdoored features
  • Billions of JavaScript applications affected downstream

3. 🔄 Persistent Backdoor Installation

Using contents:write:

  • Create scheduled workflows for ongoing access
  • Modify existing workflows to maintain persistence
  • Install hidden exfiltration mechanisms

4. 🎭 Social Engineering via TC39 Authority

  • Post malicious content appearing to be from TC39
  • Create fake proposals with backdoored code
  • Damage reputation of JavaScript standardization

Proof of Exploitation (Validated on Fork)

The vulnerability was successfully exploited on a personal fork. The same attack works on the production repository (confirmed via PR #76).

Proof of Concept Links

RCE Evidence

The PoC demonstrated:

  • ____RCE_Success output confirming code execution
  • poc_hello message displayed
  • Branch creation (bh-poc) confirming contents:write works
  • Git config with AUTHORIZATION headers exfiltrated to webhook.site
  • "creating malicious branch, can easily push to master or release" message

Impact

An attacker can:

  • ✅ Execute arbitrary commands in GitHub Actions runners
  • ✅ Exfiltrate the GITHUB_TOKEN with contents:write permission
  • Full repository takeover (push to any branch)
  • Create/modify workflows → steal ALL secrets
  • Tamper with TC39 specifications
  • Approve and merge malicious PRs
  • Install persistent backdoors
  • Damage trust in JavaScript standardization

JavaScript Ecosystem Impact

TC39 defines the future of JavaScript. A successful attack could:

  • Introduce subtle backdoors into language proposals
  • Influence how browsers and Node.js implement features
  • Affect every JavaScript developer and application worldwide
  • Undermine the integrity of web standards

Full Attack Chain Visualization

┌─────────────────────────────────────────────────────────────────────────────────┐
│                         ATTACK CHAIN VISUALIZATION                              │
├─────────────────────────────────────────────────────────────────────────────────┤
│                                                                                 │
│  STAGE 1: Initial Access via Malicious Pull Request                            │
│  ┌─────────────────────────────────────────────────────────────────────────┐   │
│  │  Attacker forks tc39/proposal-amount                                    │   │
│  │  └──► Modifies package.json with malicious preinstall script            │   │
│  │       └──► Opens Pull Request to upstream repository                    │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                    │                                            │
│                                    ▼                                            │
│  STAGE 2: Code Execution via pull_request_target                                │
│  ┌─────────────────────────────────────────────────────────────────────────┐   │
│  │  pr-preview.yml triggers on pull_request_target                         │   │
│  │  └──► Checks out PR code                                                │   │
│  │       └──► Runs npm ci with attacker-controlled package.json            │   │
│  │            └──► ATTACKER CODE RUNS IN BASE REPO CONTEXT                 │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                    │                                            │
│                                    ▼                                            │
│  STAGE 3: Exfiltrate GITHUB_TOKEN with contents:write                           │
│  ┌─────────────────────────────────────────────────────────────────────────┐   │
│  │  Extract GITHUB_TOKEN from git config                                   │   │
│  │  └──► Token has contents:write permission                               │   │
│  │       └──► Can push to any branch, create workflows                     │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                    │                                            │
│                                    ▼                                            │
│  STAGE 4: Create Malicious Workflow to Steal ALL Secrets                        │
│  ┌─────────────────────────────────────────────────────────────────────────┐   │
│  │  Using contents:write, push new workflow to repository:                 │   │
│  │  └──► .github/workflows/exfil.yml                                       │   │
│  │       └──► Workflow triggers and exfiltrates ALL repository secrets     │   │
│  │            └──► COMPLETE SECRET ACCESS ACHIEVED                         │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                    │                                            │
│                                    ▼                                            │
│  STAGE 5: TC39 Repository Takeover                                              │
│  ┌─────────────────────────────────────────────────────────────────────────┐   │
│  │  Full control over TC39 proposal:                                       │   │
│  │  └──► Modify ECMAScript specification documents                         │   │
│  │       └──► Inject backdoored code examples                              │   │
│  │            └──► Push malicious commits to main                          │   │
│  │                 └──► TC39 PROPOSAL COMPROMISED                          │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                    │                                            │
│                                    ▼                                            │
│  DOWNSTREAM IMPACT: JavaScript Ecosystem                                        │
│  ┌─────────────────────────────────────────────────────────────────────────┐   │
│  │  • Compromised proposal could become JavaScript standard                │   │
│  │  • Browser vendors implement backdoored features                        │   │
│  │  • Node.js includes malicious functionality                             │   │
│  │  • Billions of applications affected                                    │   │
│  │  • TRUST IN WEB STANDARDS DESTROYED                                     │   │
│  └─────────────────────────────────────────────────────────────────────────┘   │
│                                                                                 │
└─────────────────────────────────────────────────────────────────────────────────┘

Severity Justification

Factor Assessment
Attack Vector Network (exploitable from forked pull requests)
Attack Complexity Low (no special conditions required)
Privileges Required None (any GitHub user can fork and submit PR)
User Interaction None (workflow triggers automatically)
Scope Unchanged (but massive downstream impact)
Confidentiality Impact High (all secrets can be stolen via workflow creation)
Integrity Impact High (TC39 specifications can be modified)
Availability Impact High (repository can be disrupted)

Severity: Critical (CVSS 9.8)

Note: While CVSS scope is "Unchanged" technically, the real-world impact on the JavaScript ecosystem is immeasurable.


Recommended Remediation

Immediate Actions

  1. Do not use pull_request_target for workflows that:

    • Check out pull request code
    • Execute build or test commands
    • Access secrets
  2. Replace with pull_request and avoid secrets for PRs from forks.

  3. Restrict GITHUB_TOKEN permissions:

    permissions:
      contents: read
      pull-requests: read
  4. If pull_request_target must be used:

    • Check out only the base branch
    • Never run PR-controlled code in privileged jobs
    • Use the two-workflow pattern with workflow_run

Secret Rotation

If any secrets exist in the repository, rotate them immediately.

Organization-Wide Audit

  • Audit all TC39 organization repositories for similar vulnerabilities
  • This pattern may exist in other proposal repositories
  • Review all workflows using pull_request_target

Disclosure Notes

  • This vulnerability was responsibly disclosed
  • Testing was performed exclusively on a personal fork
  • The attack works identically on the production repository
  • An empty PR (#76) was submitted to the original repo to confirm the vulnerability triggers
  • No specifications were modified
  • No secrets were accessed beyond proof-of-execution
  • Token exfiltration was demonstrated to webhook.site (controlled endpoint)
  • No malicious commits were pushed to the original repository

Evidence Screenshots

Evidence 1: RCE Proof of Concept & Branch Creation (Fork)

Shows successful code execution with ____RCE_Success output, poc_hello message, and branch creation (bh-poc) confirming contents:write capability.

RCE PoC

Evidence 2: Git Config Exfiltration

Shows git config with AUTHORIZATION headers exfiltrated to webhook.site, proving token capture.

Git Config Exfiltration

Evidence 3: PR on Original TC39 Repository

Shows PR #76 on the original tc39/proposal-amount repository with GITHUB_TOKEN permissions (Contents:write, Metadata:read, PullRequests:write), confirming the vulnerability triggers on production.

PR on Original Repo

Evidence 4: Branch Creation Proof (Fork)

Shows successful branch creation (bh-poc) on fork demonstrating contents:write capability.

Branch Creation

Evidence 5: Docker Credentials Exfiltration

Shows webhook.site receiving Docker credentials (index.docker.io/v1/ auth token), demonstrating full credential exfiltration capability.

Docker Credentials


Contact

Barak Haryati
https://github.com/barakharyati

Severity

Critical

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
High

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H

CVE ID

No known CVE

Weaknesses

No CWEs

Credits