Open
fix(cve): 修复 CVE-2026-103001 在 python-jwt 中的漏洞, CVE-2026-102275, CVE-2026-102267, CVE-2026-101917, CVE-2026-102271, CVE-2026-102269, CVE-2026-102265, CVE-2026-102273, CVE-2026-102272 #98
infra_teamAdd on 29 days ago
fix(cve): 修复 CVE-2026-103001 在 python-jwt 中的漏洞, CVE-2026-102275, CVE-2026-102267, CVE-2026-101917, CVE-2026-102271, CVE-2026-102269, CVE-2026-102265, CVE-2026-102273, CVE-2026-102272 #98
Open
Total: 5 files changed+9-165
| @@ -1,51 +0,0 @@ | |||
| 1 | -From f91ed44dd65baaf457f4b3353ed35e98a753934c Mon Sep 17 00:00:00 2001 | ||
| 2 | -From: =?UTF-8?q?Jos=C3=A9=20Padilla?= <jpadilla@users.noreply.github.com> | ||
| 3 | -Date: Wed, 9 Sep 2026 16:29:06 -0400 | ||
| 4 | -Subject: [PATCH] Validate PyJWK HMAC keys consistently | ||
| 5 | - | ||
| 6 | -Reference: https://github.com/jpadilla/pyjwt/commit/f91ed44dd65baaf457f4b3353ed35e98a753934c.patch | ||
| 7 | -Conflict: NA | ||
| 8 | - | ||
| 9 | ---- | ||
| 10 | - jwt/api_jws.py | 2 +- | ||
| 11 | - tests/test_api_jws.py | 17 +++++++++++++++++ | ||
| 12 | - 2 files changed, 18 insertions(+), 1 deletion(-) | ||
| 13 | - | ||
| 14 | -diff --git a/jwt/api_jws.py b/jwt/api_jws.py | ||
| 15 | -index d0ae3fcc..81a2ccb4 100644 | ||
| 16 | ---- a/jwt/api_jws.py | ||
| 17 | -+++ b/jwt/api_jws.py | ||
| 18 | -@@ -414,7 +414,7 @@ def _verify_signature( | ||
| 19 | - f"algorithm {key.algorithm_name!r}" | ||
| 20 | - ) | ||
| 21 | - alg_obj = key.Algorithm | ||
| 22 | -- prepared_key = key.key | ||
| 23 | -+ prepared_key = alg_obj.prepare_key(key.key) | ||
| 24 | - else: | ||
| 25 | - try: | ||
| 26 | - alg_obj = self.get_algorithm_by_name(alg) | ||
| 27 | -diff --git a/tests/test_api_jws.py b/tests/test_api_jws.py | ||
| 28 | -index 2f5f7fc1..1eb5a8fc 100644 | ||
| 29 | ---- a/tests/test_api_jws.py | ||
| 30 | -+++ b/tests/test_api_jws.py | ||
| 31 | -@@ -1264,3 +1264,20 @@ def test_get_unverified_header_rejects_unknown_crit( | ||
| 32 | - | ||
| 33 | - with pytest.raises(InvalidTokenError, match="Unsupported critical extension"): | ||
| 34 | - jws.get_unverified_header(token) | ||
| 35 | -+ def test_decode_rejects_empty_hmac_pyjwk(self) -> None: | ||
| 36 | -+ import base64 | ||
| 37 | -+ import hashlib | ||
| 38 | -+ import hmac | ||
| 39 | -+ import jwt | ||
| 40 | -+ | ||
| 41 | -+ jwk = jwt.PyJWK.from_dict( | ||
| 42 | -+ {"kty": "oct", "k": "", "kid": "active", "alg": "HS256"} | ||
| 43 | -+ ) | ||
| 44 | -+ signing_input = b"eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhdHRhY2tlciJ9" | ||
| 45 | -+ signature = hmac.new(b"", signing_input, hashlib.sha256).digest() | ||
| 46 | -+ token = ( | ||
| 47 | -+ signing_input + b"." + base64.urlsafe_b64encode(signature).rstrip(b"=") | ||
| 48 | -+ ).decode() | ||
| 49 | -+ | ||
| 50 | -+ with pytest.raises(jwt.InvalidKeyError, match="must not be empty"): | ||
| 51 | -+ jwt.decode(token, jwk, algorithms=["HS256"]) | ||
| @@ -1,109 +0,0 @@ | |||
| 1 | -From 8b4e233a22206b34ec1186e912e75c0b2396ac07 Mon Sep 17 00:00:00 2001 | ||
| 2 | -From: =?UTF-8?q?Jos=C3=A9=20Padilla?= <jpadilla@users.noreply.github.com> | ||
| 3 | -Date: Wed, 9 Sep 2026 21:49:21 -0400 | ||
| 4 | -Subject: [PATCH] fix: reject loader-accepted PEM variants | ||
| 5 | - | ||
| 6 | -Reference: https://github.com/jpadilla/pyjwt/commit/8b4e233a22206b34ec1186e912e75c0b2396ac07.patch | ||
| 7 | -Conflict: NA | ||
| 8 | - | ||
| 9 | ---- | ||
| 10 | - jwt/utils.py | 20 ++++++++++-------- | ||
| 11 | - tests/test_algorithms.py | 44 +++++++++++++++++++++++++++++++++++++++- | ||
| 12 | - 2 files changed, 55 insertions(+), 9 deletions(-) | ||
| 13 | - | ||
| 14 | -diff --git a/jwt/utils.py b/jwt/utils.py | ||
| 15 | -index 56e89bb7..914cb50d 100644 | ||
| 16 | ---- a/jwt/utils.py | ||
| 17 | -+++ b/jwt/utils.py | ||
| 18 | -@@ -113,18 +113,22 @@ def raw_to_der_signature(raw_sig: bytes, curve: "EllipticCurve") -> bytes: | ||
| 19 | - b"X509 CRL", | ||
| 20 | - } | ||
| 21 | - | ||
| 22 | --_PEM_RE = re.compile( | ||
| 23 | -- b"----[- ]BEGIN (" | ||
| 24 | -- + b"|".join(_PEMS) | ||
| 25 | -- + b""")[- ]----\r? | ||
| 26 | --.+?\r? | ||
| 27 | ------[- ]END \\1[- ]----\r?\n?""", | ||
| 28 | -- re.DOTALL, | ||
| 29 | -+# Accept PEM formatting variants that the cryptography loader accepts, while | ||
| 30 | -+# retaining the explicit asymmetric-key marker check. | ||
| 31 | -+_PEM_MARKER_RE = re.compile( | ||
| 32 | -+ b"(?=(----[- ](BEGIN|END) (" + b"|".join(_PEMS) + b")[- ]----))" | ||
| 33 | - ) | ||
| 34 | - | ||
| 35 | - | ||
| 36 | - def is_pem_format(key: bytes) -> bool: | ||
| 37 | -- return bool(_PEM_RE.search(key)) | ||
| 38 | -+ begin_markers: dict[bytes, int] = {} | ||
| 39 | -+ for marker in _PEM_MARKER_RE.finditer(key): | ||
| 40 | -+ marker_type, label = marker.group(2), marker.group(3) | ||
| 41 | -+ if marker_type == b"BEGIN": | ||
| 42 | -+ begin_markers[label] = marker.start(1) + len(marker.group(1)) | ||
| 43 | -+ elif label in begin_markers and begin_markers[label] < marker.start(1): | ||
| 44 | -+ return True | ||
| 45 | -+ return False | ||
| 46 | - | ||
| 47 | - | ||
| 48 | - # Based on https://github.com/pyca/cryptography/blob/bcb70852d577b3f490f015378c75cba74986297b/src/cryptography/hazmat/primitives/serialization/ssh.py#L40-L46 | ||
| 49 | -diff --git a/tests/test_algorithms.py b/tests/test_algorithms.py | ||
| 50 | -index c854f723..3d85a0cd 100644 | ||
| 51 | ---- a/tests/test_algorithms.py | ||
| 52 | -+++ b/tests/test_algorithms.py | ||
| 53 | -@@ -1,6 +1,6 @@ | ||
| 54 | - import base64 | ||
| 55 | - import json | ||
| 56 | --from typing import Union, cast | ||
| 57 | -+from typing import Callable, Union, cast | ||
| 58 | - | ||
| 59 | - import pytest | ||
| 60 | - | ||
| 61 | -@@ -89,6 +89,48 @@ def test_hmac_should_throw_exception(self, key: str) -> None: | ||
| 62 | - with open(key_path(key)) as keyfile: | ||
| 63 | - algo.prepare_key(keyfile.read()) | ||
| 64 | - | ||
| 65 | -+ @pytest.mark.parametrize( | ||
| 66 | -+ "mutation", | ||
| 67 | -+ [ | ||
| 68 | -+ lambda key: key.replace(b"-----END", b"\t-----END"), | ||
| 69 | -+ lambda key: key.replace(b"\n", b"\r"), | ||
| 70 | -+ lambda key: key.replace(b"\n", b""), | ||
| 71 | -+ ], | ||
| 72 | -+ ) | ||
| 73 | -+ def test_hmac_should_reject_loader_accepted_pem_mutations( | ||
| 74 | -+ self, mutation: Callable[[bytes], bytes] | ||
| 75 | -+ ) -> None: | ||
| 76 | -+ algo = HMACAlgorithm(HMACAlgorithm.SHA256) | ||
| 77 | -+ with open(key_path("testkey2_rsa.pub.pem"), "rb") as keyfile: | ||
| 78 | -+ mutated_key = mutation(keyfile.read()) | ||
| 79 | -+ | ||
| 80 | -+ with pytest.raises(InvalidKeyError): | ||
| 81 | -+ algo.prepare_key(mutated_key) | ||
| 82 | -+ | ||
| 83 | -+ def test_hmac_should_accept_incomplete_pem_marker(self) -> None: | ||
| 84 | -+ algo = HMACAlgorithm(HMACAlgorithm.SHA256) | ||
| 85 | -+ key = b"-----BEGIN PUBLIC KEY-----" * 10000 | ||
| 86 | -+ | ||
| 87 | -+ assert algo.prepare_key(key) == key | ||
| 88 | -+ | ||
| 89 | -+ def test_hmac_should_reject_a_later_pem_block(self) -> None: | ||
| 90 | -+ algo = HMACAlgorithm(HMACAlgorithm.SHA256) | ||
| 91 | -+ with open(key_path("testkey2_rsa.pub.pem"), "rb") as keyfile: | ||
| 92 | -+ key = b"-----BEGIN PUBLIC KEY-----" + keyfile.read() | ||
| 93 | -+ | ||
| 94 | -+ with pytest.raises(InvalidKeyError): | ||
| 95 | -+ algo.prepare_key(key) | ||
| 96 | -+ | ||
| 97 | -+ def test_hmac_should_reject_a_pem_block_after_an_overlapping_end_marker( | ||
| 98 | -+ self, | ||
| 99 | -+ ) -> None: | ||
| 100 | -+ algo = HMACAlgorithm(HMACAlgorithm.SHA256) | ||
| 101 | -+ with open(key_path("testkey2_rsa.pub.pem"), "rb") as keyfile: | ||
| 102 | -+ key = b"-----END CERTIFICATE" + keyfile.read() | ||
| 103 | -+ | ||
| 104 | -+ with pytest.raises(InvalidKeyError): | ||
| 105 | -+ algo.prepare_key(key) | ||
| 106 | -+ | ||
| 107 | - def test_hmac_jwk_should_parse_and_verify(self) -> None: | ||
| 108 | - algo = HMACAlgorithm(HMACAlgorithm.SHA256) | ||
| 109 | - | ||
Binary files do not support preview
| @@ -1,14 +1,12 @@ | |||
| 1 | %global srcname jwt | 1 | %global srcname jwt |
| 2 | %bcond_without tests | 2 | %bcond_without tests |
| 3 | Name: python-jwt | 3 | Name: python-jwt |
| 4 | -Version: 2.13.0 | 4 | +Version: 2.15.0 |
| 5 | -Release: 3 | 5 | +Release: 4 |
| 6 | Summary: JSON Web Token implementation in Python | 6 | Summary: JSON Web Token implementation in Python |
| 7 | License: MIT | 7 | License: MIT |
| 8 | URL: https://github.com/jpadilla/pyjwt | 8 | URL: https://github.com/jpadilla/pyjwt |
| 9 | Source0: https://files.pythonhosted.org/packages/source/p/pyjwt/pyjwt-%{version}.tar.gz | 9 | Source0: https://files.pythonhosted.org/packages/source/p/pyjwt/pyjwt-%{version}.tar.gz |
| 10 | -Patch6000: backport-CVE-2026-102268.patch | ||
| 11 | -Patch6001: backport-CVE-2026-102266.patch | ||
| 12 | BuildArch: noarch | 10 | BuildArch: noarch |
| 13 | 11 | ||
| 14 | BuildRequires: python3-devel | 12 | BuildRequires: python3-devel |
| @@ -43,7 +41,7 @@ from setuptools import setup, find_packages | |||
| 43 | 41 | ||
| 44 | setup( | 42 | setup( |
| 45 | name='PyJWT', | 43 | name='PyJWT', |
| 46 | - version='2.13.0', | 44 | + version='2.15.0', |
| 47 | description='JSON Web Token implementation in Python', | 45 | description='JSON Web Token implementation in Python', |
| 48 | url='https://github.com/jpadilla/pyjwt', | 46 | url='https://github.com/jpadilla/pyjwt', |
| 49 | packages=find_packages(exclude=['tests', 'tests.*']), | 47 | packages=find_packages(exclude=['tests', 'tests.*']), |
| @@ -79,6 +77,12 @@ rm -f tests/test_jwks_client.py | |||
| 79 | %doc README.rst | 77 | %doc README.rst |
| 80 | 78 | ||
| 81 | %changelog | 79 | %changelog |
| 80 | +* Sat Oct 10 2026 infra_team <zhaiwenjie1@huawei.com> - 2.15.0-4 | ||
| 81 | +- Type:CVE | ||
| 82 | +- CVE:CVE-2026-103001 | ||
| 83 | +- SUG:NA | ||
| 84 | +- DESC:update to 2.15.0, fix CVE-2026-103001 | ||
| 85 | + | ||
| 82 | * Fri Oct 09 2026 yixiangzhike <yixiangzhike007@163.com> - 2.13.0-3 | 86 | * Fri Oct 09 2026 yixiangzhike <yixiangzhike007@163.com> - 2.13.0-3 |
| 83 | - Type:CVE | 87 | - Type:CVE |
| 84 | - CVE:CVE-2026-102266 | 88 | - CVE:CVE-2026-102266 |