python3-pyjwt: fix CVE-2026-103001

This patch applies the upstream fix as referenced in [2], using the
commit shown in [1].

[1] https://github.com/jpadilla/pyjwt/commit/0c87c8c8b1a74cac99ad8115f3050efcb7fbed35
[2] https://github.com/jpadilla/pyjwt/security/advisories/GHSA-gvp8-978c-rx2q

Signed-off-by: Hetvi Thakar <hthakar@cisco.com>
Signed-off-by: Anuj Mittal <anuj.mittal@oss.qualcomm.com>
This commit is contained in:
Hetvi Thakar authored and Anuj Mittal committed 2026-10-28 12:31:34 +05:30
1 parent 36cb562576
commit e9ab64a631
2 files changed
+129 -1

No files matched your search

@@ -0,0 +1,128 @@
From 0c87c8c8b1a74cac99ad8115f3050efcb7fbed35 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Jos=C3=A9=20Padilla?= <jpadilla@webapplicate.com>
Date: Tue, 8 Sep 2026 15:10:07 -0400
Subject: [PATCH] Merge commit from fork
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-authored-by: José Padilla <jpadilla@users.noreply.github.com>
CVE: CVE-2026-103001
Upstream-Status: Backport [https://github.com/jpadilla/pyjwt/commit/0c87c8c8b1a74cac99ad8115f3050efcb7fbed35]
(cherry picked from commit 0c87c8c8b1a74cac99ad8115f3050efcb7fbed35)
Signed-off-by: Hetvi Thakar <hthakar@cisco.com>
---
jwt/api_jwt.py | 20 +++++++++-------
tests/test_api_jwt.py | 56 +++++++++++++++++++++++++++++++++++++++++++
2 files changed, 67 insertions(+), 9 deletions(-)
diff --git a/jwt/api_jwt.py b/jwt/api_jwt.py
index 429c2d7..f298ea9 100644
--- a/jwt/api_jwt.py
+++ b/jwt/api_jwt.py
@@ -76,16 +76,18 @@ class PyJWT:
if options is None:
return self.options
+ merged_options = cast("Options", dict(options))
+
# (defensive) set defaults for verify_x to False if verify_signature is False
- if not options.get("verify_signature", True):
- options["verify_exp"] = options.get("verify_exp", False)
- options["verify_nbf"] = options.get("verify_nbf", False)
- options["verify_iat"] = options.get("verify_iat", False)
- options["verify_aud"] = options.get("verify_aud", False)
- options["verify_iss"] = options.get("verify_iss", False)
- options["verify_sub"] = options.get("verify_sub", False)
- options["verify_jti"] = options.get("verify_jti", False)
- return {**self.options, **options}
+ if not merged_options.get("verify_signature", True):
+ merged_options["verify_exp"] = merged_options.get("verify_exp", False)
+ merged_options["verify_nbf"] = merged_options.get("verify_nbf", False)
+ merged_options["verify_iat"] = merged_options.get("verify_iat", False)
+ merged_options["verify_aud"] = merged_options.get("verify_aud", False)
+ merged_options["verify_iss"] = merged_options.get("verify_iss", False)
+ merged_options["verify_sub"] = merged_options.get("verify_sub", False)
+ merged_options["verify_jti"] = merged_options.get("verify_jti", False)
+ return {**self.options, **merged_options}
def encode(
self,
diff --git a/tests/test_api_jwt.py b/tests/test_api_jwt.py
index 8b54204..dd28816 100644
--- a/tests/test_api_jwt.py
+++ b/tests/test_api_jwt.py
@@ -1,6 +1,7 @@
import json
import time
from calendar import timegm
+from collections.abc import MutableMapping
from datetime import datetime, timedelta, timezone
from decimal import Decimal
@@ -38,6 +39,61 @@ def payload() -> dict[str, object]:
class TestJWT:
+ def test_decode_does_not_mutate_options_when_signature_verification_is_disabled(
+ self, jwt: PyJWT
+ ) -> None:
+ options: Options = {"verify_signature": False}
+ token = jwt.encode({"claim": "value"}, "a" * 32, algorithm="HS256")
+ original_options = options.copy()
+
+ jwt.decode(token, options=options)
+
+ assert options == original_options
+
+ def test_reused_options_do_not_disable_claim_verification(self, jwt: PyJWT) -> None:
+ options: Options = {"verify_signature": False}
+ token = jwt.encode({"exp": utc_timestamp() - 1}, "a" * 32, algorithm="HS256")
+
+ jwt.decode(token, options=options)
+ options["verify_signature"] = True
+
+ with pytest.raises(ExpiredSignatureError):
+ jwt.decode(token, "a" * 32, algorithms=["HS256"], options=options)
+
+ def test_decode_complete_preserves_mutable_mapping_input(self, jwt: PyJWT) -> None:
+ class MappingWithoutCopy(MutableMapping[str, object]):
+ def __init__(self, values: dict[str, object]) -> None:
+ self.values = values
+
+ def __getitem__(self, key: str) -> object:
+ return self.values[key]
+
+ def __setitem__(self, key: str, value: object) -> None:
+ self.values[key] = value
+
+ def __delitem__(self, key: str) -> None:
+ del self.values[key]
+
+ def __iter__(self):
+ return iter(self.values)
+
+ def __len__(self) -> int:
+ return len(self.values)
+
+ options = MappingWithoutCopy({"verify_signature": False})
+ token = jwt.encode({"claim": "value"}, "a" * 32, algorithm="HS256")
+
+ jwt.decode_complete(token, options=options) # type: ignore[arg-type]
+
+ assert dict(options) == {"verify_signature": False}
+
+ def test_constructor_does_not_mutate_options_input(self, jwt: PyJWT) -> None:
+ options: Options = {"verify_signature": False}
+
+ PyJWT(options=options)
+
+ assert options == {"verify_signature": False}
+
def test_jwt_with_options(self) -> None:
jwt = PyJWT(options={"verify_signature": False})
assert jwt.options["verify_signature"] is False
--
2.35.6
@@ -10,6 +10,7 @@ SRC_URI += "file://CVE-2026-48522.patch \
file://CVE-2026-48524.patch \
file://CVE-2026-48525.patch \
file://CVE-2026-48526.patch \
file://CVE-2026-103001.patch \
"
SRC_URI[sha256sum] = "c74a7a2adf861c04d002db713dd85f84beb242228e671280bf709d765b03672b"
@@ -25,4 +26,3 @@ RDEPENDS:${PN} = "\
"
BBCLASSEXTEND = "native nativesdk"