[meta-python][wrynose][PATCH 3/6] python3-pyjwt: Fix CVE-2026-48524

"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-48524. 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-fhv5-28vv-h8m8

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

diff --git a/meta-python/recipes-devtools/python/files/CVE-2026-48524.patch b/meta-python/recipes-devtools/python/files/CVE-2026-48524.patch
new file mode 100644
index 0000000000..d583ea1275
--- /dev/null
+++ b/meta-python/recipes-devtools/python/files/CVE-2026-48524.patch
@@ -0,0 +1,125 @@
+From 9d971fad56d6571a3bfc037ca21e3be694b9cabd 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-48524
+Upstream-Status: Backport [https://github.com/jpadilla/pyjwt/commit/95791b1759b8aa4f2203575d344d5c78564cdc81]
+
+Backport Changes:
+- Split out the cached-JWK-set preservation fix because the upstream
+  commit bundles multiple CVEs.
+- Omitted CHANGELOG.rst because it conflicted and is release documentation.
+- 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/jwks_client.py        | 14 ++++++++------
+ tests/test_jwks_client.py | 16 ++++++++++++++--
+ 2 files changed, 22 insertions(+), 8 deletions(-)
+
+diff --git a/jwt/jwks_client.py b/jwt/jwks_client.py
+index 8d1b0c4..41f333a 100644
+--- a/jwt/jwks_client.py
++++ b/jwt/jwks_client.py
+@@ -113,7 +113,6 @@ class PyJWKClient:
+         :returns: The parsed JWK Set as a dictionary.
+         :raises PyJWKClientConnectionError: If the HTTP request fails.
+         """
+-        jwk_set: Any = None
+         try:
+             r = urllib.request.Request(url=self.uri, headers=self.headers)
+             with urllib.request.urlopen(
+@@ -126,11 +125,14 @@ class PyJWKClient:
+             raise PyJWKClientConnectionError(
+                 f'Fail to fetch data from the url, err: "{e}"'
+             ) from e
+-        else:
+-            return jwk_set
+-        finally:
+-            if self.jwk_set_cache is not None:
+-                self.jwk_set_cache.put(jwk_set)
++
++        # Only update the cache on a successful fetch. Writing in a
++        # `finally` block with `jwk_set=None` on error clears any
++        # previously-cached JWKS, turning a transient outage into a cache
++        # wipe that breaks legitimate auth.
++        if self.jwk_set_cache is not None:
++            self.jwk_set_cache.put(jwk_set)
++        return jwk_set
+ 
+     def get_jwk_set(self, refresh: bool = False) -> PyJWKSet:
+         """Return the JWK Set, using the cache when available.
+diff --git a/tests/test_jwks_client.py b/tests/test_jwks_client.py
+index d6793cc..da68664 100644
+--- a/tests/test_jwks_client.py
++++ b/tests/test_jwks_client.py
+@@ -288,18 +288,30 @@ class TestPyJWKClient:
+ 
+         assert repeated_call.call_count == 1
+ 
+-    def test_get_jwt_set_failed_request_should_clear_cache(self) -> None:
++    def test_get_jwt_set_failed_refresh_preserves_cached_jwks(self) -> None:
++        # Regression: a transient fetch failure used to clear the cache via
++        # the previous `finally: put(jwk_set=None)` pattern, turning one bad
++        # request from the JWKS endpoint into application-wide auth failure.
++        # The cache must survive.
+         url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"
+ 
+         jwks_client = PyJWKClient(url)
+         with mocked_success_response(RESPONSE_DATA_WITH_MATCHING_KID):
+             jwks_client.get_jwk_set()
+ 
++        assert jwks_client.jwk_set_cache is not None
++        assert jwks_client.jwk_set_cache.get() is not None
++
+         with pytest.raises(PyJWKClientError):
+             with mocked_failed_response():
+                 jwks_client.get_jwk_set(refresh=True)
+ 
+-            assert jwks_client.jwk_set_cache is None
++        cached = jwks_client.jwk_set_cache.get()
++        assert cached is not None
++        # Subsequent reads still serve from cache without another fetch.
++        with mocked_success_response(RESPONSE_DATA_WITH_MATCHING_KID) as call:
++            jwks_client.get_jwk_set()
++        assert call.call_count == 0
+ 
+     def test_failed_request_should_raise_connection_error(self) -> None:
+         token = "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6Ik5FRTFRVVJCT1RNNE16STVSa0ZETlRZeE9UVTFNRGcyT0Rnd1EwVXpNVGsxUWpZeVJrUkZRdyJ9.eyJpc3MiOiJodHRwczovL2Rldi04N2V2eDlydS5hdXRoMC5jb20vIiwic3ViIjoiYVc0Q2NhNzl4UmVMV1V6MGFFMkg2a0QwTzNjWEJWdENAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vZXhwZW5zZXMtYXBpIiwiaWF0IjoxNTcyMDA2OTU0LCJleHAiOjE1NzIwMDY5NjQsImF6cCI6ImFXNENjYTc5eFJlTFdVejBhRTJINmtEME8zY1hCVnRDIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.PUxE7xn52aTCohGiWoSdMBZGiYAHwE5FYie0Y1qUT68IHSTXwXVd6hn02HTah6epvHHVKA2FqcFZ4GGv5VTHEvYpeggiiZMgbxFrmTEY0csL6VNkX1eaJGcuehwQCRBKRLL3zKmA5IKGy5GeUnIbpPHLHDxr-GXvgFzsdsyWlVQvPX2xjeaQ217r2PtxDeqjlf66UYl6oY6AqNS8DH3iryCvIfCcybRZkc_hdy-6ZMoKT6Piijvk_aXdm7-QQqKJFHLuEqrVSOuBqqiNfVrG27QzAPuPOxvfXTVLXL2jek5meH6n-VWgrBdoMFH93QEszEDowDAEhQPHVs0xj7SIzA"
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 e67c7bae2f..ff3eb8814d 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
@@ -7,6 +7,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 \
            "
 
 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.