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>
10 KiB
Plan 1C-α — Manual Test Matrix
Walkthrough for validating the extension on both Chrome and Firefox after the six-slice implementation.
Branch: feature/typed-items-1c-alpha @ 3238ef4 (tag candidate: plan-1c-alpha-complete)
Worktree: /home/alee/Sources/relicario/.worktrees/typed-items-1c-alpha
Pre-flight
-
P1. Bundles built:
cd /home/alee/Sources/relicario/.worktrees/typed-items-1c-alpha/extension bun run build:allExpected: "compiled with 2 warnings" (WASM size only) for each bundle.
dist/anddist-firefox/populated. -
P2. Fresh-profile browsers ready (or existing profile's
chrome.storage.localfor this extension cleared). StalevaultConfig/imageBase64from the pre-renameidfotoera must not persist. -
P3. Test git repo for the vault is reachable (SSH key / HTTPS PAT working). Use a throwaway repo to avoid polluting your real vault history.
-
P4. Reference image ready (any JPEG; DCT-steg secret is embedded at init time).
Loading
Chrome
- L1.
chrome://extensions→ Developer mode ON → "Load unpacked" → selectextension/dist/. - L2. Toolbar icon visible (pin if needed).
- L3. Click icon → first open triggers setup tab (not a popup-embedded wizard).
Firefox
- L4.
about:debugging#/runtime/this-firefox→ "Load Temporary Add-on…" → selectextension/dist-firefox/manifest.json. - L5. Toolbar icon visible.
- L6. Click icon → setup tab opens.
11-step core matrix — Chrome
Notes column: write what you saw. Check box only when matching expected.
1. Setup tab opens from popup (audit C1)
- Do: Fresh install, click toolbar icon.
- Expected:
setup.htmlopens in a new tab; popup closes immediately; WAR is empty so this MUST work via extension-origin tab, not WAR. - Notes: ___
2. zxcvbn gate in setup (audit H3)
- Do: Type weak passphrase like
password. - Expected: Submit disabled, bar shows red/orange segments, feedback "Too weak…".
- Do: Type stronger phrase until bar fills.
- Expected: At score ≥ 3, submit enables, feedback "Strong enough."
- Notes: ___
3. Setup completes → unlock → list renders
- Do: Upload reference JPEG, fill vault config (git host/URL/repo/token), submit. Then open popup, enter passphrase, unlock.
- Expected: Manifest decrypts client-side. Empty list view appears with toolbar (search, + New, sync, lock, ⚙).
- Notes: ___
4. Add Login with TOTP (typed-item wire format)
- Do: "+ New" → Login form. Fill:
- title:
GitHub - url:
https://github.com - username: your handle
- password: click "gen" (uses
DEFAULT_PASSWORD_REQUEST— 20 chars, safe symbols) - totp:
JBSWY3DPEHPK3PXP(well-known base32 test vector) - Save.
- title:
- Expected: Row appears with 🔑 icon + title + favorite star position.
- Expected (CLI cross-check, optional): From main worktree:
Should show the same item. TOTP secret should decode identically.
relicario list relicario get "GitHub" --show - Notes: ___
5. TOFU origin-ack prompt (audit C4 first half)
- Do: Navigate to
https://github.com/login. Click the blueidicon next to the password field. - Expected: Closed Shadow DOM hint appears ("First autofill on github.com / Open relicario to confirm"). In DevTools, verify
document.querySelector('[data-rel]')finds the host but.shadowRootisnull(closed mode). - Expected: No credentials fill on this click.
- Notes: ___
6. Confirm origin + autofill fills correctly
- Do: Open popup (on the github.com tab). Look for a pending-ack prompt OR (α behavior) just confirm manually: any
get_credentialscall after the hostname is acked inVaultSettings.autofill_origin_ackswill return credentials.- Simplest α path: click the item in the popup list, click "autofill" button. This uses the popup-captured tab state path (audit M5).
- Expected: Username + password fields fill. On React/Vue sites, the native-setter trick fires input+change events.
- Notes: ___
7. Multiple candidates → picker
- Do: Add a second Login for github.com with a different username. Back on
github.com/login, click theidicon. - Expected: Picker shows both titles. Click one → fills that set.
- Notes: ___
8. Capture prompt → capture_save_login flow (Slice 5 critical-fix)
- Do: Go to a site not in your vault. Fill signup form (or real trial). Submit.
- Expected: Capture prompt appears inside closed Shadow DOM. No stable element IDs — running
document.querySelector('#relicario-save-btn')in the page console returnsnull. - Do: Click "Save" in the prompt.
- Expected: ✓ Saved confirmation; prompt dismisses. Open popup → item present in list with the new hostname as title.
- CRITICAL: If "Save" silently fails, the
capture_save_logincontent-callable handler is broken — file a bug before proceeding. - Notes: ___
9. Edit Login → password rotates; field history captured
- Do: Select the GitHub item → edit → change password → save.
- Expected: Detail view shows new password on reveal. List's "modified" time updates.
- Expected (CLI cross-check):
relicario get "GitHub" --show # confirm field_history now has entry for the old password - Notes: ___
10. Delete Login → soft-delete
- Do: Select an item → "trash" → confirm.
- Expected: Row disappears from list immediately. Popup list filters
trashed_at !== undefined. - Expected (CLI cross-check):
relicario list --trashedshows the item. - Notes: ___
11. Lock → re-unlock
- Do: Click "lock" in the toolbar. Try to open the popup again.
- Expected: Unlock screen. Session handle was cleared in WASM (not just JS).
- Do: Re-unlock.
- Expected: Same list (including the item from step 10 still in trash, invisible).
- Notes: ___
11-step core matrix — Firefox
Re-run 1–11 on Firefox. Critical Firefox-only check: the background script runs as a persistent script (not MV3 service worker); WASM loads via initDefault(wasmUrl) not initSync. Anything broken here that works in Chrome indicates WASM-loading drift.
- FF1–FF11. Re-run the 11 steps above. Summarize anomalies:
- Notes: ___
Security probes (bonus)
Open DevTools on any page (not extension origin) and try to defeat the router:
SP1. Content-script-originated popup-only message
- Do: In a page console (not popup DevTools):
chrome.runtime.sendMessage({ type: 'unlock', passphrase: 'guess' }, console.log) - Expected:
{ ok: false, error: 'unauthorized_sender' }(audit C2). - Notes: ___
SP2. Cross-origin get_credentials attempt
- Do: Pick an item id from the popup (e.g., via popup DevTools:
copy(currentState.selectedId)). Go to a different-origin page's console:chrome.runtime.sendMessage({ type: 'get_credentials', id: '<the-id>' }, console.log) - Expected:
{ ok: false, error: 'origin_mismatch' }(audit C4). No item data leaks. - Notes: ___
SP3. Closed Shadow DOM verification
- Do: Trigger the capture prompt (step 8). In the page console:
const hosts = document.querySelectorAll('[data-rel]'); for (const h of hosts) console.log(h, h.shadowRoot); // shadowRoot should be null console.log(document.querySelector('#relicario-save-btn')); // should be null console.log(document.querySelector('.relicario-capture')); // should be null - Expected: All
shadowRootvalues arenull; no stable selectors match (audit C3). - Notes: ___
SP4. Captured-tab navigation during fill (audit M5)
- Do: Open popup on
https://github.com/login. Select a github item, click "autofill", but BEFORE the fill lands, rapidly navigate the github tab tohttps://example.com. - Expected: No credentials typed on example.com. SW rejects with
tab_navigated; if somehow the message reaches the content script,fill.tsre-checksexpectedHostand rejects withorigin_changed. - Notes: ___ (this one's hard to time; skip if not easily reproducible)
SP5. WAR probe
- Do: In a page console on any site:
fetch('chrome-extension://<your-extension-id>/setup.html').catch(e => console.log('blocked:', e)) - Expected: Blocked (either CORS error or net::ERR). WAR is empty, so no resource is web-accessible.
<all_urls>pages cannot reach setup.html. - Notes: ___
Final acceptance
- A1.
cargo test --workspacegreen (should still be 151+ Rust tests). - A2.
cd extension && bun run testgreen (should be 52 passing — 11 base32 + 41 router). - A3.
cd extension && bun run buildgreen (Chrome bundle). - A4.
cd extension && bun run build:firefoxgreen (Firefox bundle). - A5. Lint greps clean:
git grep -n 'innerHTML\|insertAdjacentHTML' extension/src/content/ # zero hits git grep -n 'idfoto' extension/ # zero hits git grep -n '@ts-nocheck' extension/src/ # zero hits - A6. WAR empty:
grep -A2 web_accessible_resources extension/manifest.json # [] grep -A2 web_accessible_resources extension/manifest.firefox.json # []
Sign-off
- All 11 core-matrix steps pass on Chrome
- All 11 core-matrix steps pass on Firefox
- All 5 security probes pass (or SP4 skipped, others pass)
- All 6 final acceptance checks pass
- Ready to tag
plan-1c-alpha-completeand decide on merge path
Findings / issues
Use this space to log anything weird:
(fill in as you go)
Decision
- Merge straight to
main - Open a PR first for review
- Need rework on: ___
Generated 2026-04-20 — source: spec 2026-04-20-relicario-extension-1c-alpha-design.md §5.4, plan 2026-04-20-relicario-extension-1c-alpha.md Task 27.