PyJWT: Unauthenticated RecursionError DoS in pre-verification payload parse (PyJWKClient.get_signing_key_from_jwt / verify_signature=False)
## Summary `PyJWKClient.get_signing_key_from_jwt(token)` — the first step of the JWKS verification flow documented in `docs/usage.rst` — must decode a token's payload before its signature can be checked, via `jwt.api_jwt.decode_complete(token, options={"verify_signature": False})`. That call parses the payload with `json.loads` in `PyJWT._decode_payload` (`jwt/api_jwt.py:297-300`), whose `except` clause catches only `ValueError`. A payload that is valid JSON but nested ~20,000 levels deep makes `json.loads` raise `RecursionError`, which is **not** a `ValueError` and escapes as a raw, undocumented exception type — not `DecodeError`, `InvalidTokenError`, or `PyJWTError`, so every documented error-handling pattern in `docs/usage.rst` misses it. The token needs no valid signature and no network access — the crash happens during payload parsing, before the `kid` is even looked up. A single unauthenticated ~50KB request crashes the caller's auth handler (HTTP 500 / dead worker), repeatably. The same path is reachable through plain `jwt.decode(token, options={"verify_signature": False})` too. Notably, this project already fixed the **identical** bug class for the JWS **header** path — `PyJWS._load` catches `(ValueError, RecursionError)` and wraps it in `DecodeError` (`jwt/api_jws.py:360-361`), and `CHANGELOG.rst` (v2.14.0, "Security") states "Handle deeply nested and malformed JWS/JWK input without uncaught recursion errors." The payload path — the one part of a forged token an unau
GHSA-42vr-xj54-vc7v
Primary source · Revision 1
Imported from structured advisory data. Source references identify upstream records or fields; verify affected versions and your deployed configuration.
Security context
Source reference: osv:GHSA-42vr-xj54-vc7v
pyjwt
Source reference: /affected/0/package/name
- Affected versions
- 2.0.0
Source reference:
/affected/0/versions2.0.0a1Source reference:
/affected/0/versions2.0.0a2Source reference:
/affected/0/versions2.0.1Source reference:
/affected/0/versions2.1.0Source reference:
/affected/0/versions2.10.0Source reference:
/affected/0/versions2.10.1Source reference:
/affected/0/versions2.11.0Source reference:
/affected/0/versions2.12.0Source reference:
/affected/0/versions2.12.1Source reference:
/affected/0/versions2.13.0Source reference:
/affected/0/versions2.14.0Source reference:
/affected/0/versions2.2.0Source reference:
/affected/0/versions2.3.0Source reference:
/affected/0/versions2.4.0Source reference:
/affected/0/versions2.5.0Source reference:
/affected/0/versions2.6.0Source reference:
/affected/0/versions2.7.0Source reference:
/affected/0/versions2.8.0Source reference:
/affected/0/versions2.9.0Source reference:
/affected/0/versions - Fixed versions
- 2.15.0
Source reference:
/affected/0/ranges - Prerequisites
- Not stated in this record
Structured affected-version data
Original package identities and version rules from this source.
[
{
"ranges": [
{
"type": "ECOSYSTEM",
"events": [
{
"introduced": "2.0.0a1"
},
{
"fixed": "2.15.0"
}
]
}
],
"package": {
"name": "pyjwt",
"purl": "pkg:pypi/pyjwt",
"ecosystem": "PyPI"
},
"versions": [
"2.0.0",
"2.0.0a1",
"2.0.0a2",
"2.0.1",
"2.1.0",
"2.10.0",
"2.10.1",
"2.11.0",
"2.12.0",
"2.12.1",
"2.13.0",
"2.14.0",
"2.2.0",
"2.3.0",
"2.4.0",
"2.5.0",
"2.6.0",
"2.7.0",
"2.8.0",
"2.9.0"
],
"database_specific": {
"source": "https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-42vr-xj54-vc7v/GHSA-42vr-xj54-vc7v.json",
"last_known_affected_version_range": "<= 2.14.0"
}
}
]- Related CVEs
- CVE-2026-101918
Source reference:
/aliases
Publisher severity
No explicit severity was provided.
- Event
- #62573 · Revision 1
- Added to Heist Feed
- Publisher date