[meta-python][wrynose][PATCH 4/6] python3-pyjwt: Fix CVE-2026-48525

"Hetvi Thakar -X (hthakar - E INFOCHIPS PRIVATE LIMITED at Cisco)" <[email protected]>
Newsgroups org.openembedded.lists.openembedded-devel
Message-ID <[email protected]>
From: Hetvi Thakar <[email protected]>

This patch applies the upstream 2.13.0 backport for
CVE-2026-48525. The upstream fix commit is referenced in [1],
and the public CVE advisory is referenced in [2].

[1] https://github.com/jpadilla/pyjwt/commit/95791b1759b8aa4f2203575d344d5c78564cdc81
[2] https://github.com/advisories/GHSA-w7vc-732c-9m39

Signed-off-by: Hetvi Thakar <[email protected]>
---
 .../python/files/CVE-2026-48525.patch         | 146 ++++++++++++++++++
 .../python/python3-pyjwt_2.12.1.bb            |   1 +
 2 files changed, 147 insertions(+)
 create mode 100644 meta-python/recipes-devtools/python/files/CVE-2026-48525.patch

diff --git a/meta-python/recipes-devtools/python/files/CVE-2026-48525.patch b/meta-python/recipes-devtools/python/files/CVE-2026-48525.patch
new file mode 100644
index 0000000000..9b7ffb97eb
--- /dev/null
+++ b/meta-python/recipes-devtools/python/files/CVE-2026-48525.patch
@@ -0,0 +1,146 @@
+From e6152fb27dd439bf4541aab9b26a93134c48135f Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Jos=C3=A9=20Padilla?= <[email protected]>
+Date: Thu, 21 May 2026 14:11:10 -0400
+Subject: [PATCH] Bundle security fixes and hardening into 2.13.0
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Security:
+- `HMACAlgorithm.prepare_key` rejects JWK JSON documents passed as raw
+  HMAC secrets to close an algorithm-confusion gap not covered by the
+  existing PEM/SSH guard. Reported by @aradona91 in GHSA-xgmm-8j9v-c9wx.
+- Bind the JWT header `alg` to `PyJWK.algorithm_name` during verification
+  so the caller's `algorithms` allow-list cannot be bypassed when decoding
+  with a `PyJWK` / `PyJWKClient` key. Reported by @sushi-gif in
+  GHSA-jq35-7prp-9v3f.
+- Skip the unconditional base64 decode of the compact-form payload
+  segment when `b64=false` is set, and require that segment to be empty
+  (RFC 7515 Appendix F detached form). Closes an unauthenticated DoS
+  amplifier. Reported by @thesmartshadow in GHSA-w7vc-732c-9m39.
+- `PyJWKClient` rejects any URI whose scheme is not `http` or `https` so
+  attacker-influenced URIs cannot read local files or reach unintended
+  schemes via urllib's default `file://` / `ftp://` / `data:` handlers.
+  Reported by @KEIJOT in GHSA-993g-76c3-p5m4.
+- Preserve the cached JWK Set on fetch errors in `PyJWKClient.fetch_data`.
+  The previous `finally`-block `put(None)` pattern cleared the cache on
+  any transient outage. Reported by @eddieran in GHSA-fhv5-28vv-h8m8.
+
+Fixes:
+- Reject empty HMAC keys outright in `HMACAlgorithm.prepare_key` with
+  `InvalidKeyError` instead of accepting them with only a warning.
+  Hardening prompted by reports from @SnailSploit and @spartan8806.
+- Forward per-call `options` (including `enforce_minimum_key_length`)
+  from `PyJWT.decode` through to `PyJWS._verify_signature`. Thanks to
+  @WLUB.
+- RFC 7797 §3 compliance for `b64=false`: encoder auto-adds `"b64"` to
+  `crit`; decoder rejects tokens that set `b64=false` without listing
+  it in `crit`. Thanks to @MachineLearning-Nerd.
+
+CVE: CVE-2026-48525
+Upstream-Status: Backport [https://github.com/jpadilla/pyjwt/commit/95791b1759b8aa4f2203575d344d5c78564cdc81]
+
+Backport Changes:
+- Split out the b64=false payload-decoding DoS fix because the upstream
+  commit bundles multiple CVEs.
+- Omitted CHANGELOG.rst because it conflicted and is release documentation.
+- Carried the focused
+  `test_decode_b64_false_rejects_non_empty_payload_segment` regression test.
+  Omitted other tests/test_api_jws.py hunks for unrelated bundled fixes.
+- Omitted the 2.13.0 version bump and other bundled fixes; applicable CVE
+  fixes are carried in separate patches.
+
+(cherry picked from commit 95791b1759b8aa4f2203575d344d5c78564cdc81)
+Signed-off-by: Hetvi Thakar <[email protected]>
+---
+ jwt/api_jws.py        | 22 ++++++++++++++++++----
+ tests/test_api_jws.py | 36 +++++++++++++++++++++++++++++++++++-
+ 2 files changed, 53 insertions(+), 5 deletions(-)
+
+diff --git a/jwt/api_jws.py b/jwt/api_jws.py
+index 91d2ae3..5a91bce 100644
+--- a/jwt/api_jws.py
++++ b/jwt/api_jws.py
+@@ -317,10 +317,24 @@ 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 payload form (RFC 7515 Appendix F): the compact-form
++            # payload segment must be empty; the caller supplies the actual
++            # payload via the `detached_payload` argument in decode_complete.
++            # Skipping the base64 decode here removes an unauthenticated work
++            # amplifier — otherwise an attacker can inflate the unused
++            # segment to force CPU + memory cost before the signature is
++            # even checked.
++            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 0715b9e..01de813 100644
+--- a/tests/test_api_jws.py
++++ b/tests/test_api_jws.py
+@@ -12,7 +12,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
+@@ -984,6 +984,40 @@ 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:
++        # RFC 7515 Appendix F detached form: when b64=false, the compact-
++        # serialization payload segment must be empty. PyJWT must reject a
++        # non-empty middle segment without doing any base64-decoding work
++        # on it — that decode used to be the unauthenticated DoS amplifier.
++        secret = "secret"
++        import hmac as _hmac
++        import hashlib as _hashlib
++
++        header_obj = {
++            "typ": "JWT",
++            "alg": "HS256",
++            "b64": False,
++            "crit": ["b64"],
++        }
++        header_b64 = base64url_encode(
++            json.dumps(header_obj, separators=(",", ":")).encode()
++        )
++        # Stuff the middle segment with arbitrary attacker-controlled bytes.
++        # This should be rejected without being base64-decoded.
++        attacker_segment = b"A" * 1024
++        signing_input = b".".join([header_b64, payload])
++        sig = _hmac.new(secret.encode(), signing_input, _hashlib.sha256).digest()
++        token = b".".join(
++            [header_b64, attacker_segment, base64url_encode(sig)]
++        ).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: PyJWS) -> None:
+         example_jws = (
+             "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImI2NCI6ZmFsc2V9"
diff --git a/meta-python/recipes-devtools/python/python3-pyjwt_2.12.1.bb b/meta-python/recipes-devtools/python/python3-pyjwt_2.12.1.bb
index ff3eb8814d..3e68f9c0b3 100644
--- a/meta-python/recipes-devtools/python/python3-pyjwt_2.12.1.bb
+++ b/meta-python/recipes-devtools/python/python3-pyjwt_2.12.1.bb
@@ -8,6 +8,7 @@ LIC_FILES_CHKSUM = "file://LICENSE;md5=e4b56d2c9973d8cf54655555be06e551"
 SRC_URI += "file://CVE-2026-48522.patch \
            file://CVE-2026-48523.patch \
            file://CVE-2026-48524.patch \
+           file://CVE-2026-48525.patch \
            "
 
 SRC_URI[sha256sum] = "c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b"
-- 
2.35.6
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.