π¨ [security] Update rails 6.1.7.10 β 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 (6.1.7.10 β 8.1.3.1) Β· Repo Β· Changelog
Security Advisories π¨
π¨ Rails has possible XSS Vulnerability in Action Controller
Possible XSS Vulnerability in Action Controller
There is a possible XSS vulnerability when using the translation helpers
(translate,t, etc) in Action Controller. This vulnerability has been
assigned the CVE identifier CVE-2024-26143.Versions Affected: >= 7.0.0.
Not affected: < 7.0.0
Fixed Versions: 7.1.3.1, 7.0.8.1Impact
Applications using translation methods like
translate, orton a
controller, with a key ending in "_html", a:defaultkey which contains
untrusted user input, and the resulting string is used in a view, may be
susceptible to an XSS vulnerability.For example, impacted code will look something like this:
class ArticlesController < ApplicationController def show @message = t("message_html", default: untrusted_input) # The `show` template displays the contents of `@message` end endTo reiterate the pre-conditions, applications must:
- Use a translation function from a controller (i.e. not I18n.t, or
tfrom
a view)- Use a key that ends in
_html- Use a default value where the default value is untrusted and unescaped input
- Send the text to the victim (whether that's part of a template, or a
rendercall)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
There are no feasible workarounds for this issue.
Patches
To aid users who aren't able to upgrade immediately we have provided patches for
the two supported release series. They are in git-am format and consist of a
single changeset.
- 7-0-translate-xss.patch - Patch for 7.0 series
- 7-1-translate-xss.patch - Patch for 7.1 series
Credits
Thanks to ooooooo_q for the patch and fix!
π¨ Rails has possible XSS Vulnerability in Action Controller
Possible XSS Vulnerability in Action Controller
There is a possible XSS vulnerability when using the translation helpers
(translate,t, etc) in Action Controller. This vulnerability has been
assigned the CVE identifier CVE-2024-26143.Versions Affected: >= 7.0.0.
Not affected: < 7.0.0
Fixed Versions: 7.1.3.1, 7.0.8.1Impact
Applications using translation methods like
translate, orton a
controller, with a key ending in "_html", a:defaultkey which contains
untrusted user input, and the resulting string is used in a view, may be
susceptible to an XSS vulnerability.For example, impacted code will look something like this:
class ArticlesController < ApplicationController def show @message = t("message_html", default: untrusted_input) # The `show` template displays the contents of `@message` end endTo reiterate the pre-conditions, applications must:
- Use a translation function from a controller (i.e. not I18n.t, or
tfrom
a view)- Use a key that ends in
_html- Use a default value where the default value is untrusted and unescaped input
- Send the text to the victim (whether that's part of a template, or a
rendercall)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
There are no feasible workarounds for this issue.
Patches
To aid users who aren't able to upgrade immediately we have provided patches for
the two supported release series. They are in git-am format and consist of a
single changeset.
- 7-0-translate-xss.patch - Patch for 7.0 series
- 7-1-translate-xss.patch - Patch for 7.1 series
Credits
Thanks to ooooooo_q for the patch and fix!
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.
β³οΈ actionpack (6.1.7.10 β 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 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!
π¨ 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!
π¨ 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!
π¨ Missing security headers in Action Pack on non-HTML responses
Permissions-Policy is Only Served on HTML Content-Type
The application configurable Permissions-Policy is only served on responses
with an HTML related Content-Type.This has been assigned the CVE identifier CVE-2024-28103.
Versions Affected: >= 6.1.0
Not affected: < 6.1.0
Fixed Versions: 6.1.7.8, 7.0.8.4, and 7.1.3.4Impact
Responses with a non-HTML Content-Type are not serving the configured Permissions-Policy. There are certain non-HTML Content-Types that would benefit from having the Permissions-Policy enforced.
Releases
The fixed releases are available at the normal locations.
Workarounds
N/A
Patches
To aid users who aren't able to upgrade immediately we have provided patches for
the supported release series in accordance with our
maintenance policy
regarding security issues. They are in git-am format and consist of a
single changeset.
- 6-1-include-permissions-policy-header-on-non-html.patch - Patch for 6.1 series
- 7-0-include-permissions-policy-header-on-non-html.patch - Patch for 7.0 series
- 7-1-include-permissions-policy-header-on-non-html.patch - Patch for 7.1 series
Credits
Thank you shinkbr for reporting this!
π¨ Missing security headers in Action Pack on non-HTML responses
Permissions-Policy is Only Served on HTML Content-Type
The application configurable Permissions-Policy is only served on responses
with an HTML related Content-Type.This has been assigned the CVE identifier CVE-2024-28103.
Versions Affected: >= 6.1.0
Not affected: < 6.1.0
Fixed Versions: 6.1.7.8, 7.0.8.4, and 7.1.3.4Impact
Responses with a non-HTML Content-Type are not serving the configured Permissions-Policy. There are certain non-HTML Content-Types that would benefit from having the Permissions-Policy enforced.
Releases
The fixed releases are available at the normal locations.
Workarounds
N/A
Patches
To aid users who aren't able to upgrade immediately we have provided patches for
the supported release series in accordance with our
maintenance policy
regarding security issues. They are in git-am format and consist of a
single changeset.
- 6-1-include-permissions-policy-header-on-non-html.patch - Patch for 6.1 series
- 7-0-include-permissions-policy-header-on-non-html.patch - Patch for 7.0 series
- 7-1-include-permissions-policy-header-on-non-html.patch - Patch for 7.1 series
Credits
Thank you shinkbr for reporting this!
π¨ Rails has possible XSS Vulnerability in Action Controller
Possible XSS Vulnerability in Action Controller
There is a possible XSS vulnerability when using the translation helpers
(translate,t, etc) in Action Controller. This vulnerability has been
assigned the CVE identifier CVE-2024-26143.Versions Affected: >= 7.0.0.
Not affected: < 7.0.0
Fixed Versions: 7.1.3.1, 7.0.8.1Impact
Applications using translation methods like
translate, orton a
controller, with a key ending in "_html", a:defaultkey which contains
untrusted user input, and the resulting string is used in a view, may be
susceptible to an XSS vulnerability.For example, impacted code will look something like this:
class ArticlesController < ApplicationController def show @message = t("message_html", default: untrusted_input) # The `show` template displays the contents of `@message` end endTo reiterate the pre-conditions, applications must:
- Use a translation function from a controller (i.e. not I18n.t, or
tfrom
a view)- Use a key that ends in
_html- Use a default value where the default value is untrusted and unescaped input
- Send the text to the victim (whether that's part of a template, or a
rendercall)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
There are no feasible workarounds for this issue.
Patches
To aid users who aren't able to upgrade immediately we have provided patches for
the two supported release series. They are in git-am format and consist of a
single changeset.
- 7-0-translate-xss.patch - Patch for 7.0 series
- 7-1-translate-xss.patch - Patch for 7.1 series
Credits
Thanks to ooooooo_q for the patch and fix!
π¨ Rails has possible XSS Vulnerability in Action Controller
Possible XSS Vulnerability in Action Controller
There is a possible XSS vulnerability when using the translation helpers
(translate,t, etc) in Action Controller. This vulnerability has been
assigned the CVE identifier CVE-2024-26143.Versions Affected: >= 7.0.0.
Not affected: < 7.0.0
Fixed Versions: 7.1.3.1, 7.0.8.1Impact
Applications using translation methods like
translate, orton a
controller, with a key ending in "_html", a:defaultkey which contains
untrusted user input, and the resulting string is used in a view, may be
susceptible to an XSS vulnerability.For example, impacted code will look something like this:
class ArticlesController < ApplicationController def show @message = t("message_html", default: untrusted_input) # The `show` template displays the contents of `@message` end endTo reiterate the pre-conditions, applications must:
- Use a translation function from a controller (i.e. not I18n.t, or
tfrom
a view)- Use a key that ends in
_html- Use a default value where the default value is untrusted and unescaped input
- Send the text to the victim (whether that's part of a template, or a
rendercall)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
There are no feasible workarounds for this issue.
Patches
To aid users who aren't able to upgrade immediately we have provided patches for
the two supported release series. They are in git-am format and consist of a
single changeset.
- 7-0-translate-xss.patch - Patch for 7.0 series
- 7-1-translate-xss.patch - Patch for 7.1 series
Credits
Thanks to ooooooo_q for the patch and fix!
π¨ Rails has possible ReDoS vulnerability in Accept header parsing in Action Dispatch
Possible ReDoS vulnerability in Accept header parsing in Action Dispatch
There is a possible ReDoS vulnerability in the Accept header parsing routines
of Action Dispatch. This vulnerability has been assigned the CVE identifier
CVE-2024-26142.Versions Affected: >= 7.1.0, < 7.1.3.1
Not affected: < 7.1.0
Fixed Versions: 7.1.3.1Impact
Carefully crafted Accept headers can cause Accept header parsing in Action
Dispatch to take an unexpected amount of time, possibly resulting in a DoS
vulnerability. All users running an affected release should either upgrade or
use one of the workarounds immediately.Ruby 3.2 has mitigations for this problem, so Rails applications using Ruby
3.2 or newer are unaffected.Releases
The fixed releases are available at the normal locations.
Workarounds
There are no feasible workarounds for this issue.
Patches
To aid users who aren't able to upgrade immediately we have provided patches for
the two supported release series. They are in git-am format and consist of a
single changeset.
- 7-1-accept-redox.patch - Patch for 7.1 series
Credits
Thanks svalkanov for the report and patch!
π¨ Actionpack has possible cross-site scripting vulnerability via User Supplied Values to redirect_to
The
redirect_tomethod in Rails allows provided values to contain characters which are not legal in an HTTP header value. This results in the potential for downstream services which enforce RFC compliance on HTTP response headers to remove the assigned Location header. This vulnerability has been assigned the CVE identifier CVE-2023-28362.Versions Affected: All. Not affected: None Fixed Versions: 7.0.5.1, 6.1.7.4
Impact
This introduces the potential for a Cross-site-scripting (XSS) payload to be delivered on the now static redirection page. Note that this both requires user interaction and for a Rails app to be configured to allow redirects to external hosts (defaults to false in Rails >= 7.0.x).
Releases
The FIXED releases are available at the normal locations.
Workarounds
Avoid providing user supplied URLs with arbitrary schemes to the
redirect_tomethod.
π¨ ReDoS based DoS vulnerability in Action Dispatch
There is a possible regular expression based DoS vulnerability in Action Dispatch. This vulnerability has been assigned the CVE identifier CVE-2023-22792.
Versions Affected: >= 3.0.0 Not affected: < 3.0.0 Fixed Versions: 5.2.8.15 (Rails LTS), 6.1.7.1, 7.0.4.1
ImpactSpecially crafted cookies, in combination with a specially crafted X_FORWARDED_HOST header can cause the regular expression engine to enter a state of catastrophic backtracking. This can cause the process to use large amounts of CPU and memory, leading to a possible DoS vulnerability All users running an affected release should either upgrade or use one of the workarounds immediately.
ReleasesThe FIXED releases are available at the normal locations.
WorkaroundsWe recommend that all users upgrade to one of the FIXED versions. In the meantime, users can mitigate this vulnerability by using a load balancer or other device to filter out malicious X_FORWARDED_HOST headers before they reach the application.
PatchesTo aid users who arenβt able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format and consist of a single changeset.
6-1-Use-string-split-instead-of-regex-for-domain-parts.patch - Patch for 6.1 series 7-0-Use-string-split-instead-of-regex-for-domain-parts.patch - Patch for 7.0 seriesPlease note that only the 7.0.Z and 6.1.Z series are supported at present, and 6.0.Z for severe vulnerabilities. Users of earlier unsupported releases are advised to upgrade as soon as possible as we cannot guarantee the continued availability of security fixes for unsupported releases.
https://rubyonrails.org/2023/1/17/Rails-Versions-6-0-6-1-6-1-7-1-7-0-4-1-have-been-released
π¨ Open Redirect Vulnerability in Action Pack
There is a vulnerability in Action Controllerβs redirect_to. This vulnerability has been assigned the CVE identifier CVE-2023-22797.
Versions Affected: >= 7.0.0 Not affected: < 7.0.0 Fixed Versions: 7.0.4.1
ImpactThere is a possible open redirect when using the redirect_to helper with untrusted user input.
Vulnerable code will look like this:
redirect_to(params[:some_param])Rails 7.0 introduced protection against open redirects from calling redirect_to with untrusted user input. In prior versions the developer was fully responsible for only providing trusted input. However the check introduced could be bypassed by a carefully crafted URL.
All users running an affected release should either upgrade or use one of the workarounds immediately.
ReleasesThe FIXED releases are available at the normal locations.
WorkaroundsThere are no feasible workarounds for this issue.
PatchesTo aid users who arenβt able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format and consist of a single changeset.
7-0-Fix-sec-issue-with-_url_host_allowed.patch - Patch for 7.0 seriesPlease note that only the 7.0.Z and 6.1.Z series are supported at present, and 6.0.Z for severe vulnerabilities. Users of earlier unsupported releases are advised to upgrade as soon as possible as we cannot guarantee the continued availability of security fixes for unsupported releases.
π¨ ReDoS based DoS vulnerability in Action Dispatch
There is a possible regular expression based DoS vulnerability in Action Dispatch related to the If-None-Match header. This vulnerability has been assigned the CVE identifier CVE-2023-22795.
Versions Affected: All Not affected: None Fixed Versions: 5.2.8.15 (Rails LTS), 6.1.7.1, 7.0.4.1
Impact
A specially crafted HTTP If-None-Match header can cause the regular expression engine to enter a state of catastrophic backtracking, when on a version of Ruby below 3.2.0. This can cause the process to use large amounts of CPU and memory, leading to a possible DoS vulnerability All users running an affected release should either upgrade or use one of the workarounds immediately.
ReleasesThe FIXED releases are available at the normal locations.
WorkaroundsWe recommend that all users upgrade to one of the FIXED versions. In the meantime, users can mitigate this vulnerability by using a load balancer or other device to filter out malicious If-None-Match headers before they reach the application.
Users on Ruby 3.2.0 or greater are not affected by this vulnerability.
PatchesTo aid users who arenβt able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format and consist of a single changeset.
6-1-Avoid-regex-backtracking-on-If-None-Match-header.patch - Patch for 6.1 series 7-0-Avoid-regex-backtracking-on-If-None-Match-header.patch - Patch for 7.0 seriesPlease note that only the 7.0.Z and 6.1.Z series are supported at present, and 6.0.Z for severe vulnerabilities. Users of earlier unsupported releases are advised to upgrade as soon as possible as we cannot guarantee the continued availability of security fixes for unsupported releases.
π¨ Cross-site Scripting Vulnerability in Action Pack
There is a possible XSS vulnerability in Rails / Action Pack. This vulnerability has been
assigned the CVE identifier CVE-2022-22577.Versions Affected: >= 5.2.0
Not affected: < 5.2.0
Fixed Versions: 7.0.2.4, 6.1.5.1, 6.0.4.8, 5.2.7.1Impact
CSP headers were only sent along with responses that Rails considered as
"HTML" responses. This left API requests without CSP headers, which could
possibly expose users to XSS attacks.Releases
The FIXED releases are available at the normal locations.
Workarounds
Set a CSP for your API responses manually.
π¨ Exposure of information in Action Pack
Impact
Under certain circumstances response bodies will not be closed, for example a bug in a webserver or a bug in a Rack middleware. In the event a response is not notified of a
close,ActionDispatch::Executorwill not know to reset thread local state for the next request. This can lead to data being leaked to subsequent requests, especially when interacting withActiveSupport::CurrentAttributes.Upgrading to the FIXED versions of Rails will ensure mitigation of this issue even in the context of a buggy webserver or middleware implementation.
Patches
This has been fixed in Rails 7.0.2.2, 6.1.4.6, 6.0.4.6, and 5.2.6.2.
Workarounds
Upgrading is highly recommended, but to work around this problem the following middleware can be used:
class GuardedExecutor < ActionDispatch::Executor def call(env) ensure_completed! super end private def ensure_completed! @executor.new.complete! if @executor.active? end end # Ensure the guard is inserted before ActionDispatch::Executor Rails.application.configure do config.middleware.swap ActionDispatch::Executor, GuardedExecutor, executor 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.
β³οΈ activesupport (6.1.7.10 β 8.1.3.1) Β· Repo Β· Changelog
Security Advisories π¨
π¨ 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 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 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
π¨ Active Support Possibly Discloses Locally Encrypted Files
There is a possible file disclosure of locally encrypted files in Active Support. This vulnerability has been assigned the CVE identifier CVE-2023-38037.
Versions Affected: >= 5.2.0 Not affected: < 5.2.0 Fixed Versions: 7.0.7.1, 6.1.7.5
Impact
ActiveSupport::EncryptedFile writes contents that will be encrypted to a temporary file. The temporary fileβs permissions are defaulted to the userβs current umask settings, meaning that itβs possible for other users on the same system to read the contents of the temporary file.
Attackers that have access to the file system could possibly read the contents of this temporary file while a user is editing it.
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
To work around this issue, you can set your umask to be more restrictive like this:
$ umask 0077
π¨ Possible XSS Security Vulnerability in SafeBuffer#bytesplice
There is a vulnerability in ActiveSupport if the new bytesplice method is called on a SafeBuffer with untrusted user input.
This vulnerability has been assigned the CVE identifier CVE-2023-28120.Versions Affected: All. Not affected: None Fixed Versions: 7.0.4.3, 6.1.7.3
Impact
ActiveSupport uses the SafeBuffer string subclass to tag strings as html_safe after they have been sanitized.
When these strings are mutated, the tag is should be removed to mark them as no longer being html_safe.Ruby 3.2 introduced a new bytesplice method which ActiveSupport did not yet understand to be a mutation.
Users on older versions of Ruby are likely unaffected.All users running an affected release and using bytesplice should either upgrade or use one of the workarounds immediately.
Workarounds
Avoid calling bytesplice on a SafeBuffer (html_safe) string with untrusted user input.
π¨ ReDoS based DoS vulnerability in Active Support's underscore
There is a possible regular expression based DoS vulnerability in Active Support. This vulnerability has been assigned the CVE identifier CVE-2023-22796.
Versions Affected: All Not affected: None Fixed Versions: 5.2.8.15 (Rails LTS, which is a paid service and not part of the rubygem), 6.1.7.1, 7.0.4.1
ImpactA specially crafted string passed to the underscore method can cause the regular expression engine to enter a state of catastrophic backtracking. This can cause the process to use large amounts of CPU and memory, leading to a possible DoS vulnerability.
This affects String#underscore, ActiveSupport::Inflector.underscore, String#titleize, and any other methods using these.
All users running an affected release should either upgrade or use one of the workarounds immediately.
ReleasesThe FIXED releases are available at the normal locations.
WorkaroundsThere are no feasible workarounds for this issue.
Users on Ruby 3.2.0 or greater may be able to reduce the impact by configuring Regexp.timeout.
PatchesTo aid users who arenβt able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format and consist of a single changeset.
6-1-Avoid-regex-backtracking-in-Inflector.underscore.patch - Patch for 6.1 series 7-0-Avoid-regex-backtracking-in-Inflector.underscore.patch - Patch for 7.0 seriesPlease note that only the 7.0.Z and 6.1.Z series are supported at present, and 6.0.Z for severe vulnerabilities. Users of earlier unsupported releases are advised to upgrade as soon as possible as we cannot guarantee the continued availability of security fixes for unsupported releases.
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.
β³οΈ minitest (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.
β³οΈ rake (13.0.6 β 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
13.3.0
What's Changed
- Add missing changelog by @VitaliySerov in #555
- Exclude 2.3-2.5 on macos-14 iamge by @hsbt in #563
- Use
require_relativein the Rake codebase by @koic in #566- Provide a 'Changelog' link on rubygems.org/gems/rake by @mark-young-atg in #572
- Remove dependency on
win32oleby @Earlopain in #573- Switch changelog_uri to releases tab by @fynsta in #577
- chore: refactor/reformat the heredocs (in tests) ... by @pvdb in #589
- chore: remove
$traceglobal variable / option by @pvdb in #592- Link to Jim's last
rakecommit (not the git tree with that SHA) by @pvdb in #593- chore: refactor how temporary files are created (in tests) by @pvdb in #590
- refactor: use
$LOADED_FEATURESbuilt-in instead of$"by @pvdb in #605- refactor: remove "exposed"
@system_dirinstance variable (in helper method) by @pvdb in #604- refactor: simplify
Rake::Application#system_dirmethod by @pvdb in #591- Remove unused argument by @takmar in #623
- Use latest RDoc release instead of Ruby 3.2's default version by @st0012 in #630
- Enabled trusted publisher for rubygems.org by @hsbt in #634
- refactor: use
Dir.hometo findrake's standard system dir by @pvdb in #608- Fix RDoc links in Rake Information section by @komagata in #627
- refactor: move dependency requires to
ruby_runner.rbfile by @pvdb in #609- Pattern matching support for arguments by @rgarner in #515
New Contributors
- @VitaliySerov made their first contribution in #555
- @koic made their first contribution in #566
- @mark-young-atg made their first contribution in #572
- @Earlopain made their first contribution in #573
- @fynsta made their first contribution in #577
- @takmar made their first contribution in #623
- @st0012 made their first contribution in #630
- @komagata made their first contribution in #627
- @rgarner made their first contribution in #515
Full Changelog: v13.2.1...v13.3.0
13.2.1
What's Changed
- Suppressed "internal:array:52:in 'Array#each'" from backtrace by @hsbt in #554
- Bump actions/configure-pages from 4 to 5 by @dependabot in #553
Full Changelog: v13.2.0...v13.2.1
13.2.0
What's Changed
- Fix rule example to be correct by @zenspider in #525
- Switch to use test-unit by @hsbt in #536
- Removed redundant block by @hsbt in #537
- Use Struct instead of OpenStruct. by @hsbt in #545
- Accept FileList object as directory task's target by @gemmaro in #530
- Fix exception when exception has nil backtrace by @janbiedermann in #451
- Add TruffleRuby on CI by @andrykonchin in #551
New Contributors
- @zenspider made their first contribution in #525
- @gemmaro made their first contribution in #530
- @janbiedermann made their first contribution in #451
- @andrykonchin made their first contribution in #551
Full Changelog: v13.1.0...v13.2.0
13.1.0
What's Changed
- Added dependabot.yml for actions by @hsbt in #416
- Add Ruby 3.1 to the CI matrix by @petergoldstein in #415
- (Performance) Remove unnecessary I/O syscalls for FileTasks by @da2x in #393
- Skip test failure with JRuby by @hsbt in #418
- Bump actions/checkout from 2 to 3 by @dependabot in #417
- Remove bin/rdoc by @tnir in #421
- Remove bin/rake by @tnir in #422
- Remove bin/bundle by @tnir in #425
- Apply RuboCop linting for Ruby 2.3 by @tnir in #423
- Update rubocop to work with Ruby 2.4 compatible by @tnir in #424
- chore: fix typo in comments by @tnir in #429
- Use 'test' as workflow name on Actions by @tnir in #427
- docs: update CONTRIBUTING.rdoc by @tnir in #428
- Add RuboCop job to Actions by @tnir in #426
- Lock minitest-5.15.0 for Ruby 2.2 by @hsbt in #442
- Eagerly require set in thread_pool.rb by @jeremyevans in #440
- Avoid creating an unnecessary thread pool by @jeremyevans in #441
- Add credit for maintenance in Rake 12/13 by @tnir in #443
- Sh fully echoes commands which error exit by @MarkDBlackwell in #147
- Correct RuboCop offenses by @deivid-rodriguez in #444
- [StepSecurity] ci: Harden GitHub Actions by @step-security-bot in #450
- Bump ruby/setup-ruby from 1.126.0 to 1.127.0 by @dependabot in #453
- Bump actions/checkout from 3.1.0 to 3.2.0 by @dependabot in #454
- Bump ruby/setup-ruby from 1.127.0 to 1.131.0 by @dependabot in #457
- Add ruby 3.2 to test matrix by @hanneskaeufler in #458
- Bump ruby/setup-ruby from 1.131.0 to 1.133.0 by @dependabot in #459
- Bump actions/checkout from 3.2.0 to 3.3.0 by @dependabot in #463
- Bump ruby/setup-ruby from 1.133.0 to 1.133.1 by @dependabot in #462
- Bump ruby/setup-ruby from 1.133.1 to 1.133.2 by @dependabot in #464
- Bump ruby/setup-ruby from 1.133.2 to 1.134.0 by @dependabot in #466
- Missing 'do' on example by @zzak in #467
- Try to use dependabot automerge by @hsbt in #470
- Rewrite auto-merge feature for dependabot by @hsbt in #471
- Bump ruby/setup-ruby from 1.134.0 to 1.137.2 by @dependabot in #469
- Update bundler in Dependabot by @ono-max in #472
- Bump ruby/setup-ruby from 1.137.2 to 1.138.0 by @dependabot in #473
- Update minitest requirement from 5.15.0 to 5.17.0 by @dependabot in #474
- Fix grammar in help text by @mebezac in #381
- Try to use ruby/ruby/.github/workflows/ruby_versions.yml@master by @hsbt in #475
- Bump lewagon/wait-on-check-action from 1.2.0 to 1.3.1 by @dependabot in #476
- Use GitHub Pages Action for generating rdoc page by @hsbt in #477
- Bump ruby/setup-ruby from 1.138.0 to 1.143.0 by @dependabot in #478
- Update minitest requirement from 5.17.0 to 5.18.0 by @dependabot in #479
- Bump ruby/setup-ruby from 1.143.0 to 1.144.0 by @dependabot in #480
- Bump ruby/setup-ruby from 1.144.0 to 1.144.1 by @dependabot in #482
- Bump actions/deploy-pages from 1 to 2 by @dependabot in #481
- Bump ruby/setup-ruby from 1.144.1 to 1.144.2 by @dependabot in #484
- Update rubocop requirement from ~> 1.12.1 to ~> 1.48.1 by @dependabot in #485
- Bump ruby/setup-ruby from 1.144.2 to 1.145.0 by @dependabot in #487
- Update rubocop requirement from ~> 1.48.1 to ~> 1.49.0 by @dependabot in #488
- Support
#detailed_messagewhen task failed by @ksss in #486- Debug at stop when task fail by @ksss in #489
- Drop to support Ruby 2.2 by @hsbt in #492
- Bump ruby/setup-ruby from 1.145.0 to 1.146.0 by @dependabot in #491
- Update rubocop requirement from ~> 1.49.0 to ~> 1.50.1 by @dependabot in #493
- Bump up setup-ruby by @hsbt in #497
- Bump ruby/setup-ruby from 1.148.0 to 1.149.0 by @dependabot in #498
- Update rubocop requirement from ~> 1.50.1 to ~> 1.51.0 by @dependabot in #499
- Bump ruby/setup-ruby from 1.149.0 to 1.150.0 by @dependabot in #500
- Update rubocop requirement from ~> 1.51.0 to ~> 1.52.0 by @dependabot in #502
- Bump ruby/setup-ruby from 1.150.0 to 1.151.0 by @dependabot in #503
- Update development dependencies by @hsbt in #505
- Bump ruby/setup-ruby from 1.151.0 to 1.152.0 by @dependabot in #506
- Bump actions/upload-pages-artifact from 1 to 2 by @dependabot in #508
- Bump actions/checkout from 3 to 4 by @dependabot in #513
- Bump ruby/setup-ruby from 1.152.0 to 1.153.0 by @dependabot in #514
- Bump actions/checkout from 4.0.0 to 4.1.0 by @dependabot in #516
- Bump ruby/setup-ruby from 1.153.0 to 1.154.0 by @dependabot in #517
- Bump ruby/setup-ruby from 1.154.0 to 1.155.0 by @dependabot in #518
- Bump ruby/setup-ruby from 1.155.0 to 1.156.0 by @dependabot in #519
- Bump actions/checkout from 4.1.0 to 4.1.1 by @dependabot in #520
- Bump ruby/setup-ruby from 1.156.0 to 1.157.0 by @dependabot in #521
New Contributors
- @petergoldstein made their first contribution in #415
- @da2x made their first contribution in #393
- @dependabot made their first contribution in #417
- @tnir made their first contribution in #421
- @step-security-bot made their first contribution in #450
- @hanneskaeufler made their first contribution in #458
- @ono-max made their first contribution in #472
- @mebezac made their first contribution in #381
- @ksss made their first contribution in #486
Full Changelog: v13.0.6...v13.1.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.
βοΈ actioncable (indirect, 6.1.7.10 β 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.
βοΈ actionmailbox (indirect, 6.1.7.10 β 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, 6.1.7.10 β 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!
π¨ 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!
π¨ 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
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.
βοΈ actiontext (indirect, 6.1.7.10 β 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!
π¨ 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!
π¨ 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!
π¨ ActionText ContentAttachment can Contain Unsanitized HTML
Instances of ActionText::Attachable::ContentAttachment included within a rich_text_area tag could potentially contain unsanitized HTML.
This has been assigned the CVE identifier CVE-2024-32464.
Versions Affected: >= 7.1.0
Not affected: < 7.1.0
Fixed Versions: 7.1.3.4Impact
This could lead to a potential cross site scripting issue within the Trix editor.
Releases
The fixed releases are available at the normal locations.
Workarounds
N/A
Patches
To aid users who aren't able to upgrade immediately we have provided patches for the supported release series in accordance with our maintenance policy regarding security issues. They are in git-am format and consist of a single changeset.
- action_text_content_attachment_xss_7_1_stable.patch - Patch for 7.1 series
Credits
Thank you ooooooo_q for reporting this!
π¨ Trix Editor Arbitrary Code Execution Vulnerability
The Trix editor, versions prior to 2.1.1, is vulnerable to arbitrary code execution when copying and pasting content from the web or other documents with markup into the editor. The vulnerability stems from improper sanitization of pasted content, allowing an attacker to embed malicious scripts which are executed within the context of the application.
Vulnerable Versions:
- 1.x series up to and including 1.3.1
- 2.x series up to and including 2.1.0
Fixed Versions:
- v1.3.2
- v2.1.1
Vector:
- Bug 1: When copying content manipulated by a script, such as:
document.addEventListener('copy', function(e){ e.clipboardData.setData('text/html', '<div><noscript><div class="123</noscript>456<img src=1 onerror=alert(1)//"></div></noscript></div>'); e.preventDefault(); });and pasting into the Trix editor, the script within the content is executed.
- Bug 2: Similar execution occurs with content structured as:
document.write(`copy<div data-trix-attachment="{"contentType":"text/html","content":"<img src=1 onerror=alert(101)>HELLO123"}"></div>me`);Impact:
An attacker could exploit these vulnerabilities to execute arbitrary JavaScript code within the context of the user's session, potentially leading to unauthorized actions being performed or sensitive information being disclosed.
Remediation:
Update Recommendation: Users should upgrade to Trix editor version 2.1.1 or later, which incorporates proper sanitization of input from copied content.
CSP Enhancement: Additionally, enhancing the Content Security Policy (CSP) to disallow inline scripts can significantly mitigate the risk of such vulnerabilities. Set CSP policies such as script-src 'self' to ensure that only scripts hosted on the same origin are executed, and explicitly prohibit inline scripts using script-src-elem.
References:
- https://github.com/basecamp/trix/releases/tag/v2.1.1
- basecamp/trix#1147
- basecamp/trix#1149
- basecamp/trix#1153
Credit: These issues were reported by security researchers loknop and pinpie.
π¨ Trix Editor Arbitrary Code Execution Vulnerability
The Trix editor, versions prior to 2.1.1, is vulnerable to arbitrary code execution when copying and pasting content from the web or other documents with markup into the editor. The vulnerability stems from improper sanitization of pasted content, allowing an attacker to embed malicious scripts which are executed within the context of the application.
Vulnerable Versions:
- 1.x series up to and including 1.3.1
- 2.x series up to and including 2.1.0
Fixed Versions:
- v1.3.2
- v2.1.1
Vector:
- Bug 1: When copying content manipulated by a script, such as:
document.addEventListener('copy', function(e){ e.clipboardData.setData('text/html', '<div><noscript><div class="123</noscript>456<img src=1 onerror=alert(1)//"></div></noscript></div>'); e.preventDefault(); });and pasting into the Trix editor, the script within the content is executed.
- Bug 2: Similar execution occurs with content structured as:
document.write(`copy<div data-trix-attachment="{"contentType":"text/html","content":"<img src=1 onerror=alert(101)>HELLO123"}"></div>me`);Impact:
An attacker could exploit these vulnerabilities to execute arbitrary JavaScript code within the context of the user's session, potentially leading to unauthorized actions being performed or sensitive information being disclosed.
Remediation:
Update Recommendation: Users should upgrade to Trix editor version 2.1.1 or later, which incorporates proper sanitization of input from copied content.
CSP Enhancement: Additionally, enhancing the Content Security Policy (CSP) to disallow inline scripts can significantly mitigate the risk of such vulnerabilities. Set CSP policies such as script-src 'self' to ensure that only scripts hosted on the same origin are executed, and explicitly prohibit inline scripts using script-src-elem.
References:
- https://github.com/basecamp/trix/releases/tag/v2.1.1
- basecamp/trix#1147
- basecamp/trix#1149
- basecamp/trix#1153
Credit: These issues were reported by security researchers loknop and pinpie.
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.
βοΈ actionview (indirect, 6.1.7.10 β 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.
π¨ rails-ujs vulnerable to DOM Based Cross-site Scripting contenteditable HTML Elements
NOTE: rails-ujs is part of Rails/actionview since 5.1.0.
There is a potential DOM based cross-site scripting issue in rails-ujs
which leverages the Clipboard API to target HTML elements that are
assigned the contenteditable attribute. This has the potential to
occur when pasting malicious HTML content from the clipboard that
includes a data-method, data-remote or data-disable-with attribute.This vulnerability has been assigned the CVE identifier CVE-2023-23913.
Not affected: < 5.1.0
Versions Affected: >= 5.1.0
Fixed Versions: 6.1.7.3, 7.0.4.3Impact
If the specified malicious HTML clipboard content is provided to a
contenteditable element, this could result in the arbitrary execution
of javascript on the origin in question.Releases
The FIXED releases are available at the normal locations.Workarounds
We recommend that all users upgrade to one of the FIXED versions.
In the meantime, users can attempt to mitigate this vulnerability
by removing the contenteditable attribute from elements in pages
that rails-ujs will interact with.Patches
To aid users who arenβt able to upgrade immediately we have provided
patches for the two supported release series. They are in git-am
format and consist of a single changeset.
- rails-ujs-data-method-contenteditable-6-1.patch - Patch for 6.1 series
- rails-ujs-data-method-contenteditable-7-0.patch - Patch for 7.0 series
Please note that only the 7.0.Z and 6.1.Z series are
supported at present, and 6.0.Z for severe vulnerabilities.Users of earlier unsupported releases are advised to upgrade as
soon as possible as we cannot guarantee the continued availability
of security fixes for unsupported releases.Credits
We would like to thank ryotak 15 for reporting this!
- rails-ujs-data-method-contenteditable-6-1.patch (8.5 KB)
- rails-ujs-data-method-contenteditable-7-0.patch (8.5 KB)
- rails-ujs-data-method-contenteditable-main.patch (8.9 KB)
π¨ XSS Vulnerability in Action View tag helpers
There is a possible XSS vulnerability in Action View tag helpers. Passing untrusted input as hash keys can lead to a possible XSS vulnerability. This vulnerability has been assigned the CVE identifier CVE-2022-27777.
Versions Affected: ALL
Not affected: NONE
Fixed Versions: 7.0.2.4, 6.1.5.1, 6.0.4.8, 5.2.7.1Impact
If untrusted data is passed as the hash key for tag attributes, there is a possibility that the untrusted data may not be properly escaped which can lead to an XSS vulnerability.
Impacted code will look something like this:
check_box_tag('thename', 'thevalue', false, aria: { malicious_input => 'thevalueofaria' })Where the "malicious_input" variable contains untrusted data.
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
Escape the untrusted data before using it as a key for tag helper methods.
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.
βοΈ activejob (indirect, 6.1.7.10 β 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.
βοΈ activemodel (indirect, 6.1.7.10 β 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.
βοΈ activerecord (indirect, 6.1.7.10 β 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
π¨ 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
π¨ Denial of Service Vulnerability in ActiveRecord's PostgreSQL adapter
There is a potential denial of service vulnerability present in ActiveRecord's PostgreSQL adapter.
This has been assigned the CVE identifier CVE-2022-44566.
Versions Affected: All. Not affected: None.
Fixed Versions
- 2.3.18.47 (Rails LTS, which is a paid service and not part of the rubygem)
- 3.2.22.34 (Rails LTS, which is a paid service and not part of the rubygem)
- 4.2.11.27 (Rails LTS, which is a paid service and not part of the rubygem)
- 5.2.8.15 (Rails LTS, which is a paid service and not part of the rubygem)
- 6.1.7.1
- 7.0.4.1
Impact
In ActiveRecord < 7.0.4.1 and < 6.1.7.1, when a value outside the range for a 64bit signed integer is provided to the PostgreSQL connection adapter, it will treat the target column type as numeric. Comparing integer values against numeric values can result in a slow sequential scan resulting in potential Denial of Service.
Releases
The fixed releases are available at the normal locations.
Workarounds
Ensure that user supplied input which is provided to ActiveRecord clauses do not contain integers wider than a signed 64bit representation or floats.
Patches
To aid users who aren't able to upgrade immediately we have provided patches for the supported release series in accordance with our maintenance policy 1 regarding security issues. They are in git-am format and consist of a single changeset.
6-1-Added-integer-width-check-to-PostgreSQL-Quoting.patch - Patch for 6.1 series
7-0-Added-integer-width-check-to-PostgreSQL-Quoting.patch - Patch for 7.0 series
π¨ SQL Injection Vulnerability via ActiveRecord comments
There is a possible vulnerability in ActiveRecord related to the sanitization of comments. This vulnerability has been assigned the CVE identifier CVE-2023-22794.
Versions Affected: >= 6.0.0 Not affected: < 6.0.0 Fixed Versions: 6.0.6.1, 6.1.7.1, 7.0.4.1
ImpactPreviously the implementation of escaping for comments was insufficient for
If malicious user input is passed to either the annotate query method, the optimizer_hints query method, or through the QueryLogs interface which automatically adds annotations, it may be sent to the database with insufficient sanitization and be able to inject SQL outside of the comment.
In most cases these interfaces wonβt be used with user input and users should avoid doing so.
Example vulnerable code:
Post.where(id: 1).annotate("#{params[:user_input]}") Post.where(id: 1).optimizer_hints("#{params[:user_input]}")Example vulnerable QueryLogs configuration (the default configuration is not vulnerable):
config.active_record.query_log_tags = [ { something: -> { <some value including user input> } } ]All users running an affected release should either upgrade or use one of the workarounds immediately.
ReleasesThe FIXED releases are available at the normal locations.
WorkaroundsAvoid passing user input to annotate and avoid using QueryLogs configuration which can include user input.
PatchesTo aid users who arenβt able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format and consist of a single changeset.
6-0-Make-sanitize_as_sql_comment-more-strict.patch - Patch for 6.0 series 6-1-Make-sanitize_as_sql_comment-more-strict.patch - Patch for 6.1 series 7-0-Make-sanitize_as_sql_comment-more-strict.patch - Patch for 7.0 seriesPlease note that only the 7.0.Z and 6.1.Z series are supported at present, and 6.0.Z for severe vulnerabilities. Users of earlier unsupported releases are advised to upgrade as soon as possible as we cannot guarantee the continued availability of security fixes for unsupported releases.
π¨ Active Record RCE bug with Serialized Columns
When serialized columns that use YAML (the default) are deserialized, Rails uses YAML.unsafe_load to convert the YAML data in to Ruby objects. If an attacker can manipulate data in the database (via means like SQL injection), then it may be possible for the attacker to escalate to an RCE.
There are no feasible workarounds for this issue, but other coders (such as JSON) are not impacted.
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.
βοΈ activestorage (indirect, 6.1.7.10 β 8.1.3.1) Β· Repo Β· Changelog
Security Advisories π¨
π¨ Active Storage has possible arbitrary file read and remote code execution in Active Storage variant processing
Impact
In its default configuration, a Rails application that displays image variants may allow an
unauthenticated attacker to read arbitrary files from the server, including the process environment.
That environment typically holdssecret_key_baseand often credentials for external systems, which
may in turn allow escalation to remote code execution or lateral movement to those systems.Details
libvips reads and writes file formats through "loaders" and "savers" (or more generally
"operations"), many of which are backed by third-party libraries. It marks some of these operations
as "unfuzzed", meaning they are unsafe for untrusted content, and several handle formats unrelated
to web images. Active Storage did not disable the unfuzzed operations, so an attacker who can upload
a crafted file and cause a variant to be generated from it may be able to invoke one.We are aware of a mechanism by which an attacker, by uploading a crafted file, is able to cause
disclosure of the contents of arbitrary files accessible on the filesystem of the targeted
application. One specific attack chain has been reported to us (see "Disclosure" below), but we do
not assume it is the only one that exists.Affected applications
An application is affected if it meets all of these requirements:
- Uses libvips for Active Storage image processing. This is
config.active_storage.variant_processor = :vips,
whichload_defaults 7.0set and no later default has changed.- Allows image uploads from untrusted users.
Generating variants is not a separate requirement.
Mitigation
- Upgrade to a fixed version of
activestorage.- The minimum version of libvips must be upgraded to
>= 8.13.- Change
secret_key_baseand change any secrets accessible in the application environment (see "Expire and change secrets" below)Earlier versions of libvips (
< 8.13) cannot disable unfuzzed operations at all, and Active Storage
will raise an exception during boot in such an unsecurable environment.Expire and change secrets
Upgrading closes the vulnerability but does not undo an exfiltrated secret if that already
occurred. An affected application should treat every secret readable by the application process as
potentially exposed and change it, including:
secret_key_base- The master key, whether stored in
config/master.keyor supplied asRAILS_MASTER_KEY, along
with everything inconfig/credentials.yml.encthat it decrypts- Credentials for the Active Storage service, such as S3, GCS, or Azure keys
- Database credentials
- Tokens and keys for any third-party service the application calls
Changing
secret_key_baseexpires active sessions and requires users to log in again. Encrypted
cookies, signed cookies, signed global IDs, and Active Storage URLs are also affected.Rotation should only be used as an intermediate step if necessary. Do not retain an exposed secret
as a fallback.Workarounds
If libvips
< 8.13is being used, there are no workarounds available other than removing the
dependency on libvips from the application. Some applications may haveruby-vipsdeclared as a
dependency only for image analysis, and those applications may be able to simply removeruby-vips
from the Gemfile to remove libvips from the application. Applications that do not use Active Storage
can removeruby-vipsfrom the Gemfile to avoid the boot-time checks.If libvips
>= 8.13is present on the system, applications can disable the unfuzzed operations
without upgrading Rails by setting theVIPS_BLOCK_UNTRUSTEDenvironment variable, which libvips
reads while initializing.Applications also running ruby-vips
>= 2.2.1or later can instead call
Vips.block_untrusted(true)from an initializer.Releases
The fixed releases are available at the normal locations.
Versions affected
- activestorage < 7.2.3.2
- activestorage >= 8.0, < 8.0.5.1
- activestorage >= 8.1, < 8.1.3.1
Disclosure
Technical details of the attack chain are intentionally omitted from this advisory. They would add
nothing to an administrator's decision to upgrade, while making it substantially easier to attack
applications that have not yet done so.Details will be disclosed no later than 2026-08-28, via the Rails Security
Announcements forum.Credit
This issue was responsibly reported by 0xacb, s3np41k1r1t0 and castilho from Ethiack, and RyotaK from GMO Flatt Security Inc..
References
- libvips 8.13 release notes, blocking of unfuzzed loaders
- W.A. Arbaugh, W.L. Fithen, and J. McHugh, "Windows of Vulnerability: A Case Study
Analysis", IEEE Computer 33(12), December 2000- https://ethiack.com/info-hub/research/kindarails2shell-rails-rce-cve
- https://blog.flatt.tech/entry/kindarails2shell_rails
π¨ Active Storage has possible arbitrary file read and remote code execution in Active Storage variant processing
Impact
In its default configuration, a Rails application that displays image variants may allow an
unauthenticated attacker to read arbitrary files from the server, including the process environment.
That environment typically holdssecret_key_baseand often credentials for external systems, which
may in turn allow escalation to remote code execution or lateral movement to those systems.Details
libvips reads and writes file formats through "loaders" and "savers" (or more generally
"operations"), many of which are backed by third-party libraries. It marks some of these operations
as "unfuzzed", meaning they are unsafe for untrusted content, and several handle formats unrelated
to web images. Active Storage did not disable the unfuzzed operations, so an attacker who can upload
a crafted file and cause a variant to be generated from it may be able to invoke one.We are aware of a mechanism by which an attacker, by uploading a crafted file, is able to cause
disclosure of the contents of arbitrary files accessible on the filesystem of the targeted
application. One specific attack chain has been reported to us (see "Disclosure" below), but we do
not assume it is the only one that exists.Affected applications
An application is affected if it meets all of these requirements:
- Uses libvips for Active Storage image processing. This is
config.active_storage.variant_processor = :vips,
whichload_defaults 7.0set and no later default has changed.- Allows image uploads from untrusted users.
Generating variants is not a separate requirement.
Mitigation
- Upgrade to a fixed version of
activestorage.- The minimum version of libvips must be upgraded to
>= 8.13.- Change
secret_key_baseand change any secrets accessible in the application environment (see "Expire and change secrets" below)Earlier versions of libvips (
< 8.13) cannot disable unfuzzed operations at all, and Active Storage
will raise an exception during boot in such an unsecurable environment.Expire and change secrets
Upgrading closes the vulnerability but does not undo an exfiltrated secret if that already
occurred. An affected application should treat every secret readable by the application process as
potentially exposed and change it, including:
secret_key_base- The master key, whether stored in
config/master.keyor supplied asRAILS_MASTER_KEY, along
with everything inconfig/credentials.yml.encthat it decrypts- Credentials for the Active Storage service, such as S3, GCS, or Azure keys
- Database credentials
- Tokens and keys for any third-party service the application calls
Changing
secret_key_baseexpires active sessions and requires users to log in again. Encrypted
cookies, signed cookies, signed global IDs, and Active Storage URLs are also affected.Rotation should only be used as an intermediate step if necessary. Do not retain an exposed secret
as a fallback.Workarounds
If libvips
< 8.13is being used, there are no workarounds available other than removing the
dependency on libvips from the application. Some applications may haveruby-vipsdeclared as a
dependency only for image analysis, and those applications may be able to simply removeruby-vips
from the Gemfile to remove libvips from the application. Applications that do not use Active Storage
can removeruby-vipsfrom the Gemfile to avoid the boot-time checks.If libvips
>= 8.13is present on the system, applications can disable the unfuzzed operations
without upgrading Rails by setting theVIPS_BLOCK_UNTRUSTEDenvironment variable, which libvips
reads while initializing.Applications also running ruby-vips
>= 2.2.1or later can instead call
Vips.block_untrusted(true)from an initializer.Releases
The fixed releases are available at the normal locations.
Versions affected
- activestorage < 7.2.3.2
- activestorage >= 8.0, < 8.0.5.1
- activestorage >= 8.1, < 8.1.3.1
Disclosure
Technical details of the attack chain are intentionally omitted from this advisory. They would add
nothing to an administrator's decision to upgrade, while making it substantially easier to attack
applications that have not yet done so.Details will be disclosed no later than 2026-08-28, via the Rails Security
Announcements forum.Credit
This issue was responsibly reported by 0xacb, s3np41k1r1t0 and castilho from Ethiack, and RyotaK from GMO Flatt Security Inc..
References
- libvips 8.13 release notes, blocking of unfuzzed loaders
- W.A. Arbaugh, W.L. Fithen, and J. McHugh, "Windows of Vulnerability: A Case Study
Analysis", IEEE Computer 33(12), December 2000- https://ethiack.com/info-hub/research/kindarails2shell-rails-rce-cve
- https://blog.flatt.tech/entry/kindarails2shell_rails
π¨ Active Storage has possible arbitrary file read and remote code execution in Active Storage variant processing
Impact
In its default configuration, a Rails application that displays image variants may allow an
unauthenticated attacker to read arbitrary files from the server, including the process environment.
That environment typically holdssecret_key_baseand often credentials for external systems, which
may in turn allow escalation to remote code execution or lateral movement to those systems.Details
libvips reads and writes file formats through "loaders" and "savers" (or more generally
"operations"), many of which are backed by third-party libraries. It marks some of these operations
as "unfuzzed", meaning they are unsafe for untrusted content, and several handle formats unrelated
to web images. Active Storage did not disable the unfuzzed operations, so an attacker who can upload
a crafted file and cause a variant to be generated from it may be able to invoke one.We are aware of a mechanism by which an attacker, by uploading a crafted file, is able to cause
disclosure of the contents of arbitrary files accessible on the filesystem of the targeted
application. One specific attack chain has been reported to us (see "Disclosure" below), but we do
not assume it is the only one that exists.Affected applications
An application is affected if it meets all of these requirements:
- Uses libvips for Active Storage image processing. This is
config.active_storage.variant_processor = :vips,
whichload_defaults 7.0set and no later default has changed.- Allows image uploads from untrusted users.
Generating variants is not a separate requirement.
Mitigation
- Upgrade to a fixed version of
activestorage.- The minimum version of libvips must be upgraded to
>= 8.13.- Change
secret_key_baseand change any secrets accessible in the application environment (see "Expire and change secrets" below)Earlier versions of libvips (
< 8.13) cannot disable unfuzzed operations at all, and Active Storage
will raise an exception during boot in such an unsecurable environment.Expire and change secrets
Upgrading closes the vulnerability but does not undo an exfiltrated secret if that already
occurred. An affected application should treat every secret readable by the application process as
potentially exposed and change it, including:
secret_key_base- The master key, whether stored in
config/master.keyor supplied asRAILS_MASTER_KEY, along
with everything inconfig/credentials.yml.encthat it decrypts- Credentials for the Active Storage service, such as S3, GCS, or Azure keys
- Database credentials
- Tokens and keys for any third-party service the application calls
Changing
secret_key_baseexpires active sessions and requires users to log in again. Encrypted
cookies, signed cookies, signed global IDs, and Active Storage URLs are also affected.Rotation should only be used as an intermediate step if necessary. Do not retain an exposed secret
as a fallback.Workarounds
If libvips
< 8.13is being used, there are no workarounds available other than removing the
dependency on libvips from the application. Some applications may haveruby-vipsdeclared as a
dependency only for image analysis, and those applications may be able to simply removeruby-vips
from the Gemfile to remove libvips from the application. Applications that do not use Active Storage
can removeruby-vipsfrom the Gemfile to avoid the boot-time checks.If libvips
>= 8.13is present on the system, applications can disable the unfuzzed operations
without upgrading Rails by setting theVIPS_BLOCK_UNTRUSTEDenvironment variable, which libvips
reads while initializing.Applications also running ruby-vips
>= 2.2.1or later can instead call
Vips.block_untrusted(true)from an initializer.Releases
The fixed releases are available at the normal locations.
Versions affected
- activestorage < 7.2.3.2
- activestorage >= 8.0, < 8.0.5.1
- activestorage >= 8.1, < 8.1.3.1
Disclosure
Technical details of the attack chain are intentionally omitted from this advisory. They would add
nothing to an administrator's decision to upgrade, while making it substantially easier to attack
applications that have not yet done so.Details will be disclosed no later than 2026-08-28, via the Rails Security
Announcements forum.Credit
This issue was responsibly reported by 0xacb, s3np41k1r1t0 and castilho from Ethiack, and RyotaK from GMO Flatt Security Inc..
References
- libvips 8.13 release notes, blocking of unfuzzed loaders
- W.A. Arbaugh, W.L. Fithen, and J. McHugh, "Windows of Vulnerability: A Case Study
Analysis", IEEE Computer 33(12), December 2000- https://ethiack.com/info-hub/research/kindarails2shell-rails-rce-cve
- https://blog.flatt.tech/entry/kindarails2shell_rails
π¨ 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 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 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.
π¨ 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!
π¨ 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!
π¨ Rails has possible Sensitive Session Information Leak in Active Storage
Possible Sensitive Session Information Leak in Active Storage
There is a possible sensitive session information leak in Active Storage. By
default, Active Storage sends aSet-Cookieheader along with the user's
session cookie when serving blobs. It also setsCache-Controlto public.
Certain proxies may cache the Set-Cookie, leading to an information leak.This vulnerability has been assigned the CVE identifier CVE-2024-26144.
Versions Affected: >= 5.2.0, < 7.1.0
Not affected: < 5.2.0, > 7.1.0
Fixed Versions: 7.0.8.1, 6.1.7.7Impact
A proxy which chooses to caches this request can cause users to share
sessions. This may include a user receiving an attacker's session or vice
versa.This was patched in 7.1.0 but not previously identified as a security
vulnerability.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
Upgrade to Rails 7.1.X, or configure caching proxies not to cache the
Set-Cookie headers.Credits
Thanks to tyage for reporting this!
π¨ Possible code injection vulnerability in Rails / Active Storage
The Active Storage module of Rails starting with version 5.2.0 is possibly vulnerable to code injection. This issue was patched in versions 5.2.6.3, 6.0.4.7, 6.1.4.7, and 7.0.2.3. To work around this issue, applications should implement a strict allow-list on accepted transformation methods or arguments. Additionally, a strict ImageMagick security policy will help mitigate this issue.
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.
βοΈ 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
βοΈ 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.
βοΈ 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
βοΈ 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
βοΈ 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:
βοΈ nokogiri (indirect, 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
βοΈ rack (indirect, 2.2.18 β 3.2.6) Β· Repo Β· Changelog
Security Advisories π¨
π¨ 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'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'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 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::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 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: 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'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'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.
π¨ 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 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:: 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::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::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 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::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'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 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 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: 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'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.
π¨ 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 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:: 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::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 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::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'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 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 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'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.
π¨ 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 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 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 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.
π¨ 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.
π¨ 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)
π¨ 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 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 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 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'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.
π¨ 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: 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 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.
π¨ 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: 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 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.
π¨ 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: 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.
π¨ ReDoS Vulnerability in Rack::Multipart handle_mime_head
Summary
There is a denial of service vulnerability in the Content-Disposition parsing component of Rack. This is very similar to the previous security issue CVE-2022-44571.
Details
Carefully crafted input can cause Content-Disposition header parsing in Rack to take an unexpected amount of time, possibly resulting in a denial of service attack vector. This header is used typically used in multipart parsing. Any applications that parse multipart posts using Rack (virtually all Rails applications) are impacted.
Credits
Thanks to scyoon for reporting this to the Rails security team
π¨ Rack has an Unbounded-Parameter DoS in Rack::QueryParser
Summary
Rack::QueryParserparses query strings andapplication/x-www-form-urlencodedbodies into Ruby data structures without imposing any limit on the number of parameters, allowing attackers to send requests with extremely large numbers of parameters.Details
The vulnerability arises because
Rack::QueryParseriterates over each&-separated key-value pair and adds it to a Hash without enforcing an upper bound on the total number of parameters. This allows an attacker to send a single request containing hundreds of thousands (or more) of parameters, which consumes excessive memory and CPU during parsing.Impact
An attacker can trigger denial of service by sending specifically crafted HTTP requests, which can cause memory exhaustion or pin CPU resources, stalling or crashing the Rack server. This results in full service disruption until the affected worker is restarted.
Mitigation
- Update to a version of Rack that limits the number of parameters parsed, or
- Use middleware to enforce a maximum query string size or parameter count, or
- Employ a reverse proxy (such as Nginx) to limit request sizes and reject oversized query strings or bodies.
Limiting request body sizes and query string lengths at the web server or CDN level is an effective mitigation.
π¨ Rack has an Unbounded-Parameter DoS in Rack::QueryParser
Summary
Rack::QueryParserparses query strings andapplication/x-www-form-urlencodedbodies into Ruby data structures without imposing any limit on the number of parameters, allowing attackers to send requests with extremely large numbers of parameters.Details
The vulnerability arises because
Rack::QueryParseriterates over each&-separated key-value pair and adds it to a Hash without enforcing an upper bound on the total number of parameters. This allows an attacker to send a single request containing hundreds of thousands (or more) of parameters, which consumes excessive memory and CPU during parsing.Impact
An attacker can trigger denial of service by sending specifically crafted HTTP requests, which can cause memory exhaustion or pin CPU resources, stalling or crashing the Rack server. This results in full service disruption until the affected worker is restarted.
Mitigation
- Update to a version of Rack that limits the number of parameters parsed, or
- Use middleware to enforce a maximum query string size or parameter count, or
- Employ a reverse proxy (such as Nginx) to limit request sizes and reject oversized query strings or bodies.
Limiting request body sizes and query string lengths at the web server or CDN level is an effective mitigation.
π¨ Local File Inclusion in Rack::Static
Summary
Rack::Staticcan serve files under the specifiedroot:even ifurls:are provided, which may expose other files under the specifiedroot:unexpectedly.Details
The vulnerability occurs because
Rack::Staticdoes not properly sanitize user-supplied paths before serving files. Specifically, encoded path traversal sequences are not correctly validated, allowing attackers to access files outside the designated static file directory.Impact
By exploiting this vulnerability, an attacker can gain access to all files under the specified
root:directory, provided they are able to determine then path of the file.Mitigation
- Update to the latest version of Rack, or
- Remove usage of
Rack::Static, or- Ensure that
root:points at a directory path which only contains files which should be accessed publicly.It is likely that a CDN or similar static file server would also mitigate the issue.
π¨ Local File Inclusion in Rack::Static
Summary
Rack::Staticcan serve files under the specifiedroot:even ifurls:are provided, which may expose other files under the specifiedroot:unexpectedly.Details
The vulnerability occurs because
Rack::Staticdoes not properly sanitize user-supplied paths before serving files. Specifically, encoded path traversal sequences are not correctly validated, allowing attackers to access files outside the designated static file directory.Impact
By exploiting this vulnerability, an attacker can gain access to all files under the specified
root:directory, provided they are able to determine then path of the file.Mitigation
- Update to the latest version of Rack, or
- Remove usage of
Rack::Static, or- Ensure that
root:points at a directory path which only contains files which should be accessed publicly.It is likely that a CDN or similar static file server would also mitigate the issue.
π¨ Escape Sequence Injection vulnerability in Rack lead to Possible Log Injection
Summary
Rack::Sendfilecan be exploited by crafting input that includes newline characters to manipulate log entries.Details
The
Rack::Sendfilemiddleware logs unsanitized header values from theX-Sendfile-Typeheader. An attacker can exploit this by injecting escape sequences (such as newline characters) into the header, resulting in log injection.Impact
This vulnerability can distort log files, obscure attack traces, and complicate security auditing.
Mitigation
- Update to the latest version of Rack, or
- Remove usage of
Rack::Sendfile.
π¨ Escape Sequence Injection vulnerability in Rack lead to Possible Log Injection
Summary
Rack::Sendfilecan be exploited by crafting input that includes newline characters to manipulate log entries.Details
The
Rack::Sendfilemiddleware logs unsanitized header values from theX-Sendfile-Typeheader. An attacker can exploit this by injecting escape sequences (such as newline characters) into the header, resulting in log injection.Impact
This vulnerability can distort log files, obscure attack traces, and complicate security auditing.
Mitigation
- Update to the latest version of Rack, or
- Remove usage of
Rack::Sendfile.
π¨ Possible Log Injection in Rack::CommonLogger
Summary
Rack::CommonLoggercan be exploited by crafting input that includes newline characters to manipulate log entries. The supplied proof-of-concept demonstrates injecting malicious content into logs.Details
When a user provides the authorization credentials via
Rack::Auth::Basic, if success, the username will be put inenv['REMOTE_USER']and later be used byRack::CommonLoggerfor logging purposes.The issue occurs when a server intentionally or unintentionally allows a user creation with the username contain CRLF and white space characters, or the server just want to log every login attempts. If an attacker enters a username with CRLF character, the logger will log the malicious username with CRLF characters into the logfile.
Impact
Attackers can break log formats or insert fraudulent entries, potentially obscuring real activity or injecting malicious data into log files.
Mitigation
- Update to the latest version of Rack.
π¨ Rack ReDoS Vulnerability in HTTP Accept Headers Parsing
Summary
A Regular Expression Denial of Service (ReDoS) vulnerability exists in the
Rack::Request::Helpersmodule when parsing HTTP Accept headers. This vulnerability can be exploited by an attacker sending specially craftedAccept-EncodingorAccept-Languageheaders, causing the server to spend excessive time processing the request and leading to a Denial of Service (DoS).Details
The fix for GHSA-54rr-7fvw-6x8f was not applied to the main branch and thus while the issue was fixed for the Rack v3.0 release series, it was not fixed in the v3.1 release series until v3.1.5.
π¨ Rack has possible DoS Vulnerability with Range Header
Possible DoS Vulnerability with Range Header in Rack
There is a possible DoS vulnerability relating to the Range request header in
Rack. This vulnerability has been assigned the CVE identifier CVE-2024-26141.Versions Affected: >= 1.3.0.
Not affected: < 1.3.0
Fixed Versions: 3.0.9.1, 2.2.8.1Impact
Carefully crafted Range headers can cause a server to respond with an
unexpectedly large response. Responding with such large responses could lead
to a denial of service issue.Vulnerable applications will use the
Rack::Filemiddleware or the
Rack::Utils.byte_rangesmethods (this includes Rails applications).Releases
The fixed releases are available at the normal locations.
Workarounds
There are no feasible workarounds for this issue.
Patches
To aid users who aren't able to upgrade immediately we have provided patches for
the two supported release series. They are in git-am format and consist of a
single changeset.
- 3-0-range.patch - Patch for 3.0 series
- 2-2-range.patch - Patch for 2.2 series
Credits
Thank you ooooooo_q for the report and
patch
π¨ Rack Header Parsing leads to Possible Denial of Service Vulnerability
Possible Denial of Service Vulnerability in Rack Header Parsing
There is a possible denial of service vulnerability in the header parsing
routines in Rack. This vulnerability has been assigned the CVE identifier
CVE-2024-26146.Versions Affected: All.
Not affected: None
Fixed Versions: 2.0.9.4, 2.1.4.4, 2.2.8.1, 3.0.9.1Impact
Carefully crafted headers can cause header parsing in Rack to take longer than
expected resulting in a possible denial of service issue. Accept and Forwarded
headers are impacted.Ruby 3.2 has mitigations for this problem, so Rack applications using Ruby 3.2
or newer are unaffected.Releases
The fixed releases are available at the normal locations.
Workarounds
There are no feasible workarounds for this issue.
Patches
To aid users who aren't able to upgrade immediately we have provided patches for
the two supported release series. They are in git-am format and consist of a
single changeset.
- 2-0-header-redos.patch - Patch for 2.0 series
- 2-1-header-redos.patch - Patch for 2.1 series
- 2-2-header-redos.patch - Patch for 2.2 series
- 3-0-header-redos.patch - Patch for 3.0 series
Credits
Thanks to svalkanov for reporting this and
providing patches!
π¨ Rack vulnerable to ReDoS in content type parsing (2nd degree polynomial)
Summary
module Rack class MediaType SPLIT_PATTERN = %r{\s*[;,]\s*}The above regexp is subject to ReDos. 50K blank characters as a prefix to the header will take over 10s to split.
PoC
A simple HTTP request with lots of blank characters in the content-type header:
request["Content-Type"] = (" " * 50_000) + "a,"Impact
It's a very easy to craft ReDoS. Like all ReDoS the impact is debatable.
π¨ Possible Denial of Service Vulnerability in Rack's header parsing
There is a denial of service vulnerability in the header parsing component of Rack. This vulnerability has been assigned the CVE identifier CVE-2023-27539.
Versions Affected: >= 2.0.0 Not affected: None. Fixed Versions: 2.2.6.4, 3.0.6.1
Impact
Carefully crafted input can cause header parsing in Rack to take an unexpected amount of time, possibly resulting in a denial of service attack vector. Any applications that parse headers using Rack (virtually all Rails applications) are impacted.
Workarounds
Setting Regexp.timeout in Ruby 3.2 is a possible workaround.
π¨ Rack has possible DoS Vulnerability in Multipart MIME parsing
There is a possible DoS vulnerability in the Multipart MIME parsing code in Rack. This vulnerability has been assigned the CVE identifier CVE-2023-27530.
Versions Affected: All. Not affected: None Fixed Versions: 3.0.4.2, 2.2.6.3, 2.1.4.3, 2.0.9.3
Impact
The Multipart MIME parsing code in Rack limits the number of file parts, but does not limit the total number of parts that can be uploaded. Carefully crafted requests can abuse this and cause multipart parsing to take longer than expected.
All users running an affected release should either upgrade or use one of the workarounds immediately.
Workarounds
A proxy can be configured to limit the POST body size which will mitigate this issue.
π¨ Denial of Service Vulnerability in Rack Content-Disposition parsing
There is a denial of service vulnerability in the Content-Disposition parsing component of Rack. This vulnerability has been assigned the CVE identifier CVE-2022-44571.
Versions Affected: >= 2.0.0 Not affected: None. Fixed Versions: 2.0.9.2, 2.1.4.2, 2.2.6.1, 3.0.0.1
ImpactCarefully crafted input can cause Content-Disposition header parsing in Rack to take an unexpected amount of time, possibly resulting in a denial of service attack vector. This header is used typically used in multipart parsing. Any applications that parse multipart posts using Rack (virtually all Rails applications) are impacted.
ReleasesThe fixed releases are available at the normal locations.
WorkaroundsThere are no feasible workarounds for this issue.
PatchesTo aid users who arenβt able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format and consist of a single changeset.
2-0-Fix-ReDoS-vulnerability-in-multipart-parser - Patch for 2.0 series 2-1-Fix-ReDoS-vulnerability-in-multipart-parser - Patch for 2.1 series 2-2-Fix-ReDoS-vulnerability-in-multipart-parser - Patch for 2.2 series 3-0-Fix-ReDoS-vulnerability-in-multipart-parser - Patch for 3.0 series
π¨ Denial of service via header parsing in Rack
There is a possible denial of service vulnerability in the Range header parsing component of Rack. This vulnerability has been assigned the CVE identifier CVE-2022-44570.
Versions Affected: >= 1.5.0 Not affected: None. Fixed Versions: 2.0.9.2, 2.1.4.2, 2.2.6.2, 3.0.0.1
ImpactCarefully crafted input can cause the Range header parsing component in Rack to take an unexpected amount of time, possibly resulting in a denial of service attack vector. Any applications that deal with Range requests (such as streaming applications, or applications that serve files) may be impacted.
ReleasesThe fixed releases are available at the normal locations.
WorkaroundsThere are no feasible workarounds for this issue.
PatchesTo aid users who arenβt able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format and consist of a single changeset.
2-0-Fix-ReDoS-in-Rack-Utils.get_byte_ranges.patch - Patch for 2.0 series 2-1-Fix-ReDoS-in-Rack-Utils.get_byte_ranges.patch - Patch for 2.1 series 2-2-Fix-ReDoS-in-Rack-Utils.get_byte_ranges.patch - Patch for 2.2 series 3-0-Fix-ReDoS-in-Rack-Utils.get_byte_ranges.patch - Patch for 3.0 series
π¨ Denial of service via multipart parsing in Rack
There is a denial of service vulnerability in the multipart parsing component of Rack. This vulnerability has been assigned the CVE identifier CVE-2022-44572.
Versions Affected: >= 2.0.0 Not affected: None. Fixed Versions: 2.0.9.2, 2.1.4.2, 2.2.6.1, 3.0.0.1
ImpactCarefully crafted input can cause RFC2183 multipart boundary parsing in Rack to take an unexpected amount of time, possibly resulting in a denial of service attack vector. Any applications that parse multipart posts using Rack (virtually all Rails applications) are impacted.
ReleasesThe fixed releases are available at the normal locations.
WorkaroundsThere are no feasible workarounds for this issue.
PatchesTo aid users who arenβt able to upgrade immediately we have provided patches for the two supported release series. They are in git-am format and consist of a single changeset.
2-0-Forbid-control-characters-in-attributes.patch - Patch for 2.0 series 2-1-Forbid-control-characters-in-attributes.patch - Patch for 2.1 series 2-2-Forbid-control-characters-in-attributes.patch - Patch for 2.2 series 3-0-Forbid-control-characters-in-attributes.patch - Patch for 3.0 series
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.
βοΈ 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, 6.1.7.10 β 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.
βοΈ 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.6.18 β 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.
2.7.3 (from changelog)
The helper
Zeitwerk::Loader#cpath_expected_atdid not work correctly if the inflector had logic that relied on the absolute path of the given file or directory. This has been fixed.This bug was found by Codex.
Perpetual internal work.
2.7.2 (from changelog)
Internal improvements and micro-optimizations.
Add stable TruffleRuby to CI.
2.7.1 (from changelog)
Micro-optimization in a hot path.
Raises
Zeitwerk::Errorif an autoloaded constant expected to represent a namespace does not store a class or module object.Adds
truffleruby-headto CI, except for autoloading thread-safety (see why in oracle/truffleruby#2431).
2.7.0 (from changelog)
Explicit namespaces can now also be defined using constant assignments.
While constant assignments like
# coordinates.rbCoordinates = Data.define(:x, :y)
worked for most objects, they did not for classes and modules that were also namespaces (i.e., those defined by a file and matching subdirectories). In such cases, their child constants could not be autoloaded.
This limitation has been removed.
TracePointis no longer used.Requires Ruby 3.2 or later.
Gems that work with previous versions of Zeitwerk also work with this one. If they support Ruby versions older than 3.2 they can specify a relaxed version constraint for Zeitwerk like "~> 2.6", for example.
In client projects, Bundler takes the Ruby requirement into account when resolving dependencies, so
Gemfile.lockwill get one compatible with the Ruby version being used.
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.