PyJWT / CVE-2022-29217
Public key formats missing from the HMAC key blocklist
The HMAC algorithm blocks only some public-key strings, so OpenSSH keys such as ssh-ed25519 can be used as an HMAC secret and an attacker can sign accepted tokens with the public key.
Evidence chain
Follow each source in order. Missing material is explicit; an advisory or fix link does not stand in for a product test.
Product observation
An L0 scan (no hints) of the released archive produced a finding that meets the location and CWE criteria registered before the scan. This is detection of a known issue, not an original discovery or a successful exploit.
Release-archive scan resultHuman / upstream attribution
The advisory credits Aapo Oksman (Nixu) with the disclosure; see the PyJWT advisory for fix credits. Vettalon performed detection of a known issue only.
Upstream advisoryFixed-version comparison
The upstream fixed release was scanned with the same parameters to see whether the patched code is still flagged. This is a scan control, not an exploit or differential test.
Fixed-release scan controlUnverified scope
The models may have seen this advisory or its fix during training, so a match does not show that they reasoned it out from the code. Candidates and needs_human results are not automatic confirmation, and no PoC or exploit was run. This was a single run and model output is nondeterministic. Five hand-picked known issues cannot support a recall or false-positive rate. Other output is published as counts only and was not reviewed. The raw report.json and trace are not public; only their SHA-256 is listed. The run status in the table is what the product reported: incomplete means some unit was deferred, truncated, failed or could not be cross-checked, with the reason shown, but the target file was sent for review in every arm. On the fixed release, "not flagged" only means no unrefuted output in the corresponding range carried a listed CWE; the other output in that arm was not reviewed and says nothing about the fixed release being free of other issues. The 2.4.0 archive ships a regression test named after the advisory (tests/test_advisory.py); it was removed before scanning. 2.3.0 has no such file.
Read limits
What this evidence supports
The 2.3.0 archive scan raised a CWE-327 candidate at jwt/algorithms.py:193 (HMACAlgorithm.prepare_key); grok judged it plausible and it ended as needs_human, meeting the registered criteria. The fixed 2.4.0 scan with the same parameters produced nothing in the corresponding range with a listed CWE.
Test method
Official release archives of the vulnerable and fixed versions were downloaded from the package registry, checked against SHA-256 and scanned as whole trees without git history. Both arms used the same parameters: deep profile, claude as finder, grok and agy (GPT-OSS) as cross-family verifiers, at most 8 audit units, one verifier run per candidate, PoC off. Model input contained no CVE id, advisory text or fix diff, and changelog files were removed from both arms. Match criteria (file, line ranges, CWE list) were committed publicly at 12:51 UTC on 2026-10-20; the first scan started 29 seconds later. The registeredAt field inside the file says 12:55; it was a hand-typed estimate made before the commit and is wrong. The commit time is authoritative, and the file was left unchanged so its digest still matches.
- Mode
- release-archive
- Finding status
- needs_human
- Location
jwt/algorithms.py:193
Public detection evidence snapshot
The HMAC key blocklist misses OpenSSH EC/EdDSA public keys, allowing HS256 algorithm confusion.
grok: plausible. The verifier did not refute the candidate; it remains needs_human and is not a confirmation.
- Finder
- claude-opus-5-5 (Anthropic)
- Verifier and verdict
- grok-4.7 (xAI): plausible
- Tool version
- vettalon 0.3.0 (built from source on 2026-10-20)
- Model versions
- Finder claude-opus-5-5 (claude CLI 2.1.300); verifiers grok-4.7 (grok CLI 1.0.62) and gpt-oss-120b-medium (agy 1.4.3)
- Historical scope
- Whole release archive, no git history; up to 8 audit units; changelog files removed from both arms; L0 (no CWE, location or advisory hint)
Rerun detection
Install the Vettalon CLI and configure your models and credentials in config.toml. These steps download the official release archive, check its SHA-256 and scan the whole tree; the pre-registration lists excluded files. Model and rule versions may differ; identical results are not guaranteed. Deep mode sends source to configured models. This is detection only; it does not run the upstream project or verify exploitation.
curl -sSfLo release-archive https://files.pythonhosted.org/packages/1d/8e/01bdcfdbb352daaba8ea406d9df149c5bba7dbf70f908d4fa4c269fe6a08/PyJWT-2.3.0.tar.gz
shasum -a 256 release-archive # expect b888b4d56f06f6dcd777210c334e69c737be74755d3e5e9ee3fe67dc18a0ee41
mkdir release-tree && bsdtar -xf release-archive --strip-components 1 -C release-tree
# remove the files listed under "excluded" in the pre-registration, then:
vettalon scan ./release-tree --profile deep --config ./config.toml --finders claude --verifiers grok,agy --max-modules 8 --refuter-runs 1 --no-prior --time-budget 2400 --report ./result.jsonPre-registered release-archive scan
Both arms scan the official release archive (no git history) with the same parameters. Model input contains no CVE id, advisory text or fix diff. A match is judged against the file, line ranges and CWE registered publicly before the first scan; other output is listed as counts only and was not reviewed. "Not flagged" on the fixed release only means nothing in the corresponding range met the criteria. The models may have seen this advisory during training, and a candidate is not an automatic confirmation.
| Arm | Version | Result | Model calls | Run status | Target file reviewed |
|---|---|---|---|---|---|
| Vulnerable | 2.3.0 | Found | 10 | incomplete (injection_uncrosschecked) | yes |
| Fixed | 2.4.0 | Not flagged | 10 | incomplete (injection_uncrosschecked) | yes |
Summary artifact SHA-256
cf7636c10081000a50f1f26a7d76c5b29d369e8289f8c8f675ac31f709bc1f08Match criteria were registered publicly before the first scan: Pre-registration (JSON)
Pre-registration SHA-256
6c7ef71cffa3729de787444c67823b01afcda71d61b42f2a005fda892a07db91Limits and prerequisites
The models may have seen this advisory or its fix during training, so a match does not show that they reasoned it out from the code. Candidates and needs_human results are not automatic confirmation, and no PoC or exploit was run. This was a single run and model output is nondeterministic. Five hand-picked known issues cannot support a recall or false-positive rate. Other output is published as counts only and was not reviewed. The raw report.json and trace are not public; only their SHA-256 is listed. The run status in the table is what the product reported: incomplete means some unit was deferred, truncated, failed or could not be cross-checked, with the reason shown, but the target file was sent for review in every arm. On the fixed release, "not flagged" only means no unrefuted output in the corresponding range carried a listed CWE; the other output in that arm was not reviewed and says nothing about the fixed release being free of other issues. The 2.4.0 archive ships a regression test named after the advisory (tests/test_advisory.py); it was removed before scanning. 2.3.0 has no such file.
Sources and attribution
The advisory credits Aapo Oksman (Nixu) with the disclosure; see the PyJWT advisory for fix credits. Vettalon performed detection of a known issue only.
Original detection report SHA-256
For comparison by holders of the original report. The download is a curated record, not the raw scan report.
81fb9120cbb14c416b5ec50f84185a4bae09ff7478d72b51cb2e769ac34baf94