05/18 Update documents

This commit is contained in:
2026-05-18 22:14:21 -04:00
parent ec1f1fe374
commit 5ede6326c8
2 changed files with 59 additions and 3 deletions
+55 -1
View File
@@ -272,6 +272,54 @@ Requires Chrome 111+. `content.js` `onChanged` listener watches `area === 'sessi
## Key Implementation Details
### `password_changed_at` — accurate password age tracking
Stored as `plain.password_changed_at: ISO-8601 string` inside the encrypted `enc_data` blob. Server never sees it.
**Written by:**
- `vault.js` `handleFormSubmit`**create**: always set to `now`. **Edit**: only updated when the password field value actually changed vs. the decrypted existing item in `_items`. Unchanged password → existing `password_changed_at` preserved. No existing timestamp + unchanged → absent (dashboard falls back to `created_at`).
- `popup.js` `saveCredential` and `addItemToVault` — always set to `now` for new extension-saved credentials.
- `toggleFavorite` — spreads `{ ...item.plain, tags: newTags }`, automatically preserving the timestamp.
**Read by:**
- `renderSecurityDashboard``ageRef = plain?.password_changed_at || created_at`; items with a recent `password_changed_at` are no longer falsely flagged as old even if the item itself is old.
- Backwards compatible: items without `password_changed_at` fall back to `created_at`.
### Auto-lock on tab visibility change
`_startWebIdleTracking()` now registers a `visibilitychange` listener in addition to the mouse/keyboard events.
**Hidden:** clears the inactivity timer (user cannot be active on a hidden tab), records `_hiddenAt = Date.now()`.
**Visible again:** compares elapsed hidden time against the idle timeout.
- Hidden `≥ timeout` → immediate lock (same `VaultSession.clear()` + `showUnlockOverlay()` + toast as inactivity lock).
- Hidden `< timeout` → clears `_hiddenAt`, resumes timer from zero.
This catches screen-lock, minimize, and long tab switches. Short tab switches (< timeout) do not trigger a lock.
### Emergency access stale snapshot detection
`EmergencyAccess._enc_vault_is_legacy()` — parses `enc_vault` JSON server-side (no decryption) and returns `True` if any item has a `name` key but lacks `enc_name`. Exposed as `enc_vault_is_legacy` in `to_dict()`.
`renderEmergencyGrants()``ready` grants now show three states:
- `enc_vault_is_legacy: true` → amber `⚠ Outdated snapshot` badge + **Re-provision** button
- `enc_vault_is_legacy: false` → secondary **Update Recovery Data** button
- `accepted` (no snapshot yet) → primary **Provide Recovery Data** button
After re-provisioning, `enc_vault_is_legacy` returns `false` and the warning disappears.
### Extension health badge
`_runPopupHealthCheck()` in `popup.js` — fires after every `fetchAndDecryptVault()`. Computes weak/reused synchronously, sends a preliminary `HEALTH_UPDATE` to the background SW, then runs HIBP in parallel (`_popupCheckHibp`). Sends a final `HEALTH_UPDATE` with breach count.
`background.js` / `background.firefox.js`:
- `HEALTH_UPDATE` handler: stores `{ breached, weak, reused }` in `chrome.storage.local` as `health_status`, calls `applyHealthBadge()`.
- `applyHealthBadge()`: on tabs with no match-count badge, shows red `⚠` (breach) or amber `⚠` (weak/reused). Never overwrites the blue match-count badge or the pending-save `!` badge.
- `CLEAR_SAVE_BADGE`: after clearing `!`, immediately re-applies health badge.
- Idle lock: removes `health_status` from local storage.
**Badge priority:** pending-save `!` (red) > match count (blue) > health `⚠` (red/amber).
### Vault health notifications (background checks)
`runBackgroundHealthCheck()` fires after every `loadVault()` call — async, non-blocking.
@@ -342,7 +390,7 @@ DELETE /api/webauthn/credentials/<id> → remove a passkey
**`auth.js` login wiring:** "Sign in with Passkey" button on `login.html` calls `PasskeyAuth.loginWithPasskey(email)`. On success, stores tokens and navigates to `/vault` — unlock overlay fires if master password field was empty.
**Authenticator attachment:** currently `PLATFORM` (biometrics / device passkey). To support roaming authenticators (YubiKey, phone-as-key), remove or change `authenticator_attachment` in `register_begin`.
**Authenticator attachment:** `register_begin` accepts optional `attachment` in the POST body: `"platform"` (default — device biometrics) or `"cross-platform"` (roaming — YubiKey, phone QR, NFC). The settings UI exposes a `<select>` with both options. Credential type is shown in the passkeys list using transport hints (`internal` → 📱 Device, `usb/nfc/ble` → 🔑 Security key).
**Exception names** (`webauthn>=2.0`):
- `InvalidRegistrationResponse` — use in `register_complete`
@@ -509,6 +557,9 @@ Audit log details **never** contain plaintext item names, shared item names, or
- `_validate_folder_id` must be called for any user-supplied `folder_id` before DB write
- `verify_auth_token` must receive `user=user` at login to enable Argon2 rehash
- APScheduler cleanup job handles `TokenBlacklist`, `RecoveryChallenge`, AND `TotpUsedCode`
- `password_changed_at` lives inside `plain` (encrypted) — never in the server schema
- WebAuthn `attachment`: `"cross-platform"` for security keys; `"platform"` for device biometrics (default)
- `enc_vault_is_legacy` check in `EmergencyAccess.to_dict()` is pure JSON inspection — no decryption
---
@@ -616,6 +667,9 @@ webauthn>=2.0 # Passkey / WebAuthn (py-webauthn)
- Never log decrypted vault data server-side — audit details use `item_type` + `id` only
- Background health check runs after every vault load — badge + banner update without user action
- HIBP results cached in `_healthCache` per session — Security tab reuses them, no double-query
- Extension health badge stored in `chrome.storage.local` (`health_status`) — survives SW restarts
- `password_changed_at` in `plain` enables accurate old-password detection without schema changes
- Tab visibility lock fires immediately when hidden duration ≥ idle timeout — catches screen lock
- Tags live in `enc_data` as `plain.tags: string[]` — no schema change ever needed
- `vault_items_cs` is in `chrome.storage.session` — decrypted data never written to disk
- HIBP checks run progressively — synchronous sections render first, then parallel async checks
+4 -2
View File
@@ -19,13 +19,13 @@ A self-hosted, zero-knowledge password manager — web app and Chrome/Firefox ex
- **Proactive health notifications** — sidebar badge and dismissible banner on vault load when breached, weak, or reused passwords are detected; HIBP runs in the background without blocking the UI; results cached so opening the Security tab is instant
- **Import / Export** — encrypted JSON backup; CSV export (plaintext, handle carefully); import from Chrome, Bitwarden, and 1Password CSV formats (RFC 4180 compliant parser)
- **Account MFA** — TOTP-based login (Google Authenticator / Authy); single-use code enforcement prevents replay attacks
- **Passkeys / WebAuthn** — register device biometrics or hardware keys as a sign-in method; master password still required to unlock vault (zero-knowledge preserved); manage passkeys in Account Settings
- **Passkeys / WebAuthn** — register device biometrics or hardware keys (YubiKey, cross-device QR) as a sign-in method; master password still required to unlock vault (zero-knowledge preserved); manage passkeys in Account Settings; transport type shown per credential (📱 Device / 🔑 Security key)
- **Master password change** — atomic zero-knowledge re-encryption of entire vault including item names
- **Account recovery** — 128-bit recovery code; server never stores it; challenge-response proof prevents forgery
- **Audit log** — server-side trail of all create/edit/delete/import/export actions; no plaintext names ever logged
- **Encrypted item names** — `enc_name`/`iv_name`; server holds only the item type as a label
- **Browser history** — back/forward button works for all views (`history.pushState`)
- **Web-app auto-lock** — configurable inactivity timeout (5/10/15/30/60 min or Never); stored per browser in `localStorage`
- **Web-app auto-lock** — configurable inactivity timeout (5/10/15/30/60 min or Never); also locks immediately when tab is hidden (screen lock, minimize) for longer than the timeout
- **Clipboard auto-clear** — sensitive copies cleared after 30 seconds
- **Responsive layout** — phone, tablet, laptop, large desktop; collapsible sidebar
@@ -79,6 +79,8 @@ Sharing: ECDH(Alice_priv, Bob_pub) ──► sharedKey ──► AES-256-GCM(en
- **Audit log privacy** — item names and shared item names never appear in server-side audit logs
- **Extension fingerprinting** — `web_accessible_resources: []` blocks external pages from probing extension files
- **Passkey / WebAuthn** — FIDO2 assertion proves identity to the server without a password; vault key still derived from master password client-side; `sign_count` updated on each use for clone detection
- **Password age tracking** — `password_changed_at` stored inside encrypted blob; security dashboard uses actual password change date, not item creation date
- **Emergency access stale snapshot detection** — server flags provisioned snapshots created before the `enc_name` fix; UI prompts grantor to re-provision
A database breach exposes only encrypted ciphertext. The server cannot read vault names, passwords, tags, or shared item names.