Skip to content

fix(design): verify the whiteboard bundle against SHA256SUMS before unpacking - #204

Merged
filipeforattini merged 1 commit into
mainfrom
whiteboard-checksum
Sep 11, 2026
Merged

fix(design): verify the whiteboard bundle against SHA256SUMS before unpacking#204
filipeforattini merged 1 commit into
mainfrom
whiteboard-checksum

Conversation

@filipeforattini

@filipeforattini filipeforattini commented Sep 11, 2026

Copy link
Copy Markdown

Problem

packages/core/src/design/whiteboard.ts fetched the whiteboard release tarball from GitHub Releases and piped it straight into tar -xzf: no checksum verification, no fetch timeout or abort signal, and a non-zero tar exit surfaced as a bare Error. The release build already generates and uploads SHA256SUMS alongside the tarball (packages/redcode/script/build.ts), but the loader never read it.

Fix

  • Download SHA256SUMS from the same release, find the <sha256> redcode-whiteboard-<version>.tar.gz line, hash the tarball with Bun.CryptoHasher, and refuse to unpack on a missing entry or a mismatch.
  • Both downloads run under a 60 s AbortSignal.timeout; HTTP errors, network errors and timeouts all become Design.Error unavailable (still 409) with a message naming the asset and the reason.
  • tar exit code and stderr become a Design.Error; an archive that unpacks without whiteboard.js is rejected too. Extraction still happens in a temp directory that is only renamed into place after every check, so a partial install is never seen as cached and the next call retries.
  • The release source (fetch, version, data, checkout, timeout) is a module-level Source with a configure() entry point so tests can serve a release from memory. Build script untouched.

Test

packages/core/test/design-whiteboard.test.ts gains six tests that serve a local tarball + SHA256SUMS through the injected fetch:

  • verified tarball unpacks, renders, and is served from cache on the second call
  • mismatched digest fails with unavailable, caches nothing, and the next call re-downloads and succeeds
  • tarball missing from SHA256SUMS fails before the tarball is requested
  • SHA256SUMS 404 fails with unavailable
  • network error and timed-out download both fail with unavailable
  • listed archive that tar cannot unpack fails cleanly with the exit code, nothing cached

Before the fix: 0 pass / 8 fail (the original loader has no injection point, configure is not a function). After: 8 pass / 0 fail (PLAYWRIGHT_BROWSERS_PATH=~/.cache/ms-playwright mise x -- bun test --timeout 30000 test/design-whiteboard.test.ts). bun run typecheck clean in packages/core and packages/redcode.

https://claude.ai/code/session_01KAcoL6wpgVEs2ebUrUGpHS


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…npacking

The whiteboard tarball was fetched from GitHub Releases and piped straight
into tar: no digest check, no timeout, no abort signal, and a failing tar
surfaced as a bare Error. The release build already publishes SHA256SUMS
next to the tarball, so the loader now downloads that first, finds the line
for redcode-whiteboard-<version>.tar.gz, hashes the archive with
Bun.CryptoHasher, and refuses to unpack on a missing entry or a mismatch.
Both downloads carry a 60 s AbortSignal.timeout, tar's exit code and stderr
become a Design.Error, and an archive without whiteboard.js is rejected
before the temp directory is renamed into place. Every failure keeps the
existing `unavailable` semantics (409) and leaves the release directory
absent so the next call retries.

The release source (fetch, version, data dir, checkout dir, timeout) is a
module-level Source with a configure() entry point, so the test suite serves
a tarball and SHA256SUMS from memory and covers: a verified unpack that is
then served from cache, a mismatched digest that caches nothing and retries,
a tarball missing from SHA256SUMS, a 404 on SHA256SUMS, a network error and
a timed-out download, and a listed archive that tar cannot unpack.

Claude-Session: https://claude.ai/code/session_01KAcoL6wpgVEs2ebUrUGpHS
@filipeforattini
filipeforattini merged commit 10c6faa into main Sep 11, 2026
12 of 15 checks passed
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