diff --git a/meta-python/recipes-devtools/python/python3-pyjwt/CVE-2026-48525.patch b/meta-python/recipes-devtools/python/python3-pyjwt/CVE-2026-48525.patch new file mode 100644 index 0000000000..c43e576118 --- /dev/null +++ b/meta-python/recipes-devtools/python/python3-pyjwt/CVE-2026-48525.patch @@ -0,0 +1,115 @@ +From 91ac94bdc85d24c6d35a5f6cdd58eb8c6c4df0f0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jos=C3=A9=20Padilla?= +Date: Mon, 3 Aug 2026 03:21:30 -0700 +Subject: [PATCH] api_jws: reject non-empty detached payload segments + +For b64=false tokens, reject a non-empty compact payload segment before +Base64URL decoding. The segment is unused when detached_payload is +supplied, so decoding attacker-controlled data only consumes CPU and +memory. + +CVE: CVE-2026-48525 +Upstream-Status: Backport [https://github.com/jpadilla/pyjwt/commit/95791b1759b8aa4f2203575d344d5c78564cdc81] + +Backport Changes: +- Extracted only the CVE-2026-48525 detached-payload segment check and + regression test from the bundled upstream 2.13.0 commit. The other + requested CVE fixes are carried as separate patches. +- Adapted the hunk and test locations to PyJWT 2.8.0, used module-level + hashlib and hmac imports, and shortened explanatory comments without + changing the tested behavior. +- Excluded the separate RFC 7797 b64/crit hardening bundled upstream. +- Omitted the 2.13.0 version and changelog updates, CVE-2026-48523 (which + does not affect 2.8.0), and unrelated hardening from the bundled commit. + +(cherry picked from commit 95791b1759b8aa4f2203575d344d5c78564cdc81) +Signed-off-by: Hetvi Thakar +--- + jwt/api_jws.py | 17 +++++++++++++---- + tests/test_api_jws.py | 34 +++++++++++++++++++++++++++++++++- + 2 files changed, 46 insertions(+), 5 deletions(-) + +diff --git a/jwt/api_jws.py b/jwt/api_jws.py +index 1750442..0d5ab2b 100644 +--- a/jwt/api_jws.py ++++ b/jwt/api_jws.py +@@ -274,10 +274,19 @@ class PyJWS: + if not isinstance(header, dict): + raise DecodeError("Invalid header string: must be a json object") + +- try: +- payload = base64url_decode(payload_segment) +- except (TypeError, binascii.Error) as err: +- raise DecodeError("Invalid payload padding") from err ++ if header.get("b64", True) is False: ++ # Detached compact serialization requires an empty payload ++ # segment. Reject it before decoding attacker-controlled data. ++ if payload_segment: ++ raise DecodeError( ++ "Payload segment must be empty when 'b64' is false." ++ ) ++ payload = b"" ++ else: ++ try: ++ payload = base64url_decode(payload_segment) ++ except (TypeError, binascii.Error) as err: ++ raise DecodeError("Invalid payload padding") from err + + try: + signature = base64url_decode(crypto_segment) +diff --git a/tests/test_api_jws.py b/tests/test_api_jws.py +index 434874b..29f84a7 100644 +--- a/tests/test_api_jws.py ++++ b/tests/test_api_jws.py +@@ -1,3 +1,5 @@ ++import hashlib ++import hmac + import json + from decimal import Decimal + +@@ -11,7 +13,7 @@ from jwt.exceptions import ( + InvalidSignatureError, + InvalidTokenError, + ) +-from jwt.utils import base64url_decode ++from jwt.utils import base64url_decode, base64url_encode + from jwt.warnings import RemovedInPyjwt3Warning + + from .utils import crypto_required, key_path, no_crypto_required +@@ -766,6 +768,36 @@ class TestJWS: + assert "b64" not in msg_header_obj + assert msg_payload + ++ def test_decode_b64_false_rejects_non_empty_payload_segment( ++ self, jws: PyJWS, payload: bytes ++ ) -> None: ++ secret = "secret" ++ header = { ++ "typ": "JWT", ++ "alg": "HS256", ++ "b64": False, ++ "crit": ["b64"], ++ } ++ encoded_header = base64url_encode( ++ json.dumps(header, separators=(",", ":")).encode() ++ ) ++ attacker_segment = b"A" * 1024 ++ signing_input = b".".join([encoded_header, payload]) ++ signature = hmac.new( ++ secret.encode(), signing_input, hashlib.sha256 ++ ).digest() ++ token = b".".join( ++ [encoded_header, attacker_segment, base64url_encode(signature)] ++ ).decode() ++ ++ with pytest.raises(DecodeError, match="Payload segment must be empty"): ++ jws.decode( ++ token, ++ secret, ++ algorithms=["HS256"], ++ detached_payload=payload, ++ ) ++ + def test_decode_detached_content_without_proper_argument(self, jws): + example_jws = ( + "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2V9" diff --git a/meta-python/recipes-devtools/python/python3-pyjwt_2.8.0.bb b/meta-python/recipes-devtools/python/python3-pyjwt_2.8.0.bb index 7b72cfb4bd..fc3e0bc31d 100644 --- a/meta-python/recipes-devtools/python/python3-pyjwt_2.8.0.bb +++ b/meta-python/recipes-devtools/python/python3-pyjwt_2.8.0.bb @@ -9,6 +9,7 @@ SRC_URI += " \ file://CVE-2026-32597.patch \ file://CVE-2026-48522.patch \ file://CVE-2026-48524.patch \ + file://CVE-2026-48525.patch \ " SRC_URI[sha256sum] = "57e28d156e3d5c10088e0c68abb90bfac3df82b40a71bd0daa20c65ccd5c23de"