Commit Graph

25 Commits

Author SHA1 Message Date
Zaki cf94f67488 feat(2fa): TOTP secret storage + live 6-digit code generation
Adds RFC 6238 TOTP (Google Authenticator-style) support to every entry.
The secret is encrypted client-side with the same AES-GCM key as the
password — the server stores opaque ciphertext and never sees the
plaintext base32 secret.

Schema
======
vault_entries.totp_secret TEXT  -- AES-GCM ciphertext, base64
vault_entries.totp_iv     TEXT  -- 12-byte IV, base64
Both NULL when the entry has no 2FA configured. Added via
ApplyMigrations.AddColumnIfMissing so existing vaults migrate cleanly.

Backend
=======
HandleListEntries: includes totp_secret + totp_iv in the response (or
JSON null when not configured).
HandleCreateEntry / HandleUpdateEntry: accept both fields; empty string
in the body → server stores NULL. Clearing the secret removes 2FA
from the entry.

Frontend
========
TOTP primitives (pure crypto.subtle, no external lib):
 - base32Decode(s)         — RFC 4648, tolerates spaces / lowercase
 - generateTOTP(secret)    — HMAC-SHA1 + RFC 4226 dynamic truncation
 - parseOtpAuthUri(raw)    — extracts ?secret from otpauth:// URIs

UI in the slide-over (the canonical entry detail view):
 - New "Two-factor (TOTP)" field below the password row.
 - Input is password-masked by default with eye-toggle to reveal.
 - Pasting a full otpauth:// URI auto-extracts the secret param so the
   user can copy directly from a QR-code scanner without manual cleanup.
 - X button clears the secret (= removes 2FA on next save).
 - Live code panel below: large monospace "123 456" + Copy button
   (routes through Bridge.copySecure → secure clipboard + 30s auto-clear).
 - Linear progress bar drains over the 30s window, turns red < 5s.
 - Refresh tick runs once per second while the slide-over is open;
   stops on closeSlideOver to avoid background work.

Entry card meta now shows a "2FA" chip when totp_secret is non-null —
quick visual scan for which accounts have 2FA configured without
opening the slide-over.

Validation
==========
soSave calls base32Decode(secret) before encrypting to refuse obviously
broken input. Otherwise garbled base32 would save fine and only fail
in the code panel next time.

Migration interaction (KDF 100k→600k)
=====================================
KNOWN MINOR ISSUE: /migrate-kdf only re-encrypts encrypted_password+iv,
not totp_secret+totp_iv. In practice this is harmless because:
  1) KDF migration runs immediately after login on legacy accounts —
     before the user has a chance to add a TOTP secret.
  2) New accounts start at 600k iterations, no migration ever needed.
A legacy user who somehow added a TOTP between login and the
background migration completing would end up with a TOTP encrypted
under the old key. The fix (extend /migrate-kdf to re-encrypt TOTP
fields too) is a one-line follow-up if anyone hits the edge case.
2026-05-23 05:13:50 +01:00
Zaki a45897c33d feat(security): HIBP password breach check + CSP tightening
HIBP integration
================
Opt-in (default OFF) password breach check via the Have I Been Pwned
range API. The full master / entry password never leaves the machine —
only the first 5 characters of its SHA-1 hash. HIBP returns ~500
candidate suffixes; the client matches its own suffix locally.

UI:
 - New "Check passwords against breach database (HIBP)" toggle in
   Settings → Security with an explainer hint about k-anonymity.
 - On enable: background batch scan of all entries, results cached in
   state.hibpResults keyed by entry id. Concurrency capped at 6 to
   avoid hammering HIBP / hitting browser connection limits.
 - Entry cards show a red "Pwned" chip + breach count in the tooltip
   when count > 0. New i-alert icon added to the SVG sprite.
 - Auto-scan triggered after every enterApp() when the toggle is on.

Functions added to app.js:
 - sha1Hex(text)                       — crypto.subtle wrapper
 - hibpCheckPassword(plaintext)        — single-password check, returns count
 - hibpCheckAllEntries()               — batched scan over state.entries

The "Add-Padding: true" header is sent on every range request to defeat
the response-size side-channel (HIBP adds 800-1000 random extra entries
so an observer counting bytes can't narrow the prefix queried).

CSP tightening
==============
Audited the served HTML: zero <script> tags inline, only the external
js/app.js. Removed 'unsafe-inline' from script-src — real XSS defense.

Kept 'unsafe-inline' on style-src for now because index.html contains
inline style="" attributes and app.js calls element.style.cssText
extensively. Refactoring to CSS classes is a separate cleanup. Style
injection alone cannot execute code, so the residual risk is bounded
to visual manipulation in a single-user loopback app.

Added api.pwnedpasswords.com to connect-src as the only allowed
external origin (required by the HIBP feature above). Default still
'self' — everything else stays loopback.

Before:
  script-src 'self' 'unsafe-inline';
  style-src  'self' 'unsafe-inline';
  connect-src 'self';

After:
  script-src 'self';
  style-src  'self' 'unsafe-inline';
  connect-src 'self' https://api.pwnedpasswords.com;
2026-05-23 05:05:50 +01:00
Zaki 73818e4e2e Add undo to drag-drop trash. Fix toast undo button visibility (darker bg, border, max-width). 2026-05-12 19:46:01 +01:00
Zaki ff9802e685 Add undo button in toast for trash actions (single + batch) 2026-05-12 19:40:23 +01:00
Zaki 617b8a7efe Hide FABs when auth visible (locked). Trash FAB glow shadow when active. 2026-05-12 19:36:35 +01:00
Zaki eb15d9e849 Trash FAB: always transparent background, no shadow 2026-05-12 15:49:40 +01:00
Zaki 59d407558b Trash FAB: always shows 🗑️; active mode has transparent background, no shadow 2026-05-12 15:33:12 +01:00
Zaki 5836bd168d Fix dblclick: clear selection, select entry, render, open edit. closeEdit calls render. Trash FAB: moved from toolbar to floating button at bottom-left (like add FAB). 2026-05-12 15:14:39 +01:00
Zaki d55be5f19e Fix entry card contrast (lighter card, more visible border). Fix dblclick: force-select entry before opening edit. 2026-05-12 14:20:46 +01:00
Zaki b51e9c3a73 Theme: neutral dark gray, card shadows, accent-rgb vars. Fix: arrows skip when modals open. Fix: detail view respects folder filter.
Dark theme: neutral dark grays (#0e1015 bg, #6b7280 accent). Light theme: white cards, better contrast. Cards now have subtle shadows for separation. All hardcoded rgba(59,130,246) replaced with rgba(var(--accent-rgb), ...). Arrow key handler skips when add/edit modal is open. Detail view now uses getFilteredEntries() instead of getFilteredEntries(true) to respect folder selection.
2026-05-12 13:51:41 +01:00
Zaki dfcbc3f568 Theme: neutral dark gray, higher card contrast. Detail view: folder filtering works. Modals: arrow nav blocked when modal open, Enter saves from anywhere inside modal. 2026-05-12 13:35:46 +01:00
Zaki 02e5868d66 Fix batch confirm: modal overlay + positioned beside button
Replace showCenterConfirm with showBatchConfirm(btn,message,callback) that creates a transparent modal overlay (z-index 9999) to block background clicks, positions the confirm dialog beside the trigger button. Updated all callers (batchDelete, batchPermanentDelete, permanentDelete, emptyTrash) to pass the clicked button. Added CSS for batch-confirm-overlay backdrop.
2026-05-09 18:37:57 +01:00
Zaki d50aea6845 Add 3 new views (Card, Grouped, Detail) and view dropdown
Replace view toggle buttons with dropdown menu containing all 7 views. Card view: 2-column grid with bigger cards. Grouped view: entries grouped by folder with sticky headers. Detail view: single entry at a time, large text, prev/next navigation. Arrow key navigation for detail view.
2026-05-09 16:51:38 +01:00
Zaki 757efe0908 Prevent native text selection on .vault during rectangle selection 2026-05-09 16:30:20 +01:00
Zaki 6a26dd78cb Fix rect selection in table view: prevent native text drag/selection
Added user-select:none and -webkit-user-drag:none to #entriesContainer.
Also added a dragstart listener that prevents default for any element
that is not [draggable='true'], stopping the browser from creating drag
ghosts of selected table text/headers during rectangle selection.
2026-05-09 16:18:03 +01:00
Zaki 9cc4463177 Use CSS classes for drag-dim effect and DOM-appended canvas for drag ghost 2026-05-09 15:39:14 +01:00
Zaki 5da0deaf94 Enhance selection visibility, add rectangle select, multi drag/drop
- Brighter selection highlight with stronger box-shadow, background, and left-border accent
- Rectangle/band selection: click-drag on empty space to select multiple entries via intersection test
- Drag selected entries to a folder chip to move all, or to trash button to multi-delete
- Multi-drag reorder: dragging a selected entry reorders all selected together
- Table view selection now highlights individual td cells (box-shadow on tr was invisible)
- Escape cancels rectangle selection in progress
2026-05-09 14:09:48 +01:00
Zaki eac02ed17c Replace batch mode toggle with click/Shift/Ctrl selection
- Remove separate 'select mode' toggle and checkbox UI
- Click any entry to select it (blue highlight border)
- Shift+click for range selection between two entries
- Ctrl/Cmd+click to toggle individual entries
- Click entry background or press Escape to clear selection
- Batch action bar appears automatically when items are selected
- Selected entries get accent-color border and highlight
- Single delEntry() cleans up selectedIds
- Shortcut help updated with selection tips
2026-05-09 13:48:22 +01:00
Zaki 8dd2fc71cf Fix star icon color for dark theme 2026-05-09 00:01:13 +01:00
Zaki 8abb1c5ad0 Add hover reveal, drag-to-folder, generator presets, favorites
- Inline password reveal on hover (controlled by showView setting, replaces eye button)
- Drag an entry card onto a folder chip to move it (no modal needed)
- Generator presets: Strong 16, Strong 20, Paranoid 32 buttons
- Favorites: star toggle button per entry, entries sort to top
- Add favorite column to vault_entries, toggle endpoint, star UI in all views
- Gold border/background for favorited entries
2026-05-08 23:53:47 +01:00
Zaki c6504f70d1 UI improvements: fix keyboard shortcuts, search highlighting, strength meter on register, colored folders, shortcut help
- Rewrite keyboard shortcuts using e.code and early preventDefault() to reliably override browser defaults
- Add ? key and toolbar button for shortcuts help modal
- Add password strength meter to register form
- Add search highlighting in all view modes (grid/list/compact/table)
- Add hash-based color coding for folder chips
- Add highlightText utility with regex escaping
2026-05-08 23:35:59 +01:00
Zaki 5ad1498afc add delete confirm, cancel shortcut + batch operations 2026-05-08 19:59:56 +01:00
Zaki a0a081e2db add clear for search + regroup options in settings btn 2026-05-08 17:21:38 +01:00
Zaki 8743f023b2 Fix refresh logout+ add trash (undo deleted) 2026-05-08 14:39:24 +01:00
Zaki a4528eb1f7 Standard password manager 2026-05-06 20:49:51 +01:00