mirror of
https://github.com/openembedded/meta-openembedded.git
synced 2026-09-04 17:10:17 +00:00
This patch applies the upstream 2.13.0 backport for CVE-2026-48522. 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-993g-76c3-p5m4 Signed-off-by: Hetvi Thakar <hthakar@cisco.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
127 lines
5.5 KiB
Diff
127 lines
5.5 KiB
Diff
From f369d32169410b17717d2a9c00d5bf3ac655c85d Mon Sep 17 00:00:00 2001
|
|
From: =?UTF-8?q?Jos=C3=A9=20Padilla?= <jpadilla@users.noreply.github.com>
|
|
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-48522
|
|
Upstream-Status: Backport [https://github.com/jpadilla/pyjwt/commit/95791b1759b8aa4f2203575d344d5c78564cdc81]
|
|
|
|
Backport Changes:
|
|
- Split out the PyJWKClient URI-scheme validation 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 <hthakar@cisco.com>
|
|
---
|
|
jwt/jwks_client.py | 11 +++++++++++
|
|
tests/test_jwks_client.py | 30 ++++++++++++++++++++++++++++++
|
|
2 files changed, 41 insertions(+)
|
|
|
|
diff --git a/jwt/jwks_client.py b/jwt/jwks_client.py
|
|
index b81e8f4..8d1b0c4 100644
|
|
--- a/jwt/jwks_client.py
|
|
+++ b/jwt/jwks_client.py
|
|
@@ -6,6 +6,7 @@ from functools import lru_cache
|
|
from ssl import SSLContext
|
|
from typing import Any
|
|
from urllib.error import HTTPError, URLError
|
|
+from urllib.parse import urlparse
|
|
|
|
from .api_jwk import PyJWK, PyJWKSet
|
|
from .api_jwt import decode_complete as decode_token
|
|
@@ -69,6 +70,16 @@ class PyJWKClient:
|
|
"""
|
|
if headers is None:
|
|
headers = {}
|
|
+ # urllib's default OpenerDirector also handles file://, ftp://, and
|
|
+ # data: URIs. Reject anything that isn't http(s) eagerly so a caller
|
|
+ # passing an attacker-influenced URL (e.g. taken from a `jku` token
|
|
+ # header) can't read local files or reach other unintended schemes.
|
|
+ scheme = urlparse(uri).scheme.lower()
|
|
+ if scheme not in ("http", "https"):
|
|
+ raise PyJWKClientError(
|
|
+ f"Invalid JWKS URI scheme {scheme!r}: only 'http' and 'https' "
|
|
+ f"are supported."
|
|
+ )
|
|
self.uri = uri
|
|
self.jwk_set_cache: JWKSetCache | None = None
|
|
self.headers = headers
|
|
diff --git a/tests/test_jwks_client.py b/tests/test_jwks_client.py
|
|
index ceee672..d6793cc 100644
|
|
--- a/tests/test_jwks_client.py
|
|
+++ b/tests/test_jwks_client.py
|
|
@@ -344,6 +344,36 @@ class TestPyJWKClient:
|
|
jwks_client = PyJWKClient(url, lifespan=-1)
|
|
assert jwks_client is None
|
|
|
|
+ @pytest.mark.parametrize(
|
|
+ "uri",
|
|
+ [
|
|
+ "file:///etc/passwd",
|
|
+ "ftp://example.org/keys.json",
|
|
+ 'data:application/json,{"keys":[]}',
|
|
+ "/etc/passwd", # urlparse gives scheme="" — also rejected
|
|
+ "ldap://internal.test/jwks",
|
|
+ ],
|
|
+ )
|
|
+ def test_pyjwkclient_rejects_non_http_schemes(self, uri: str) -> None:
|
|
+ # urllib's default OpenerDirector handles file://, ftp://, and data:
|
|
+ # URIs. PyJWKClient must reject these so callers can't be tricked
|
|
+ # into reading attacker-controlled local files or other unintended
|
|
+ # schemes via a manipulated URI.
|
|
+ with pytest.raises(PyJWKClientError, match="Invalid JWKS URI scheme"):
|
|
+ PyJWKClient(uri)
|
|
+
|
|
+ @pytest.mark.parametrize(
|
|
+ "uri",
|
|
+ [
|
|
+ "http://localhost/jwks.json",
|
|
+ "https://example.test/jwks.json",
|
|
+ "HTTPS://Example.Test/jwks.json", # case-insensitive
|
|
+ ],
|
|
+ )
|
|
+ def test_pyjwkclient_accepts_http_https_schemes(self, uri: str) -> None:
|
|
+ # Construction succeeds; no fetch is made until get_jwk_set().
|
|
+ PyJWKClient(uri)
|
|
+
|
|
def test_get_jwt_set_timeout(self) -> None:
|
|
url = "https://dev-87evx9ru.auth0.com/.well-known/jwks.json"
|
|
jwks_client = PyJWKClient(url, timeout=5)
|