Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,73 @@ jobs:
# We want to ensure that static exports for all locales do not occur on `pull_request` events
# TODO: The output of this is too large, and it crashes the GitHub Runner
NEXT_PUBLIC_STATIC_EXPORT_LOCALE: false # ${{ github.event_name == 'push' }}

compare-bundle-size:
name: Compare Bundle Size
runs-on: ubuntu-latest
needs: build
if: github.event_name == 'pull_request'

steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

- name: Git Checkout
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false

- name: Download Stats (HEAD)
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: webpack-stats
path: head-stats

- name: Get Run ID from BASE
id: base-run
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
GH_TOKEN: ${{ github.token }}
run: |
ID=$(gh run list -c "$BASE_SHA" -w build.yml -s success -L 1 --json databaseId --jq ".[].databaseId")
echo "run_id=$ID" >> "$GITHUB_OUTPUT"

- name: Download Stats (BASE)
id: base-stats
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: webpack-stats
path: base-stats
run-id: ${{ steps.base-run.outputs.run_id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
Comment thread
avivkeller marked this conversation as resolved.

- name: Compare Bundle Size
id: compare-bundle-size
if: steps.base-stats.outcome == 'success'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
env:
HEAD_STATS_PATH: ./head-stats/webpack-stats.json
BASE_STATS_PATH: ./base-stats/webpack-stats.json
with:
script: |
const { compare } = await import('${{github.workspace}}/apps/site/scripts/compare-size/index.mjs')
await compare({core})

- name: Prepare Comment
if: steps.base-stats.outcome == 'success'
env:
COMMENT: ${{ steps.compare-bundle-size.outputs.comment }}
run: |
mkdir -p pr-comment
printf '%s' "$COMMENT" > pr-comment/comment.md
printf '%s' 'compare_bundle_size' > pr-comment/tag.txt

- name: Upload Comment
if: steps.base-stats.outcome == 'success'
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: pr-comment
path: pr-comment/

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Empty comment after compare failure

Medium Severity

Prepare Comment and Upload Comment only require base-stats to succeed, not the Compare Bundle Size step. If comparison fails after base artifacts download, those steps can still run with an empty comment output, upload a pr-comment artifact, and the Leave Comment workflow may replace the PR’s bundle-size comment with blank content.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 0fa0f8d. Configure here.

77 changes: 0 additions & 77 deletions .github/workflows/bundle-compare.yml

This file was deleted.

9 changes: 3 additions & 6 deletions .github/workflows/chromatic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,12 @@ on:
paths:
- packages/ui-components/**
- .github/workflows/chromatic.yml
pull_request_target:
pull_request:
branches:
- main
paths:
- packages/ui-components/**
- .github/workflows/chromatic.yml
types:
- labeled
workflow_dispatch:

defaults:
Expand All @@ -42,9 +40,8 @@ jobs:
# We only need to run Storybook Builds and Storybook Visual Regression Tests within Pull Requests that actually
# introduce changes to the Storybook. Hence, we skip running these on Crowdin PRs and Dependabot PRs
if: |
github.event_name != 'pull_request_target' ||
github.event_name != 'pull_request' ||
(
github.event.label.name == 'github_actions:pull-request' &&
github.actor != 'dependabot[bot]' &&
github.event.pull_request.head.ref != 'chore/crowdin'
Comment thread
avivkeller marked this conversation as resolved.
)
Expand All @@ -70,6 +67,6 @@ jobs:
with:
workingDir: packages/ui-components
buildScriptName: storybook:build
projectToken: ${{ secrets.CHROMATIC_PROJECT_TOKEN }}
projectToken: ${{ vars.CHROMATIC_PROJECT_TOKEN }}
Comment thread
avivkeller marked this conversation as resolved.
exitOnceUploaded: true
onlyChanged: true
89 changes: 89 additions & 0 deletions .github/workflows/leave-comment.yml

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should probably determine the PR ourselves from the workflow_run data, otherwise this would allow an attacker to post a comment on any PR.

@avivkeller avivkeller Jul 1, 2026

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While workflow_run does send PR data, I found that it's not 100% reliable, as often (for some reason) is omitted. That being said, we can also,

Rely on it anyway,
Compare the SHA of the HEAD with open PRs

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

WDYT?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what we do for js.org: https://github.com/js-org/js.org/blob/30b6a762eff9c2bf8a2fde8c05f0f394c0f44ae0/.github/workflows/rename_comment.yml#L30-L39

I suppose including a PR number in the payload, and then doing that same head check, is also safe.

Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: Leave Comment

on:
workflow_run:
# Any Workflow that uploads a `pr-comment` artifact should be listed here
workflows: ['Build', 'Lighthouse']
types: [completed]

Check failure

Code scanning / zizmor

use of fundamentally insecure workflow trigger: workflow_run is almost always used insecurely Error

use of fundamentally insecure workflow trigger: workflow_run is almost always used insecurely
Comment on lines +3 to +7

permissions:
contents: read
actions: read

concurrency:
group: ${{ github.workflow }}-${{ github.event.workflow_run.id }}
cancel-in-progress: true

jobs:
leave-comment:
name: Leave Comment
runs-on: ubuntu-latest
permissions:
pull-requests: write
Comment on lines +18 to +22

steps:
- name: Harden Runner
uses: step-security/harden-runner@9af89fc71515a100421586dfdb3dc9c984fbf411 # v2.19.4
with:
egress-policy: audit

- name: Download Comment Artifact
# The Workflow may not have produced a comment (e.g. the comparison was skipped), so this is
# allowed to fail and every subsequent step is gated on it having succeeded.
id: download
continue-on-error: true
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: pr-comment
path: pr-comment
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}

- name: Resolve Pull Request Number
id: pr
if: steps.download.outcome == 'success'
uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0
with:
script: |
const run = context.payload.workflow_run;

// 1. For same-repo Pull Requests the run is already linked to its PR(s).
if (run.pull_requests && run.pull_requests.length) {
core.setOutput('number', run.pull_requests[0].number);
return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR linked without head check

Medium Severity

When Leave Comment resolves the pull request from workflow_run.pull_requests, it uses the first entry’s number and returns without checking that pull request’s head commit matches workflow_run.head_sha. The fork fallback path does perform that check, so inconsistent or stale PR links can post bundle or Lighthouse comments on the wrong pull request.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a7c4907. Configure here.

}

// 2. For forks that list is empty, so find the open Pull Request whose HEAD SHA matches
// the commit that triggered the run.
const pulls = await github.paginate(github.rest.pulls.list, {
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
per_page: 100,
});

const match = pulls.find(pull => pull.head.sha === run.head_sha);

if (!match) {
core.info(`No open pull request found for HEAD ${run.head_sha}`);
return;
}

core.setOutput('number', match.number);

- name: Read Comment Tag
id: meta
if: steps.download.outcome == 'success'
run: |
tag="$(tr -cd 'A-Za-z0-9_-' < pr-comment/tag.txt)"
echo "tag=$tag" >> "$GITHUB_OUTPUT"

- name: Add Comment to PR
# The comment body is untrusted markdown, so it is passed as a file (data) rather than
# interpolated into an expression or shell command.
if: steps.download.outcome == 'success' && steps.pr.outputs.number != ''
uses: thollander/actions-comment-pull-request@e2c37e53a7d2227b61585343765f73a9ca57eda9 # v3.0.0
with:
file-path: pr-comment/comment.md
comment-tag: ${{ steps.meta.outputs.tag }}
pr-number: ${{ steps.pr.outputs.number }}
40 changes: 17 additions & 23 deletions .github/workflows/lighthouse.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Security Notes
# This workflow uses `pull_request_target`, so will run against all PRs automatically (without approval), be careful with allowing any user-provided code to be run here
# This Workflow runs in the untrusted `pull_request` context and therefore must not rely on any
# repository secrets. It does not comment on the Pull Request itself; instead it uploads a
# `pr-comment` artifact which the trusted `Leave Comment` Workflow posts once this Workflow completes.
Comment thread
avivkeller marked this conversation as resolved.
# Only selected Actions are allowed within this repository. Please refer to (https://github.com/nodejs/nodejs.org/settings/actions)
# for the full list of available actions. If you want to add a new one, please reach out a maintainer with Admin permissions.
# REVIEWERS, please always double-check security practices before merging a PR that contains Workflow changes!!
Expand All @@ -9,7 +11,7 @@
name: Lighthouse

on:
pull_request_target:
pull_request:
branches:
- main
types:
Expand All @@ -36,9 +38,6 @@ jobs:
github.event.label.name == 'github_actions:pull-request'
name: Lighthouse Report
runs-on: ubuntu-latest
permissions:
# Required by `thollander/actions-comment-pull-request`
pull-requests: write

steps:
- name: Harden Runner
Expand All @@ -55,21 +54,11 @@ jobs:
check_interval: 10 # check every 10 seconds

- name: Git Checkout
# Only needed for the Lighthouse formatting script; no credentials are persisted.
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Provides the Pull Request commit SHA or the GitHub merge group ref
ref: ${{ github.event_name == 'pull_request_target' && github.event.pull_request.head.sha || github.ref }}
persist-credentials: false

- name: Add Comment to PR
# Signal that a lighthouse run is about to start
uses: thollander/actions-comment-pull-request@e2c37e53a7d2227b61585343765f73a9ca57eda9 # v3.0.0
with:
message: |
Running Lighthouse audit...
# Used later to edit the existing comment
comment-tag: 'lighthouse_audit'

- name: Audit Preview URL with Lighthouse
# Conduct the lighthouse audit
id: lighthouse_audit
Expand Down Expand Up @@ -105,11 +94,16 @@ jobs:
const { formatLighthouseResults } = await import('${{github.workspace}}/apps/site/scripts/lighthouse/index.mjs')
await formatLighthouseResults({core})

- name: Add Comment to PR
# Replace the previous message with our formatted lighthouse results
uses: thollander/actions-comment-pull-request@e2c37e53a7d2227b61585343765f73a9ca57eda9 # v3.0.0
- name: Prepare Comment
env:
COMMENT: ${{ steps.format_lighthouse_score.outputs.comment }}
run: |
mkdir -p pr-comment
printf '%s' "$COMMENT" > pr-comment/comment.md
printf '%s' 'lighthouse_audit' > pr-comment/tag.txt

- name: Upload Comment
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
# Reference the previously created comment
comment-tag: 'lighthouse_audit'
message: |
${{ steps.format_lighthouse_score.outputs.comment }}
name: pr-comment
path: pr-comment/
Loading