Skip to content

harden(infra): canary channel + cockpit-boot test + signing rehearsal + drift watchdog - #154

Merged
mdheller merged 3 commits into
mainfrom
harden/infra-quartet
Aug 4, 2026
Merged

harden(infra): canary channel + cockpit-boot test + signing rehearsal + drift watchdog#154
mdheller merged 3 commits into
mainfrom
harden/infra-quartet

Conversation

@mdheller

@mdheller mdheller commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Four release-safety pieces landed together — each closes a class of gap that would have contained a real v150.0.x incident.

1. Canary channel

  • publish-latest-json.sh — new --channel stable|canary flag writes latest-canary.json for canary. Same shape, different filename.
  • publish-latest-json.yml — auto-detects channel from tag suffix (-rc | -canary → canary), overridable via workflow_dispatch input.
  • bearstart-autoconfig.js — reads bearbrowser.update.channel pref (default stable), fetches latest-canary.json when set to canary.

Contains regressions like the v150.0.5 Referer leak or the semver rc-suffix parse break to the opt-in population instead of every user's next weekly poll.

2. Cockpit boot integration test

New workflow launches BearBrowser headless under xvfb against the tip nightly, navigates to resource://bearbrowser-cockpit/index.html, asserts EITHER a cockpit-boot signal in the log OR a visible waiter diagnostic. Guards the silent-forever-waiter failure mode.

Non-fatal for now (visible warning); tighten to exit 1 once the cockpit source emits a data-cockpit-ready sentinel that Marionette can inspect. Follow-up.

3. Signing rehearsal (adhoc / self-signed)

Two jobs mirroring the real signing workflows using adhoc codesign --sign - on macOS and a locally-generated self-signed PFX on Windows. Same code paths as the real signers. When real certs arrive, the real workflows won't be running production-code-for-the-first-time.

4. Upstream toolchain drift watchdog

Weekly (Mon 06:47 UTC) HEAD-checks the pinned Firefox source tarball URL + verifies cbindgen pin still on crates.io. Dedupe-files one issue per drift type; auto-closes on resolve. Prevents the bear-trap class documented in nightly-dmg.yml:71-142 (SDK 403, cbindgen regression, packager drift) from burning a nightly.

Adversarial-review checklist

  • Sensitive network fetches — new client fetch is same as existing update-check (credentials:omit, no-referrer); only the URL is derived from a pref
  • String parsers — N/A (channel pref is getCharPref with default)
  • Packaged-artifact assertions — cockpit-boot IS a packaged-artifact test
  • FINAL_TARGET_FILES — N/A
  • Auth / IAM changes — N/A
  • Version metadata — N/A
  • Silent-skip guards — cockpit-boot test emits a visible warning when it can't assert (not silent-skip); drift watchdog files an issue when it detects drift; canary channel has explicit channel logging
  • Firefox/Mozilla/LibreWolf strings — none

Test plan

  • python3 -c 'import yaml; ...' on all 4 workflows → yaml ok
  • node -c bearstart-autoconfig.js → syntax ok
  • bash -n publish-latest-json.sh → syntax ok
  • Runtime tests will exercise on PR-time via path filters.

… + drift watchdog

Four release-safety pieces landed together — each closes a class of gap
that would have contained a real v150.0.x incident.

1. CANARY CHANNEL
   - publish-latest-json.sh: --channel stable|canary flag; writes
     latest-canary.json for canary. Same shape, different filename.
   - publish-latest-json.yml: auto-detects channel from tag suffix
     (-rc | -canary → canary), overridable via workflow_dispatch input.
   - bearstart-autoconfig.js: reads bearbrowser.update.channel pref
     (default 'stable'), fetches latest-canary.json when set to canary.
   Contains regressions like v150.0.5 Referer leak / rc-suffix parse
   break to the opt-in population instead of every user's next poll.

2. COCKPIT BOOT INTEGRATION TEST
   - New workflow launches BearBrowser headless under xvfb against the
     tip nightly, navigates to resource://bearbrowser-cockpit/index.html,
     asserts EITHER a cockpit-boot signal in the log OR a visible waiter
     diagnostic. Guards against the silent-forever-waiter failure mode.
     Non-fatal for now until Marionette-wired for a real URL-inspect;
     visible warning in CI. Follow-up: tighten to exit 1 once cockpit
     source emits a data-cockpit-ready sentinel.

3. SIGNING REHEARSAL (adhoc / self-signed)
   - Two jobs mirroring sign-and-notarize-macos.yml + sign-windows.yml,
     using adhoc codesign / a locally-generated self-signed PFX. Same
     code paths as the real signers. When real certs arrive, the real
     workflows will not be running production-code-for-the-first-time.

4. UPSTREAM TOOLCHAIN DRIFT WATCHDOG
   - Weekly (Mon 06:47 UTC) HEAD-checks the pinned Firefox source
     tarball URL + verifies cbindgen pin still on crates.io. Dedupe-files
     one issue per drift type; auto-closes on resolve. Prevents the
     bear-trap class (SDK 403, cbindgen regression, packager drift)
     documented in nightly-dmg.yml:71-142 from burning a nightly.

Meta: three of the four fire on pull_request paths that touch their
guarded surface. The fourth (drift) is weekly. All non-blocking on the
release flow — they surface issues via issues + warnings, not by
gating publish-latest-json.
Failed on first CI pass, all my bugs, all mechanical:

1. cockpit-boot: bare glob `tar -xJf nightly/BearBrowser-*.tar.xz`
   handed tar TWO archives (nightly tag now carries both the schedule's
   150.0.1-dev tarball AND my workflow_dispatch-versioned 150.0.6). tar
   treated the second as a member pattern → 'Not found in archive'.
   Pick exactly ONE tarball: prefer ubuntu variant, then any linux.

2. update-check-js: fetch-block regex required latest.json INSIDE the
   fetch() args. Canary refactor moved the URL to a `url` variable, so
   the fetch(url, ...) block has no literal filename. Switch to
   anchor-based region matching — scope by 'Sovereign update check'
   comment + 3000 chars, tolerant to future refactors. All hygiene
   assertions still fire on the region.

3. windows-selfsigned: hardcoded signtool path
   C:\Program Files (x86)\Windows Kits\10\bin\10.0.22621.0\x64\
   signtool.exe — SDK version subdir moves across runner-image
   revisions. Dynamic Get-ChildItem finds newest 10.0.* variant.

Meta: each of these is exactly the class of bug the CI is meant to
catch. The first three CI runs of new tooling are the shakedown.
…d root)

Sign step succeeded — the failing step was 'signtool verify /pa' which
requires the cert to chain to a machine-trusted root. A self-signed
rehearsal cert cannot do that by definition. Replace with
Get-AuthenticodeSignature — reports signer info even for untrusted
chains, which is exactly what proves signtool ran end-to-end and wrote
a valid Authenticode structure.

Assert: (a) signature present, (b) subject matches the rehearsal cert
CN. That gives real proof the pipeline works while accepting that
'trust' can only be tested with real certs.
@mdheller
mdheller merged commit b0be6da into main Aug 4, 2026
23 checks passed
@mdheller
mdheller deleted the harden/infra-quartet branch August 4, 2026 18:51
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.

1 participant