summaryrefslogtreecommitdiffstats
path: root/meta-python/recipes-devtools/python/python3-pyjwt/CVE-2026-32597.patch
diff options
context:
space:
mode:
authorHitendra Prajapati <hprajapati@mvista.com>2026-03-18 11:25:43 +0530
committerAnuj Mittal <anuj.mittal@oss.qualcomm.com>2026-03-24 08:52:14 +0530
commitd3a45ead9c22009c08095920104b177d1c90ee7d (patch)
tree72a123038b7d2533ce76fc28d1c9bf3f6c82f219 /meta-python/recipes-devtools/python/python3-pyjwt/CVE-2026-32597.patch
parentd5de98d28b974861fc6de983e8ceecdb24cb1864 (diff)
downloadmeta-openembedded-d3a45ead9c22009c08095920104b177d1c90ee7d.tar.gz
python3-pyjwt: Fix CVE-2026-32597
Details: https://nvd.nist.gov/vuln/detail/CVE-2026-32597 Backport commit[1] which fixes this vulnerability as mentioned in [2]. [1] https://github.com/jpadilla/pyjwt/commit/051ea341b5573fe3edcd53042f347929b92c2b92 [2] https://security-tracker.debian.org/tracker/CVE-2026-32597 Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com> Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
Diffstat (limited to 'meta-python/recipes-devtools/python/python3-pyjwt/CVE-2026-32597.patch')
-rw-r--r--meta-python/recipes-devtools/python/python3-pyjwt/CVE-2026-32597.patch216
1 files changed, 216 insertions, 0 deletions
diff --git a/meta-python/recipes-devtools/python/python3-pyjwt/CVE-2026-32597.patch b/meta-python/recipes-devtools/python/python3-pyjwt/CVE-2026-32597.patch
new file mode 100644
index 0000000000..c38628d0b3
--- /dev/null
+++ b/meta-python/recipes-devtools/python/python3-pyjwt/CVE-2026-32597.patch
@@ -0,0 +1,216 @@
1From 051ea341b5573fe3edcd53042f347929b92c2b92 Mon Sep 17 00:00:00 2001
2From: =?UTF-8?q?Jos=C3=A9=20Padilla?= <jpadilla@webapplicate.com>
3Date: Thu, 12 Mar 2026 12:46:08 -0400
4Subject: [PATCH] Merge commit from fork
5MIME-Version: 1.0
6Content-Type: text/plain; charset=UTF-8
7Content-Transfer-Encoding: 8bit
8
9Co-authored-by: José Padilla <jpadilla@users.noreply.github.com>
10
11CVE: CVE-2026-32597
12Upstream-Status: Backport [https://github.com/jpadilla/pyjwt/commit/051ea341b5573fe3edcd53042f347929b92c2b92]
13Signed-off-by: Hitendra Prajapati <hprajapati@mvista.com>
14---
15 CHANGELOG.rst | 2 +
16 jwt/api_jws.py | 27 +++++++++++++-
17 tests/test_api_jws.py | 87 +++++++++++++++++++++++++++++++++++++++++++
18 tests/test_api_jwt.py | 18 +++++++++
19 4 files changed, 132 insertions(+), 2 deletions(-)
20
21diff --git a/CHANGELOG.rst b/CHANGELOG.rst
22index 8bc2319..289f45b 100644
23--- a/CHANGELOG.rst
24+++ b/CHANGELOG.rst
25@@ -26,6 +26,8 @@ Changed
26
27 Fixed
28 ~~~~~
29+- Validate the crit (Critical) Header Parameter defined in RFC 7515 §4.1.11. by @dmbs335 in
30+ `GHSA-752w-5fwx-jx9f <https://github.com/jpadilla/pyjwt/security/advisories/GHSA-752w-5fwx-jx9f>`__
31
32 Added
33 ~~~~~
34diff --git a/jwt/api_jws.py b/jwt/api_jws.py
35index fa6708c..1750442 100644
36--- a/jwt/api_jws.py
37+++ b/jwt/api_jws.py
38@@ -129,7 +129,7 @@ class PyJWS:
39 header: dict[str, Any] = {"typ": self.header_typ, "alg": algorithm_}
40
41 if headers:
42- self._validate_headers(headers)
43+ self._validate_headers(headers, encoding=True)
44 header.update(headers)
45
46 if not header["typ"]:
47@@ -197,6 +197,8 @@ class PyJWS:
48
49 payload, signing_input, header, signature = self._load(jwt)
50
51+ self._validate_headers(header)
52+
53 if header.get("b64", True) is False:
54 if detached_payload is None:
55 raise DecodeError(
56@@ -309,14 +311,35 @@ class PyJWS:
57 if not alg_obj.verify(signing_input, prepared_key, signature):
58 raise InvalidSignatureError("Signature verification failed")
59
60- def _validate_headers(self, headers: dict[str, Any]) -> None:
61+ # Extensions that PyJWT actually understands and supports
62+ _supported_crit: set[str] = {"b64"}
63+
64+ def _validate_headers(
65+ self, headers: dict[str, Any], *, encoding: bool = False
66+ ) -> None:
67 if "kid" in headers:
68 self._validate_kid(headers["kid"])
69+ if not encoding and "crit" in headers:
70+ self._validate_crit(headers)
71
72 def _validate_kid(self, kid: Any) -> None:
73 if not isinstance(kid, str):
74 raise InvalidTokenError("Key ID header parameter must be a string")
75
76+ def _validate_crit(self, headers: dict[str, Any]) -> None:
77+ crit = headers["crit"]
78+ if not isinstance(crit, list) or len(crit) == 0:
79+ raise InvalidTokenError("Invalid 'crit' header: must be a non-empty list")
80+ for ext in crit:
81+ if not isinstance(ext, str):
82+ raise InvalidTokenError("Invalid 'crit' header: values must be strings")
83+ if ext not in self._supported_crit:
84+ raise InvalidTokenError(f"Unsupported critical extension: {ext}")
85+ if ext not in headers:
86+ raise InvalidTokenError(
87+ f"Critical extension '{ext}' is missing from headers"
88+ )
89+
90
91 _jws_global_obj = PyJWS()
92 encode = _jws_global_obj.encode
93diff --git a/tests/test_api_jws.py b/tests/test_api_jws.py
94index 3385716..434874b 100644
95--- a/tests/test_api_jws.py
96+++ b/tests/test_api_jws.py
97@@ -815,3 +815,90 @@ class TestJWS:
98 )
99 assert len(record) == 1
100 assert "foo" in str(record[0].message)
101+
102+ def test_decode_rejects_unknown_crit_extension(
103+ self, jws: PyJWS, payload: bytes
104+ ) -> None:
105+ secret = "secret"
106+ token = jws.encode(
107+ payload,
108+ secret,
109+ algorithm="HS256",
110+ headers={"crit": ["x-custom-policy"], "x-custom-policy": "require-mfa"},
111+ )
112+ with pytest.raises(InvalidTokenError, match="Unsupported critical extension"):
113+ jws.decode(token, secret, algorithms=["HS256"])
114+ def test_decode_rejects_empty_crit(self, jws: PyJWS, payload: bytes) -> None:
115+ secret = "secret"
116+ token = jws.encode(
117+ payload,
118+ secret,
119+ algorithm="HS256",
120+ headers={"crit": []},
121+ )
122+ with pytest.raises(InvalidTokenError, match="must be a non-empty list"):
123+ jws.decode(token, secret, algorithms=["HS256"])
124+ def test_decode_rejects_non_list_crit(self, jws: PyJWS, payload: bytes) -> None:
125+ secret = "secret"
126+ token = jws.encode(
127+ payload,
128+ secret,
129+ algorithm="HS256",
130+ headers={"crit": "b64"},
131+ )
132+ with pytest.raises(InvalidTokenError, match="must be a non-empty list"):
133+ jws.decode(token, secret, algorithms=["HS256"])
134+ def test_decode_rejects_crit_with_non_string_values(
135+ self, jws: PyJWS, payload: bytes
136+ ) -> None:
137+ secret = "secret"
138+ token = jws.encode(
139+ payload,
140+ secret,
141+ algorithm="HS256",
142+ headers={"crit": [123]},
143+ )
144+ with pytest.raises(InvalidTokenError, match="values must be strings"):
145+ jws.decode(token, secret, algorithms=["HS256"])
146+ def test_decode_rejects_crit_extension_missing_from_header(
147+ self, jws: PyJWS, payload: bytes
148+ ) -> None:
149+ secret = "secret"
150+ token = jws.encode(
151+ payload,
152+ secret,
153+ algorithm="HS256",
154+ headers={"crit": ["b64"]},
155+ )
156+ with pytest.raises(InvalidTokenError, match="missing from headers"):
157+ jws.decode(token, secret, algorithms=["HS256"])
158+ def test_decode_accepts_supported_crit_extension(
159+ self, jws: PyJWS, payload: bytes
160+ ) -> None:
161+ secret = "secret"
162+ token = jws.encode(
163+ payload,
164+ secret,
165+ algorithm="HS256",
166+ headers={"crit": ["b64"], "b64": False},
167+ is_payload_detached=True,
168+ )
169+ decoded = jws.decode(
170+ token,
171+ secret,
172+ algorithms=["HS256"],
173+ detached_payload=payload,
174+ )
175+ assert decoded == payload
176+ def test_get_unverified_header_rejects_unknown_crit(
177+ self, jws: PyJWS, payload: bytes
178+ ) -> None:
179+ secret = "secret"
180+ token = jws.encode(
181+ payload,
182+ secret,
183+ algorithm="HS256",
184+ headers={"crit": ["x-unknown"], "x-unknown": "value"},
185+ )
186+ with pytest.raises(InvalidTokenError, match="Unsupported critical extension"):
187+ jws.get_unverified_header(token)
188diff --git a/tests/test_api_jwt.py b/tests/test_api_jwt.py
189index 82b9299..618b60f 100644
190--- a/tests/test_api_jwt.py
191+++ b/tests/test_api_jwt.py
192@@ -802,3 +802,21 @@ class TestJWT:
193 options={"strict_aud": True},
194 algorithms=["HS256"],
195 )
196+
197+ # -------------------- Crit Header Tests --------------------
198+
199+ def test_decode_rejects_token_with_unknown_crit_extension(self, jwt: PyJWT) -> None:
200+ """RFC 7515 §4.1.11: tokens with unsupported critical extensions MUST be rejected."""
201+ from jwt.exceptions import InvalidTokenError
202+
203+ secret = "secret"
204+ payload = {"sub": "attacker", "role": "admin"}
205+ token = jwt.encode(
206+ payload,
207+ secret,
208+ algorithm="HS256",
209+ headers={"crit": ["x-custom-policy"], "x-custom-policy": "require-mfa"},
210+ )
211+
212+ with pytest.raises(InvalidTokenError, match="Unsupported critical extension"):
213+ jwt.decode(token, secret, algorithms=["HS256"])
214--
2152.50.1
216