fix(extension): binary-safe message transport + carrier-image guard
chrome.runtime.sendMessage serializes with a JSON-like algorithm that drops ArrayBuffer/TypedArray payloads — they arrive as empty objects, so a carrier JPEG reached the service worker as 0 bytes and failed with the opaque WASM error "no SOF marker found in JPEG". This broke extension vault creation. - shared/message-binary.ts: encodeBinary/decodeBinary deep-walk a message tree and base64-envelope every binary buffer so it survives the channel (chunked base64 to avoid stack overflow on multi-MB buffers); plain number[] untouched. - Wire encode/decode at all four message boundaries: popup, service-worker onMessage listener (decode request, encode response), setup swSend, vault postToServiceWorker. - handleCreateVault: magic-byte guard classifies empty / non-JPEG carriers into actionable errors (carrier_image_empty / carrier_image_not_jpeg) instead of the opaque WASM error; error-copy.ts adds user-facing text; setup errors now route through lookupErrorCopy. - router SETUP_ALLOWED: add create_vault / attach_vault / generate_recovery_qr — the Phase-3 setup wizard sends these from the setup page and they were being rejected as unauthorized_sender. - Tests: message-binary round-trip suite (7) + carrier-guard (2) + setup allowlist (3). 435/435 vitest, build:all clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Pe8qw5KePDqAEBsAxnVQuJ
This commit is contained in:
@@ -16,14 +16,21 @@ export interface RouterState {
|
||||
wasm: any;
|
||||
}
|
||||
|
||||
/// Popup-only messages the setup tab is also allowed to send.
|
||||
/// Popup-only messages the setup tab is also allowed to send. The setup page
|
||||
/// (setup.html) is an extension-internal page — same trust level as the popup —
|
||||
/// so this set mirrors exactly what the wizard sends, no more.
|
||||
/// - save_setup: wires vault config + image into chrome.storage.local at end of init.
|
||||
/// - rate_passphrase: drives the zxcvbn strength meter during passphrase entry.
|
||||
/// - is_unlocked: setup step-4 pings the extension to detect "save config to extension" availability.
|
||||
/// - create_vault / attach_vault: the "name this device" step performs the full vault create/attach.
|
||||
/// - generate_recovery_qr: the done-step recovery-QR banner generates a QR before the user leaves.
|
||||
const SETUP_ALLOWED: ReadonlySet<PopupMessage['type']> = new Set<PopupMessage['type']>([
|
||||
'save_setup',
|
||||
'rate_passphrase',
|
||||
'is_unlocked',
|
||||
'create_vault',
|
||||
'attach_vault',
|
||||
'generate_recovery_qr',
|
||||
]);
|
||||
|
||||
export async function route(
|
||||
|
||||
Reference in New Issue
Block a user