This release includes 1 security fix for security teams reviewing exposed deployments.
Affected surfaces
ReleasePort's take
Moderate signalThe release patches two critical security flaws: a CSRF bypass via unauthenticated API token handling and a chained SSO‑only account takeover in the /change_password route.
Why it matters: Both vulnerabilities carry severity 95; patching immediately prevents unauthorized access for all users interacting with API tokens or password change flows.
Summary
AI summaryCSRF bypass and chained SSO-only account takeover vulnerabilities are fixed.
Changes in this release
| Type | Severity | Summary | CVE |
|---|---|---|---|
| Security | Critical |
Fixes CSRF bypass via unauthenticated API token parameter (GHSA-x4q4-3ww4-h329). Fixes CSRF bypass via unauthenticated API token parameter (GHSA-x4q4-3ww4-h329). Source: llm_adapter@2026-06-05 Confidence: high |
— |
| Security | Critical |
Prevents chained SSO-only account takeover in /change_password endpoint. Prevents chained SSO-only account takeover in /change_password endpoint. Source: llm_adapter@2026-06-05 Confidence: high |
— |
| Dependency | Low |
No dependency changes introduced in this release. No dependency changes introduced in this release. Source: llm_adapter@2026-06-05 Confidence: low |
— |
| Bugfix | High |
Adds per‑request CSRF token validation using header authentication only; query‑string tokens are rejected. Adds per‑request CSRF token validation using header authentication only; query‑string tokens are rejected. Source: llm_adapter@2026-06-05 Confidence: high |
— |
| Bugfix | Medium |
Updates /forgot-password to support password reset for SSO‑only users via email. Updates /forgot-password to support password reset for SSO‑only users via email. Source: llm_adapter@2026-06-05 Confidence: high |
— |
| Refactor | Low |
Disables Flask‑WTF automatic CSRF check (`WTF_CSRF_CHECK_DEFAULT = False`) and uses custom wrapper. Disables Flask‑WTF automatic CSRF check (`WTF_CSRF_CHECK_DEFAULT = False`) and uses custom wrapper. Source: llm_adapter@2026-06-05 Confidence: high |
— |
Full changelog
v0.8.21-alpha — Security: CSRF bypass + chained SSO-only account takeover
Security patch release on top of v0.8.20-alpha. All users should upgrade promptly. Tracked as GitHub Security Advisory GHSA-x4q4-3ww4-h329.
Fixed
-
CSRF bypass via the unauthenticated API token parameter (CWE-287, CVSS 7.1). The
csrf_exempt_for_api_tokensbefore_request hook calledcsrf.exempt(view_func)from inside a request handler whenever the request carried a token-shaped value, without validating the token against the database. Two consequences:csrf.exempt()mutates Flask-WTF's process-global_exempt_viewsset permanently, so the exemption persisted for the lifetime of the worker — one bogus request was enough to disable CSRF on the targeted endpoint indefinitely.- The query-string token (
?token=...) can be set by a Simple Cross-Origin Request without triggering CORS preflight, so a victim's browser could be tricked into making the disabling request from an attacker-controlled page. - Chained with the next finding, this enabled remote modification of arbitrary user state from a cross-origin attacker page: changing summary prompts, transcription settings, even toggling admin privileges.
The hook is gone. CSRF skipping is now a per-request decision made by a new
csrf_token_aware_checkbefore_request handler that calls a newload_user_from_token_headers_only()helper. That helper hashes the token, looks it up in the database, and checksis_valid(), and only accepts the token from the three request headers (Authorization, X-API-Token, API-Token) — never from the query string. Flask-WTF's automatic CSRF check is disabled (WTF_CSRF_CHECK_DEFAULT = False) so only our wrapper runs. -
Chained SSO-only account takeover in
/change_password. Whencurrent_user.passwordwas None (every SSO-only account), the current-password check was skipped and the route silently set a new password. Combined with the CSRF bypass above, this allowed an attacker to set a password on a victim's SSO-only account from a cross-origin page and then log in directly with the new credentials, bypassing SSO entirely. The route now refuses to act on accounts with no existing local password and directs users to the password-reset flow instead. To preserve the legitimate "add a password so I can later unlink SSO" workflow,/forgot-passwordnow also issues reset emails for SSO-only users; the user's email account is the proof-of-ownership gate (the same trust boundary every password-reset flow uses), so the takeover is closed while the flow keeps working.
Tests
tests/test_csrf_token_bypass_fix.py — 12 regression tests covering both attacks from the advisory and the backwards-compat surfaces: query-string and header fake-token rejection, the valid-header-token per-request bypass, the valid-query-string-token-on-non-v1 rejection, the SSO-only change_password refusal, the end-to-end SSO-only-add-password flow via forgot_password, the SMTP-not-configured safety net, all four documented API token methods on /api/v1/*, the ?token= POST on /api/v1/* (preserved by blueprint-level exemption), the browser session POST with a valid CSRF token, the change_password happy path for users with an existing password, and the Authorization: Bearer POST on /api/v1/*. The exempt-set immutability is asserted before and after each attempted bypass.
Credits
Reported privately under GitHub's Private Vulnerability Reporting by @Irench1k. Thank you for the detailed report, the working proof-of-concept, and the patience while the disclosure timeline aligned.
Migration
No configuration changes are required. Existing API-token automation continues to work via the v1 endpoints (still blueprint-level CSRF-exempt) and via header authentication on any other endpoint. Automation that relied on the ?token=... query parameter to bypass CSRF on state-changing requests will need to switch to header authentication; this was always the documented method.
Upgrade is the usual docker compose pull && docker compose up -d.
Breaking Changes
- Removed `csrf_exempt_for_api_tokens` before_request hook; query-string token bypass (`?token=`) no longer disables CSRF.
Security Fixes
- GHSA-x4q4-3ww4-h329 — Fixed CSRF bypass (CWE-287, CVSS 7.1) via unauthenticated API token parameter and chained SSO-only account takeover in `/change_password`.
Weekly OSS security release digest.
The CVE patches and breaking changes that affected production tools this week. One email, every Sunday.
No spam, unsubscribe anytime.
Share this release
About speakr
Speakr is a personal, self-hosted web application designed for transcribing audio recordings
Related context
Related tools
Beta — feedback welcome: [email protected]