Bug: setup tab's zxcvbn meter silently stayed at score=-1 because the
router's isSetup exception only allowed save_setup, so rate_passphrase
got unauthorized_sender. Result: the "create vault" button stayed
disabled forever even with a strong passphrase.
Fix: add a narrow SETUP_ALLOWED set containing save_setup,
rate_passphrase, and is_unlocked (step-4 extension detection). Reject
everything else from the setup tab. Also clean up setup.ts's unlock
call — it was passing the raw 32-byte imageSecret where JPEG bytes with
embedded secret are required; the Rust-side unlock calls imgsecret::
extract internally.
Diagnostic logging across the message path so the next silent failure
speaks up:
- [relicario setup] staged logs through vault-init; console.error
with the failure stage name in the UI banner.
- [relicario setup] rate_passphrase lastError / rejected / threw
branches each log their own warning.
- [relicario router] console.warn on unauthorized_sender (with sender
classification) and unknown_message_type.
- [relicario sw] first-message wasm init announced; per-message
non-ok result logged; thrown errors console.error'd.
Tests: +3 setup-allowlist tests (rate_passphrase accepted, is_unlocked
accepted, fill_credentials + unlock rejected). 55/55 green.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
71 lines
2.4 KiB
TypeScript
71 lines
2.4 KiB
TypeScript
/// Thin service-worker entry: loads WASM, constructs the router state, and
|
|
/// forwards every message into router/index.route().
|
|
|
|
import type { Request, Response } from '../shared/messages';
|
|
import type { RouterState } from './router/index';
|
|
import { route } from './router/index';
|
|
import * as vault from './vault';
|
|
|
|
// @ts-ignore TS2307 — resolved by webpack alias / copy
|
|
import initDefault, { initSync } from '../../wasm/relicario_wasm.js';
|
|
// @ts-ignore TS2307
|
|
import * as wasmBindings from '../../wasm/relicario_wasm.js';
|
|
|
|
type WasmModule = typeof wasmBindings;
|
|
let wasm: WasmModule | null = null;
|
|
|
|
async function initWasm(): Promise<WasmModule> {
|
|
if (wasm) return wasm;
|
|
|
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
const SWGlobalScope = (globalThis as any).ServiceWorkerGlobalScope as (new () => ServiceWorker) | undefined;
|
|
const isServiceWorker = typeof SWGlobalScope !== 'undefined'
|
|
&& self instanceof (SWGlobalScope as unknown as typeof EventTarget);
|
|
|
|
if (isServiceWorker) {
|
|
const wasmResponse = await fetch(chrome.runtime.getURL('relicario_wasm_bg.wasm'));
|
|
const wasmBytes = await wasmResponse.arrayBuffer();
|
|
initSync({ module: new WebAssembly.Module(wasmBytes) });
|
|
} else {
|
|
const wasmUrl = chrome.runtime.getURL('relicario_wasm_bg.wasm');
|
|
await initDefault(wasmUrl);
|
|
}
|
|
|
|
vault.setWasm(wasmBindings);
|
|
wasm = wasmBindings;
|
|
return wasm;
|
|
}
|
|
|
|
// Single router-state object shared by all messages for this SW instance.
|
|
const state: RouterState = {
|
|
manifest: null,
|
|
gitHost: null,
|
|
wasm: null,
|
|
};
|
|
|
|
chrome.runtime.onMessage.addListener(
|
|
(request: Request, sender: chrome.runtime.MessageSender, sendResponse: (r: Response) => void) => {
|
|
(async () => {
|
|
if (!state.wasm) {
|
|
// eslint-disable-next-line no-console
|
|
console.log('[relicario sw] initializing WASM on first message');
|
|
state.wasm = await initWasm();
|
|
}
|
|
return route(request, state, sender);
|
|
})()
|
|
.then((r) => {
|
|
if (!r.ok) {
|
|
// eslint-disable-next-line no-console
|
|
console.warn(`[relicario sw] ${request.type} -> error:`, r.error);
|
|
}
|
|
sendResponse(r);
|
|
})
|
|
.catch((err: Error) => {
|
|
// eslint-disable-next-line no-console
|
|
console.error(`[relicario sw] ${request.type} threw:`, err);
|
|
sendResponse({ ok: false, error: err.message });
|
|
});
|
|
return true; // async response
|
|
},
|
|
);
|