Skip to content

v0.8.2: security hardening + Google prefill#14

Merged
pretyflaco merged 1 commit into
mainfrom
security-0.8.2
Jun 13, 2026
Merged

v0.8.2: security hardening + Google prefill#14
pretyflaco merged 1 commit into
mainfrom
security-0.8.2

Conversation

@pretyflaco

Copy link
Copy Markdown
Owner

Response to a 0.8.x security audit, plus a Google sign-in prefill fix.

Security

  • H1 — NIP-98 replay protection. A valid login event could be replayed within its ~180s freshness window to mint a second session JWT. nip98.verify_nip98 now records consumed event ids (in-memory TTL set) and rejects reuse (401).
  • H2 — header-injection-resistant login URL. _login_url reconstructed the NIP-98 u-tag target from X-Forwarded-Proto/Host (spoofable if uvicorn is reached directly). New VEZIR_PUBLIC_URL (config public_url) pins it; falls back to the old header behavior when unset. Set in prod.
  • H3 hardening — Google Workspace-domain check is now exact (rsplit("@",1)) instead of a suffix endswith.
  • M5/health no longer returns data_dir.
  • M4 — loud startup warning if VEZIR_DISABLE_RATELIMIT is set.
  • DST_parse_iso (+ doctor/cli) use calendar.timegm (true UTC) instead of mktime(...) - time.timezone.

Fixed

  • Google prefill. Google's device endpoint returns only a bare verification_url; device/start now synthesizes verification_url_complete (…/device?user_code=<CODE>) so clients open a pre-filled page.

Audit notes

  • H1/H2/M5/M4/DST: valid → fixed.
  • H3 was essentially a false positiveendswith("@blinkbtc.com") already anchors on the @ (verified: user@evilblinkbtc.com, subdomains, suffix tricks all fail); allowlist gates regardless. Hardened anyway.
  • "No google_auth tests" was staletests/test_google_auth.py (14 tests) has existed since PR Add Google sign-in for Blink members (OAuth device flow) #11.
  • Deferred: JWT per-token revocation, session-secret hot-rotation, NIP-46 "ack" tightening, infra LOWs.

Tests

11 new (replay→401; VEZIR_PUBLIC_URL ignores spoofed headers; exact-domain rejects lookalike/subdomain; synthesized complete URL; /health omits data_dir; _parse_iso UTC). Full suite: 685 passed. ruff + mypy clean.

Version 0.8.1 → 0.8.2.

…ogle prefill

Response to a 0.8.x security audit plus a Google sign-in prefill fix.

Security:
- H1 NIP-98 replay protection: in-memory TTL set of consumed event ids in
  nip98.verify_nip98; a login event can't mint a second JWT within its
  freshness window (401 on reuse).
- H2 header-injection-resistant login URL: new VEZIR_PUBLIC_URL (config
  public_url); _login_url pins to it instead of trusting X-Forwarded-Proto/
  Host, so a caller reaching uvicorn directly can't spoof the NIP-98 u-tag
  target. Falls back to header reconstruction when unset (local/dev).
- H3 hardening: Google Workspace-domain check is now exact (rsplit("@",1))
  not a suffix endswith.
- M5: /health no longer returns data_dir (info disclosure).
- M4: loud startup warning when VEZIR_DISABLE_RATELIMIT is set.
- DST: _parse_iso (+ doctor/cli siblings) use calendar.timegm (true UTC)
  instead of mktime(...) - time.timezone.

Fixed:
- Google prefill: Google's device endpoint returns only a bare
  verification_url; device/start now synthesizes verification_url_complete
  (.../device?user_code=<CODE>) so clients open a pre-filled page.

Deferred (audit, tracked in operator plan): JWT per-token revocation,
session-secret hot-rotation, NIP-46 "ack" tightening, infra LOWs.
Note: audit's H3 was essentially a false positive (endswith already anchors
on '@'); hardened anyway. Its "no google_auth tests" claim was stale —
test_google_auth.py has existed since PR #11.

11 new tests (replay reuse->401, VEZIR_PUBLIC_URL ignores spoofed headers,
exact-domain reject lookalike/subdomain, synthesized complete URL, /health
omits data_dir, _parse_iso UTC). Full suite 685 passed. ruff + mypy clean.
@pretyflaco pretyflaco merged commit 4867640 into main Jun 13, 2026
5 checks passed
@pretyflaco pretyflaco deleted the security-0.8.2 branch June 13, 2026 15:26
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.

1 participant