fix(ext): allow rate_passphrase + is_unlocked from setup tab; add diagnostic logging
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>
This commit is contained in:
244
docs/superpowers/test-runs/2026-04-20-1c-alpha-manual-matrix.md
Normal file
244
docs/superpowers/test-runs/2026-04-20-1c-alpha-manual-matrix.md
Normal file
@@ -0,0 +1,244 @@
|
||||
# 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:
|
||||
```bash
|
||||
cd /home/alee/Sources/relicario/.worktrees/typed-items-1c-alpha/extension
|
||||
bun run build:all
|
||||
```
|
||||
Expected: "compiled with 2 warnings" (WASM size only) for each bundle. `dist/` and `dist-firefox/` populated.
|
||||
|
||||
- [ ] **P2.** Fresh-profile browsers ready (or existing profile's `chrome.storage.local` for this extension cleared). Stale `vaultConfig`/`imageBase64` from the pre-rename `idfoto` era 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" → select `extension/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…" → select `extension/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.html` opens 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.
|
||||
- [ ] **Expected:** Row appears with 🔑 icon + title + favorite star position.
|
||||
- [ ] **Expected (CLI cross-check, optional):** From main worktree:
|
||||
```bash
|
||||
relicario list
|
||||
relicario get "GitHub" --show
|
||||
```
|
||||
Should show the same item. TOTP secret should decode identically.
|
||||
- [ ] **Notes:** ___
|
||||
|
||||
### 5. TOFU origin-ack prompt (audit C4 first half)
|
||||
|
||||
- [ ] **Do:** Navigate to `https://github.com/login`. Click the blue `id` icon 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 `.shadowRoot` is `null` (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_credentials` call after the hostname is acked in `VaultSettings.autofill_origin_acks` will 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 the `id` icon.
|
||||
- [ ] **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 returns `null`.
|
||||
- [ ] **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_login` content-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):**
|
||||
```bash
|
||||
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 --trashed` shows 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):
|
||||
```js
|
||||
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:
|
||||
```js
|
||||
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:
|
||||
```js
|
||||
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 `shadowRoot` values are `null`; 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 to `https://example.com`.
|
||||
- [ ] **Expected:** No credentials typed on example.com. SW rejects with `tab_navigated`; if somehow the message reaches the content script, `fill.ts` re-checks `expectedHost` and rejects with `origin_changed`.
|
||||
- [ ] **Notes:** ___ (this one's hard to time; skip if not easily reproducible)
|
||||
|
||||
### SP5. WAR probe
|
||||
|
||||
- [ ] **Do:** In a page console on any site:
|
||||
```js
|
||||
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 --workspace` green (should still be 151+ Rust tests).
|
||||
- [ ] **A2.** `cd extension && bun run test` green (should be 52 passing — 11 base32 + 41 router).
|
||||
- [ ] **A3.** `cd extension && bun run build` green (Chrome bundle).
|
||||
- [ ] **A4.** `cd extension && bun run build:firefox` green (Firefox bundle).
|
||||
- [ ] **A5.** Lint greps clean:
|
||||
```bash
|
||||
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:
|
||||
```bash
|
||||
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-complete` and 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.*
|
||||
Reference in New Issue
Block a user