fix: Parse JSON cache entries safely and stop sharing authenticated responses - #147
Merged
georgeguimaraes merged 3 commits intoSep 15, 2026
Merged
Conversation
The default serializer read entries back with JSON.load, which honours a json_class key and calls json_create on that class. Response headers are stored verbatim in the entry, so an origin server could name any loaded class in a single header and have it instantiated on the next cache hit, through both strategies. JSON.parse builds plain Ruby objects only. Passing create_additions: false to JSON.load is not a fix: the keyword binds to the proc positional parameter and the option never arrives. Cache keys are unchanged, so existing entries stay readable and become inert.
…t permission A shared cache (the default) stored responses to requests carrying an Authorization header and served them to later requests from other callers whenever the origin omitted Vary and did not mark the response private. RFC 9111 section 3.5 requires a shared cache to leave such responses alone unless they carry public, must-revalidate or s-maxage. The check runs when storing, so what is never stored is never served, and again when reading, so entries written by earlier versions are treated as a miss rather than reused. Private caches (shared_cache: false) are unchanged. This changes behaviour for applications that relied on shared cache hits for authenticated responses. Origins can opt back in with Cache-Control: public or s-maxage. Release-As: 2.8.0
…the README Only recommend Marshal for fully trusted stores, since Marshal.load instantiates whatever is in the data, and document the RFC 9111 section 3.5 rule for responses to authenticated requests.
georgeguimaraes
deleted the
security/json-load-and-shared-cache-authorization
branch
September 15, 2026 20:39
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.
Fixes for GHSA-p8jg-8p9f-pgmr and GHSA-c33f-42f2-gwcc, reported by Matthew Mongeau (Ruby Central).
BaseStrategy#deserialize_objectparses JSON entries withJSON.parseinstead ofJSON.load, so ajson_classkey planted through a response header no longer instantiates a class. Cache keys are unchanged.Authorizationunless the response haspublic,must-revalidateors-maxage(RFC 9111 section 3.5). The read-side check covers entries written by older versions.Ships as 2.8.0 since the second change alters shared cache behaviour.
Locally: both reporter PoCs exit 1 before and 0 after; 179 to 186 examples on Ruby 4.0 and 3.3 with Faraday 1 and 2; the new specs fail 5 of 7 against the unpatched lib; rubocop clean.