CVE-2026-102268
PyJWT before 2.14.0: is_pem_format does not recognise every PEM representation, so an asymmetric public key reaches HMACAlgorithm.prepare_key as an ordinary secret - anyone holding the published public key can forge a token the library then verifies
- Severity
- high
- Affected product
- PyJWT
- Affected versions
- PyJWT < 2.14.0
- Fixed in
- PyJWT 2.14.0
- Detection basis
- Component version range
- Shipped rule
- PyJWT: PyJWT before 2.14.0: is_pem_format does not recognise every PEM representation, so an asymmetric public key reaches HMACAlgorithm.prepare_key as an ordinary secret - anyone holding the published public key can forge a token the library then verifies
- Added to NewScan
- 2026-09-29
- Detected by
- NewScan — free, self-hosted
How NewScan reports it
COMPONENT VERSION RANGE
NewScan fingerprints PyJWT from its response and reports this CVE when the detected version falls inside the affected range below.
Added 2026-09-29 (/daily-cve). ONE row for the ten ids PyJWT 2.14.0 fixes, on the Open WebUI / Langflow precedent - ten rows would be ten findings that all say 'upgrade PyJWT'. The version-matched id is CVE-2026-102268 (9.1) because it is affected across this row's whole range; the nine in `cves` share the 2.14.0 release and four of them have their own floor inside it (102271 from 2.4.0, 102274 from 2.9.0, and 102266/102272/102273 from 2.13.0), which is what `cves` is for - it names ids the row covers without version-matching them (docs/signature-packs.md section 3). The cluster is one bug wearing six faces: PyJWT's guard against using an ASYMMETRIC key with a SYMMETRIC algorithm is a textual test on the key material, and 102268, 102266, 102271, 102272 and 102273 are five different key representations that slip past it. That is the classic HS256/RS256 confusion, and it is the reason for `high`: the RSA or EC public key is by definition published, so an attacker who can get the service to accept HS256 signs their own admin token with a key the service handed them. Recorded high rather than critical because the application must still permit HS256 in its `algorithms` list, which a correctly-pinned verifier does not. The remainder are narrower: 102267 (PyJWKClient does not revalidate redirect destinations against the JWKS trust boundary), 102270 (catastrophic backtracking in the lazy is_pem_format regex), 101917 (an unknown kid forces a JWKS refresh with no negative cache - a remote-triggered request amplifier), 102269 and 102274 (parser robustness). `fixed_in` is 2.14.0, the release that fixes THESE ids; 2.15.1 is current and 2.15.0 fixes three further parser ids (CVE-2026-102275, CVE-2026-101918, CVE-2026-102265) that are deliberately NOT in this row - they are 4.8-6.5 robustness bugs affecting only 2.14.x, and a second row for them would be a second finding on a release days old. Backlogged instead. Release existence verified against pypi.org/pypi/PyJWT/json on 2026-09-29: 2.13.0, 2.14.0, 2.15.0 and 2.15.1 are all published, so neither bound is invented. VERSION SOURCE, named because a key whose product nothing observes is dead data: the mined manifest route ONLY. `mine_versions` fetches /requirements.txt and `version_tools._techdb_names` joins it through an alias map DERIVED from this pack's keys, so `PyJWT` as a key makes `pyjwt==2.13.0` join case-insensitively with no code change. PyJWT is a library with no HTTP banner and no fingerprint of its own, exactly like the Log4j and Spring rows - the manifest is the only route that can ever supply its version, and this row is honest that it fires on an install that serves its requirements.txt and nowhere else. Version-match only: proving any of these means forging a token against someone's authentication.
References
Scan for this yourself — local, in-band scanning is free.
Get NewScan (FREE) →Review the measured benchmark, then use the verified remediation process.