Commit 36816c9
authored
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` |

|

|
### 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 @​tamird in `#​1134 <https://github.com/jpadilla/pyjwt/pull/1134>`__
- Close ``HTTPError`` response to prevent ``ResourceWarning`` on Python 3.14 by @​veeceey in `#​1133 <https://github.com/jpadilla/pyjwt/pull/1133>`__
- Do not keep ``algorithms`` dict in PyJWK instances by @​akx in `#​1143 <https://github.com/jpadilla/pyjwt/pull/1143>`__
- Validate the crit (Critical) Header Parameter defined in RFC 7515 §4.1.11. by @​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 `#​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 `#​964 <https://github.com/jpadilla/pyjwt/pull/964>`__
- Add iterator for JWKSet in `#​1041 <https://github.com/jpadilla/pyjwt/pull/1041>`__
- Validate `iss` claim is a string during encoding and decoding by @​pachewise in `#​1040 <https://github.com/jpadilla/pyjwt/pull/1040>`__
- Improve typing/logic for `options` in decode, decode_complete by @​pachewise in `#​1045 <https://github.com/jpadilla/pyjwt/pull/1045>`__
- Declare float supported type for lifespan and timeout by @​nikitagashkov in `#​1068 <https://github.com/jpadilla/pyjwt/pull/1068>`__
Added
```
- Docs: Add example of using leeway with nbf by
[@​djw8605](https://redirect.github.com/djw8605) in `#​1034
<https://github.com/jpadilla/pyjwt/pull/1034>`\_\_
- Docs: Refactored docs with `autodoc`; added `PyJWS` and
`jwt.algorithms` docs by
[@​pachewise](https://redirect.github.com/pachewise) in
`#​1045 <https://github.com/jpadilla/pyjwt/pull/1045>`\_\_
- Docs: Documentation improvements for "sub" and "jti" claims by
[@​cleder](https://redirect.github.com/cleder) in `#​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 @​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
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
0 commit comments