Browse Source
Server hardening:
- accounts.rs: reject duplicate ids in `passkey_unlock_data` before they
collapse through the HashSet superset check and silently double-apply
with the second write winning.
- accounts.rs: re-check the PRF credential set immediately before
`set_password` commits the new account key. A concurrent enrol between
the validation snapshot and the rewrap loop would otherwise leave the
new credential's `encrypted_user_key` wrapped under the OLD akey,
permanently broken once the rotation commits.
- mod.rs: drop the legacy bare-`PasskeyRegistration` fallback in
`passkey_registration_challenge_state` — no writer produces that
shape, and the fallback permitted token-less finishes against a
hypothetical un-tokened row.
- mod.rs: switch token compares in both challenge-state extractors to
`crypto::ct_eq` (matches the `cred_id` pattern already used at
mod.rs:1424).
- mod.rs: normalise `passkey_assertion_challenge_state`'s deserialise
error to the generic "Invalid assertion challenge" rather than leaking
the underlying serde shape.
- mod.rs: document why `post_api_webauthn_delete` is intentionally
exempt from the SSO_ONLY gate (delete narrows capability, never
grants it; session is still SSO-authed).
- mod.rs: document that the post-`start_passkey_registration` mutation
of `authenticator_selection.require_resident_key` is a client-side
hint only (webauthn-rs destructures the field as unused), so readers
don't mistake it for a server-enforced policy.
Observability:
- web_authn_credential.rs / two_factor.rs: log Diesel errors from the
SELECT+DELETE transactions in `WebAuthnLoginChallenge::take` and
`TwoFactor::take_by_user_and_type` before collapsing to `None`. The
prior `.unwrap_or(None)` made degraded DB behaviour (deadlock, lock
timeout, conn drop) indistinguishable from a normal stale-token
rejection.
- identity.rs: log the failure path of `passkey_transports`'s
`serde_json::to_value(passkey)` instead of silently emitting
`Transports: []`. Clients can't otherwise distinguish "authenticator
reported no transports" from "we failed to serialise the passkey".
UX:
- vaultwarden.scss.hbs: re-add the `.vw-passkey-login` hide under
`sso_enabled && sso_only`. Without it the login page renders the
"Log in with passkey" button under SSO_ONLY, but the server gate at
`identity.rs:1250` rejects the click — UX dead end.
Test reliability:
- passkey.spec.ts: the "Non-PRF passkey" test now logs out + logs back
in via passkey between enrol and the lock-screen check. Without
this refresh the lock-screen reads the pre-enrol cached `/api/sync`
and the assertion passes for the wrong reason (cached
webAuthnPrfOptions was empty before enrol regardless of what the
server stored).
- passkey.spec.ts: guard the three describe blocks that restart the
vault container (`Passkey grant is rejected when SSO_ONLY is on`,
`Passkey enrolment is rejected when SSO_ONLY is on`, `Passkey login
rejects forged unverified-email handles`) on `useExternalVault` so
`PW_USE_EXTERNAL_VAULT=1` (host-cargo iteration) cleanly skips them
instead of colliding with the host process on port 8003.
- passkey.spec.ts: the SSO_ONLY-enrolment `afterAll` now also blanks
`sso_authority`, `sso_client_id`, `sso_client_secret` — the previous
reset left those placeholders in `config.json` and contaminated any
later test that toggled `sso_enabled=true`.
- passkey.spec.ts: the bearer-sniffer in the SSO_ONLY-enrolment
beforeAll now filters on `url.includes('/api/')` instead of capturing
whatever token happened to fly last during createAccount; mirrors
the disciplined sniffer in `account_lifecycle.spec.ts:178` and
insulates the test from web-vault request-order churn.
- 2fa.ts: export `resetTotpTimeStep()`; call it from the
`test.beforeEach` in `account_lifecycle.spec.ts` and `passkey.spec.ts`
so the module-scoped `lastSubmittedTotpTimeStep` doesn't leak across
tests / projects under `workers: 1` (would otherwise force a 30s
wait before the first TOTP of project N+1 based on project N's
history).
- global-utils.ts: document the `resetDB=false` contract honestly —
docker recreates the Vaultwarden container on any env-var change in
the compose `environment:` block, dropping the tmpfs sqlite DB along
with the in-process RSA key.
- login.smtp.spec.ts: drop the redundant "Dismiss extension prompts"
step from the 2fa test. `logUser` already calls
`utils.ignoreExtension` and lands on /vault, so the explicit "Add
it later" click timed out on a button the prior step had already
dismissed.
Test coverage additions:
- accounts.rs rotation validator: add a regression test for the
duplicate-id rejection in `passkey_unlock_data`. With a HashSet-only
superset check the rewrap loop in `post_rotatekey` could silently
apply two updates to the same credential id (second blob wins); the
test pins the new len-equality guard.
- mod.rs registration-challenge state: replace
`legacy_registration_challenge_rejects_finish_token` (which asserted
a "legacy bare-`PasskeyRegistration` accepted without token" fallback
no longer present) with
`registration_challenge_rejects_unwrapped_legacy_state`, matching
the assertion-side test's stricter contract: any blob that's not the
`{token, state}` wrapper is rejected regardless of whether a token
is sent. Pins the token-binding bypass closure.
pull/7297/head
10 changed files with 213 additions and 47 deletions
Loading…
Reference in new issue