Skip to content

ci: verify the bundled runtime is built from source, not supplied by PRs - #5759

Merged
leaanthony merged 4 commits into
masterfrom
ci/verify-runtime-assets
Jul 6, 2026
Merged

ci: verify the bundled runtime is built from source, not supplied by PRs#5759
leaanthony merged 4 commits into
masterfrom
ci/verify-runtime-assets

Conversation

@taliesin-ai

@taliesin-ai taliesin-ai commented Jul 6, 2026

Copy link
Copy Markdown
Collaborator

Why

v3/internal/assetserver/bundledassets/runtime{,.debug}.js are go:embed'ed into every Wails v3 application. Minified JS cannot be reviewed by eye, so a PR that supplies its own bundle bytes is a straightforward malware vector — and today nothing checks that the committed bundles correspond to the source. Worse, publish-npm's post-merge rebuild only watches the runtime source paths, so a bundle-only change on master would never be overwritten.

Proof this is a real blind spot: rebuilding master's bundles from master's own source produced a diff — the committed bundles are already stale (missing DateFromTime and the mac:WebViewWebContentProcessDidTerminate event that exist in src/). That drift shipped unnoticed; a hostile diff would hide the same way.

What

  1. Pin esbuild to 0.28.1 in the runtime Taskfile (was npx esbuild@latest, which makes byte-for-byte verification impossible) and add a public runtime:build:assets task that rebuilds only the embedded bundles.
  2. New required-check candidate: Verify Runtime Assets — on every PR, if bundledassets/** or v3/internal/runtime/** changed, hermetically rebuild from the PR's source and fail unless the committed bundles match byte-for-byte. Runs under plain pull_request with a read-only token and no secrets, so building untrusted source is safe. Self-skips (green, ~20s) when no runtime paths changed, so it can be marked required without wedging unrelated PRs.
  3. Self-heal on master: publish-npm now also watches bundledassets/**, so any bundle bytes that land on master get overwritten by a trusted source-derived rebuild.
  4. One-time resync of the committed bundles with master's source (built with the pinned esbuild), fixing the stale drift above.

Contributor impact

Anyone touching runtime source keeps committing the rebuilt bundles as before — the check just proves the bytes match. On mismatch the failure message gives the exact regen commands (npm ci + task runtime:build:assets).

Follow-up for a maintainer

Mark “Bundled runtime matches source” as a required status check in branch protection — that's what turns this from a signal into a guarantee.

Note: #5462 will grow a trivial bundle conflict on its next master-merge (both sides touch the minified files); resolution is just task runtime:build:assets on the merged tree.

Type of change

  • New feature (non-breaking change which adds functionality)

How Has This Been Tested?

  • Rebuilt the bundles from a clean checkout (rm -rf node_modules + task runtime:build:assets) on macOS with Node 20 and compared against CI's Node 22 output: byte-identical, confirming the pinned esbuild makes verification deterministic across environments.

  • Rebuilt master's bundles from master's source with the pinned esbuild, which surfaced the pre-existing drift this PR fixes (committed bundles were missing DateFromTime and the mac:WebViewWebContentProcessDidTerminate event).

  • The verify workflow runs on this PR itself (it touches bundledassets/**), exercising the "relevant change → rebuild → compare" path; unrelated PRs exercise the self-skip path.

  • Windows

  • macOS

  • Linux (ubuntu-latest, via the workflow run on this PR)

Test Configuration

Not applicable: this changes CI workflows and the runtime build Taskfile, not platform runtime code, so wails doctor output is not relevant.

Checklist:

  • My code follows the general coding style of this project
  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added tests that prove my fix is effective or that my feature works (the verify workflow is itself the test; it runs on this PR)
  • New and existing unit tests pass locally with my changes

Summary by CodeRabbit

  • New Features

    • Added a pull-request check that verifies bundled runtime assets stay consistent with current changes, rebuilding and failing if mismatched.
    • Enhanced the publish workflow so bundle-only updates can trigger rebuild-and-publish automatically.
  • Bug Fixes

    • Updated the embedded runtime bundle to correct event dispatch wiring and system capability/environment detection.
  • Chores

    • Improved runtime build tooling reliability by using the local build tool, preventing parallel dependency installs, and adding an assets rebuild step.

The files in v3/internal/assetserver/bundledassets are go:embed'ed into
every Wails v3 app, and minified JS cannot be reviewed by eye — a PR
supplying its own bundle bytes is a malware vector.

- Pin esbuild to 0.28.1 in the runtime Taskfile (npx esbuild@latest made
  byte-for-byte verification impossible) and expose a public
  runtime:build:assets task that rebuilds only the embedded bundles.
- Add a Verify Runtime Assets check on every PR: when bundledassets or
  runtime source changed, hermetically rebuild from the PR's source and
  fail unless the committed bundles match byte-for-byte. Runs with a
  read-only token and no secrets, and self-skips on unrelated PRs so it
  can be made a required status check.
- Watch bundledassets in publish-npm so any bundle bytes that land on
  master are overwritten by a trusted source-derived rebuild.
- Resync the committed bundles with master's source: rebuilding revealed
  they were already stale (missing DateFromTime and the
  mac:WebViewWebContentProcessDidTerminate event present in src/) —
  exactly the drift this check is meant to catch.
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR pins the runtime esbuild version, adds a bundle rebuild task, regenerates bundledassets/runtime.js, and updates CI so publish and verification workflows react to bundled asset changes.

Changes

Runtime bundle build and verification

Layer / File(s) Summary
Pin esbuild and add build:assets
v3/internal/runtime/Taskfile.yaml, v3/internal/runtime/desktop/@wailsio/runtime/package.json
build:debug and build:production use the local esbuild binary, install-deps runs once, build:assets is added, and esbuild is added as a dev dependency.
Regenerate bundled runtime output
v3/internal/assetserver/bundledassets/runtime.js
The bundled runtime is replaced with new internal wiring for Events and System helpers while preserving the exported surface.
Update publish workflow triggers
.github/workflows/publish-npm.yml
Publish detection now includes bundled assets in push path filters and committed-source change detection.
Add runtime asset verification workflow
.github/workflows/verify-runtime-assets.yml
A new PR workflow detects relevant runtime changes, rebuilds bundled assets, and fails when the committed bundle differs from the rebuild.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant PR as Pull Request
  participant Workflow as verify-runtime-assets.yml
  participant Task as task runner
  participant Repo as Repository

  PR->>Workflow: trigger on pull request to master
  Workflow->>Repo: diff HEAD vs HEAD^1 for bundledassets/runtime paths
  alt no relevant changes
    Workflow-->>PR: set relevant=false and skip
  else relevant changes detected
    Workflow->>Task: npm ci and task runtime:build:assets
    Task->>Repo: rebuild bundledassets output
    Workflow->>Repo: compare rebuilt output vs committed bundledassets
    alt mismatch
      Workflow-->>PR: fail with remediation instructions
    else match
      Workflow-->>PR: report success
    end
  end
Loading

Possibly related PRs

  • wailsapp/wails#5280: Both PRs adjust .github/workflows/publish-npm.yml path filters that control publish-trigger behavior.
  • wailsapp/wails#5571: Both PRs touch the runtime event dispatch wiring around window._wails.dispatchWailsEvent.
  • wailsapp/wails#5647: Both PRs modify the CI publishing pipeline in .github/workflows/publish-npm.yml.

Suggested labels: reviewed ✅

Suggested reviewers: leaanthony

Poem

A bunny built with careful care,
Pinned tools and bundles everywhere.
The bytes now match, the checks stand tall,
CI watches over all.
thump thump 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise, specific, and accurately reflects the main CI/runtime asset verification change.
Description check ✅ Passed The description follows the template well and covers motivation, changes, testing, and checklist items, though the issue reference is still blank.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ci/verify-runtime-assets

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

task runs build:debug and build:production as concurrent deps; two
simultaneous 'npx esbuild@0.28.1' invocations race installing into the
npx cache on a cold runner and one dies (seen on the JS Tests job).
Pin esbuild as an exact devDependency and call the npm-ci-installed
binary: one install, no build-time network fetch, and the version now
lives in package-lock.json where npm ci enforces it.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
v3/internal/runtime/Taskfile.yaml (1)

31-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Extract the repeated esbuild binary path into a var.

The literal desktop/@wailsio/runtime/node_modules/.bin/esbuild path is duplicated across build:debug and build:production. A task-level var would reduce duplication if the path ever changes.

♻️ Suggested refactor
+vars:
+  ESBUILD: desktop/@wailsio/runtime/node_modules/.bin/esbuild
+
   build:debug:
     internal: true
     cmds:
-      - desktop/@wailsio/runtime/node_modules/.bin/esbuild desktop/@wailsio/runtime/src/index.ts ...
+      - "{{.ESBUILD}} desktop/@wailsio/runtime/src/index.ts ..."
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@v3/internal/runtime/Taskfile.yaml`:
- Around line 31-36: The build:debug and build:production tasks in Taskfile.yaml
directly invoke desktop/@wailsio/runtime/node_modules/.bin/esbuild, so they will
fail unless dependencies are installed first. Add install-deps as a dependency
on these task entries so the build:assets and build:runtime task chains can run
successfully without requiring a prior npm install.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d783fa63-af13-46e4-9fe4-1fd127fb5111

📥 Commits

Reviewing files that changed from the base of the PR and between d575aef and 65fbe20.

⛔ Files ignored due to path filters (1)
  • v3/internal/runtime/desktop/@wailsio/runtime/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • v3/internal/runtime/Taskfile.yaml
  • v3/internal/runtime/desktop/@wailsio/runtime/package.json
✅ Files skipped from review due to trivial changes (1)
  • v3/internal/runtime/desktop/@wailsio/runtime/package.json

Comment thread v3/internal/runtime/Taskfile.yaml Outdated
Review feedback: build:debug/build:production invoked the local esbuild
binary but nothing guaranteed node_modules existed, so a bare
'task runtime:build:assets' failed without a prior npm install. Both
tasks now dep on install-deps, which gets 'run: once' so the two
parallel builds don't race concurrent npm installs. The duplicated
binary path moves into an ESBUILD var.
@leaanthony
leaanthony enabled auto-merge (squash) July 6, 2026 05:15
@leaanthony
leaanthony merged commit 198c9ce into master Jul 6, 2026
78 checks passed
@leaanthony
leaanthony deleted the ci/verify-runtime-assets branch July 6, 2026 05:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants