Skip to content

fix: require validated API key on GET /usage (GHSA-5rhx-xgvv-h78h)#183

Draft
dobby-coder[bot] wants to merge 1 commit into
mainfrom
fix/usage-require-auth-ghsa-5rhx
Draft

fix: require validated API key on GET /usage (GHSA-5rhx-xgvv-h78h)#183
dobby-coder[bot] wants to merge 1 commit into
mainfrom
fix/usage-require-auth-ghsa-5rhx

Conversation

@dobby-coder

@dobby-coder dobby-coder Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Addresses the access-control issue tracked in the draft security advisory GHSA-5rhx-xgvv-h78h (see #182). Specifics of the vulnerability are intentionally kept in the private advisory.

Changes

  • Enforce authentication on GET /usage by construction. Adds a ValidatedApiKey request guard whose FromRequest fails the request rather than degrading to the anonymous default tier:

    • NoCredentials / Rejected401 Unauthorized
    • PkgUnreachable503 Service Unavailable (the key cannot be confirmed)

    This contrasts with the existing ApiKey guard, which always returns Outcome::Success. /usage now carries ValidatedApiKey, so the "authenticated" intent is enforced by the type system. Usage is keyed to the validated tenant; the email query parameter is now optional and only echoed back in the response — it no longer drives the lookup.

  • Constant-time cryptify_token comparison. New cryptify_tokens_match helper using subtle::ConstantTimeEq, mirroring recovery_tokens_match. Applied in check_cryptify_token (finalize) and the chunk-upload token checks (upload_chunk, classify_chunk_request).

  • Regression test usage_rejects_unauthenticated_request: an unauthenticated GET /usage now returns 401.

  • OpenAPI + CHANGELOG updated to reflect the auth requirement.

Behavioural note

GET /usage now requires a valid Authorization: Bearer PG-… API key. Callers on the default (no-API-key) tier that previously used this endpoint will receive 401.

Verification

  • cargo fmt --all -- --check
  • cargo clippy --all-targets -- -D warnings
  • cargo test --all-targets ✅ (129 passed)

Kept as draft pending review.

The `/usage` route used the `ApiKey` guard, whose `FromRequest` always
succeeds (yielding the anonymous default tier when no key is present).
The handler's default-tier branch then looked up usage by the
caller-supplied `email` query parameter with no ownership check, so any
unauthenticated caller could probe usage for an arbitrary address.

Introduce a `ValidatedApiKey` request guard that fails the request
(401 on NoCredentials/Rejected, 503 on PkgUnreachable) so the
"authenticated" intent is enforced by the type system. Switch `/usage`
to that guard and key the lookup on the validated tenant; the `email`
query param is now optional and only echoed back.

Also make the `cryptify_token` comparisons constant-time via
`subtle::ConstantTimeEq` (new `cryptify_tokens_match` helper), mirroring
`recovery_tokens_match`, in `check_cryptify_token`, `upload_chunk`, and
`classify_chunk_request`.

Adds a regression test asserting unauthenticated `/usage` returns 401,
and updates the OpenAPI description.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants