Files
relicario/docs/superpowers/test-runs/2026-04-20-1c-alpha-manual-matrix.md
adlee-was-taken 4341124d38 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>
2026-04-22 19:32:00 -04:00

245 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 111 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.
- [ ] **FF1FF11.** 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.*