Aug 26 - Enhance security 4
CI / Python lint (flake8) (push) Has been cancelled
CI / Python syntax check (push) Has been cancelled
CI / Alembic migration chain (push) Has been cancelled
CI / JavaScript syntax check (push) Has been cancelled
CI / Pytest (push) Has been cancelled
CI / Build extension zip (push) Has been cancelled
CI / Python lint (flake8) (push) Has been cancelled
CI / Python syntax check (push) Has been cancelled
CI / Alembic migration chain (push) Has been cancelled
CI / JavaScript syntax check (push) Has been cancelled
CI / Pytest (push) Has been cancelled
CI / Build extension zip (push) Has been cancelled
This commit is contained in:
@@ -118,6 +118,8 @@ passkeeper/
|
||||
│ ├── test_session_revocation.py # token_epoch revocation; deleted-account 401
|
||||
│ ├── test_webauthn_uv.py # user verification required on both ceremonies
|
||||
│ ├── test_sharing_expiry.py # expires_days fails closed
|
||||
│ ├── test_registration_privacy.py # register does not disclose account existence
|
||||
│ ├── test_emergency_visibility.py # grantor sees requests + retrievals
|
||||
│ ├── test_deploy_config.py # nginx/gunicorn/systemd/extension packaging guards
|
||||
│ └── js/test_psl.js # PSL same-site matching (node, run in CI)
|
||||
├── gunicorn.conf.py # worker class, timeouts, preload_app=False
|
||||
@@ -244,6 +246,7 @@ CREATE TABLE webauthn_credentials (
|
||||
| `i9j0k1l2m3n4` | Add expires_at to shared_items |
|
||||
| `j0k1l2m3n4o5` | Add recovery_verifier (decouple recovery proof) |
|
||||
| `k1l2m3n4o5p6` | Add token_epoch (revoke sessions on pw change) |
|
||||
| `l2m3n4o5p6q7` | Add emergency vault retrieval tracking |
|
||||
|
||||
---
|
||||
|
||||
@@ -301,6 +304,20 @@ CREATE TABLE webauthn_credentials (
|
||||
possession of an unlocked device must not be sufficient.
|
||||
- **Account recovery:** challenge-response via HMAC-SHA256; `enc_key_salt` NOT returned by `/recovery/data` — client must derive it by decrypting the recovery blob (proves possession of recovery code without transmitting it); challenge rotated on each `/recovery/items` call to prevent proof replay; recovery key derived with the user's email as a per-user PBKDF2 salt — legacy fixed salt `'passkeeper-recovery'` accepted transparently for codes created before this change
|
||||
- **folder_id ownership:** validated server-side on all create/update/import operations — user cannot assign items to another user's folder
|
||||
- **Registration privacy:** `POST /api/auth/register` returns an identical 202
|
||||
whether or not the address exists, and performs an equivalent Argon2id hash on
|
||||
both branches so timing does not reinstate the oracle. Duplicate attempts are
|
||||
audited under `auth.register_duplicate`. Fully closing this needs email
|
||||
verification so the address owner is told — until then the oracle is removed
|
||||
but the owner cannot be notified.
|
||||
- **Emergency access visibility:** `accept`, `request` and `vault_retrieved` are
|
||||
audited under BOTH parties' user_ids. `/api/auth/audit-log` filters by
|
||||
`user_id`, so an entry written only under the acting user is invisible to the
|
||||
other — which meant a grantee could request and retrieve a vault snapshot
|
||||
without anything reaching the grantor. `vault_retrieved_at` /
|
||||
`vault_retrieval_count` on `emergency_access` record every fetch. Retrieval is
|
||||
intentionally NOT blocked after the first time (the grantor may be unable to
|
||||
re-provision); the wait period is the gate, and the grantor can revoke.
|
||||
- **Audit logs:** never contain plaintext item names, shared item names, or vault data
|
||||
|
||||
---
|
||||
@@ -642,6 +659,8 @@ Audit log details **never** contain plaintext item names, shared item names, or
|
||||
- `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
|
||||
- Audit entries are only visible to the user_id they are written under — mirror cross-party events (use `_log_for_both` in `emergency.py`) or the other party never sees them
|
||||
- `/register` must return the SAME body and status for new and existing addresses, and hash on both paths — returning early on duplicate reinstates a timing oracle
|
||||
- Never return `str(e)` from exception handlers — log with `_log.exception(...)` and return a generic user-facing message to avoid leaking DB schema details or query fragments
|
||||
- `extension/shared/psl.js` is GENERATED — never hand-edit; run `python scripts/update_psl.py`. It must load BEFORE content.js / popup.js / background.js in every manifest
|
||||
- Never reintroduce `endsWith("." + host)` host matching anywhere in the extension — `tests/test_deploy_config.py` fails the build if it reappears
|
||||
@@ -661,6 +680,7 @@ Audit log details **never** contain plaintext item names, shared item names, or
|
||||
| Module | Action | Trigger |
|
||||
| -------------- | ------------------------------------------------------ | ------------------------------ |
|
||||
| `auth.py` | `auth.register` | New account |
|
||||
| `auth.py` | `auth.register_duplicate` | Register attempt on an existing address (no email in detail) |
|
||||
| `auth.py` | `auth.login` / `auth.login_failed` | Login success/fail |
|
||||
| `auth.py` | `auth.account_locked` | Failed login lockout |
|
||||
| `auth.py` | `auth.mfa_enable/disable/verify` | TOTP actions |
|
||||
@@ -676,6 +696,7 @@ Audit log details **never** contain plaintext item names, shared item names, or
|
||||
| `sharing.py` | `sharing_keys.create/update` | ECDH key setup |
|
||||
| `sharing.py` | `shared_item.create/delete/accept` | Sharing (detail: type + id) |
|
||||
| `emergency.py` | `emergency_access.*` | All EA state transitions |
|
||||
| `emergency.py` | `emergency_access.accept/request/vault_retrieved` | Logged under BOTH grantor and grantee user_ids |
|
||||
| `webauthn.py` | `webauthn.register` | Passkey registered |
|
||||
| `webauthn.py` | `webauthn.auth_success` / `webauthn.auth_failed` | Passkey login attempt |
|
||||
| `webauthn.py` | `webauthn.rename` / `webauthn.delete` | Credential management |
|
||||
@@ -857,7 +878,9 @@ Features planned for future implementation. Ordered by priority within each cate
|
||||
- Autofill matching moved onto the Public Suffix List (registrable domains)
|
||||
- Share `expires_days` fails closed instead of silently meaning "never"
|
||||
- nginx `api_limit` corrected from 60r/m to 10r/s
|
||||
- pytest suite (51 tests) + PSL node test + CI jobs; `gunicorn.conf.py`;
|
||||
- Registration no longer discloses account existence (status, body and timing)
|
||||
- Emergency access: requests and vault retrievals are now visible to the grantor
|
||||
- pytest suite (66 tests) + PSL node test + CI jobs; `gunicorn.conf.py`;
|
||||
systemd watchdog removed
|
||||
|
||||
### High priority — user-facing
|
||||
|
||||
Reference in New Issue
Block a user