Bump PyJWT to 2.15.0 and cedarpy to 4.12.1 - #1732
Merged
Merged
Conversation
cedarpy 4.12.1 wraps the same Cedar engine as 4.12.0 (v4.12.0). The only runtime change is in the diagnostics of is_authorized and is_authorized_batch: they now give the cause. A request that does not build reports "failed to build request: <cause>" instead of the flat "failed to parse schema from request", and an entity parse error gets ": <cause>" at the end. The decisions do not change, and the is_authorized_partial output is the same byte for byte. The errors-emptiness guard in authorize_request_preview in server/pbac/cedar.py reads only whether the errors are empty, so it is not affected. The text that _log_cedar_errors writes to the logs changes. No test matches on the old text. The dependency metadata does not change, so no other pin moves. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Security release. 2.15.0 closes two advisories against 2.14.0. GHSA-42vr-xj54-vc7v, medium, is reachable here without authentication. PyJWKClient.get_signing_key_from_jwt decodes the token payload before it looks up the kid, so before any signature check. On 2.14.0, a payload that is valid JSON but very deeply nested makes json.loads raise RecursionError, and RecursionError is not a PyJWTError. zentral.utils.oidc.verify_jws catches only PyJWTError, so the error escapes. The OIDC API token issuer auth view in server/accounts/api_views.py is AllowAny and gives the token to verify_jws, so one forged token of approximately 270 KB gets a 500. 2.15.0 wraps the error in DecodeError, and the view returns "Invalid token" with a 400. The new test sends such a token to verify_jws. It fails with RecursionError on 2.14.0 and passes on 2.15.0. It also makes sure that no JWKS fetch occurs. On Python 3.14, json.loads raises RecursionError only when the C stack is full, not at a fixed depth. 20,000 levels, the depth in the advisory, parses without error, and 100,000 levels fills the default 8 MB stack. The test uses 1,000,000 levels to keep a margin for a larger stack. GHSA-x33g-cr3x-6449, medium, is not reachable: it is about OKP private JWKs whose public x part does not agree with the private d part. verify_jws uses only the public keys of the issuer JWKS. Also in 2.15.0, and reachable from the JWKS of a configured issuer: PyJWKClient wraps http.client.HTTPException, for example IncompleteRead from a truncated response, in PyJWKClientConnectionError, which verify_jws catches. A JWK Set member that is not correct is skipped, and does not make the whole set fail. An exp, nbf or iat claim that is a list, a dict or null raises a PyJWTError instead of a TypeError. That check occurs after the signature check, so only the issuer can send such a claim. 2.15.0 does not fix the same RecursionError on the JWKS response. PyJWKClient.fetch_data still catches only the network errors around json.load. Only the JWKS endpoint of an issuer that an administrator configured can send that response, and the result is a 500, not a bypass. JWKSetCache now keeps the parsed PyJWKSet. verify_jws makes a new PyJWKClient for each verification, so this does not change the behaviour here. The dependency metadata is the same for 2.14.0 and 2.15.0, so no other pin moves. The comment in the OIDC tests now points to the 2.15.0 line that opens the JWKS URI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Coverage Report for CI Build 36226727561Coverage remained the same at 90.908%Details
Uncovered ChangesNo uncovered changes found. Coverage RegressionsNo coverage regressions found. Coverage Stats
💛 - Coveralls |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two dependency bumps, one commit each. Each commit can be reverted alone.
PyJWT 2.14.0 → 2.15.0
Security release. It closes two advisories against 2.14.0.
GHSA-42vr-xj54-vc7v (medium) is reachable without authentication.
PyJWKClient.get_signing_key_from_jwtdecodes the token payload before the signature check.json.loadsraiseRecursionError. This is not aPyJWTError, sozentral.utils.oidc.verify_jwsdoes not catch it.server/accounts/api_views.py) isAllowAny. One forged token of approximately 270 KB gets a 500.DecodeError, and the view returns "Invalid token" with a 400.GHSA-x33g-cr3x-6449 (medium) is not reachable. It is about OKP private JWKs.
verify_jwsuses only the public keys of the issuer JWKS.Other changes in 2.15.0 that the JWKS of a configured issuer can reach:
http.client.HTTPException) is aPyJWKClientConnectionError, whichverify_jwscatches.exp,nbforiatclaim that is a list, a dict ornullraises aPyJWTError, not aTypeError. This check occurs after the signature check.2.15.0 does not fix the same
RecursionErroron the JWKS response. Only the JWKS endpoint of an issuer that an administrator configured can send it, and the result is a 500, not a bypass.New test
test_verify_jws_deeply_nested_payloadsends a forged token with a deeply nested payload toverify_jws. It also checks that no JWKS fetch occurs.RecursionError: Stack overflow.On Python 3.14,
json.loadsraisesRecursionErroronly when the C stack is full. 20,000 levels (the advisory example) parse without error. 100,000 levels fill the default 8 MB stack. The test uses 1,000,000 levels to keep a margin for a larger stack.cedarpy 4.12.0 → 4.12.1
Same Cedar engine (v4.12.0). The only runtime change: the diagnostics of
is_authorizedandis_authorized_batchgive the cause. For example,failed to build request: <cause>replacesfailed to parse schema from request.is_authorized_partialoutput is the same byte for byte.authorize_request_previewinserver/pbac/cedar.pyreads only whether the errors are empty, so it is not affected.Cost
_log_cedar_errorswrites to the logs changes. A log search or alert on the old cedarpy text does not match after this change. No test matches on it.Tests
pip checkis clean in the rebuilt image.🤖 Generated with Claude Code