Skip to content

Commit 36816c9

Browse files
chore(deps): update dependency pyjwt to v2.12.0 [security] (#27)
This PR contains the following updates: | Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) | |---|---|---|---| | [PyJWT](https://redirect.github.com/jpadilla/pyjwt) | `2.9.0` → `2.12.0` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/pyjwt/2.12.0?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/pyjwt/2.9.0/2.12.0?slim=true) | ### GitHub Vulnerability Alerts #### [CVE-2026-32597](https://redirect.github.com/jpadilla/pyjwt/security/advisories/GHSA-752w-5fwx-jx9f) ## Summary PyJWT does not validate the `crit` (Critical) Header Parameter defined in RFC 7515 §4.1.11. When a JWS token contains a `crit` array listing extensions that PyJWT does not understand, the library accepts the token instead of rejecting it. This violates the **MUST** requirement in the RFC. This is the same class of vulnerability as CVE-2025-59420 (Authlib), which received CVSS 7.5 (HIGH). --- ## RFC Requirement RFC 7515 §4.1.11: > The "crit" (Critical) Header Parameter indicates that extensions to this > specification and/or [JWA] are being used that **MUST** be understood and > processed. [...] If any of the listed extension Header Parameters are > **not understood and supported** by the recipient, then the **JWS is invalid**. --- ## Proof of Concept ```python import jwt # PyJWT 2.8.0 import hmac, hashlib, base64, json # Construct token with unknown critical extension header = {"alg": "HS256", "crit": ["x-custom-policy"], "x-custom-policy": "require-mfa"} payload = {"sub": "attacker", "role": "admin"} def b64url(data): return base64.urlsafe_b64encode(data).rstrip(b"=").decode() h = b64url(json.dumps(header, separators=(",", ":")).encode()) p = b64url(json.dumps(payload, separators=(",", ":")).encode()) sig = b64url(hmac.new(b"secret", f"{h}.{p}".encode(), hashlib.sha256).digest()) token = f"{h}.{p}.{sig}" # Should REJECT — x-custom-policy is not understood by PyJWT try: result = jwt.decode(token, "secret", algorithms=["HS256"]) print(f"ACCEPTED: {result}") # Output: ACCEPTED: {'sub': 'attacker', 'role': 'admin'} except Exception as e: print(f"REJECTED: {e}") ``` **Expected:** `jwt.exceptions.InvalidTokenError: Unsupported critical extension: x-custom-policy` **Actual:** Token accepted, payload returned. ### Comparison with RFC-compliant library ```python # jwcrypto — correctly rejects from jwcrypto import jwt as jw_jwt, jwk key = jwk.JWK(kty="oct", k=b64url(b"secret")) jw_jwt.JWT(jwt=token, key=key, algs=["HS256"]) # raises: InvalidJWSObject('Unknown critical header: "x-custom-policy"') ``` --- ## Impact - **Split-brain verification** in mixed-library deployments (e.g., API gateway using jwcrypto rejects, backend using PyJWT accepts) - **Security policy bypass** when `crit` carries enforcement semantics (MFA, token binding, scope restrictions) - **Token binding bypass** — RFC 7800 `cnf` (Proof-of-Possession) can be silently ignored - See CVE-2025-59420 for full impact analysis --- ## Suggested Fix In `jwt/api_jwt.py`, add validation in `_validate_headers()` or `decode()`: ```python _SUPPORTED_CRIT = {"b64"} # Add extensions PyJWT actually supports def _validate_crit(self, headers: dict) -> None: crit = headers.get("crit") if crit is None: return if not isinstance(crit, list) or len(crit) == 0: raise InvalidTokenError("crit must be a non-empty array") for ext in crit: if ext not in self._SUPPORTED_CRIT: raise InvalidTokenError(f"Unsupported critical extension: {ext}") if ext not in headers: raise InvalidTokenError(f"Critical extension {ext} not in header") ``` --- ## CWE - CWE-345: Insufficient Verification of Data Authenticity - CWE-863: Incorrect Authorization ## References - [RFC 7515 §4.1.11](https://www.rfc-editor.org/rfc/rfc7515.html#section-4.1.11) - [CVE-2025-59420 — Authlib crit bypass (CVSS 7.5)](https://osv.dev/vulnerability/GHSA-9ggr-2464-2j32) - [RFC 7800 — Proof-of-Possession Key Semantics](https://www.rfc-editor.org/rfc/rfc7800) --- ### Release Notes <details> <summary>jpadilla/pyjwt (PyJWT)</summary> ### [`v2.12.0`](https://redirect.github.com/jpadilla/pyjwt/blob/HEAD/CHANGELOG.rst#v2120-httpsgithubcomjpadillapyjwtcompare21102120) [Compare Source](https://redirect.github.com/jpadilla/pyjwt/compare/2.11.0...2.12.0) Fixed ``` - Annotate PyJWKSet.keys for pyright by @&#8203;tamird in `#&#8203;1134 <https://github.com/jpadilla/pyjwt/pull/1134>`__ - Close ``HTTPError`` response to prevent ``ResourceWarning`` on Python 3.14 by @&#8203;veeceey in `#&#8203;1133 <https://github.com/jpadilla/pyjwt/pull/1133>`__ - Do not keep ``algorithms`` dict in PyJWK instances by @&#8203;akx in `#&#8203;1143 <https://github.com/jpadilla/pyjwt/pull/1143>`__ - Validate the crit (Critical) Header Parameter defined in RFC 7515 §4.1.11. by @&#8203;dmbs335 in `GHSA-752w-5fwx-jx9f <https://github.com/jpadilla/pyjwt/security/advisories/GHSA-752w-5fwx-jx9f>`__ - Use PyJWK algorithm when encoding without explicit algorithm in `#&#8203;1148 <https://github.com/jpadilla/pyjwt/pull/1148>`__ Added ``` - Docs: Add `PyJWKClient` API reference and document the two-tier caching system (JWK Set cache and signing key LRU cache). ### [`v2.11.0`](https://redirect.github.com/jpadilla/pyjwt/blob/HEAD/CHANGELOG.rst#Unreleased-httpsgithubcomjpadillapyjwtcompare2110HEAD) [Compare Source](https://redirect.github.com/jpadilla/pyjwt/compare/2.10.1...2.11.0) Fixed ``` Added ``` ### [`v2.10.1`](https://redirect.github.com/jpadilla/pyjwt/blob/HEAD/CHANGELOG.rst#Unreleased-httpsgithubcomjpadillapyjwtcompare2101HEAD) [Compare Source](https://redirect.github.com/jpadilla/pyjwt/compare/2.10.0...2.10.1) Fixed ``` - Validate key against allowed types for Algorithm family in `#&#8203;964 <https://github.com/jpadilla/pyjwt/pull/964>`__ - Add iterator for JWKSet in `#&#8203;1041 <https://github.com/jpadilla/pyjwt/pull/1041>`__ - Validate `iss` claim is a string during encoding and decoding by @&#8203;pachewise in `#&#8203;1040 <https://github.com/jpadilla/pyjwt/pull/1040>`__ - Improve typing/logic for `options` in decode, decode_complete by @&#8203;pachewise in `#&#8203;1045 <https://github.com/jpadilla/pyjwt/pull/1045>`__ - Declare float supported type for lifespan and timeout by @&#8203;nikitagashkov in `#&#8203;1068 <https://github.com/jpadilla/pyjwt/pull/1068>`__ Added ``` - Docs: Add example of using leeway with nbf by [@&#8203;djw8605](https://redirect.github.com/djw8605) in `#&#8203;1034 <https://github.com/jpadilla/pyjwt/pull/1034>`\_\_ - Docs: Refactored docs with `autodoc`; added `PyJWS` and `jwt.algorithms` docs by [@&#8203;pachewise](https://redirect.github.com/pachewise) in `#&#8203;1045 <https://github.com/jpadilla/pyjwt/pull/1045>`\_\_ - Docs: Documentation improvements for "sub" and "jti" claims by [@&#8203;cleder](https://redirect.github.com/cleder) in `#&#8203;1088 <https://github.com/jpadilla/pyjwt/pull/1088>` ### [`v2.10.0`](https://redirect.github.com/jpadilla/pyjwt/blob/HEAD/CHANGELOG.rst#v2101-httpsgithubcomjpadillapyjwtcompare21002101) [Compare Source](https://redirect.github.com/jpadilla/pyjwt/compare/2.9.0...2.10.0) Fixed ``` - Prevent partial matching of `iss` claim by @&#8203;fabianbadoi in `GHSA-75c5-xw7c-p5pm <https://github.com/jpadilla/pyjwt/security/advisories/GHSA-75c5-xw7c-p5pm>`__ ``` </details> --- ### Configuration 📅 **Schedule**: Branch creation - "" (UTC), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Enabled. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 🔕 **Ignore**: Close this PR and you won't be reminded about this update again. --- - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/NiklasRosenstein/python-github-bot-api). <!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0My42Ni40IiwidXBkYXRlZEluVmVyIjoiNDMuNjYuNCIsInRhcmdldEJyYW5jaCI6ImRldmVsb3AiLCJsYWJlbHMiOlsiZGVwZW5kZW5jaWVzIl19--> Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
1 parent 5d09367 commit 36816c9

1 file changed

Lines changed: 6 additions & 3 deletions

File tree

uv.lock

Lines changed: 6 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)