b00da43ab0
- PIN unlock: device-local 4-12 digit shortcut, DPAPI-wrapped vault
key. Three modes (state.unlockMode): pw / pin / pw+pin. PIN
derives a wrap key via PBKDF2(pin, salt, 100k) and unwraps the
stored vault key (mirrors the Quick Unlock blob shape).
Anti-brute-force: 5 wrong attempts wipes the blob. Setup gated by
master-pw reauth so an unattended unlocked laptop can't be
backdoored. Master pw rotation clears the PIN blob (key drift).
loadServerSettings post-sync demotes pin/both -> pw when the local
blob is missing, so a wiped device re-syncs the correct mode up.
New unit PM.PinUnlock.pas + cmd://pin/{store,get,clear,status}.
- Table column picker: ⚙ in topbar (table view only), checkbox menu
for Site/Username/Folder/Updated. Site also drives showSiteOnCards
so the existing "Show site / URL" toggle in Settings stays in
sync. NAME column auto-widths (180px min, content max, +32px
right padding) so column hugs the next one without truncating.
- Editor position chooser (Appearance setting): Slide-over right /
left / Centered modal. Scoped to #slideover + #settingsPanel so
the click-outside / pointer-events logic doesn't accidentally
trap the modal-style empty viewport.
- Confirm before discarding unsaved edits: state.confirmOnUnsaved
setting (default ON), prompts on X / Esc / click-outside / switch-
to-other-entry. Also gates Lock vault / Sign out actions when the
editor is dirty; auto-lock and system-lock paths bypass to avoid
blocking on an unattended machine.
- Open-in-browser button added to the actions cell of the table
view (was card-only).
- Entry templates pass folder customization + template id through
duplicate / export / import / auto-backup roundtrips.
- Folder color + icon now persisted across export/import: payload.
folders carries name/color/icon; import creates missing folders
additively (existing local customisation kept).
- Bulk move-to-folder, batch add-tag, single add-tag now re-ship
the full entry payload so partial PUTs don't silently wipe
TOTP / custom_fields / kind / template.
- FireDAC: switched ftString -> ftMemo for icon_b64 / custom_fields
/ TOTP / template params and replaced .AsString with .Value so a
large (~200 KB) DeepSeek favicon no longer gets truncated at the
default ANSI 4000-char cap.
- Unicode filenames: attachment INSERT now uses ftWideString +
.AsWideString so non-ANSI filenames round-trip instead of being
mangled to "?".
- HandleSetEntryIcon cap raised 256 KB -> 512 KB chars to accept
base64 data URIs produced by max-raw favicon fetches.
- promptDialog + askReauth support inline `error` line + retry-
with-count loops on doExport reauth and auto-backup password
setup (5 attempts cap before bailing).
- Recently used moved from Tools to Vault section in the sidebar.
- Auth screen passkey button hidden (Delphi backend stubs WebAuthn).
- Sensitive cmd://favicon/refresh-style buttons in Settings now
stopPropagation so the document-level "close panel" handler
doesn't dismiss Settings mid-async during DOM reparenting.
- TEST_PLAN.md: +PIN unlock section.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
9.9 KiB
9.9 KiB
Test plan — commit fa7ea19 (native save + auto-backup + folder customization + attachments + UX bundle)
Coche au fur et à mesure. Les 🔥 regression sont les bugs déjà vus à re-vérifier explicitement.
0. Setup
- [✅]
delphi-backend\assets\BuildAssets.cmd+ F9 (sinon embedded ≠ disque) - [✅] Lance avec
config.txtprésent (debug panel + log) - [✅] Crée 2 users de test ou utilise vault existant non critique
1. Native Save As
- [✅] Settings → Export encrypted JSON → maître pw OK → backup pw → boîte "Enregistrer sous" Windows native (pas le popup Téléchargements Edge)
- [✅] Export CSV → idem natif
- [✅] Annule la dialog → toast "save cancelled" attendu, pas d'erreur silencieuse
2. Auto-backup
- [✅] Settings → Auto-backup → toggle on → prompt pwd (< 6 chars rejeté)
- [✅] "Choose…" → folder Windows valide → path s'affiche
- [✅] "Backup now" → fichier
vault-autobackup-yyyymmdd-HHmmss.jsoncréé dans le folder - [✅] Lance plusieurs "Backup now" (interval = 1 jour) → fichiers s'accumulent → après N+1, le plus vieux est purgé (vérifier
keepretention) - [✅] Toggle off → re-on → prompt pwd à nouveau (confirme que le pwd est effacé)
- Restart app, unlock → 5s plus tard, si
intervalpassé, backup auto silencieux (regarde le folder) - [✅] Restore : install fresh → import "Encrypted JSON" → choisis un backup → tape le pwd → entries restaurées intactes
3. Folder customization
- [✅] Sidebar → + Folders → modal s'ouvre → name+color+icon → Create → folder visible avec custom icon + color
- [✅] Hover sur folder → pencil apparaît → click → modal pré-rempli → change icon → Save → re-render OK
- [✅] Folder chip sur card : crée entry dans "Banking" → la chip sous le pwd montre l'icône shield + couleur (pas le default folder gris) 🔥
- [✅] Drag-reorder : grab folder → drag au-dessus d'un autre → ligne d'insertion cyan visible (above/below selon mouse Y) → drop → ordre changé persisté (refresh app)
4. Drag entries vers folders 🔥
- [✅] Drag d'une LOGIN entry → folder → toast "Moved to X" → entry change de folder
- [✅] Drag d'une NOTE entry → folder → toast "Moved to X" (régression v1, doit marcher maintenant)
- [✅] Drag d'une entry avec TOTP configuré → folder → ouvre l'entry après → TOTP toujours là (pas wipé)
- [✅] Drag d'une entry avec custom_fields → folder → custom_fields préservés
- [✅] Drag d'une entry vers son propre folder → no-op (pas d'erreur)
5. Recently used
- [✅] Vue vide au start (rien jamais touché) → "No entries"
- [✅] Copy password d'une entry → attends 2s → sidebar "Recently used" → entry visible en tête
- [✅] Ouvre une entry (slideover) → idem
- [✅] Copy 5 entries rapidement → max 10 visibles, ordre = plus récent d'abord
- [✅] Lock vault + unlock → liste rechargée du serveur (accessed_at persiste)
6. Encrypted attachments
- [✅] Crée entry, sauve, ré-ouvre → section "Attachments" visible (pas sur new entry)
- [✅] "Attach file" → file picker → upload PDF/PNG < 5 MB → toast OK → liste affiche filename+size+mime
- [✅] Upload fichier > 5 MB → toast erreur "File too large"
- [✅] Download → boîte Save As → fichier décrypté identique à l'original (hash check si paranoid)
- [✅] Delete → confirm modal → row disparaît
- [✅] Crée 2nd attachment, supprime entry parent → vérifier que les rows entry_attachments sont aussi delete (CASCADE FK) via
sqlite vault.db "select count(*) from entry_attachments" - [✅] Master pw rotation avec attachments → toast "Re-encrypting N attachment(s)…" puis "Attachments re-encrypted" → download d'un attachment après rotation réussit (re-chiffré client-side dans le flux change-master)
7. Password expiry
- [✅] Settings → Flag aged → 90 days
- [✅] Backdoor :
sqlite vault.db "UPDATE vault_entries SET password_changed_at = '2024-01-01' WHERE id = X"→ recharge → badge "Aged" visible sur la card de X - [✅] Edit l'entry, change le password, Save → badge disparaît (password_changed_at bumpé)
- [✅] Edit l'entry, change uniquement le title, Save → badge toujours là (password_changed_at pas bumpé — le CASE SQL filtre)
- [✅] Filters dropdown → "Aged password" → liste filtrée
8. Print recovery code
- [✅] Settings → Recovery → Generate (ou regenerate) → modal avec code → bouton "Print"
- [✅] Click Print → preview Edge → vérifie : code 32px monospace, account name, date, instructions 3 étapes, page A4 propre (le reste de l'app caché)
- [✅] Annule la print preview → app revient normalement (cleanup du #printRecoveryArea)
9. Audit log
- [✅] Sidebar → Tools → Audit log → table visible avec actions chronologiques
- [✅] Fais une action (add entry, copy pwd) → refresh audit log → nouvelle ligne en tête
- [✅] Search dans audit log → filtre marche
10. Pinned entries
- [✅] Sur n'importe quelle card, click pushpin → toast "Pinned to top" → entry remonte en tête de la liste
- [✅] Re-click → toast "Unpinned" → entry retrouve sa place dans le sort courant
- [✅] Change le sort (name asc → date desc) → entries pinned restent en tête, le tri interne s'applique aux deux groupes
- [✅] Compact mode (Settings → Compact actions) → kebab menu contient "Pin to top" / "Unpin"
- [✅] Pin 3 entries, lock vault, unlock → les 3 sont toujours en tête (persist serveur)
- Master pw rotation → pinned préservé (pas wipé)
- [✅] Duplicate d'une entry pinned → la copie est non pinned (comportement voulu)
11. Search history
- [✅] Tape une recherche, attends 1s sans bouger → recherche ajoutée à l'historique (pas besoin d'Enter)
- [✅] Vide la searchbar → focus → dropdown "Recent searches" avec les dernières
- [✅] Click sur un item → re-filtre et bump en tête
- [✅] X individuel sur un item → suppression isolée
- [✅] "Clear" → vide tout l'historique
- [✅] Dropdown disparaît au click hors searchbar / Escape / blur => un petit probleme > focus search bar > dropdown search affiché > clique à nouveau search (focus non changé) dropdown non affiché
12. PIN unlock 🔥
12a. Setup
- [✅] Settings → "PIN unlock" section visible (Bridge actif requis)
- [✅] Status "No PIN set." + bouton "Set PIN" visible, "Remove PIN" caché
- [✅] Click "Set PIN" → prompt → tape 3 chiffres → erreur inline "PIN must be 4–12 digits" (modal reste ouvert)
- [✅] Tape abcd → même erreur (digits only)
- [✅] Tape 123456 → toast "PIN set" → status passe à "PIN is set on this device." → bouton "Change PIN" + "Remove PIN" visibles
- [✅] Vérifie
%LOCALAPPDATA%\PMServer\pin-unlock.binexiste
12b. Mode 'pin' (PIN only)
- [✅] Settings → dropdown "Unlock method" = "PIN only" → toast "Unlock method updated"
- [✅] Lock vault → auth screen affiche PIN input (pas master pw) + lien "Use master password instead"
- [✅] Tape bon PIN → unlock instant
- [✅] Lock → tape mauvais PIN → "Wrong PIN. Try again..."
- [✅] Lock → tape mauvais PIN 5 fois → blob auto-supprimé → écran retombe sur master pw → message "Too many wrong PIN attempts. Sign in with your master password."
- [✅] Settings après ça : status "No PIN set." → re-set possible
12c. Mode 'both' (master + PIN)
- [✅] Re-set PIN, dropdown = "Master password + PIN"
- [✅] Lock → auth affiche pw + PIN ensemble
- [✅] Tape bon pw + mauvais PIN → unlock initie, puis lockVault avec "Wrong PIN. Try again."
- [✅] Tape bon pw + bon PIN → unlock OK
- [✅] Pw seul (PIN vide) → submit bloqué (HTML5 required)
12d. Escape PIN-only
- [✅] Mode 'pin', PIN configuré → auth → click "Use master password instead" → champ pw apparaît, PIN caché → unlock master pw marche
- [✅] Lock après → revient en mode PIN (le choix master était one-shot)
12e. Quick Unlock + PIN coexistence
- [✅] Active Quick Unlock + définis PIN en mode 'both'
- [✅] Restart app → Quick Unlock cold-start réussit sans demander PIN (device trusted)
- [✅] Lock manuel → auth demande pw + PIN comme prévu
12f. Sensitive actions = master pw obligatoire
- Mode 'pin', unlocked → Settings → Export encrypted JSON → askReauth demande master pw (pas PIN)
- Idem : Change master password → master pw demandé
- Idem : Generate recovery code → master pw demandé
12g. Master pw rotation invalide le PIN
- Mode 'pin', PIN configuré → Change master pw → après rotation, status PIN passe à "No PIN set." (blob wipé) + unlockMode tombe à 'pw'
- Next lock → champ master pw → unlock OK avec nouveau pw
12h. Remove PIN
- [✅] Click "Remove PIN" → confirm dialog
- [✅] Confirm → blob deleted, status repasse à "No PIN set.", mode revert à 'pw'
12i. Recovery key continue à marcher
- Avec mode 'pin' actif, ouvre auth → click "Forgot master password? Use a recovery code" → flow recovery normal (le bouton reste visible)
13. Regressions générales 🔥
- [✅] Quick unlock encore fonctionnel après update
- [✅] Master password rotation OK + entries déchiffrables après
- [✅] Autofill (Ctrl+Shift+L/P) toujours OK
- Recovery code redeem fonctionne (sur compte test)
- [✅] Lock vault → unlock → toutes entries présentes
- Trash → restore → entry revient avec tous ses champs (kind, custom_fields, TOTP)
13. Console / log scrutiny
- Ouvre debug panel → execute scénarios ci-dessus → log Delphi sans
FAILED/ERRORnon attendu - Si tu peux ouvrir console JS (DevTools désactivé en prod, faut hack — sinon skip) → pas d'erreur JS rouge pendant les flows
Ordre conseillé : 0 → 4 → 6 → 7 → 1 → 2 → 3 → 5 → 8 → 9 → 10 → 11 → 12 → 13. Le drag entries (#4) est le plus à risque (régression confirmée hier), le faire tôt + à fond.