Skip to content

Harden browser sync against untrusted servers and recovery kits - #7

Open
lobbystack wants to merge 2 commits into
mainfrom
browser-sync-hardening
Open

lobbystack wants to merge 2 commits into
mainfrom
browser-sync-hardening

Conversation

@lobbystack

Copy link
Copy Markdown
Owner

Summary

Browser sync now rejects hostile or malformed input from the sync server, device bundles, and recovery kits instead of trusting it.

  • Response size bound: JSON responses are read incrementally and cancelled past 64 MiB. Attachment range reads are bounded by the expected length. Invalid UTF-8 is rejected.
  • Key pagination: key pages must have at most 100 envelopes, move the cursor strictly forward, and not claim hasMore with no envelopes.
  • Device bundles: openBundle checks that the signing seed and X25519 secret actually generate the stored public keys.
  • Recovery kits: workspace, object, and device IDs must be identifiers, not paths. Recovered objects are marked unmapped, so they can decrypt operations but never own a local file or attachment. Importing a kit into a browser workspace already bound to a different remote workspace is refused.
  • Redirects: the same-origin fetch sets redirect: 'error', so the server can't bounce requests to another origin.
  • Access policy: version-2 policy objects without a document descriptor are rejected, in both signing and response parsing.
  • Native coordinator: the unsigned relay device roster no longer durably deletes local trust; a pruned view applies per pass.

Verification

Ran the package against a malicious local HTTP server and the real native recovery-kit fixture, before and after this change:

Probe Before After
Endless response body read fully cancelled at 65 MiB
Empty hasMore key page / out-of-order / repeating cursor / 101 keys accepted rejected
Bundle with mismatched signing or recipient key opened rejected
302 redirect to another origin followed, attacker hit refused, attacker not contacted
Kit import over a binding to another remote workspace overwrote the binding refused, binding unchanged
Path-like workspace or object ID in recovery binding workspace ID accepted rejected
Recovered objects no unmapped flag all unmapped: true

After merging main: browser-sync typecheck and 165 tests pass, apps/app check and 53 browser-sync tests pass, and 121 local-core sync tests pass.

Reviewer notes

  • The coordinator change was covered by unit tests only; it wasn't driven against a live relay.
  • Under Bun, fetch buffers a server stream regardless of the reader, so the size bound only limits memory in browsers. This package targets the browser.

🤖 Generated with Claude Code

Repository owner deleted a comment from chatgpt-codex-connector Bot Sep 18, 2026
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