Skip to content

fix(cli): require patched Chrome 148+ and rebuild Docker image on Dockerfile change - #6

Open
teamkareem wants to merge 2 commits into
mainfrom
cursor/cli-chrome-min-version-bc39
Open

teamkareem wants to merge 2 commits into
mainfrom
cursor/cli-chrome-min-version-bc39

Conversation

@teamkareem

Copy link
Copy Markdown
Owner

What

Follow-up to #5 (CVE-2026-111497 / GHSA-9v8h-qqgm-4m35, Brotli decoder out-of-bounds write). This PR fixes two leftovers from that PR.

  1. The CLI's fallback Chrome download was vulnerable. In packages/cli/src/browser/manager.ts, the download version moves from 131.0.6778.85 to 148.0.7778.167, the same build Dockerfile.test pins. I also added a minimum-version floor (MIN_CHROME_VERSION = 148.0.0.0) so older cached or system Chrome builds are no longer used.
  2. render --docker never rebuilt its image. In packages/cli/src/commands/render.ts, the image tag is now hyperframes-renderer:<version>-<sha256(Dockerfile)[0:12]>. A changed Dockerfile now gets a new tag and a fresh build, in both release and dev (latest) mode.

Why

Chromium releases before 148 bundle a Brotli revision without the upstream fix (0ed4268a). From 148.0.7778.0 onward, Chromium bundles Brotli cfa184a, which contains it. Before this PR, the CLI downloaded Chrome 131, and findBrowser() accepted any cached headless-shell or system Chrome regardless of version. The Docker image was cached by CLI version only, so changes to Dockerfile.render (such as #5's Node pin) never reached users who had already built the image.

How

Version floor in findBrowser() / ensureBrowser()

Source Behavior for builds older than 148
HyperFrames cache (~/.cache/hyperframes/chrome) Skipped, with a one-time warning naming the CVE. The newest patched build is picked. If none is cached, ensureBrowser() downloads 148.0.7778.167 into the same cache.
Puppeteer cache (~/.cache/puppeteer/chrome-headless-shell) Skipped, with a warning. Directory names that don't parse as a version are skipped silently.
System Chrome or Chromium The version is read with <binary> --version (memoized, 5s timeout). Old versions, or binaries whose version can't be read, are rejected and fall through to the download. On Windows, --version is not run because it opens a browser window, so system Chrome there is treated as unknown and rejected.
HYPERFRAMES_BROWSER_PATH Still used, because it's an explicit override. The CLI warns if it's detectably older than 148.
Linux ARM64 (no headless-shell build) Old system Chromium is rejected. The existing apt auto-install runs. If that still doesn't yield 148+, the error message now states the minimum version and the reason.

The published engines range is unchanged.

Render-output / golden-baseline impact

  • Producer golden baselines: no impact. packages/producer/tests/*/output/output.mp4 are rendered inside Dockerfile.test, which installs chrome-headless-shell 148.0.7778.167 directly and doesn't use this CLI code path. The CLI fallback now matches that build.
  • Local CLI renders can change pixels for users who were on the old fallback. Anyone using the cached 131 download, or a system Chrome older than 148, will get a one-time download (~100 MB) of 148.0.7778.167 on the next render. Small text and antialiasing differences are expected, the same kind of drift Dockerfile.test documents for Chrome bumps. Users whose system Chrome was older than 148 also switch from screenshot capture to BeginFrame capture on Linux, because they now get headless-shell.
  • Users already on Chrome 148+ see no change.
  • render --docker: the first run after upgrading builds a new image (several minutes) under the hash-suffixed tag. The rendered output depends on what's in Dockerfile.render, not on the tag. Old hyperframes-renderer:<version> images stay on disk until the user removes them.

Files changed

  • packages/cli/src/browser/manager.ts: download version bumped, minimum-version floor and version detection added, clearer error on ARM64.
  • packages/cli/src/browser/manager.test.ts: node:child_process is now mocked, fixtures moved to 148, and 7 new tests cover the floor (cache, puppeteer cache, system Chrome, unknown version, Debian version string, env override, re-download).
  • packages/cli/src/commands/render.ts: dockerImageTag(version, dockerfile) hashes the Dockerfile, and the Dockerfile is read once and reused for the build context.
  • packages/cli/src/commands/render.test.ts: 2 tests for the tag.
  • docs/packages/cli.mdx: notes the minimum Chrome version under browser.

Leftovers

  1. The engine's own lookup has no floor. packages/engine/src/services/browserManager.ts resolveHeadlessShellPath() still scans the puppeteer cache, sorts names lexicographically, and applies no minimum version. CLI renders aren't affected because the CLI passes its vetted path through PRODUCER_HEADLESS_SHELL_PATH. Direct library users of @hyperframes/engine or @hyperframes/producer can still pick up a stale build.
  2. Studio has the same gap. packages/studio/vite.adapter.ts sets PRODUCER_HEADLESS_SHELL_PATH from its own system-Chrome lookup without a version check.
  3. Old images and downloads aren't cleaned up. Stale 131 downloads stay in ~/.cache/hyperframes/chrome, and old hyperframes-renderer:* images aren't removed automatically. They're never selected again; users can delete them with hyperframes browser clear and docker image rm.
  4. Dev-mode Docker images still pin the npm release. The hyperframes-renderer:latest-<hash> image still pins whatever hyperframes@latest was on npm when it was built, until the Dockerfile changes.

Test plan

  • vitest run src/browser/manager.test.ts: 16/16 pass.
  • The dockerImageTag tests in render.test.ts pass (2/2).
  • oxlint and oxfmt --check are clean on the changed files.
  • Strict tsc --noEmit passes on manager.ts and manager.test.ts.
  • Not run: the full bun run build and bun run test. Installing Bun was blocked in the agent environment, so tests ran through an isolated vitest install. For render.test.ts, @hyperframes/* imports were stubbed, so the existing renderLocal tests in that file (which need the real @hyperframes/core) could not run locally. CI covers them.
Open in Web Open in Cursor 

cursoragent and others added 2 commits October 26, 2026 07:17
Co-authored-by: Kareem <teamkareem@users.noreply.github.com>
Co-authored-by: Kareem <teamkareem@users.noreply.github.com>
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