Action not permitted
Modal body text goes here.
Modal Title
Modal Body
CVE-2026-102274 (GCVE-0-2026-102274)
Vulnerability from cvelistv5 – Published: 2026-09-28 20:42 – Updated: 2026-09-29 19:38- CWE-755 - Improper Handling of Exceptional Conditions
| URL | Tags |
|---|---|
| https://github.com/jpadilla/pyjwt/security/adviso… | x_refsource_CONFIRM |
| https://github.com/jpadilla/pyjwt/commit/8915570a… | x_refsource_MISC |
| https://github.com/jpadilla/pyjwt/releases/tag/2.14.0 | x_refsource_MISC |
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-102274",
"options": [
{
"Exploitation": "poc"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-09-29T19:37:35.694440Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-09-29T19:38:02.520Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"references": [
{
"tags": [
"exploit"
],
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
}
],
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"product": "pyjwt",
"vendor": "jpadilla",
"versions": [
{
"status": "affected",
"version": "\u003e= 2.9.0, \u003c 2.14.0"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "PyJWT is a Python implementation of JSON Web Token standards. From 2.9.0 until 2.14.0, PyJWKSet does not catch the plain ValueError raised for malformed RSA JWK components by RSAAlgorithm.from_jwk in jwt/api_jwk.py. This occurs when a JWK Set contains a malformed RSA key alongside otherwise usable keys. As a result, one malformed member aborts construction of the entire PyJWKSet. Consequently, applications can experience authentication failures or request-level denial of service. This issue is fixed in version 2.14.0."
}
],
"metrics": [
{
"cvssV3_1": {
"attackComplexity": "HIGH",
"attackVector": "NETWORK",
"availabilityImpact": "HIGH",
"baseScore": 5.9,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "NONE",
"integrityImpact": "NONE",
"privilegesRequired": "NONE",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"version": "3.1"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-755",
"description": "CWE-755: Improper Handling of Exceptional Conditions",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-09-28T20:42:28.355Z",
"orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"shortName": "GitHub_M"
},
"references": [
{
"name": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq",
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"name": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"name": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
}
],
"source": {
"advisory": "GHSA-w6j9-cwv2-h6wq",
"discovery": "UNKNOWN"
},
"title": "PyJWT: Malformed RSA JWK aborts parsing of an entire JWK Set"
}
},
"cveMetadata": {
"assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"assignerShortName": "GitHub_M",
"cveId": "CVE-2026-102274",
"datePublished": "2026-09-28T20:42:28.355Z",
"dateReserved": "2026-09-28T20:11:16.658Z",
"dateUpdated": "2026-09-29T19:38:02.520Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2",
"vulnerability-lookup:meta": {
"epss": {
"cve": "CVE-2026-102274",
"date": "2026-10-02",
"epss": "0.00352",
"percentile": "0.26605"
},
"nvd": {
"cve": {
"affected": [
{
"affectedData": [
{
"product": "pyjwt",
"vendor": "jpadilla",
"versions": [
{
"status": "affected",
"version": "\u003e= 2.9.0, \u003c 2.14.0"
}
]
}
],
"source": "security-advisories@github.com"
}
],
"cveTags": [],
"descriptions": [
{
"lang": "en",
"value": "PyJWT is a Python implementation of JSON Web Token standards. From 2.9.0 until 2.14.0, PyJWKSet does not catch the plain ValueError raised for malformed RSA JWK components by RSAAlgorithm.from_jwk in jwt/api_jwk.py. This occurs when a JWK Set contains a malformed RSA key alongside otherwise usable keys. As a result, one malformed member aborts construction of the entire PyJWKSet. Consequently, applications can experience authentication failures or request-level denial of service. This issue is fixed in version 2.14.0."
}
],
"id": "CVE-2026-102274",
"lastModified": "2026-09-30T19:38:27.293",
"metrics": {
"cvssMetricV31": [
{
"cvssData": {
"attackComplexity": "HIGH",
"attackVector": "NETWORK",
"availabilityImpact": "HIGH",
"baseScore": 5.9,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "NONE",
"integrityImpact": "NONE",
"privilegesRequired": "NONE",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"version": "3.1"
},
"exploitabilityScore": 2.2,
"impactScore": 3.6,
"source": "security-advisories@github.com",
"type": "Secondary"
}
],
"ssvcV203": [
{
"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"ssvcData": {
"id": "CVE-2026-102274",
"options": [
{
"exploitation": "poc"
},
{
"automatable": "no"
},
{
"technicalImpact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-09-29T19:37:35.694440Z",
"version": "2.0.3"
}
}
]
},
"published": "2026-09-28T21:17:15.677",
"references": [
{
"source": "security-advisories@github.com",
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"source": "security-advisories@github.com",
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
},
{
"source": "security-advisories@github.com",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
}
],
"sourceIdentifier": "security-advisories@github.com",
"vulnStatus": "Awaiting Analysis",
"weaknesses": [
{
"description": [
{
"lang": "en",
"value": "CWE-755"
}
],
"source": "security-advisories@github.com",
"type": "Secondary"
}
]
}
},
"redhat_vex": {
"aggregate_severity": "Moderate",
"current_release_date": "2026-09-28T22:46:48+00:00",
"cve": "CVE-2026-102274",
"id": "CVE-2026-102274",
"initial_release_date": "2026-09-28T20:42:28.355000+00:00",
"product_status:known_not_affected": "4",
"source": "Red Hat CSAF VEX",
"status": "final",
"title": "pyjwt: pyjwt: Denial of Service via malformed RSA key in JWK set",
"url": "https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-102274.json",
"version": "3"
},
"suse_vex": {
"aggregate_severity": "moderate",
"current_release_date": "2026-09-29T23:39:12Z",
"cve": "CVE-2026-102274",
"id": "CVE-2026-102274",
"initial_release_date": "2026-09-29T23:39:12Z",
"product_status:known_affected": "15",
"product_status:known_not_affected": "111",
"source": "SUSE CSAF VEX",
"status": "interim",
"title": "SUSE CVE CVE-2026-102274",
"url": "https://ftp.suse.com/pub/projects/security/csaf-vex/cve-2026-102274.json",
"version": "2"
},
"vulnrichment": {
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-102274",
"options": [
{
"Exploitation": "poc"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-09-29T19:37:35.694440Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-09-29T19:37:56.921Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"references": [
{
"tags": [
"exploit"
],
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
}
],
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"product": "pyjwt",
"vendor": "jpadilla",
"versions": [
{
"status": "affected",
"version": "\u003e= 2.9.0, \u003c 2.14.0"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "PyJWT is a Python implementation of JSON Web Token standards. From 2.9.0 until 2.14.0, PyJWKSet does not catch the plain ValueError raised for malformed RSA JWK components by RSAAlgorithm.from_jwk in jwt/api_jwk.py. This occurs when a JWK Set contains a malformed RSA key alongside otherwise usable keys. As a result, one malformed member aborts construction of the entire PyJWKSet. Consequently, applications can experience authentication failures or request-level denial of service. This issue is fixed in version 2.14.0."
}
],
"metrics": [
{
"cvssV3_1": {
"attackComplexity": "HIGH",
"attackVector": "NETWORK",
"availabilityImpact": "HIGH",
"baseScore": 5.9,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "NONE",
"integrityImpact": "NONE",
"privilegesRequired": "NONE",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"version": "3.1"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-755",
"description": "CWE-755: Improper Handling of Exceptional Conditions",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-09-28T20:42:28.355Z",
"orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"shortName": "GitHub_M"
},
"references": [
{
"name": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq",
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"name": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"name": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0",
"tags": [
"x_refsource_MISC"
],
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
}
],
"source": {
"advisory": "GHSA-w6j9-cwv2-h6wq",
"discovery": "UNKNOWN"
},
"title": "PyJWT: Malformed RSA JWK aborts parsing of an entire JWK Set"
}
},
"cveMetadata": {
"assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"assignerShortName": "GitHub_M",
"cveId": "CVE-2026-102274",
"datePublished": "2026-09-28T20:42:28.355Z",
"dateReserved": "2026-09-28T20:11:16.658Z",
"dateUpdated": "2026-09-29T19:38:02.520Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
}
}
BREW-RAPID-MLX-CVE-2026-102274 (GHSA-W6J9-CWV2-H6WQ)
Vulnerability from osv_homebrew – Published: 2026-09-30 10:08 – Updated: 2026-10-02 10:11 – Source websiteSummary
A malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because RSAAlgorithm.from_jwk can raise a plain ValueError that isn't caught by PyJWKSet's per-key error-skipping logic.
Affected component / version
- Package:
PyJWT(PyPI, ecosystempip) - Files:
jwt/api_jwk.py(PyJWK.__init__,PyJWKSet.__init__),jwt/algorithms.py(RSAAlgorithm.from_jwk) - Confirmed present in the
masterbranch as of 2026-09-05 (commit7144e4534c34810f4525dc4578a32addd8212cff, tag2.13.0). Directly verified identical in tags2.9.0,2.10.0,2.11.0,2.12.0,2.12.1,2.13.0-- the vulnerable call and theexcept PyJWTErrorguard are unchanged across all six releases. Not verified against any release prior to2.9.0.
Details
PyJWKSet.__init__ (jwt/api_jwk.py:145-152) iterates each key in a JWK Set:
for key in keys:
try:
self.keys.append(PyJWK(key))
except PyJWTError as error:
if isinstance(error, MissingCryptographyError):
raise error
# skip unusable keys
continue
PyJWK.__init__ (api_jwk.py:82) calls self.Algorithm.from_jwk(self._jwk_data) with no try/except of its own. For an RSA JWK, this dispatches to RSAAlgorithm.from_jwk (jwt/algorithms.py:539-586). When the JWK supplies d, e, n without the CRT parameters (p, q, dp, dq, qi), from_jwk calls cryptography's rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e) (algorithms.py:572-574) to derive the key. If d is not the correct private exponent for that n/e pair, rsa_recover_prime_factors raises a plain ValueError.
ValueError is a built-in Python exception and is not a subclass of jwt.exceptions.PyJWTError (PyJWTError(Exception) is the root of PyJWT's own exception hierarchy). It is therefore not caught by PyJWKSet.__init__'s except PyJWTError, and propagates out of the constructor, aborting the for key in keys: loop before any subsequent key in the list is processed.
PyJWKSet.from_dict/from_json and PyJWK.from_dict/from_json are exported public API (jwt/__init__.py). PyJWKClient.get_jwk_set (jwt/jwks_client.py:158) feeds a fetched JWKS HTTP response directly into PyJWKSet.from_dict with no per-key pre-validation, so this is reachable through the documented PyJWKClient flow whenever the fetched JWKS contains a malformed key alongside valid ones.
Proof of concept
import jwt
good_jwk = {
"kty": "RSA",
"n": "<a valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key>",
"e": "AQAB",
}
bad_jwk = {
"kty": "RSA",
"n": good_jwk["n"],
"e": "AQAB",
"d": "AAAAAA", # not the true private exponent for n/e, no CRT params present
}
jwks_doc = {"keys": [bad_jwk, good_jwk]}
jwt.PyJWKSet.from_dict(jwks_doc)
# raises: ValueError: Unable to compute factors p and q from exponent d.
# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)
Impact
PyJWKSet.__init__ raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught ValueError, not one of PyJWT's documented jwt.exceptions.* types, so exception handling written against PyJWT's documented contract (except jwt.exceptions.PyJWTError) will not catch it either.
Suggested remediation
Wrap the key-construction call in PyJWK.__init__ (api_jwk.py:82) so a ValueError is converted into InvalidKeyError (a PyJWTError subclass):
try:
self.key = self.Algorithm.from_jwk(self._jwk_data)
except ValueError as e:
raise InvalidKeyError(f"Unable to construct key from JWK: {e}") from e
This lets PyJWKSet.__init__'s existing except PyJWTError: continue skip the one malformed key as its own comment already states is intended.
Responsible Disclosure Timeline
Per the OWASP Vulnerability Disclosure Cheat Sheet and Google Project Zero's 2020 disclosure policy:
- Day 0 (date of submission): to be set to the actual API response's
created_attimestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05. - Day 90 (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.
- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.
- This window may shorten instead of extend if the issue is confirmed under active exploitation.
Try It Yourself (Sandbox)
Reproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.
- Sandbox: jwt.py
- Course: Tenant Zero: AI & SaaS Authz Failures
Credit
Discovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.
Maintainer update — 2026-09-08
We reproduced the reported behavior on PyJWT 2.13.0 with cryptography installed: a malformed RSA private JWK containing an invalid d value and no CRT parameters raised a plain ValueError while parsing a JWK set. Because PyJWKSet skips PyJWTError instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.
The independently verified affected range is >= 2.9.0, <= 2.13.0; earlier releases were not checked. A narrow fix has been prepared in commit 8915570 based on master commit 5fa7594: PyJWK converts key-construction ValueError exceptions to InvalidKeyError, allowing the existing PyJWKSet skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.15.0",
"upstream_fixed_in": "2.14.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "rapid-mlx",
"purl": "pkg:brew/rapid-mlx"
},
"ranges": [
{
"events": [
{
"introduced": "0.10.12"
},
{
"fixed": "0.14.2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"database_specific": {
"confidence": "high",
"source": "matched",
"strategy": "registry",
"upstream_evidence": [
{
"ecosystem": "PyPI",
"key": "pkg:pypi/pyjwt@2.15.0",
"name": "pyjwt",
"resource": "pyjwt",
"strategy": "registry",
"subject_version": "2.15.0"
}
]
},
"details": "## Summary\n\nA malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because `RSAAlgorithm.from_jwk` can raise a plain `ValueError` that isn\u0027t caught by `PyJWKSet`\u0027s per-key error-skipping logic.\n\n## Affected component / version\n\n- Package: `PyJWT` (PyPI, ecosystem `pip`)\n- Files: `jwt/api_jwk.py` (`PyJWK.__init__`, `PyJWKSet.__init__`), `jwt/algorithms.py` (`RSAAlgorithm.from_jwk`)\n- Confirmed present in the `master` branch as of 2026-09-05 (commit `7144e4534c34810f4525dc4578a32addd8212cff`, tag `2.13.0`). Directly verified identical in tags `2.9.0`, `2.10.0`, `2.11.0`, `2.12.0`, `2.12.1`, `2.13.0` -- the vulnerable call and the `except PyJWTError` guard are unchanged across all six releases. Not verified against any release prior to `2.9.0`.\n\n## Details\n\n`PyJWKSet.__init__` (`jwt/api_jwk.py:145-152`) iterates each key in a JWK Set:\n\n```python\nfor key in keys:\n try:\n self.keys.append(PyJWK(key))\n except PyJWTError as error:\n if isinstance(error, MissingCryptographyError):\n raise error\n # skip unusable keys\n continue\n```\n\n`PyJWK.__init__` (`api_jwk.py:82`) calls `self.Algorithm.from_jwk(self._jwk_data)` with no try/except of its own. For an RSA JWK, this dispatches to `RSAAlgorithm.from_jwk` (`jwt/algorithms.py:539-586`). When the JWK supplies `d`, `e`, `n` without the CRT parameters (`p`, `q`, `dp`, `dq`, `qi`), `from_jwk` calls `cryptography`\u0027s `rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e)` (`algorithms.py:572-574`) to derive the key. If `d` is not the correct private exponent for that `n`/`e` pair, `rsa_recover_prime_factors` raises a plain `ValueError`.\n\n`ValueError` is a built-in Python exception and is not a subclass of `jwt.exceptions.PyJWTError` (`PyJWTError(Exception)` is the root of PyJWT\u0027s own exception hierarchy). It is therefore not caught by `PyJWKSet.__init__`\u0027s `except PyJWTError`, and propagates out of the constructor, aborting the `for key in keys:` loop before any subsequent key in the list is processed.\n\n`PyJWKSet.from_dict`/`from_json` and `PyJWK.from_dict`/`from_json` are exported public API (`jwt/__init__.py`). `PyJWKClient.get_jwk_set` (`jwt/jwks_client.py:158`) feeds a fetched JWKS HTTP response directly into `PyJWKSet.from_dict` with no per-key pre-validation, so this is reachable through the documented `PyJWKClient` flow whenever the fetched JWKS contains a malformed key alongside valid ones.\n\n## Proof of concept\n\n```python\nimport jwt\n\ngood_jwk = {\n \"kty\": \"RSA\",\n \"n\": \"\u003ca valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key\u003e\",\n \"e\": \"AQAB\",\n}\n\nbad_jwk = {\n \"kty\": \"RSA\",\n \"n\": good_jwk[\"n\"],\n \"e\": \"AQAB\",\n \"d\": \"AAAAAA\", # not the true private exponent for n/e, no CRT params present\n}\n\njwks_doc = {\"keys\": [bad_jwk, good_jwk]}\n\njwt.PyJWKSet.from_dict(jwks_doc)\n# raises: ValueError: Unable to compute factors p and q from exponent d.\n# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)\n```\n\n## Impact\n\n`PyJWKSet.__init__` raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught `ValueError`, not one of PyJWT\u0027s documented `jwt.exceptions.*` types, so exception handling written against PyJWT\u0027s documented contract (`except jwt.exceptions.PyJWTError`) will not catch it either.\n\n## Suggested remediation\n\nWrap the key-construction call in `PyJWK.__init__` (`api_jwk.py:82`) so a `ValueError` is converted into `InvalidKeyError` (a `PyJWTError` subclass):\n\n```python\ntry:\n self.key = self.Algorithm.from_jwk(self._jwk_data)\nexcept ValueError as e:\n raise InvalidKeyError(f\"Unable to construct key from JWK: {e}\") from e\n```\n\nThis lets `PyJWKSet.__init__`\u0027s existing `except PyJWTError: continue` skip the one malformed key as its own comment already states is intended.\n\n## Responsible Disclosure Timeline\n\nPer the [OWASP Vulnerability Disclosure Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html) and [Google Project Zero\u0027s 2020 disclosure policy](https://projectzero.google/2020/01/policy-and-disclosure-2020-edition.html):\n\n- **Day 0** (date of submission): to be set to the actual API response\u0027s `created_at` timestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05.\n- **Day 90** (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.\n- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.\n- This window may shorten instead of extend if the issue is confirmed under active exploitation.\n\n## Try It Yourself (Sandbox)\n\nReproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.\n\n- Sandbox: [jwt.py](https://play.secdim.com/game/python/challenge/jwtpy)\n- Course: [Tenant Zero: AI \u0026 SaaS Authz Failures](https://learn.secdim.com/course/tenant-zero)\n\n## Credit\n\nDiscovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.\n\n## Maintainer update \u2014 2026-09-08\n\nWe reproduced the reported behavior on PyJWT 2.13.0 with `cryptography` installed: a malformed RSA private JWK containing an invalid `d` value and no CRT parameters raised a plain `ValueError` while parsing a JWK set. Because `PyJWKSet` skips `PyJWTError` instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.\n\nThe independently verified affected range is `\u003e= 2.9.0, \u003c= 2.13.0`; earlier releases were not checked. A narrow fix has been prepared in commit `8915570` based on master commit `5fa7594`: `PyJWK` converts key-construction `ValueError` exceptions to `InvalidKeyError`, allowing the existing `PyJWKSet` skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.",
"id": "BREW-rapid-mlx-CVE-2026-102274",
"modified": "2026-10-02T10:11:57Z",
"published": "2026-09-30T10:08:51Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-102274"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Malformed RSA JWK aborts parsing of an entire JWK Set",
"upstream": [
"GHSA-w6j9-cwv2-h6wq",
"CVE-2026-102274",
"PYSEC-2026-4152"
]
}
BREW-SCOUTSUITE-CVE-2026… (GHSA-W6J9-CWV2-H6WQ)
Vulnerability from osv_homebrew – Published: 2026-09-30 20:42 – Updated: 2026-10-02 10:44 – Source websiteSummary
A malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because RSAAlgorithm.from_jwk can raise a plain ValueError that isn't caught by PyJWKSet's per-key error-skipping logic.
Affected component / version
- Package:
PyJWT(PyPI, ecosystempip) - Files:
jwt/api_jwk.py(PyJWK.__init__,PyJWKSet.__init__),jwt/algorithms.py(RSAAlgorithm.from_jwk) - Confirmed present in the
masterbranch as of 2026-09-05 (commit7144e4534c34810f4525dc4578a32addd8212cff, tag2.13.0). Directly verified identical in tags2.9.0,2.10.0,2.11.0,2.12.0,2.12.1,2.13.0-- the vulnerable call and theexcept PyJWTErrorguard are unchanged across all six releases. Not verified against any release prior to2.9.0.
Details
PyJWKSet.__init__ (jwt/api_jwk.py:145-152) iterates each key in a JWK Set:
for key in keys:
try:
self.keys.append(PyJWK(key))
except PyJWTError as error:
if isinstance(error, MissingCryptographyError):
raise error
# skip unusable keys
continue
PyJWK.__init__ (api_jwk.py:82) calls self.Algorithm.from_jwk(self._jwk_data) with no try/except of its own. For an RSA JWK, this dispatches to RSAAlgorithm.from_jwk (jwt/algorithms.py:539-586). When the JWK supplies d, e, n without the CRT parameters (p, q, dp, dq, qi), from_jwk calls cryptography's rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e) (algorithms.py:572-574) to derive the key. If d is not the correct private exponent for that n/e pair, rsa_recover_prime_factors raises a plain ValueError.
ValueError is a built-in Python exception and is not a subclass of jwt.exceptions.PyJWTError (PyJWTError(Exception) is the root of PyJWT's own exception hierarchy). It is therefore not caught by PyJWKSet.__init__'s except PyJWTError, and propagates out of the constructor, aborting the for key in keys: loop before any subsequent key in the list is processed.
PyJWKSet.from_dict/from_json and PyJWK.from_dict/from_json are exported public API (jwt/__init__.py). PyJWKClient.get_jwk_set (jwt/jwks_client.py:158) feeds a fetched JWKS HTTP response directly into PyJWKSet.from_dict with no per-key pre-validation, so this is reachable through the documented PyJWKClient flow whenever the fetched JWKS contains a malformed key alongside valid ones.
Proof of concept
import jwt
good_jwk = {
"kty": "RSA",
"n": "<a valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key>",
"e": "AQAB",
}
bad_jwk = {
"kty": "RSA",
"n": good_jwk["n"],
"e": "AQAB",
"d": "AAAAAA", # not the true private exponent for n/e, no CRT params present
}
jwks_doc = {"keys": [bad_jwk, good_jwk]}
jwt.PyJWKSet.from_dict(jwks_doc)
# raises: ValueError: Unable to compute factors p and q from exponent d.
# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)
Impact
PyJWKSet.__init__ raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught ValueError, not one of PyJWT's documented jwt.exceptions.* types, so exception handling written against PyJWT's documented contract (except jwt.exceptions.PyJWTError) will not catch it either.
Suggested remediation
Wrap the key-construction call in PyJWK.__init__ (api_jwk.py:82) so a ValueError is converted into InvalidKeyError (a PyJWTError subclass):
try:
self.key = self.Algorithm.from_jwk(self._jwk_data)
except ValueError as e:
raise InvalidKeyError(f"Unable to construct key from JWK: {e}") from e
This lets PyJWKSet.__init__'s existing except PyJWTError: continue skip the one malformed key as its own comment already states is intended.
Responsible Disclosure Timeline
Per the OWASP Vulnerability Disclosure Cheat Sheet and Google Project Zero's 2020 disclosure policy:
- Day 0 (date of submission): to be set to the actual API response's
created_attimestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05. - Day 90 (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.
- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.
- This window may shorten instead of extend if the issue is confirmed under active exploitation.
Try It Yourself (Sandbox)
Reproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.
- Sandbox: jwt.py
- Course: Tenant Zero: AI & SaaS Authz Failures
Credit
Discovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.
Maintainer update — 2026-09-08
We reproduced the reported behavior on PyJWT 2.13.0 with cryptography installed: a malformed RSA private JWK containing an invalid d value and no CRT parameters raised a plain ValueError while parsing a JWK set. Because PyJWKSet skips PyJWTError instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.
The independently verified affected range is >= 2.9.0, <= 2.13.0; earlier releases were not checked. A narrow fix has been prepared in commit 8915570 based on master commit 5fa7594: PyJWK converts key-construction ValueError exceptions to InvalidKeyError, allowing the existing PyJWKSet skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.15.1",
"upstream_fixed_in": "2.14.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "scoutsuite",
"purl": "pkg:brew/scoutsuite"
},
"ranges": [
{
"events": [
{
"introduced": "5.14.0_1"
},
{
"fixed": "5.14.0_17"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"database_specific": {
"confidence": "high",
"source": "matched",
"strategy": "registry",
"upstream_evidence": [
{
"ecosystem": "PyPI",
"key": "pkg:pypi/pyjwt@2.15.1",
"name": "pyjwt",
"resource": "pyjwt",
"strategy": "registry",
"subject_version": "2.15.1"
}
]
},
"details": "## Summary\n\nA malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because `RSAAlgorithm.from_jwk` can raise a plain `ValueError` that isn\u0027t caught by `PyJWKSet`\u0027s per-key error-skipping logic.\n\n## Affected component / version\n\n- Package: `PyJWT` (PyPI, ecosystem `pip`)\n- Files: `jwt/api_jwk.py` (`PyJWK.__init__`, `PyJWKSet.__init__`), `jwt/algorithms.py` (`RSAAlgorithm.from_jwk`)\n- Confirmed present in the `master` branch as of 2026-09-05 (commit `7144e4534c34810f4525dc4578a32addd8212cff`, tag `2.13.0`). Directly verified identical in tags `2.9.0`, `2.10.0`, `2.11.0`, `2.12.0`, `2.12.1`, `2.13.0` -- the vulnerable call and the `except PyJWTError` guard are unchanged across all six releases. Not verified against any release prior to `2.9.0`.\n\n## Details\n\n`PyJWKSet.__init__` (`jwt/api_jwk.py:145-152`) iterates each key in a JWK Set:\n\n```python\nfor key in keys:\n try:\n self.keys.append(PyJWK(key))\n except PyJWTError as error:\n if isinstance(error, MissingCryptographyError):\n raise error\n # skip unusable keys\n continue\n```\n\n`PyJWK.__init__` (`api_jwk.py:82`) calls `self.Algorithm.from_jwk(self._jwk_data)` with no try/except of its own. For an RSA JWK, this dispatches to `RSAAlgorithm.from_jwk` (`jwt/algorithms.py:539-586`). When the JWK supplies `d`, `e`, `n` without the CRT parameters (`p`, `q`, `dp`, `dq`, `qi`), `from_jwk` calls `cryptography`\u0027s `rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e)` (`algorithms.py:572-574`) to derive the key. If `d` is not the correct private exponent for that `n`/`e` pair, `rsa_recover_prime_factors` raises a plain `ValueError`.\n\n`ValueError` is a built-in Python exception and is not a subclass of `jwt.exceptions.PyJWTError` (`PyJWTError(Exception)` is the root of PyJWT\u0027s own exception hierarchy). It is therefore not caught by `PyJWKSet.__init__`\u0027s `except PyJWTError`, and propagates out of the constructor, aborting the `for key in keys:` loop before any subsequent key in the list is processed.\n\n`PyJWKSet.from_dict`/`from_json` and `PyJWK.from_dict`/`from_json` are exported public API (`jwt/__init__.py`). `PyJWKClient.get_jwk_set` (`jwt/jwks_client.py:158`) feeds a fetched JWKS HTTP response directly into `PyJWKSet.from_dict` with no per-key pre-validation, so this is reachable through the documented `PyJWKClient` flow whenever the fetched JWKS contains a malformed key alongside valid ones.\n\n## Proof of concept\n\n```python\nimport jwt\n\ngood_jwk = {\n \"kty\": \"RSA\",\n \"n\": \"\u003ca valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key\u003e\",\n \"e\": \"AQAB\",\n}\n\nbad_jwk = {\n \"kty\": \"RSA\",\n \"n\": good_jwk[\"n\"],\n \"e\": \"AQAB\",\n \"d\": \"AAAAAA\", # not the true private exponent for n/e, no CRT params present\n}\n\njwks_doc = {\"keys\": [bad_jwk, good_jwk]}\n\njwt.PyJWKSet.from_dict(jwks_doc)\n# raises: ValueError: Unable to compute factors p and q from exponent d.\n# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)\n```\n\n## Impact\n\n`PyJWKSet.__init__` raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught `ValueError`, not one of PyJWT\u0027s documented `jwt.exceptions.*` types, so exception handling written against PyJWT\u0027s documented contract (`except jwt.exceptions.PyJWTError`) will not catch it either.\n\n## Suggested remediation\n\nWrap the key-construction call in `PyJWK.__init__` (`api_jwk.py:82`) so a `ValueError` is converted into `InvalidKeyError` (a `PyJWTError` subclass):\n\n```python\ntry:\n self.key = self.Algorithm.from_jwk(self._jwk_data)\nexcept ValueError as e:\n raise InvalidKeyError(f\"Unable to construct key from JWK: {e}\") from e\n```\n\nThis lets `PyJWKSet.__init__`\u0027s existing `except PyJWTError: continue` skip the one malformed key as its own comment already states is intended.\n\n## Responsible Disclosure Timeline\n\nPer the [OWASP Vulnerability Disclosure Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html) and [Google Project Zero\u0027s 2020 disclosure policy](https://projectzero.google/2020/01/policy-and-disclosure-2020-edition.html):\n\n- **Day 0** (date of submission): to be set to the actual API response\u0027s `created_at` timestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05.\n- **Day 90** (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.\n- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.\n- This window may shorten instead of extend if the issue is confirmed under active exploitation.\n\n## Try It Yourself (Sandbox)\n\nReproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.\n\n- Sandbox: [jwt.py](https://play.secdim.com/game/python/challenge/jwtpy)\n- Course: [Tenant Zero: AI \u0026 SaaS Authz Failures](https://learn.secdim.com/course/tenant-zero)\n\n## Credit\n\nDiscovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.\n\n## Maintainer update \u2014 2026-09-08\n\nWe reproduced the reported behavior on PyJWT 2.13.0 with `cryptography` installed: a malformed RSA private JWK containing an invalid `d` value and no CRT parameters raised a plain `ValueError` while parsing a JWK set. Because `PyJWKSet` skips `PyJWTError` instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.\n\nThe independently verified affected range is `\u003e= 2.9.0, \u003c= 2.13.0`; earlier releases were not checked. A narrow fix has been prepared in commit `8915570` based on master commit `5fa7594`: `PyJWK` converts key-construction `ValueError` exceptions to `InvalidKeyError`, allowing the existing `PyJWKSet` skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.",
"id": "BREW-scoutsuite-CVE-2026-102274",
"modified": "2026-10-02T10:44:03Z",
"published": "2026-09-30T20:42:57Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-102274"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Malformed RSA JWK aborts parsing of an entire JWK Set",
"upstream": [
"GHSA-w6j9-cwv2-h6wq",
"CVE-2026-102274",
"PYSEC-2026-4152"
]
}
BREW-SEMGREP-CVE-2026-102274 (GHSA-W6J9-CWV2-H6WQ)
Vulnerability from osv_homebrew – Published: 2026-09-30 19:46 – Updated: 2026-10-02 09:43 – Source websiteSummary
A malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because RSAAlgorithm.from_jwk can raise a plain ValueError that isn't caught by PyJWKSet's per-key error-skipping logic.
Affected component / version
- Package:
PyJWT(PyPI, ecosystempip) - Files:
jwt/api_jwk.py(PyJWK.__init__,PyJWKSet.__init__),jwt/algorithms.py(RSAAlgorithm.from_jwk) - Confirmed present in the
masterbranch as of 2026-09-05 (commit7144e4534c34810f4525dc4578a32addd8212cff, tag2.13.0). Directly verified identical in tags2.9.0,2.10.0,2.11.0,2.12.0,2.12.1,2.13.0-- the vulnerable call and theexcept PyJWTErrorguard are unchanged across all six releases. Not verified against any release prior to2.9.0.
Details
PyJWKSet.__init__ (jwt/api_jwk.py:145-152) iterates each key in a JWK Set:
for key in keys:
try:
self.keys.append(PyJWK(key))
except PyJWTError as error:
if isinstance(error, MissingCryptographyError):
raise error
# skip unusable keys
continue
PyJWK.__init__ (api_jwk.py:82) calls self.Algorithm.from_jwk(self._jwk_data) with no try/except of its own. For an RSA JWK, this dispatches to RSAAlgorithm.from_jwk (jwt/algorithms.py:539-586). When the JWK supplies d, e, n without the CRT parameters (p, q, dp, dq, qi), from_jwk calls cryptography's rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e) (algorithms.py:572-574) to derive the key. If d is not the correct private exponent for that n/e pair, rsa_recover_prime_factors raises a plain ValueError.
ValueError is a built-in Python exception and is not a subclass of jwt.exceptions.PyJWTError (PyJWTError(Exception) is the root of PyJWT's own exception hierarchy). It is therefore not caught by PyJWKSet.__init__'s except PyJWTError, and propagates out of the constructor, aborting the for key in keys: loop before any subsequent key in the list is processed.
PyJWKSet.from_dict/from_json and PyJWK.from_dict/from_json are exported public API (jwt/__init__.py). PyJWKClient.get_jwk_set (jwt/jwks_client.py:158) feeds a fetched JWKS HTTP response directly into PyJWKSet.from_dict with no per-key pre-validation, so this is reachable through the documented PyJWKClient flow whenever the fetched JWKS contains a malformed key alongside valid ones.
Proof of concept
import jwt
good_jwk = {
"kty": "RSA",
"n": "<a valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key>",
"e": "AQAB",
}
bad_jwk = {
"kty": "RSA",
"n": good_jwk["n"],
"e": "AQAB",
"d": "AAAAAA", # not the true private exponent for n/e, no CRT params present
}
jwks_doc = {"keys": [bad_jwk, good_jwk]}
jwt.PyJWKSet.from_dict(jwks_doc)
# raises: ValueError: Unable to compute factors p and q from exponent d.
# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)
Impact
PyJWKSet.__init__ raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught ValueError, not one of PyJWT's documented jwt.exceptions.* types, so exception handling written against PyJWT's documented contract (except jwt.exceptions.PyJWTError) will not catch it either.
Suggested remediation
Wrap the key-construction call in PyJWK.__init__ (api_jwk.py:82) so a ValueError is converted into InvalidKeyError (a PyJWTError subclass):
try:
self.key = self.Algorithm.from_jwk(self._jwk_data)
except ValueError as e:
raise InvalidKeyError(f"Unable to construct key from JWK: {e}") from e
This lets PyJWKSet.__init__'s existing except PyJWTError: continue skip the one malformed key as its own comment already states is intended.
Responsible Disclosure Timeline
Per the OWASP Vulnerability Disclosure Cheat Sheet and Google Project Zero's 2020 disclosure policy:
- Day 0 (date of submission): to be set to the actual API response's
created_attimestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05. - Day 90 (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.
- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.
- This window may shorten instead of extend if the issue is confirmed under active exploitation.
Try It Yourself (Sandbox)
Reproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.
- Sandbox: jwt.py
- Course: Tenant Zero: AI & SaaS Authz Failures
Credit
Discovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.
Maintainer update — 2026-09-08
We reproduced the reported behavior on PyJWT 2.13.0 with cryptography installed: a malformed RSA private JWK containing an invalid d value and no CRT parameters raised a plain ValueError while parsing a JWK set. Because PyJWKSet skips PyJWTError instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.
The independently verified affected range is >= 2.9.0, <= 2.13.0; earlier releases were not checked. A narrow fix has been prepared in commit 8915570 based on master commit 5fa7594: PyJWK converts key-construction ValueError exceptions to InvalidKeyError, allowing the existing PyJWKSet skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.
{
"affected": [
{
"ecosystem_specific": {
"fix": null,
"range_state": "affected",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.13.0",
"upstream_fixed_in": "2.14.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "semgrep",
"purl": "pkg:brew/semgrep"
},
"ranges": [
{
"events": [
{
"introduced": "1.146.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"database_specific": {
"confidence": "high",
"source": "matched",
"strategy": "registry",
"upstream_evidence": [
{
"ecosystem": "PyPI",
"key": "pkg:pypi/pyjwt@2.13.0",
"name": "pyjwt",
"resource": "pyjwt",
"strategy": "registry",
"subject_version": "2.13.0"
}
]
},
"details": "## Summary\n\nA malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because `RSAAlgorithm.from_jwk` can raise a plain `ValueError` that isn\u0027t caught by `PyJWKSet`\u0027s per-key error-skipping logic.\n\n## Affected component / version\n\n- Package: `PyJWT` (PyPI, ecosystem `pip`)\n- Files: `jwt/api_jwk.py` (`PyJWK.__init__`, `PyJWKSet.__init__`), `jwt/algorithms.py` (`RSAAlgorithm.from_jwk`)\n- Confirmed present in the `master` branch as of 2026-09-05 (commit `7144e4534c34810f4525dc4578a32addd8212cff`, tag `2.13.0`). Directly verified identical in tags `2.9.0`, `2.10.0`, `2.11.0`, `2.12.0`, `2.12.1`, `2.13.0` -- the vulnerable call and the `except PyJWTError` guard are unchanged across all six releases. Not verified against any release prior to `2.9.0`.\n\n## Details\n\n`PyJWKSet.__init__` (`jwt/api_jwk.py:145-152`) iterates each key in a JWK Set:\n\n```python\nfor key in keys:\n try:\n self.keys.append(PyJWK(key))\n except PyJWTError as error:\n if isinstance(error, MissingCryptographyError):\n raise error\n # skip unusable keys\n continue\n```\n\n`PyJWK.__init__` (`api_jwk.py:82`) calls `self.Algorithm.from_jwk(self._jwk_data)` with no try/except of its own. For an RSA JWK, this dispatches to `RSAAlgorithm.from_jwk` (`jwt/algorithms.py:539-586`). When the JWK supplies `d`, `e`, `n` without the CRT parameters (`p`, `q`, `dp`, `dq`, `qi`), `from_jwk` calls `cryptography`\u0027s `rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e)` (`algorithms.py:572-574`) to derive the key. If `d` is not the correct private exponent for that `n`/`e` pair, `rsa_recover_prime_factors` raises a plain `ValueError`.\n\n`ValueError` is a built-in Python exception and is not a subclass of `jwt.exceptions.PyJWTError` (`PyJWTError(Exception)` is the root of PyJWT\u0027s own exception hierarchy). It is therefore not caught by `PyJWKSet.__init__`\u0027s `except PyJWTError`, and propagates out of the constructor, aborting the `for key in keys:` loop before any subsequent key in the list is processed.\n\n`PyJWKSet.from_dict`/`from_json` and `PyJWK.from_dict`/`from_json` are exported public API (`jwt/__init__.py`). `PyJWKClient.get_jwk_set` (`jwt/jwks_client.py:158`) feeds a fetched JWKS HTTP response directly into `PyJWKSet.from_dict` with no per-key pre-validation, so this is reachable through the documented `PyJWKClient` flow whenever the fetched JWKS contains a malformed key alongside valid ones.\n\n## Proof of concept\n\n```python\nimport jwt\n\ngood_jwk = {\n \"kty\": \"RSA\",\n \"n\": \"\u003ca valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key\u003e\",\n \"e\": \"AQAB\",\n}\n\nbad_jwk = {\n \"kty\": \"RSA\",\n \"n\": good_jwk[\"n\"],\n \"e\": \"AQAB\",\n \"d\": \"AAAAAA\", # not the true private exponent for n/e, no CRT params present\n}\n\njwks_doc = {\"keys\": [bad_jwk, good_jwk]}\n\njwt.PyJWKSet.from_dict(jwks_doc)\n# raises: ValueError: Unable to compute factors p and q from exponent d.\n# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)\n```\n\n## Impact\n\n`PyJWKSet.__init__` raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught `ValueError`, not one of PyJWT\u0027s documented `jwt.exceptions.*` types, so exception handling written against PyJWT\u0027s documented contract (`except jwt.exceptions.PyJWTError`) will not catch it either.\n\n## Suggested remediation\n\nWrap the key-construction call in `PyJWK.__init__` (`api_jwk.py:82`) so a `ValueError` is converted into `InvalidKeyError` (a `PyJWTError` subclass):\n\n```python\ntry:\n self.key = self.Algorithm.from_jwk(self._jwk_data)\nexcept ValueError as e:\n raise InvalidKeyError(f\"Unable to construct key from JWK: {e}\") from e\n```\n\nThis lets `PyJWKSet.__init__`\u0027s existing `except PyJWTError: continue` skip the one malformed key as its own comment already states is intended.\n\n## Responsible Disclosure Timeline\n\nPer the [OWASP Vulnerability Disclosure Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html) and [Google Project Zero\u0027s 2020 disclosure policy](https://projectzero.google/2020/01/policy-and-disclosure-2020-edition.html):\n\n- **Day 0** (date of submission): to be set to the actual API response\u0027s `created_at` timestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05.\n- **Day 90** (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.\n- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.\n- This window may shorten instead of extend if the issue is confirmed under active exploitation.\n\n## Try It Yourself (Sandbox)\n\nReproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.\n\n- Sandbox: [jwt.py](https://play.secdim.com/game/python/challenge/jwtpy)\n- Course: [Tenant Zero: AI \u0026 SaaS Authz Failures](https://learn.secdim.com/course/tenant-zero)\n\n## Credit\n\nDiscovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.\n\n## Maintainer update \u2014 2026-09-08\n\nWe reproduced the reported behavior on PyJWT 2.13.0 with `cryptography` installed: a malformed RSA private JWK containing an invalid `d` value and no CRT parameters raised a plain `ValueError` while parsing a JWK set. Because `PyJWKSet` skips `PyJWTError` instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.\n\nThe independently verified affected range is `\u003e= 2.9.0, \u003c= 2.13.0`; earlier releases were not checked. A narrow fix has been prepared in commit `8915570` based on master commit `5fa7594`: `PyJWK` converts key-construction `ValueError` exceptions to `InvalidKeyError`, allowing the existing `PyJWKSet` skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.",
"id": "BREW-semgrep-CVE-2026-102274",
"modified": "2026-10-02T09:43:53Z",
"published": "2026-09-30T19:46:01Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-102274"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Malformed RSA JWK aborts parsing of an entire JWK Set",
"upstream": [
"GHSA-w6j9-cwv2-h6wq",
"CVE-2026-102274",
"PYSEC-2026-4152"
]
}
BREW-SIGSTORE-CVE-2026-102274 (GHSA-W6J9-CWV2-H6WQ)
Vulnerability from osv_homebrew – Published: 2026-09-30 09:42 – Updated: 2026-10-02 09:46 – Source websiteSummary
A malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because RSAAlgorithm.from_jwk can raise a plain ValueError that isn't caught by PyJWKSet's per-key error-skipping logic.
Affected component / version
- Package:
PyJWT(PyPI, ecosystempip) - Files:
jwt/api_jwk.py(PyJWK.__init__,PyJWKSet.__init__),jwt/algorithms.py(RSAAlgorithm.from_jwk) - Confirmed present in the
masterbranch as of 2026-09-05 (commit7144e4534c34810f4525dc4578a32addd8212cff, tag2.13.0). Directly verified identical in tags2.9.0,2.10.0,2.11.0,2.12.0,2.12.1,2.13.0-- the vulnerable call and theexcept PyJWTErrorguard are unchanged across all six releases. Not verified against any release prior to2.9.0.
Details
PyJWKSet.__init__ (jwt/api_jwk.py:145-152) iterates each key in a JWK Set:
for key in keys:
try:
self.keys.append(PyJWK(key))
except PyJWTError as error:
if isinstance(error, MissingCryptographyError):
raise error
# skip unusable keys
continue
PyJWK.__init__ (api_jwk.py:82) calls self.Algorithm.from_jwk(self._jwk_data) with no try/except of its own. For an RSA JWK, this dispatches to RSAAlgorithm.from_jwk (jwt/algorithms.py:539-586). When the JWK supplies d, e, n without the CRT parameters (p, q, dp, dq, qi), from_jwk calls cryptography's rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e) (algorithms.py:572-574) to derive the key. If d is not the correct private exponent for that n/e pair, rsa_recover_prime_factors raises a plain ValueError.
ValueError is a built-in Python exception and is not a subclass of jwt.exceptions.PyJWTError (PyJWTError(Exception) is the root of PyJWT's own exception hierarchy). It is therefore not caught by PyJWKSet.__init__'s except PyJWTError, and propagates out of the constructor, aborting the for key in keys: loop before any subsequent key in the list is processed.
PyJWKSet.from_dict/from_json and PyJWK.from_dict/from_json are exported public API (jwt/__init__.py). PyJWKClient.get_jwk_set (jwt/jwks_client.py:158) feeds a fetched JWKS HTTP response directly into PyJWKSet.from_dict with no per-key pre-validation, so this is reachable through the documented PyJWKClient flow whenever the fetched JWKS contains a malformed key alongside valid ones.
Proof of concept
import jwt
good_jwk = {
"kty": "RSA",
"n": "<a valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key>",
"e": "AQAB",
}
bad_jwk = {
"kty": "RSA",
"n": good_jwk["n"],
"e": "AQAB",
"d": "AAAAAA", # not the true private exponent for n/e, no CRT params present
}
jwks_doc = {"keys": [bad_jwk, good_jwk]}
jwt.PyJWKSet.from_dict(jwks_doc)
# raises: ValueError: Unable to compute factors p and q from exponent d.
# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)
Impact
PyJWKSet.__init__ raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught ValueError, not one of PyJWT's documented jwt.exceptions.* types, so exception handling written against PyJWT's documented contract (except jwt.exceptions.PyJWTError) will not catch it either.
Suggested remediation
Wrap the key-construction call in PyJWK.__init__ (api_jwk.py:82) so a ValueError is converted into InvalidKeyError (a PyJWTError subclass):
try:
self.key = self.Algorithm.from_jwk(self._jwk_data)
except ValueError as e:
raise InvalidKeyError(f"Unable to construct key from JWK: {e}") from e
This lets PyJWKSet.__init__'s existing except PyJWTError: continue skip the one malformed key as its own comment already states is intended.
Responsible Disclosure Timeline
Per the OWASP Vulnerability Disclosure Cheat Sheet and Google Project Zero's 2020 disclosure policy:
- Day 0 (date of submission): to be set to the actual API response's
created_attimestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05. - Day 90 (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.
- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.
- This window may shorten instead of extend if the issue is confirmed under active exploitation.
Try It Yourself (Sandbox)
Reproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.
- Sandbox: jwt.py
- Course: Tenant Zero: AI & SaaS Authz Failures
Credit
Discovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.
Maintainer update — 2026-09-08
We reproduced the reported behavior on PyJWT 2.13.0 with cryptography installed: a malformed RSA private JWK containing an invalid d value and no CRT parameters raised a plain ValueError while parsing a JWK set. Because PyJWKSet skips PyJWTError instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.
The independently verified affected range is >= 2.9.0, <= 2.13.0; earlier releases were not checked. A narrow fix has been prepared in commit 8915570 based on master commit 5fa7594: PyJWK converts key-construction ValueError exceptions to InvalidKeyError, allowing the existing PyJWKSet skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.15.1",
"upstream_fixed_in": "2.14.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "sigstore",
"purl": "pkg:brew/sigstore"
},
"ranges": [
{
"events": [
{
"introduced": "3.2.0"
},
{
"fixed": "4.5.0_1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"database_specific": {
"confidence": "high",
"source": "matched",
"strategy": "registry",
"upstream_evidence": [
{
"ecosystem": "PyPI",
"key": "pkg:pypi/pyjwt@2.15.1",
"name": "pyjwt",
"resource": "pyjwt",
"strategy": "registry",
"subject_version": "2.15.1"
}
]
},
"details": "## Summary\n\nA malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because `RSAAlgorithm.from_jwk` can raise a plain `ValueError` that isn\u0027t caught by `PyJWKSet`\u0027s per-key error-skipping logic.\n\n## Affected component / version\n\n- Package: `PyJWT` (PyPI, ecosystem `pip`)\n- Files: `jwt/api_jwk.py` (`PyJWK.__init__`, `PyJWKSet.__init__`), `jwt/algorithms.py` (`RSAAlgorithm.from_jwk`)\n- Confirmed present in the `master` branch as of 2026-09-05 (commit `7144e4534c34810f4525dc4578a32addd8212cff`, tag `2.13.0`). Directly verified identical in tags `2.9.0`, `2.10.0`, `2.11.0`, `2.12.0`, `2.12.1`, `2.13.0` -- the vulnerable call and the `except PyJWTError` guard are unchanged across all six releases. Not verified against any release prior to `2.9.0`.\n\n## Details\n\n`PyJWKSet.__init__` (`jwt/api_jwk.py:145-152`) iterates each key in a JWK Set:\n\n```python\nfor key in keys:\n try:\n self.keys.append(PyJWK(key))\n except PyJWTError as error:\n if isinstance(error, MissingCryptographyError):\n raise error\n # skip unusable keys\n continue\n```\n\n`PyJWK.__init__` (`api_jwk.py:82`) calls `self.Algorithm.from_jwk(self._jwk_data)` with no try/except of its own. For an RSA JWK, this dispatches to `RSAAlgorithm.from_jwk` (`jwt/algorithms.py:539-586`). When the JWK supplies `d`, `e`, `n` without the CRT parameters (`p`, `q`, `dp`, `dq`, `qi`), `from_jwk` calls `cryptography`\u0027s `rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e)` (`algorithms.py:572-574`) to derive the key. If `d` is not the correct private exponent for that `n`/`e` pair, `rsa_recover_prime_factors` raises a plain `ValueError`.\n\n`ValueError` is a built-in Python exception and is not a subclass of `jwt.exceptions.PyJWTError` (`PyJWTError(Exception)` is the root of PyJWT\u0027s own exception hierarchy). It is therefore not caught by `PyJWKSet.__init__`\u0027s `except PyJWTError`, and propagates out of the constructor, aborting the `for key in keys:` loop before any subsequent key in the list is processed.\n\n`PyJWKSet.from_dict`/`from_json` and `PyJWK.from_dict`/`from_json` are exported public API (`jwt/__init__.py`). `PyJWKClient.get_jwk_set` (`jwt/jwks_client.py:158`) feeds a fetched JWKS HTTP response directly into `PyJWKSet.from_dict` with no per-key pre-validation, so this is reachable through the documented `PyJWKClient` flow whenever the fetched JWKS contains a malformed key alongside valid ones.\n\n## Proof of concept\n\n```python\nimport jwt\n\ngood_jwk = {\n \"kty\": \"RSA\",\n \"n\": \"\u003ca valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key\u003e\",\n \"e\": \"AQAB\",\n}\n\nbad_jwk = {\n \"kty\": \"RSA\",\n \"n\": good_jwk[\"n\"],\n \"e\": \"AQAB\",\n \"d\": \"AAAAAA\", # not the true private exponent for n/e, no CRT params present\n}\n\njwks_doc = {\"keys\": [bad_jwk, good_jwk]}\n\njwt.PyJWKSet.from_dict(jwks_doc)\n# raises: ValueError: Unable to compute factors p and q from exponent d.\n# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)\n```\n\n## Impact\n\n`PyJWKSet.__init__` raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught `ValueError`, not one of PyJWT\u0027s documented `jwt.exceptions.*` types, so exception handling written against PyJWT\u0027s documented contract (`except jwt.exceptions.PyJWTError`) will not catch it either.\n\n## Suggested remediation\n\nWrap the key-construction call in `PyJWK.__init__` (`api_jwk.py:82`) so a `ValueError` is converted into `InvalidKeyError` (a `PyJWTError` subclass):\n\n```python\ntry:\n self.key = self.Algorithm.from_jwk(self._jwk_data)\nexcept ValueError as e:\n raise InvalidKeyError(f\"Unable to construct key from JWK: {e}\") from e\n```\n\nThis lets `PyJWKSet.__init__`\u0027s existing `except PyJWTError: continue` skip the one malformed key as its own comment already states is intended.\n\n## Responsible Disclosure Timeline\n\nPer the [OWASP Vulnerability Disclosure Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html) and [Google Project Zero\u0027s 2020 disclosure policy](https://projectzero.google/2020/01/policy-and-disclosure-2020-edition.html):\n\n- **Day 0** (date of submission): to be set to the actual API response\u0027s `created_at` timestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05.\n- **Day 90** (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.\n- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.\n- This window may shorten instead of extend if the issue is confirmed under active exploitation.\n\n## Try It Yourself (Sandbox)\n\nReproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.\n\n- Sandbox: [jwt.py](https://play.secdim.com/game/python/challenge/jwtpy)\n- Course: [Tenant Zero: AI \u0026 SaaS Authz Failures](https://learn.secdim.com/course/tenant-zero)\n\n## Credit\n\nDiscovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.\n\n## Maintainer update \u2014 2026-09-08\n\nWe reproduced the reported behavior on PyJWT 2.13.0 with `cryptography` installed: a malformed RSA private JWK containing an invalid `d` value and no CRT parameters raised a plain `ValueError` while parsing a JWK set. Because `PyJWKSet` skips `PyJWTError` instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.\n\nThe independently verified affected range is `\u003e= 2.9.0, \u003c= 2.13.0`; earlier releases were not checked. A narrow fix has been prepared in commit `8915570` based on master commit `5fa7594`: `PyJWK` converts key-construction `ValueError` exceptions to `InvalidKeyError`, allowing the existing `PyJWKSet` skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.",
"id": "BREW-sigstore-CVE-2026-102274",
"modified": "2026-10-02T09:46:02Z",
"published": "2026-09-30T09:42:17Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-102274"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Malformed RSA JWK aborts parsing of an entire JWK Set",
"upstream": [
"GHSA-w6j9-cwv2-h6wq",
"CVE-2026-102274",
"PYSEC-2026-4152"
]
}
BREW-SNOWFLAKE-CLI-CVE-2… (GHSA-W6J9-CWV2-H6WQ)
Vulnerability from osv_homebrew – Published: 2026-09-30 09:56 – Updated: 2026-10-02 09:51 – Source websiteSummary
A malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because RSAAlgorithm.from_jwk can raise a plain ValueError that isn't caught by PyJWKSet's per-key error-skipping logic.
Affected component / version
- Package:
PyJWT(PyPI, ecosystempip) - Files:
jwt/api_jwk.py(PyJWK.__init__,PyJWKSet.__init__),jwt/algorithms.py(RSAAlgorithm.from_jwk) - Confirmed present in the
masterbranch as of 2026-09-05 (commit7144e4534c34810f4525dc4578a32addd8212cff, tag2.13.0). Directly verified identical in tags2.9.0,2.10.0,2.11.0,2.12.0,2.12.1,2.13.0-- the vulnerable call and theexcept PyJWTErrorguard are unchanged across all six releases. Not verified against any release prior to2.9.0.
Details
PyJWKSet.__init__ (jwt/api_jwk.py:145-152) iterates each key in a JWK Set:
for key in keys:
try:
self.keys.append(PyJWK(key))
except PyJWTError as error:
if isinstance(error, MissingCryptographyError):
raise error
# skip unusable keys
continue
PyJWK.__init__ (api_jwk.py:82) calls self.Algorithm.from_jwk(self._jwk_data) with no try/except of its own. For an RSA JWK, this dispatches to RSAAlgorithm.from_jwk (jwt/algorithms.py:539-586). When the JWK supplies d, e, n without the CRT parameters (p, q, dp, dq, qi), from_jwk calls cryptography's rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e) (algorithms.py:572-574) to derive the key. If d is not the correct private exponent for that n/e pair, rsa_recover_prime_factors raises a plain ValueError.
ValueError is a built-in Python exception and is not a subclass of jwt.exceptions.PyJWTError (PyJWTError(Exception) is the root of PyJWT's own exception hierarchy). It is therefore not caught by PyJWKSet.__init__'s except PyJWTError, and propagates out of the constructor, aborting the for key in keys: loop before any subsequent key in the list is processed.
PyJWKSet.from_dict/from_json and PyJWK.from_dict/from_json are exported public API (jwt/__init__.py). PyJWKClient.get_jwk_set (jwt/jwks_client.py:158) feeds a fetched JWKS HTTP response directly into PyJWKSet.from_dict with no per-key pre-validation, so this is reachable through the documented PyJWKClient flow whenever the fetched JWKS contains a malformed key alongside valid ones.
Proof of concept
import jwt
good_jwk = {
"kty": "RSA",
"n": "<a valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key>",
"e": "AQAB",
}
bad_jwk = {
"kty": "RSA",
"n": good_jwk["n"],
"e": "AQAB",
"d": "AAAAAA", # not the true private exponent for n/e, no CRT params present
}
jwks_doc = {"keys": [bad_jwk, good_jwk]}
jwt.PyJWKSet.from_dict(jwks_doc)
# raises: ValueError: Unable to compute factors p and q from exponent d.
# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)
Impact
PyJWKSet.__init__ raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught ValueError, not one of PyJWT's documented jwt.exceptions.* types, so exception handling written against PyJWT's documented contract (except jwt.exceptions.PyJWTError) will not catch it either.
Suggested remediation
Wrap the key-construction call in PyJWK.__init__ (api_jwk.py:82) so a ValueError is converted into InvalidKeyError (a PyJWTError subclass):
try:
self.key = self.Algorithm.from_jwk(self._jwk_data)
except ValueError as e:
raise InvalidKeyError(f"Unable to construct key from JWK: {e}") from e
This lets PyJWKSet.__init__'s existing except PyJWTError: continue skip the one malformed key as its own comment already states is intended.
Responsible Disclosure Timeline
Per the OWASP Vulnerability Disclosure Cheat Sheet and Google Project Zero's 2020 disclosure policy:
- Day 0 (date of submission): to be set to the actual API response's
created_attimestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05. - Day 90 (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.
- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.
- This window may shorten instead of extend if the issue is confirmed under active exploitation.
Try It Yourself (Sandbox)
Reproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.
- Sandbox: jwt.py
- Course: Tenant Zero: AI & SaaS Authz Failures
Credit
Discovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.
Maintainer update — 2026-09-08
We reproduced the reported behavior on PyJWT 2.13.0 with cryptography installed: a malformed RSA private JWK containing an invalid d value and no CRT parameters raised a plain ValueError while parsing a JWK set. Because PyJWKSet skips PyJWTError instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.
The independently verified affected range is >= 2.9.0, <= 2.13.0; earlier releases were not checked. A narrow fix has been prepared in commit 8915570 based on master commit 5fa7594: PyJWK converts key-construction ValueError exceptions to InvalidKeyError, allowing the existing PyJWKSet skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.15.0",
"upstream_fixed_in": "2.14.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "snowflake-cli",
"purl": "pkg:brew/snowflake-cli"
},
"ranges": [
{
"events": [
{
"introduced": "3.4.1"
},
{
"fixed": "3.28.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"database_specific": {
"confidence": "high",
"source": "matched",
"strategy": "registry",
"upstream_evidence": [
{
"ecosystem": "PyPI",
"key": "pkg:pypi/pyjwt@2.15.0",
"name": "pyjwt",
"resource": "pyjwt",
"strategy": "registry",
"subject_version": "2.15.0"
}
]
},
"details": "## Summary\n\nA malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because `RSAAlgorithm.from_jwk` can raise a plain `ValueError` that isn\u0027t caught by `PyJWKSet`\u0027s per-key error-skipping logic.\n\n## Affected component / version\n\n- Package: `PyJWT` (PyPI, ecosystem `pip`)\n- Files: `jwt/api_jwk.py` (`PyJWK.__init__`, `PyJWKSet.__init__`), `jwt/algorithms.py` (`RSAAlgorithm.from_jwk`)\n- Confirmed present in the `master` branch as of 2026-09-05 (commit `7144e4534c34810f4525dc4578a32addd8212cff`, tag `2.13.0`). Directly verified identical in tags `2.9.0`, `2.10.0`, `2.11.0`, `2.12.0`, `2.12.1`, `2.13.0` -- the vulnerable call and the `except PyJWTError` guard are unchanged across all six releases. Not verified against any release prior to `2.9.0`.\n\n## Details\n\n`PyJWKSet.__init__` (`jwt/api_jwk.py:145-152`) iterates each key in a JWK Set:\n\n```python\nfor key in keys:\n try:\n self.keys.append(PyJWK(key))\n except PyJWTError as error:\n if isinstance(error, MissingCryptographyError):\n raise error\n # skip unusable keys\n continue\n```\n\n`PyJWK.__init__` (`api_jwk.py:82`) calls `self.Algorithm.from_jwk(self._jwk_data)` with no try/except of its own. For an RSA JWK, this dispatches to `RSAAlgorithm.from_jwk` (`jwt/algorithms.py:539-586`). When the JWK supplies `d`, `e`, `n` without the CRT parameters (`p`, `q`, `dp`, `dq`, `qi`), `from_jwk` calls `cryptography`\u0027s `rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e)` (`algorithms.py:572-574`) to derive the key. If `d` is not the correct private exponent for that `n`/`e` pair, `rsa_recover_prime_factors` raises a plain `ValueError`.\n\n`ValueError` is a built-in Python exception and is not a subclass of `jwt.exceptions.PyJWTError` (`PyJWTError(Exception)` is the root of PyJWT\u0027s own exception hierarchy). It is therefore not caught by `PyJWKSet.__init__`\u0027s `except PyJWTError`, and propagates out of the constructor, aborting the `for key in keys:` loop before any subsequent key in the list is processed.\n\n`PyJWKSet.from_dict`/`from_json` and `PyJWK.from_dict`/`from_json` are exported public API (`jwt/__init__.py`). `PyJWKClient.get_jwk_set` (`jwt/jwks_client.py:158`) feeds a fetched JWKS HTTP response directly into `PyJWKSet.from_dict` with no per-key pre-validation, so this is reachable through the documented `PyJWKClient` flow whenever the fetched JWKS contains a malformed key alongside valid ones.\n\n## Proof of concept\n\n```python\nimport jwt\n\ngood_jwk = {\n \"kty\": \"RSA\",\n \"n\": \"\u003ca valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key\u003e\",\n \"e\": \"AQAB\",\n}\n\nbad_jwk = {\n \"kty\": \"RSA\",\n \"n\": good_jwk[\"n\"],\n \"e\": \"AQAB\",\n \"d\": \"AAAAAA\", # not the true private exponent for n/e, no CRT params present\n}\n\njwks_doc = {\"keys\": [bad_jwk, good_jwk]}\n\njwt.PyJWKSet.from_dict(jwks_doc)\n# raises: ValueError: Unable to compute factors p and q from exponent d.\n# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)\n```\n\n## Impact\n\n`PyJWKSet.__init__` raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught `ValueError`, not one of PyJWT\u0027s documented `jwt.exceptions.*` types, so exception handling written against PyJWT\u0027s documented contract (`except jwt.exceptions.PyJWTError`) will not catch it either.\n\n## Suggested remediation\n\nWrap the key-construction call in `PyJWK.__init__` (`api_jwk.py:82`) so a `ValueError` is converted into `InvalidKeyError` (a `PyJWTError` subclass):\n\n```python\ntry:\n self.key = self.Algorithm.from_jwk(self._jwk_data)\nexcept ValueError as e:\n raise InvalidKeyError(f\"Unable to construct key from JWK: {e}\") from e\n```\n\nThis lets `PyJWKSet.__init__`\u0027s existing `except PyJWTError: continue` skip the one malformed key as its own comment already states is intended.\n\n## Responsible Disclosure Timeline\n\nPer the [OWASP Vulnerability Disclosure Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html) and [Google Project Zero\u0027s 2020 disclosure policy](https://projectzero.google/2020/01/policy-and-disclosure-2020-edition.html):\n\n- **Day 0** (date of submission): to be set to the actual API response\u0027s `created_at` timestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05.\n- **Day 90** (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.\n- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.\n- This window may shorten instead of extend if the issue is confirmed under active exploitation.\n\n## Try It Yourself (Sandbox)\n\nReproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.\n\n- Sandbox: [jwt.py](https://play.secdim.com/game/python/challenge/jwtpy)\n- Course: [Tenant Zero: AI \u0026 SaaS Authz Failures](https://learn.secdim.com/course/tenant-zero)\n\n## Credit\n\nDiscovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.\n\n## Maintainer update \u2014 2026-09-08\n\nWe reproduced the reported behavior on PyJWT 2.13.0 with `cryptography` installed: a malformed RSA private JWK containing an invalid `d` value and no CRT parameters raised a plain `ValueError` while parsing a JWK set. Because `PyJWKSet` skips `PyJWTError` instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.\n\nThe independently verified affected range is `\u003e= 2.9.0, \u003c= 2.13.0`; earlier releases were not checked. A narrow fix has been prepared in commit `8915570` based on master commit `5fa7594`: `PyJWK` converts key-construction `ValueError` exceptions to `InvalidKeyError`, allowing the existing `PyJWKSet` skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.",
"id": "BREW-snowflake-cli-CVE-2026-102274",
"modified": "2026-10-02T09:51:40Z",
"published": "2026-09-30T09:56:06Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-102274"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Malformed RSA JWK aborts parsing of an entire JWK Set",
"upstream": [
"GHSA-w6j9-cwv2-h6wq",
"CVE-2026-102274",
"PYSEC-2026-4152"
]
}
BREW-SNYK-AGENT-SCAN-CVE… (GHSA-W6J9-CWV2-H6WQ)
Vulnerability from osv_homebrew – Published: 2026-09-30 09:42 – Updated: 2026-10-02 09:43 – Source websiteSummary
A malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because RSAAlgorithm.from_jwk can raise a plain ValueError that isn't caught by PyJWKSet's per-key error-skipping logic.
Affected component / version
- Package:
PyJWT(PyPI, ecosystempip) - Files:
jwt/api_jwk.py(PyJWK.__init__,PyJWKSet.__init__),jwt/algorithms.py(RSAAlgorithm.from_jwk) - Confirmed present in the
masterbranch as of 2026-09-05 (commit7144e4534c34810f4525dc4578a32addd8212cff, tag2.13.0). Directly verified identical in tags2.9.0,2.10.0,2.11.0,2.12.0,2.12.1,2.13.0-- the vulnerable call and theexcept PyJWTErrorguard are unchanged across all six releases. Not verified against any release prior to2.9.0.
Details
PyJWKSet.__init__ (jwt/api_jwk.py:145-152) iterates each key in a JWK Set:
for key in keys:
try:
self.keys.append(PyJWK(key))
except PyJWTError as error:
if isinstance(error, MissingCryptographyError):
raise error
# skip unusable keys
continue
PyJWK.__init__ (api_jwk.py:82) calls self.Algorithm.from_jwk(self._jwk_data) with no try/except of its own. For an RSA JWK, this dispatches to RSAAlgorithm.from_jwk (jwt/algorithms.py:539-586). When the JWK supplies d, e, n without the CRT parameters (p, q, dp, dq, qi), from_jwk calls cryptography's rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e) (algorithms.py:572-574) to derive the key. If d is not the correct private exponent for that n/e pair, rsa_recover_prime_factors raises a plain ValueError.
ValueError is a built-in Python exception and is not a subclass of jwt.exceptions.PyJWTError (PyJWTError(Exception) is the root of PyJWT's own exception hierarchy). It is therefore not caught by PyJWKSet.__init__'s except PyJWTError, and propagates out of the constructor, aborting the for key in keys: loop before any subsequent key in the list is processed.
PyJWKSet.from_dict/from_json and PyJWK.from_dict/from_json are exported public API (jwt/__init__.py). PyJWKClient.get_jwk_set (jwt/jwks_client.py:158) feeds a fetched JWKS HTTP response directly into PyJWKSet.from_dict with no per-key pre-validation, so this is reachable through the documented PyJWKClient flow whenever the fetched JWKS contains a malformed key alongside valid ones.
Proof of concept
import jwt
good_jwk = {
"kty": "RSA",
"n": "<a valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key>",
"e": "AQAB",
}
bad_jwk = {
"kty": "RSA",
"n": good_jwk["n"],
"e": "AQAB",
"d": "AAAAAA", # not the true private exponent for n/e, no CRT params present
}
jwks_doc = {"keys": [bad_jwk, good_jwk]}
jwt.PyJWKSet.from_dict(jwks_doc)
# raises: ValueError: Unable to compute factors p and q from exponent d.
# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)
Impact
PyJWKSet.__init__ raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught ValueError, not one of PyJWT's documented jwt.exceptions.* types, so exception handling written against PyJWT's documented contract (except jwt.exceptions.PyJWTError) will not catch it either.
Suggested remediation
Wrap the key-construction call in PyJWK.__init__ (api_jwk.py:82) so a ValueError is converted into InvalidKeyError (a PyJWTError subclass):
try:
self.key = self.Algorithm.from_jwk(self._jwk_data)
except ValueError as e:
raise InvalidKeyError(f"Unable to construct key from JWK: {e}") from e
This lets PyJWKSet.__init__'s existing except PyJWTError: continue skip the one malformed key as its own comment already states is intended.
Responsible Disclosure Timeline
Per the OWASP Vulnerability Disclosure Cheat Sheet and Google Project Zero's 2020 disclosure policy:
- Day 0 (date of submission): to be set to the actual API response's
created_attimestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05. - Day 90 (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.
- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.
- This window may shorten instead of extend if the issue is confirmed under active exploitation.
Try It Yourself (Sandbox)
Reproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.
- Sandbox: jwt.py
- Course: Tenant Zero: AI & SaaS Authz Failures
Credit
Discovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.
Maintainer update — 2026-09-08
We reproduced the reported behavior on PyJWT 2.13.0 with cryptography installed: a malformed RSA private JWK containing an invalid d value and no CRT parameters raised a plain ValueError while parsing a JWK set. Because PyJWKSet skips PyJWTError instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.
The independently verified affected range is >= 2.9.0, <= 2.13.0; earlier releases were not checked. A narrow fix has been prepared in commit 8915570 based on master commit 5fa7594: PyJWK converts key-construction ValueError exceptions to InvalidKeyError, allowing the existing PyJWKSet skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.15.1",
"upstream_fixed_in": "2.14.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "snyk-agent-scan",
"purl": "pkg:brew/snyk-agent-scan"
},
"ranges": [
{
"events": [
{
"introduced": "0.4.3"
},
{
"fixed": "0.6.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"database_specific": {
"confidence": "high",
"source": "matched",
"strategy": "registry",
"upstream_evidence": [
{
"ecosystem": "PyPI",
"key": "pkg:pypi/pyjwt@2.15.1",
"name": "pyjwt",
"resource": "pyjwt",
"strategy": "registry",
"subject_version": "2.15.1"
}
]
},
"details": "## Summary\n\nA malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because `RSAAlgorithm.from_jwk` can raise a plain `ValueError` that isn\u0027t caught by `PyJWKSet`\u0027s per-key error-skipping logic.\n\n## Affected component / version\n\n- Package: `PyJWT` (PyPI, ecosystem `pip`)\n- Files: `jwt/api_jwk.py` (`PyJWK.__init__`, `PyJWKSet.__init__`), `jwt/algorithms.py` (`RSAAlgorithm.from_jwk`)\n- Confirmed present in the `master` branch as of 2026-09-05 (commit `7144e4534c34810f4525dc4578a32addd8212cff`, tag `2.13.0`). Directly verified identical in tags `2.9.0`, `2.10.0`, `2.11.0`, `2.12.0`, `2.12.1`, `2.13.0` -- the vulnerable call and the `except PyJWTError` guard are unchanged across all six releases. Not verified against any release prior to `2.9.0`.\n\n## Details\n\n`PyJWKSet.__init__` (`jwt/api_jwk.py:145-152`) iterates each key in a JWK Set:\n\n```python\nfor key in keys:\n try:\n self.keys.append(PyJWK(key))\n except PyJWTError as error:\n if isinstance(error, MissingCryptographyError):\n raise error\n # skip unusable keys\n continue\n```\n\n`PyJWK.__init__` (`api_jwk.py:82`) calls `self.Algorithm.from_jwk(self._jwk_data)` with no try/except of its own. For an RSA JWK, this dispatches to `RSAAlgorithm.from_jwk` (`jwt/algorithms.py:539-586`). When the JWK supplies `d`, `e`, `n` without the CRT parameters (`p`, `q`, `dp`, `dq`, `qi`), `from_jwk` calls `cryptography`\u0027s `rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e)` (`algorithms.py:572-574`) to derive the key. If `d` is not the correct private exponent for that `n`/`e` pair, `rsa_recover_prime_factors` raises a plain `ValueError`.\n\n`ValueError` is a built-in Python exception and is not a subclass of `jwt.exceptions.PyJWTError` (`PyJWTError(Exception)` is the root of PyJWT\u0027s own exception hierarchy). It is therefore not caught by `PyJWKSet.__init__`\u0027s `except PyJWTError`, and propagates out of the constructor, aborting the `for key in keys:` loop before any subsequent key in the list is processed.\n\n`PyJWKSet.from_dict`/`from_json` and `PyJWK.from_dict`/`from_json` are exported public API (`jwt/__init__.py`). `PyJWKClient.get_jwk_set` (`jwt/jwks_client.py:158`) feeds a fetched JWKS HTTP response directly into `PyJWKSet.from_dict` with no per-key pre-validation, so this is reachable through the documented `PyJWKClient` flow whenever the fetched JWKS contains a malformed key alongside valid ones.\n\n## Proof of concept\n\n```python\nimport jwt\n\ngood_jwk = {\n \"kty\": \"RSA\",\n \"n\": \"\u003ca valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key\u003e\",\n \"e\": \"AQAB\",\n}\n\nbad_jwk = {\n \"kty\": \"RSA\",\n \"n\": good_jwk[\"n\"],\n \"e\": \"AQAB\",\n \"d\": \"AAAAAA\", # not the true private exponent for n/e, no CRT params present\n}\n\njwks_doc = {\"keys\": [bad_jwk, good_jwk]}\n\njwt.PyJWKSet.from_dict(jwks_doc)\n# raises: ValueError: Unable to compute factors p and q from exponent d.\n# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)\n```\n\n## Impact\n\n`PyJWKSet.__init__` raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught `ValueError`, not one of PyJWT\u0027s documented `jwt.exceptions.*` types, so exception handling written against PyJWT\u0027s documented contract (`except jwt.exceptions.PyJWTError`) will not catch it either.\n\n## Suggested remediation\n\nWrap the key-construction call in `PyJWK.__init__` (`api_jwk.py:82`) so a `ValueError` is converted into `InvalidKeyError` (a `PyJWTError` subclass):\n\n```python\ntry:\n self.key = self.Algorithm.from_jwk(self._jwk_data)\nexcept ValueError as e:\n raise InvalidKeyError(f\"Unable to construct key from JWK: {e}\") from e\n```\n\nThis lets `PyJWKSet.__init__`\u0027s existing `except PyJWTError: continue` skip the one malformed key as its own comment already states is intended.\n\n## Responsible Disclosure Timeline\n\nPer the [OWASP Vulnerability Disclosure Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html) and [Google Project Zero\u0027s 2020 disclosure policy](https://projectzero.google/2020/01/policy-and-disclosure-2020-edition.html):\n\n- **Day 0** (date of submission): to be set to the actual API response\u0027s `created_at` timestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05.\n- **Day 90** (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.\n- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.\n- This window may shorten instead of extend if the issue is confirmed under active exploitation.\n\n## Try It Yourself (Sandbox)\n\nReproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.\n\n- Sandbox: [jwt.py](https://play.secdim.com/game/python/challenge/jwtpy)\n- Course: [Tenant Zero: AI \u0026 SaaS Authz Failures](https://learn.secdim.com/course/tenant-zero)\n\n## Credit\n\nDiscovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.\n\n## Maintainer update \u2014 2026-09-08\n\nWe reproduced the reported behavior on PyJWT 2.13.0 with `cryptography` installed: a malformed RSA private JWK containing an invalid `d` value and no CRT parameters raised a plain `ValueError` while parsing a JWK set. Because `PyJWKSet` skips `PyJWTError` instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.\n\nThe independently verified affected range is `\u003e= 2.9.0, \u003c= 2.13.0`; earlier releases were not checked. A narrow fix has been prepared in commit `8915570` based on master commit `5fa7594`: `PyJWK` converts key-construction `ValueError` exceptions to `InvalidKeyError`, allowing the existing `PyJWKSet` skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.",
"id": "BREW-snyk-agent-scan-CVE-2026-102274",
"modified": "2026-10-02T09:43:02Z",
"published": "2026-09-30T09:42:19Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-102274"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Malformed RSA JWK aborts parsing of an entire JWK Set",
"upstream": [
"GHSA-w6j9-cwv2-h6wq",
"CVE-2026-102274",
"PYSEC-2026-4152"
]
}
BREW-STRANDS-AGENTS-SOPS… (GHSA-W6J9-CWV2-H6WQ)
Vulnerability from osv_homebrew – Published: 2026-09-30 09:58 – Updated: 2026-10-02 09:53 – Source websiteSummary
A malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because RSAAlgorithm.from_jwk can raise a plain ValueError that isn't caught by PyJWKSet's per-key error-skipping logic.
Affected component / version
- Package:
PyJWT(PyPI, ecosystempip) - Files:
jwt/api_jwk.py(PyJWK.__init__,PyJWKSet.__init__),jwt/algorithms.py(RSAAlgorithm.from_jwk) - Confirmed present in the
masterbranch as of 2026-09-05 (commit7144e4534c34810f4525dc4578a32addd8212cff, tag2.13.0). Directly verified identical in tags2.9.0,2.10.0,2.11.0,2.12.0,2.12.1,2.13.0-- the vulnerable call and theexcept PyJWTErrorguard are unchanged across all six releases. Not verified against any release prior to2.9.0.
Details
PyJWKSet.__init__ (jwt/api_jwk.py:145-152) iterates each key in a JWK Set:
for key in keys:
try:
self.keys.append(PyJWK(key))
except PyJWTError as error:
if isinstance(error, MissingCryptographyError):
raise error
# skip unusable keys
continue
PyJWK.__init__ (api_jwk.py:82) calls self.Algorithm.from_jwk(self._jwk_data) with no try/except of its own. For an RSA JWK, this dispatches to RSAAlgorithm.from_jwk (jwt/algorithms.py:539-586). When the JWK supplies d, e, n without the CRT parameters (p, q, dp, dq, qi), from_jwk calls cryptography's rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e) (algorithms.py:572-574) to derive the key. If d is not the correct private exponent for that n/e pair, rsa_recover_prime_factors raises a plain ValueError.
ValueError is a built-in Python exception and is not a subclass of jwt.exceptions.PyJWTError (PyJWTError(Exception) is the root of PyJWT's own exception hierarchy). It is therefore not caught by PyJWKSet.__init__'s except PyJWTError, and propagates out of the constructor, aborting the for key in keys: loop before any subsequent key in the list is processed.
PyJWKSet.from_dict/from_json and PyJWK.from_dict/from_json are exported public API (jwt/__init__.py). PyJWKClient.get_jwk_set (jwt/jwks_client.py:158) feeds a fetched JWKS HTTP response directly into PyJWKSet.from_dict with no per-key pre-validation, so this is reachable through the documented PyJWKClient flow whenever the fetched JWKS contains a malformed key alongside valid ones.
Proof of concept
import jwt
good_jwk = {
"kty": "RSA",
"n": "<a valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key>",
"e": "AQAB",
}
bad_jwk = {
"kty": "RSA",
"n": good_jwk["n"],
"e": "AQAB",
"d": "AAAAAA", # not the true private exponent for n/e, no CRT params present
}
jwks_doc = {"keys": [bad_jwk, good_jwk]}
jwt.PyJWKSet.from_dict(jwks_doc)
# raises: ValueError: Unable to compute factors p and q from exponent d.
# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)
Impact
PyJWKSet.__init__ raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught ValueError, not one of PyJWT's documented jwt.exceptions.* types, so exception handling written against PyJWT's documented contract (except jwt.exceptions.PyJWTError) will not catch it either.
Suggested remediation
Wrap the key-construction call in PyJWK.__init__ (api_jwk.py:82) so a ValueError is converted into InvalidKeyError (a PyJWTError subclass):
try:
self.key = self.Algorithm.from_jwk(self._jwk_data)
except ValueError as e:
raise InvalidKeyError(f"Unable to construct key from JWK: {e}") from e
This lets PyJWKSet.__init__'s existing except PyJWTError: continue skip the one malformed key as its own comment already states is intended.
Responsible Disclosure Timeline
Per the OWASP Vulnerability Disclosure Cheat Sheet and Google Project Zero's 2020 disclosure policy:
- Day 0 (date of submission): to be set to the actual API response's
created_attimestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05. - Day 90 (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.
- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.
- This window may shorten instead of extend if the issue is confirmed under active exploitation.
Try It Yourself (Sandbox)
Reproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.
- Sandbox: jwt.py
- Course: Tenant Zero: AI & SaaS Authz Failures
Credit
Discovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.
Maintainer update — 2026-09-08
We reproduced the reported behavior on PyJWT 2.13.0 with cryptography installed: a malformed RSA private JWK containing an invalid d value and no CRT parameters raised a plain ValueError while parsing a JWK set. Because PyJWKSet skips PyJWTError instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.
The independently verified affected range is >= 2.9.0, <= 2.13.0; earlier releases were not checked. A narrow fix has been prepared in commit 8915570 based on master commit 5fa7594: PyJWK converts key-construction ValueError exceptions to InvalidKeyError, allowing the existing PyJWKSet skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.15.1",
"upstream_fixed_in": "2.14.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "strands-agents-sops",
"purl": "pkg:brew/strands-agents-sops"
},
"ranges": [
{
"events": [
{
"introduced": "1.0.1"
},
{
"fixed": "1.1.3_2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"database_specific": {
"confidence": "high",
"source": "matched",
"strategy": "registry",
"upstream_evidence": [
{
"ecosystem": "PyPI",
"key": "pkg:pypi/pyjwt@2.15.1",
"name": "pyjwt",
"resource": "pyjwt",
"strategy": "registry",
"subject_version": "2.15.1"
}
]
},
"details": "## Summary\n\nA malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because `RSAAlgorithm.from_jwk` can raise a plain `ValueError` that isn\u0027t caught by `PyJWKSet`\u0027s per-key error-skipping logic.\n\n## Affected component / version\n\n- Package: `PyJWT` (PyPI, ecosystem `pip`)\n- Files: `jwt/api_jwk.py` (`PyJWK.__init__`, `PyJWKSet.__init__`), `jwt/algorithms.py` (`RSAAlgorithm.from_jwk`)\n- Confirmed present in the `master` branch as of 2026-09-05 (commit `7144e4534c34810f4525dc4578a32addd8212cff`, tag `2.13.0`). Directly verified identical in tags `2.9.0`, `2.10.0`, `2.11.0`, `2.12.0`, `2.12.1`, `2.13.0` -- the vulnerable call and the `except PyJWTError` guard are unchanged across all six releases. Not verified against any release prior to `2.9.0`.\n\n## Details\n\n`PyJWKSet.__init__` (`jwt/api_jwk.py:145-152`) iterates each key in a JWK Set:\n\n```python\nfor key in keys:\n try:\n self.keys.append(PyJWK(key))\n except PyJWTError as error:\n if isinstance(error, MissingCryptographyError):\n raise error\n # skip unusable keys\n continue\n```\n\n`PyJWK.__init__` (`api_jwk.py:82`) calls `self.Algorithm.from_jwk(self._jwk_data)` with no try/except of its own. For an RSA JWK, this dispatches to `RSAAlgorithm.from_jwk` (`jwt/algorithms.py:539-586`). When the JWK supplies `d`, `e`, `n` without the CRT parameters (`p`, `q`, `dp`, `dq`, `qi`), `from_jwk` calls `cryptography`\u0027s `rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e)` (`algorithms.py:572-574`) to derive the key. If `d` is not the correct private exponent for that `n`/`e` pair, `rsa_recover_prime_factors` raises a plain `ValueError`.\n\n`ValueError` is a built-in Python exception and is not a subclass of `jwt.exceptions.PyJWTError` (`PyJWTError(Exception)` is the root of PyJWT\u0027s own exception hierarchy). It is therefore not caught by `PyJWKSet.__init__`\u0027s `except PyJWTError`, and propagates out of the constructor, aborting the `for key in keys:` loop before any subsequent key in the list is processed.\n\n`PyJWKSet.from_dict`/`from_json` and `PyJWK.from_dict`/`from_json` are exported public API (`jwt/__init__.py`). `PyJWKClient.get_jwk_set` (`jwt/jwks_client.py:158`) feeds a fetched JWKS HTTP response directly into `PyJWKSet.from_dict` with no per-key pre-validation, so this is reachable through the documented `PyJWKClient` flow whenever the fetched JWKS contains a malformed key alongside valid ones.\n\n## Proof of concept\n\n```python\nimport jwt\n\ngood_jwk = {\n \"kty\": \"RSA\",\n \"n\": \"\u003ca valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key\u003e\",\n \"e\": \"AQAB\",\n}\n\nbad_jwk = {\n \"kty\": \"RSA\",\n \"n\": good_jwk[\"n\"],\n \"e\": \"AQAB\",\n \"d\": \"AAAAAA\", # not the true private exponent for n/e, no CRT params present\n}\n\njwks_doc = {\"keys\": [bad_jwk, good_jwk]}\n\njwt.PyJWKSet.from_dict(jwks_doc)\n# raises: ValueError: Unable to compute factors p and q from exponent d.\n# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)\n```\n\n## Impact\n\n`PyJWKSet.__init__` raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught `ValueError`, not one of PyJWT\u0027s documented `jwt.exceptions.*` types, so exception handling written against PyJWT\u0027s documented contract (`except jwt.exceptions.PyJWTError`) will not catch it either.\n\n## Suggested remediation\n\nWrap the key-construction call in `PyJWK.__init__` (`api_jwk.py:82`) so a `ValueError` is converted into `InvalidKeyError` (a `PyJWTError` subclass):\n\n```python\ntry:\n self.key = self.Algorithm.from_jwk(self._jwk_data)\nexcept ValueError as e:\n raise InvalidKeyError(f\"Unable to construct key from JWK: {e}\") from e\n```\n\nThis lets `PyJWKSet.__init__`\u0027s existing `except PyJWTError: continue` skip the one malformed key as its own comment already states is intended.\n\n## Responsible Disclosure Timeline\n\nPer the [OWASP Vulnerability Disclosure Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html) and [Google Project Zero\u0027s 2020 disclosure policy](https://projectzero.google/2020/01/policy-and-disclosure-2020-edition.html):\n\n- **Day 0** (date of submission): to be set to the actual API response\u0027s `created_at` timestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05.\n- **Day 90** (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.\n- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.\n- This window may shorten instead of extend if the issue is confirmed under active exploitation.\n\n## Try It Yourself (Sandbox)\n\nReproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.\n\n- Sandbox: [jwt.py](https://play.secdim.com/game/python/challenge/jwtpy)\n- Course: [Tenant Zero: AI \u0026 SaaS Authz Failures](https://learn.secdim.com/course/tenant-zero)\n\n## Credit\n\nDiscovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.\n\n## Maintainer update \u2014 2026-09-08\n\nWe reproduced the reported behavior on PyJWT 2.13.0 with `cryptography` installed: a malformed RSA private JWK containing an invalid `d` value and no CRT parameters raised a plain `ValueError` while parsing a JWK set. Because `PyJWKSet` skips `PyJWTError` instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.\n\nThe independently verified affected range is `\u003e= 2.9.0, \u003c= 2.13.0`; earlier releases were not checked. A narrow fix has been prepared in commit `8915570` based on master commit `5fa7594`: `PyJWK` converts key-construction `ValueError` exceptions to `InvalidKeyError`, allowing the existing `PyJWKSet` skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.",
"id": "BREW-strands-agents-sops-CVE-2026-102274",
"modified": "2026-10-02T09:53:44Z",
"published": "2026-09-30T09:58:03Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-102274"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Malformed RSA JWK aborts parsing of an entire JWK Set",
"upstream": [
"GHSA-w6j9-cwv2-h6wq",
"CVE-2026-102274",
"PYSEC-2026-4152"
]
}
CERTFR-2026-AVI-1249
Vulnerability from certfr_avis - Published: 2026-10-02 - Updated: 2026-10-02
De multiples vulnérabilités ont été découvertes dans les produits VMware. Elles permettent à un attaquant de provoquer un problème de sécurité non spécifié par l'éditeur.
Solutions
Se référer au bulletin de sécurité de l'éditeur pour l'obtention des correctifs (cf. section Documentation).
| Vendor | Product | Description | ||
|---|---|---|---|---|
| VMware | Tanzu | VMware Tanzu pour Valkey on Kubernetes versions antérieures à 3.5.1 | ||
| VMware | Tanzu | VMware Tanzu pour Postgres on Kubernetes versions antérieures à 4.5.2 | ||
| VMware | Spring Cloud Gateway | Spring Cloud Gateway pour Kubernetes versions antérieures à 2.2.17 | ||
| VMware | Tanzu | VMware Tanzu pour Valkey versions antérieures à 8.0.11 | ||
| VMware | Tanzu | VMware Tanzu pour Valkey versions 9.0.x antérieures à 9.0.6 | ||
| VMware | Tanzu | VMware Tanzu pour Valkey versions 8.1.x antérieures à 8.1.10 | ||
| VMware | Tanzu | VMware Tanzu pour Valkey versions 9.1.x antérieures à 9.1.2 | ||
| VMware | Spring Cloud Data Flow | Spring Cloud Data Flow pour Kubernetes versions antérieures à 1.6.16 |
| Title | Publication Time | Tags | ||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
||||||||||||||||||||||||||
{
"$ref": "https://www.cert.ssi.gouv.fr/openapi.json",
"affected_systems": [
{
"description": "VMware Tanzu pour Valkey on Kubernetes versions ant\u00e9rieures \u00e0 3.5.1",
"product": {
"name": "Tanzu",
"vendor": {
"name": "VMware",
"scada": false
}
}
},
{
"description": "VMware Tanzu pour Postgres on Kubernetes versions ant\u00e9rieures \u00e0 4.5.2",
"product": {
"name": "Tanzu",
"vendor": {
"name": "VMware",
"scada": false
}
}
},
{
"description": "Spring Cloud Gateway pour Kubernetes versions ant\u00e9rieures \u00e0 2.2.17",
"product": {
"name": "Spring Cloud Gateway",
"vendor": {
"name": "VMware",
"scada": false
}
}
},
{
"description": "VMware Tanzu pour Valkey versions ant\u00e9rieures \u00e0 8.0.11",
"product": {
"name": "Tanzu",
"vendor": {
"name": "VMware",
"scada": false
}
}
},
{
"description": "VMware Tanzu pour Valkey versions 9.0.x ant\u00e9rieures \u00e0 9.0.6",
"product": {
"name": "Tanzu",
"vendor": {
"name": "VMware",
"scada": false
}
}
},
{
"description": "VMware Tanzu pour Valkey versions 8.1.x ant\u00e9rieures \u00e0 8.1.10",
"product": {
"name": "Tanzu",
"vendor": {
"name": "VMware",
"scada": false
}
}
},
{
"description": "VMware Tanzu pour Valkey versions 9.1.x ant\u00e9rieures \u00e0 9.1.2",
"product": {
"name": "Tanzu",
"vendor": {
"name": "VMware",
"scada": false
}
}
},
{
"description": "Spring Cloud Data Flow pour Kubernetes versions ant\u00e9rieures \u00e0 1.6.16",
"product": {
"name": "Spring Cloud Data Flow",
"vendor": {
"name": "VMware",
"scada": false
}
}
}
],
"affected_systems_content": "",
"content": "## Solutions\n\nSe r\u00e9f\u00e9rer au bulletin de s\u00e9curit\u00e9 de l\u0027\u00e9diteur pour l\u0027obtention des correctifs (cf. section Documentation).",
"cves": [
{
"name": "CVE-2026-75595",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-75595"
},
{
"name": "CVE-2026-53910",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-53910"
},
{
"name": "CVE-2026-56404",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56404"
},
{
"name": "CVE-2026-58055",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-58055"
},
{
"name": "CVE-2025-61730",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-61730"
},
{
"name": "CVE-2026-54369",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54369"
},
{
"name": "CVE-2025-58183",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-58183"
},
{
"name": "CVE-2026-11940",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-11940"
},
{
"name": "CVE-2026-56862",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56862"
},
{
"name": "CVE-2026-53613",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-53613"
},
{
"name": "CVE-2026-102271",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-102271"
},
{
"name": "CVE-2026-42507",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42507"
},
{
"name": "CVE-2026-33818",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33818"
},
{
"name": "CVE-2026-39830",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39830"
},
{
"name": "CVE-2026-63384",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63384"
},
{
"name": "CVE-2026-34180",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34180"
},
{
"name": "CVE-2026-68763",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-68763"
},
{
"name": "CVE-2026-33186",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33186"
},
{
"name": "CVE-2026-39826",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39826"
},
{
"name": "CVE-2026-80489",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-80489"
},
{
"name": "CVE-2026-42766",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42766"
},
{
"name": "CVE-2026-13346",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-13346"
},
{
"name": "CVE-2026-56846",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56846"
},
{
"name": "CVE-2026-9076",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-9076"
},
{
"name": "CVE-2025-22872",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-22872"
},
{
"name": "CVE-2026-15310",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-15310"
},
{
"name": "CVE-2026-42508",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42508"
},
{
"name": "CVE-2026-84445",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-84445"
},
{
"name": "CVE-2026-1965",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-1965"
},
{
"name": "CVE-2026-34181",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34181"
},
{
"name": "CVE-2026-97687",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-97687"
},
{
"name": "CVE-2026-81870",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-81870"
},
{
"name": "CVE-2026-58013",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-58013"
},
{
"name": "CVE-2026-32288",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-32288"
},
{
"name": "CVE-2025-47907",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-47907"
},
{
"name": "CVE-2026-59885",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-59885"
},
{
"name": "CVE-2026-42770",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42770"
},
{
"name": "CVE-2026-91776",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-91776"
},
{
"name": "CVE-2026-27138",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27138"
},
{
"name": "CVE-2026-39822",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39822"
},
{
"name": "CVE-2026-39833",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39833"
},
{
"name": "CVE-2026-3783",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-3783"
},
{
"name": "CVE-2026-13221",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-13221"
},
{
"name": "CVE-2026-33814",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33814"
},
{
"name": "CVE-2026-27456",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27456"
},
{
"name": "CVE-2026-63074",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63074"
},
{
"name": "CVE-2026-68569",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-68569"
},
{
"name": "CVE-2026-59084",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-59084"
},
{
"name": "CVE-2025-58185",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-58185"
},
{
"name": "CVE-2026-65183",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-65183"
},
{
"name": "CVE-2026-14257",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-14257"
},
{
"name": "CVE-2025-61731",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-61731"
},
{
"name": "CVE-2026-58015",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-58015"
},
{
"name": "CVE-2026-27137",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27137"
},
{
"name": "CVE-2026-27143",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27143"
},
{
"name": "CVE-2026-46600",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-46600"
},
{
"name": "CVE-2026-69152",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-69152"
},
{
"name": "CVE-2026-45445",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-45445"
},
{
"name": "CVE-2026-68525",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-68525"
},
{
"name": "CVE-2026-18508",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-18508"
},
{
"name": "CVE-2026-39832",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39832"
},
{
"name": "CVE-2026-39829",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39829"
},
{
"name": "CVE-2026-56392",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56392"
},
{
"name": "CVE-2026-65637",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-65637"
},
{
"name": "CVE-2025-15367",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-15367"
},
{
"name": "CVE-2026-58014",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-58014"
},
{
"name": "CVE-2024-45341",
"url": "https://www.cve.org/CVERecord?id=CVE-2024-45341"
},
{
"name": "CVE-2026-13595",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-13595"
},
{
"name": "CVE-2026-6357",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-6357"
},
{
"name": "CVE-2026-54515",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54515"
},
{
"name": "CVE-2026-63374",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63374"
},
{
"name": "CVE-2026-13757",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-13757"
},
{
"name": "CVE-2026-15308",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-15308"
},
{
"name": "CVE-2026-27145",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27145"
},
{
"name": "CVE-2026-68497",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-68497"
},
{
"name": "CVE-2026-39834",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39834"
},
{
"name": "CVE-2026-63073",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63073"
},
{
"name": "CVE-2022-40897",
"url": "https://www.cve.org/CVERecord?id=CVE-2022-40897"
},
{
"name": "CVE-2026-46595",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-46595"
},
{
"name": "CVE-2026-41992",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41992"
},
{
"name": "CVE-2026-24051",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-24051"
},
{
"name": "CVE-2026-102265",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-102265"
},
{
"name": "CVE-2026-7383",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-7383"
},
{
"name": "CVE-2026-39825",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39825"
},
{
"name": "CVE-2026-39821",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39821"
},
{
"name": "CVE-2026-27144",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27144"
},
{
"name": "CVE-2026-4360",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-4360"
},
{
"name": "CVE-2026-58012",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-58012"
},
{
"name": "CVE-2026-56865",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56865"
},
{
"name": "CVE-2026-32283",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-32283"
},
{
"name": "CVE-2025-46686",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-46686"
},
{
"name": "CVE-2025-61727",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-61727"
},
{
"name": "CVE-2026-11822",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-11822"
},
{
"name": "CVE-2025-22866",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-22866"
},
{
"name": "CVE-2026-59890",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-59890"
},
{
"name": "CVE-2026-14164",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-14164"
},
{
"name": "CVE-2026-14456",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-14456"
},
{
"name": "CVE-2026-56860",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56860"
},
{
"name": "CVE-2026-6368",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-6368"
},
{
"name": "CVE-2026-1703",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-1703"
},
{
"name": "CVE-2026-14457",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-14457"
},
{
"name": "CVE-2026-15534",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-15534"
},
{
"name": "CVE-2026-41991",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41991"
},
{
"name": "CVE-2026-39883",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39883"
},
{
"name": "CVE-2026-6879",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-6879"
},
{
"name": "CVE-2026-102269",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-102269"
},
{
"name": "CVE-2026-32281",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-32281"
},
{
"name": "CVE-2026-75596",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-75596"
},
{
"name": "CVE-2026-8763",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-8763"
},
{
"name": "CVE-2026-27142",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27142"
},
{
"name": "CVE-2026-41989",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41989"
},
{
"name": "CVE-2026-42504",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42504"
},
{
"name": "CVE-2026-56412",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56412"
},
{
"name": "CVE-2026-69247",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-69247"
},
{
"name": "CVE-2025-47906",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-47906"
},
{
"name": "CVE-2026-48864",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-48864"
},
{
"name": "CVE-2026-65182",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-65182"
},
{
"name": "CVE-2026-56858",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56858"
},
{
"name": "CVE-2026-12003",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-12003"
},
{
"name": "CVE-2026-53615",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-53615"
},
{
"name": "CVE-2026-15588",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-15588"
},
{
"name": "CVE-2026-59903",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-59903"
},
{
"name": "CVE-2026-102268",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-102268"
},
{
"name": "CVE-2026-25589",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-25589"
},
{
"name": "CVE-2025-58188",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-58188"
},
{
"name": "CVE-2026-102267",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-102267"
},
{
"name": "CVE-2025-4674",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-4674"
},
{
"name": "CVE-2026-57433",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-57433"
},
{
"name": "CVE-2026-39820",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39820"
},
{
"name": "CVE-2026-56854",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56854"
},
{
"name": "CVE-2026-84303",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-84303"
},
{
"name": "CVE-2025-5278",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-5278"
},
{
"name": "CVE-2026-32952",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-32952"
},
{
"name": "CVE-2026-73180",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-73180"
},
{
"name": "CVE-2026-39819",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39819"
},
{
"name": "CVE-2026-58010",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-58010"
},
{
"name": "CVE-2026-16118",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-16118"
},
{
"name": "CVE-2026-42769",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42769"
},
{
"name": "CVE-2025-6141",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-6141"
},
{
"name": "CVE-2026-19672",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-19672"
},
{
"name": "CVE-2024-45336",
"url": "https://www.cve.org/CVERecord?id=CVE-2024-45336"
},
{
"name": "CVE-2026-25588",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-25588"
},
{
"name": "CVE-2025-22868",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-22868"
},
{
"name": "CVE-2026-5435",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-5435"
},
{
"name": "CVE-2025-61724",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-61724"
},
{
"name": "CVE-2026-3219",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-3219"
},
{
"name": "CVE-2026-11824",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-11824"
},
{
"name": "CVE-2026-2303",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-2303"
},
{
"name": "CVE-2025-61732",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-61732"
},
{
"name": "CVE-2026-101917",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-101917"
},
{
"name": "CVE-2025-61723",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-61723"
},
{
"name": "CVE-2026-5928",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-5928"
},
{
"name": "CVE-2026-39831",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39831"
},
{
"name": "CVE-2026-102273",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-102273"
},
{
"name": "CVE-2026-39828",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39828"
},
{
"name": "CVE-2026-45447",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-45447"
},
{
"name": "CVE-2025-6170",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-6170"
},
{
"name": "CVE-2026-25679",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-25679"
},
{
"name": "CVE-2026-33811",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33811"
},
{
"name": "CVE-2024-45337",
"url": "https://www.cve.org/CVERecord?id=CVE-2024-45337"
},
{
"name": "CVE-2025-61725",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-61725"
},
{
"name": "CVE-2026-63075",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63075"
},
{
"name": "CVE-2026-25680",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-25680"
},
{
"name": "CVE-2026-87910",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-87910"
},
{
"name": "CVE-2026-39835",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39835"
},
{
"name": "CVE-2026-64847",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-64847"
},
{
"name": "CVE-2026-33815",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33815"
},
{
"name": "CVE-2026-59884",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-59884"
},
{
"name": "CVE-2026-4539",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-4539"
},
{
"name": "CVE-2026-12087",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-12087"
},
{
"name": "CVE-2025-15366",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-15366"
},
{
"name": "CVE-2026-32289",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-32289"
},
{
"name": "CVE-2026-56848",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56848"
},
{
"name": "CVE-2026-45446",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-45446"
},
{
"name": "CVE-2026-91777",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-91777"
},
{
"name": "CVE-2026-58011",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-58011"
},
{
"name": "CVE-2026-63382",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63382"
},
{
"name": "CVE-2025-22874",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-22874"
},
{
"name": "CVE-2026-84304",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-84304"
},
{
"name": "CVE-2026-56405",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56405"
},
{
"name": "CVE-2026-63076",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63076"
},
{
"name": "CVE-2026-32280",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-32280"
},
{
"name": "CVE-2025-47912",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-47912"
},
{
"name": "CVE-2026-58016",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-58016"
},
{
"name": "CVE-2026-63383",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63383"
},
{
"name": "CVE-2024-2236",
"url": "https://www.cve.org/CVERecord?id=CVE-2024-2236"
},
{
"name": "CVE-2026-6653",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-6653"
},
{
"name": "CVE-2026-56859",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56859"
},
{
"name": "CVE-2025-61728",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-61728"
},
{
"name": "CVE-2026-66422",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-66422"
},
{
"name": "CVE-2025-58186",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-58186"
},
{
"name": "CVE-2026-18798",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-18798"
},
{
"name": "CVE-2026-56853",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56853"
},
{
"name": "CVE-2026-34183",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34183"
},
{
"name": "CVE-2025-8869",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-8869"
},
{
"name": "CVE-2025-58187",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-58187"
},
{
"name": "CVE-2026-77117",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-77117"
},
{
"name": "CVE-2026-13506",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-13506"
},
{
"name": "CVE-2026-42505",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42505"
},
{
"name": "CVE-2026-49844",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-49844"
},
{
"name": "CVE-2025-4673",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-4673"
},
{
"name": "CVE-2026-102266",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-102266"
},
{
"name": "CVE-2026-54370",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54370"
},
{
"name": "CVE-2025-22871",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-22871"
},
{
"name": "CVE-2026-29181",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-29181"
},
{
"name": "CVE-2026-39836",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39836"
},
{
"name": "CVE-2026-6791",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-6791"
},
{
"name": "CVE-2026-56408",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56408"
},
{
"name": "CVE-2026-6477",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-6477"
},
{
"name": "CVE-2026-42767",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42767"
},
{
"name": "CVE-2026-97689",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-97689"
},
{
"name": "CVE-2026-50219",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-50219"
},
{
"name": "CVE-2026-102270",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-102270"
},
{
"name": "CVE-2026-6238",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-6238"
},
{
"name": "CVE-2026-0864",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-0864"
},
{
"name": "CVE-2026-63379",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63379"
},
{
"name": "CVE-2026-56403",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56403"
},
{
"name": "CVE-2026-17084",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-17084"
},
{
"name": "CVE-2026-39882",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39882"
},
{
"name": "CVE-2025-58181",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-58181"
},
{
"name": "CVE-2026-59886",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-59886"
},
{
"name": "CVE-2025-47914",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-47914"
},
{
"name": "CVE-2026-66299",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-66299"
},
{
"name": "CVE-2026-11979",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-11979"
},
{
"name": "CVE-2025-22869",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-22869"
},
{
"name": "CVE-2026-63385",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63385"
},
{
"name": "CVE-2025-58189",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-58189"
},
{
"name": "CVE-2026-63387",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63387"
},
{
"name": "CVE-2026-27140",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27140"
},
{
"name": "CVE-2026-39817",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39817"
},
{
"name": "CVE-2026-65905",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-65905"
},
{
"name": "CVE-2026-42499",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42499"
},
{
"name": "CVE-2026-42764",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42764"
},
{
"name": "CVE-2026-82049",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-82049"
},
{
"name": "CVE-2025-22870",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-22870"
},
{
"name": "CVE-2026-75803",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-75803"
},
{
"name": "CVE-2026-8643",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-8643"
},
{
"name": "CVE-2026-19487",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-19487"
},
{
"name": "CVE-2026-41889",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41889"
},
{
"name": "CVE-2026-27139",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27139"
},
{
"name": "CVE-2026-42501",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42501"
},
{
"name": "CVE-2026-101918",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-101918"
},
{
"name": "CVE-2026-42250",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42250"
},
{
"name": "CVE-2026-65927",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-65927"
},
{
"name": "CVE-2026-46598",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-46598"
},
{
"name": "CVE-2025-48924",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-48924"
},
{
"name": "CVE-2026-33810",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33810"
},
{
"name": "CVE-2026-42768",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42768"
},
{
"name": "CVE-2026-15806",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-15806"
},
{
"name": "CVE-2026-57432",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-57432"
},
{
"name": "CVE-2026-32282",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-32282"
},
{
"name": "CVE-2026-63381",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63381"
},
{
"name": "CVE-2026-11972",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-11972"
},
{
"name": "CVE-2026-58043",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-58043"
},
{
"name": "CVE-2025-68121",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-68121"
},
{
"name": "CVE-2026-12912",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-12912"
},
{
"name": "CVE-2026-18477",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-18477"
},
{
"name": "CVE-2025-11065",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-11065"
},
{
"name": "CVE-2026-46597",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-46597"
},
{
"name": "CVE-2026-102274",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-102274"
},
{
"name": "CVE-2025-61726",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-61726"
},
{
"name": "CVE-2026-5704",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-5704"
},
{
"name": "CVE-2026-34182",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34182"
},
{
"name": "CVE-2026-18503",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-18503"
},
{
"name": "CVE-2026-19542",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-19542"
},
{
"name": "CVE-2026-63388",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63388"
},
{
"name": "CVE-2026-9547",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-9547"
},
{
"name": "CVE-2026-19032",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-19032"
},
{
"name": "CVE-2026-33816",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33816"
},
{
"name": "CVE-2026-56132",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56132"
},
{
"name": "CVE-2026-54874",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54874"
},
{
"name": "CVE-2026-18938",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-18938"
},
{
"name": "CVE-2026-54272",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54272"
},
{
"name": "CVE-2026-69192",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-69192"
},
{
"name": "CVE-2026-97688",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-97688"
},
{
"name": "CVE-2026-54371",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54371"
},
{
"name": "CVE-2023-5752",
"url": "https://www.cve.org/CVERecord?id=CVE-2023-5752"
},
{
"name": "CVE-2026-39827",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39827"
},
{
"name": "CVE-2026-102272",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-102272"
},
{
"name": "CVE-2025-22873",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-22873"
},
{
"name": "CVE-2025-47273",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-47273"
},
{
"name": "CVE-2026-8286",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-8286"
},
{
"name": "CVE-2026-39823",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39823"
},
{
"name": "CVE-2025-61729",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-61729"
},
{
"name": "CVE-2026-83557",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-83557"
},
{
"name": "CVE-2024-6345",
"url": "https://www.cve.org/CVERecord?id=CVE-2024-6345"
},
{
"name": "CVE-2026-63072",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-63072"
},
{
"name": "CVE-2026-77310",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-77310"
},
{
"name": "CVE-2026-103001",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-103001"
},
{
"name": "CVE-2026-59889",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-59889"
},
{
"name": "CVE-2026-56864",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-56864"
}
],
"initial_release_date": "2026-10-02T00:00:00",
"last_revision_date": "2026-10-02T00:00:00",
"links": [],
"reference": "CERTFR-2026-AVI-1249",
"revisions": [
{
"description": "Version initiale",
"revision_date": "2026-10-02T00:00:00.000000"
}
],
"risks": [
{
"description": "Non sp\u00e9cifi\u00e9 par l\u0027\u00e9diteur"
}
],
"summary": "De multiples vuln\u00e9rabilit\u00e9s ont \u00e9t\u00e9 d\u00e9couvertes dans les produits VMware. Elles permettent \u00e0 un attaquant de provoquer un probl\u00e8me de s\u00e9curit\u00e9 non sp\u00e9cifi\u00e9 par l\u0027\u00e9diteur.",
"title": "Multiples vuln\u00e9rabilit\u00e9s dans les produits VMware",
"vendor_advisories": [
{
"published_at": "2026-10-01",
"title": "Bulletin de s\u00e9curit\u00e9 VMware 39113",
"url": "https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/39113"
},
{
"published_at": "2026-10-01",
"title": "Bulletin de s\u00e9curit\u00e9 VMware 39112",
"url": "https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/39112"
},
{
"published_at": "2026-10-01",
"title": "Bulletin de s\u00e9curit\u00e9 VMware 39110",
"url": "https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/39110"
},
{
"published_at": "2026-10-01",
"title": "Bulletin de s\u00e9curit\u00e9 VMware 39115",
"url": "https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/39115"
},
{
"published_at": "2026-10-01",
"title": "Bulletin de s\u00e9curit\u00e9 VMware 39114",
"url": "https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/39114"
},
{
"published_at": "2026-10-01",
"title": "Bulletin de s\u00e9curit\u00e9 VMware 39109",
"url": "https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/39109"
},
{
"published_at": "2026-10-01",
"title": "Bulletin de s\u00e9curit\u00e9 VMware DSA-2026-31",
"url": "https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/39108"
},
{
"published_at": "2026-10-01",
"title": "Bulletin de s\u00e9curit\u00e9 VMware 39111",
"url": "https://support.broadcom.com/web/ecx/support-content-notification/-/external/content/SecurityAdvisories/0/39111"
}
]
}
FKIE_CVE-2026-102274
Vulnerability from fkie_nvd - Published: 2026-09-28 21:17 - Updated: 2026-09-30 19:38| Vendor | Product | Version |
|---|
{
"affected": [
{
"affectedData": [
{
"product": "pyjwt",
"vendor": "jpadilla",
"versions": [
{
"status": "affected",
"version": "\u003e= 2.9.0, \u003c 2.14.0"
}
]
}
],
"source": "security-advisories@github.com"
}
],
"cveTags": [],
"descriptions": [
{
"lang": "en",
"value": "PyJWT is a Python implementation of JSON Web Token standards. From 2.9.0 until 2.14.0, PyJWKSet does not catch the plain ValueError raised for malformed RSA JWK components by RSAAlgorithm.from_jwk in jwt/api_jwk.py. This occurs when a JWK Set contains a malformed RSA key alongside otherwise usable keys. As a result, one malformed member aborts construction of the entire PyJWKSet. Consequently, applications can experience authentication failures or request-level denial of service. This issue is fixed in version 2.14.0."
}
],
"id": "CVE-2026-102274",
"lastModified": "2026-09-30T19:38:27.293",
"metrics": {
"cvssMetricV31": [
{
"cvssData": {
"attackComplexity": "HIGH",
"attackVector": "NETWORK",
"availabilityImpact": "HIGH",
"baseScore": 5.9,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "NONE",
"integrityImpact": "NONE",
"privilegesRequired": "NONE",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"version": "3.1"
},
"exploitabilityScore": 2.2,
"impactScore": 3.6,
"source": "security-advisories@github.com",
"type": "Secondary"
}
],
"ssvcV203": [
{
"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"ssvcData": {
"id": "CVE-2026-102274",
"options": [
{
"exploitation": "poc"
},
{
"automatable": "no"
},
{
"technicalImpact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-09-29T19:37:35.694440Z",
"version": "2.0.3"
}
}
]
},
"published": "2026-09-28T21:17:15.677",
"references": [
{
"source": "security-advisories@github.com",
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"source": "security-advisories@github.com",
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
},
{
"source": "security-advisories@github.com",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
}
],
"sourceIdentifier": "security-advisories@github.com",
"vulnStatus": "Awaiting Analysis",
"weaknesses": [
{
"description": [
{
"lang": "en",
"value": "CWE-755"
}
],
"source": "security-advisories@github.com",
"type": "Secondary"
}
]
}
GHSA-W6J9-CWV2-H6WQ
Vulnerability from github – Published: 2026-09-29 18:23 – Updated: 2026-09-29 18:23Summary
A malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because RSAAlgorithm.from_jwk can raise a plain ValueError that isn't caught by PyJWKSet's per-key error-skipping logic.
Affected component / version
- Package:
PyJWT(PyPI, ecosystempip) - Files:
jwt/api_jwk.py(PyJWK.__init__,PyJWKSet.__init__),jwt/algorithms.py(RSAAlgorithm.from_jwk) - Confirmed present in the
masterbranch as of 2026-09-05 (commit7144e4534c34810f4525dc4578a32addd8212cff, tag2.13.0). Directly verified identical in tags2.9.0,2.10.0,2.11.0,2.12.0,2.12.1,2.13.0-- the vulnerable call and theexcept PyJWTErrorguard are unchanged across all six releases. Not verified against any release prior to2.9.0.
Details
PyJWKSet.__init__ (jwt/api_jwk.py:145-152) iterates each key in a JWK Set:
for key in keys:
try:
self.keys.append(PyJWK(key))
except PyJWTError as error:
if isinstance(error, MissingCryptographyError):
raise error
# skip unusable keys
continue
PyJWK.__init__ (api_jwk.py:82) calls self.Algorithm.from_jwk(self._jwk_data) with no try/except of its own. For an RSA JWK, this dispatches to RSAAlgorithm.from_jwk (jwt/algorithms.py:539-586). When the JWK supplies d, e, n without the CRT parameters (p, q, dp, dq, qi), from_jwk calls cryptography's rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e) (algorithms.py:572-574) to derive the key. If d is not the correct private exponent for that n/e pair, rsa_recover_prime_factors raises a plain ValueError.
ValueError is a built-in Python exception and is not a subclass of jwt.exceptions.PyJWTError (PyJWTError(Exception) is the root of PyJWT's own exception hierarchy). It is therefore not caught by PyJWKSet.__init__'s except PyJWTError, and propagates out of the constructor, aborting the for key in keys: loop before any subsequent key in the list is processed.
PyJWKSet.from_dict/from_json and PyJWK.from_dict/from_json are exported public API (jwt/__init__.py). PyJWKClient.get_jwk_set (jwt/jwks_client.py:158) feeds a fetched JWKS HTTP response directly into PyJWKSet.from_dict with no per-key pre-validation, so this is reachable through the documented PyJWKClient flow whenever the fetched JWKS contains a malformed key alongside valid ones.
Proof of concept
import jwt
good_jwk = {
"kty": "RSA",
"n": "<a valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key>",
"e": "AQAB",
}
bad_jwk = {
"kty": "RSA",
"n": good_jwk["n"],
"e": "AQAB",
"d": "AAAAAA", # not the true private exponent for n/e, no CRT params present
}
jwks_doc = {"keys": [bad_jwk, good_jwk]}
jwt.PyJWKSet.from_dict(jwks_doc)
# raises: ValueError: Unable to compute factors p and q from exponent d.
# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)
Impact
PyJWKSet.__init__ raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught ValueError, not one of PyJWT's documented jwt.exceptions.* types, so exception handling written against PyJWT's documented contract (except jwt.exceptions.PyJWTError) will not catch it either.
Suggested remediation
Wrap the key-construction call in PyJWK.__init__ (api_jwk.py:82) so a ValueError is converted into InvalidKeyError (a PyJWTError subclass):
try:
self.key = self.Algorithm.from_jwk(self._jwk_data)
except ValueError as e:
raise InvalidKeyError(f"Unable to construct key from JWK: {e}") from e
This lets PyJWKSet.__init__'s existing except PyJWTError: continue skip the one malformed key as its own comment already states is intended.
Responsible Disclosure Timeline
Per the OWASP Vulnerability Disclosure Cheat Sheet and Google Project Zero's 2020 disclosure policy:
- Day 0 (date of submission): to be set to the actual API response's
created_attimestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05. - Day 90 (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.
- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.
- This window may shorten instead of extend if the issue is confirmed under active exploitation.
Try It Yourself (Sandbox)
Reproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.
- Sandbox: jwt.py
- Course: Tenant Zero: AI & SaaS Authz Failures
Credit
Discovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.
Maintainer update — 2026-09-08
We reproduced the reported behavior on PyJWT 2.13.0 with cryptography installed: a malformed RSA private JWK containing an invalid d value and no CRT parameters raised a plain ValueError while parsing a JWK set. Because PyJWKSet skips PyJWTError instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.
The independently verified affected range is >= 2.9.0, <= 2.13.0; earlier releases were not checked. A narrow fix has been prepared in commit 8915570 based on master commit 5fa7594: PyJWK converts key-construction ValueError exceptions to InvalidKeyError, allowing the existing PyJWKSet skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2.13.0"
},
"package": {
"ecosystem": "PyPI",
"name": "PyJWT"
},
"ranges": [
{
"events": [
{
"introduced": "2.9.0"
},
{
"fixed": "2.14.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-102274"
],
"database_specific": {
"cwe_ids": [
"CWE-755"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-29T18:23:01Z",
"nvd_published_at": "2026-09-28T21:17:15Z",
"severity": "MODERATE"
},
"details": "## Summary\n\nA malformed RSA JWK inside a JWK Set aborts parsing of the entire set instead of being skipped, because `RSAAlgorithm.from_jwk` can raise a plain `ValueError` that isn\u0027t caught by `PyJWKSet`\u0027s per-key error-skipping logic.\n\n## Affected component / version\n\n- Package: `PyJWT` (PyPI, ecosystem `pip`)\n- Files: `jwt/api_jwk.py` (`PyJWK.__init__`, `PyJWKSet.__init__`), `jwt/algorithms.py` (`RSAAlgorithm.from_jwk`)\n- Confirmed present in the `master` branch as of 2026-09-05 (commit `7144e4534c34810f4525dc4578a32addd8212cff`, tag `2.13.0`). Directly verified identical in tags `2.9.0`, `2.10.0`, `2.11.0`, `2.12.0`, `2.12.1`, `2.13.0` -- the vulnerable call and the `except PyJWTError` guard are unchanged across all six releases. Not verified against any release prior to `2.9.0`.\n\n## Details\n\n`PyJWKSet.__init__` (`jwt/api_jwk.py:145-152`) iterates each key in a JWK Set:\n\n```python\nfor key in keys:\n try:\n self.keys.append(PyJWK(key))\n except PyJWTError as error:\n if isinstance(error, MissingCryptographyError):\n raise error\n # skip unusable keys\n continue\n```\n\n`PyJWK.__init__` (`api_jwk.py:82`) calls `self.Algorithm.from_jwk(self._jwk_data)` with no try/except of its own. For an RSA JWK, this dispatches to `RSAAlgorithm.from_jwk` (`jwt/algorithms.py:539-586`). When the JWK supplies `d`, `e`, `n` without the CRT parameters (`p`, `q`, `dp`, `dq`, `qi`), `from_jwk` calls `cryptography`\u0027s `rsa_recover_prime_factors(public_numbers.n, d, public_numbers.e)` (`algorithms.py:572-574`) to derive the key. If `d` is not the correct private exponent for that `n`/`e` pair, `rsa_recover_prime_factors` raises a plain `ValueError`.\n\n`ValueError` is a built-in Python exception and is not a subclass of `jwt.exceptions.PyJWTError` (`PyJWTError(Exception)` is the root of PyJWT\u0027s own exception hierarchy). It is therefore not caught by `PyJWKSet.__init__`\u0027s `except PyJWTError`, and propagates out of the constructor, aborting the `for key in keys:` loop before any subsequent key in the list is processed.\n\n`PyJWKSet.from_dict`/`from_json` and `PyJWK.from_dict`/`from_json` are exported public API (`jwt/__init__.py`). `PyJWKClient.get_jwk_set` (`jwt/jwks_client.py:158`) feeds a fetched JWKS HTTP response directly into `PyJWKSet.from_dict` with no per-key pre-validation, so this is reachable through the documented `PyJWKClient` flow whenever the fetched JWKS contains a malformed key alongside valid ones.\n\n## Proof of concept\n\n```python\nimport jwt\n\ngood_jwk = {\n \"kty\": \"RSA\",\n \"n\": \"\u003ca valid base64url-encoded RSA modulus, e.g. from a real 2048-bit public key\u003e\",\n \"e\": \"AQAB\",\n}\n\nbad_jwk = {\n \"kty\": \"RSA\",\n \"n\": good_jwk[\"n\"],\n \"e\": \"AQAB\",\n \"d\": \"AAAAAA\", # not the true private exponent for n/e, no CRT params present\n}\n\njwks_doc = {\"keys\": [bad_jwk, good_jwk]}\n\njwt.PyJWKSet.from_dict(jwks_doc)\n# raises: ValueError: Unable to compute factors p and q from exponent d.\n# (uncaught -- PyJWKSet.__init__ never returns, `good_jwk` is never added)\n```\n\n## Impact\n\n`PyJWKSet.__init__` raises before completing, so no key in the JWK Set is added to the resulting set, including keys unrelated to the malformed entry. This requires the malformed key to already be present in a JWK Set the application parses (e.g. one entry in an aggregated/federated key set, or a key affected by transit corruption before signature verification of the JWKS transport itself). Applications that vet each key individually before adding it to a trusted set are not affected. The failure is an uncaught `ValueError`, not one of PyJWT\u0027s documented `jwt.exceptions.*` types, so exception handling written against PyJWT\u0027s documented contract (`except jwt.exceptions.PyJWTError`) will not catch it either.\n\n## Suggested remediation\n\nWrap the key-construction call in `PyJWK.__init__` (`api_jwk.py:82`) so a `ValueError` is converted into `InvalidKeyError` (a `PyJWTError` subclass):\n\n```python\ntry:\n self.key = self.Algorithm.from_jwk(self._jwk_data)\nexcept ValueError as e:\n raise InvalidKeyError(f\"Unable to construct key from JWK: {e}\") from e\n```\n\nThis lets `PyJWKSet.__init__`\u0027s existing `except PyJWTError: continue` skip the one malformed key as its own comment already states is intended.\n\n## Responsible Disclosure Timeline\n\nPer the [OWASP Vulnerability Disclosure Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Vulnerability_Disclosure_Cheat_Sheet.html) and [Google Project Zero\u0027s 2020 disclosure policy](https://projectzero.google/2020/01/policy-and-disclosure-2020-edition.html):\n\n- **Day 0** (date of submission): to be set to the actual API response\u0027s `created_at` timestamp when this report is submitted. If submitted on the date of this draft (2026-09-05), Day 0 = 2026-09-05.\n- **Day 90** (full-public-disclosure deadline, regardless of fix status): Day 0 + 90 days -- 2026-12-04 if Day 0 is 2026-09-05.\n- A brief mutually-agreed extension (standard 14-day grace period) is available if a fix is scheduled but not yet shipped by Day 90 -- extending to 2026-12-18 in that case.\n- This window may shorten instead of extend if the issue is confirmed under active exploitation.\n\n## Try It Yourself (Sandbox)\n\nReproduce the PoC above, and attempt your own fix, in an isolated sandbox with no access to production systems, secrets, or real data.\n\n- Sandbox: [jwt.py](https://play.secdim.com/game/python/challenge/jwtpy)\n- Course: [Tenant Zero: AI \u0026 SaaS Authz Failures](https://learn.secdim.com/course/tenant-zero)\n\n## Credit\n\nDiscovered and reported by SecDim Security Research: secdim.com, @secdim, security@secdim.com.\n\n## Maintainer update \u2014 2026-09-08\n\nWe reproduced the reported behavior on PyJWT 2.13.0 with `cryptography` installed: a malformed RSA private JWK containing an invalid `d` value and no CRT parameters raised a plain `ValueError` while parsing a JWK set. Because `PyJWKSet` skips `PyJWTError` instances only, that exception aborted parsing before subsequent valid keys were loaded. The impact is a conditional availability failure for applications that parse a key set containing a malformed entry; it is not a signature-forgery or claims-verification bypass.\n\nThe independently verified affected range is `\u003e= 2.9.0, \u003c= 2.13.0`; earlier releases were not checked. A narrow fix has been prepared in commit `8915570` based on master commit `5fa7594`: `PyJWK` converts key-construction `ValueError` exceptions to `InvalidKeyError`, allowing the existing `PyJWKSet` skip path to continue. Regression coverage verifies that a malformed RSA key is skipped while a valid key in the same set remains usable. The full suite passes with 370 tests and 4 intentional cryptography-environment skips; formatting, lint, and targeted type checks pass. The advisory remains in triage while the fix goes through release planning.",
"id": "GHSA-w6j9-cwv2-h6wq",
"modified": "2026-09-29T18:23:01Z",
"published": "2026-09-29T18:23:01Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-w6j9-cwv2-h6wq"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-102274"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/commit/8915570a0bfda9f0ff0e34e7fb09bdb9d71580cf"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/releases/tag/2.14.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Malformed RSA JWK aborts parsing of an entire JWK Set"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.