diff --git a/gems/puma/GHSA-86xc-wrr9-xmmq.yml b/gems/puma/GHSA-86xc-wrr9-xmmq.yml new file mode 100644 index 0000000000..ed0ab5d6ad --- /dev/null +++ b/gems/puma/GHSA-86xc-wrr9-xmmq.yml @@ -0,0 +1,54 @@ +--- +gem: puma +ghsa: 86xc-wrr9-xmmq +url: https://github.com/puma/puma/security/advisories/GHSA-86xc-wrr9-xmmq +title: pumactl does not verify the TLS identity of SSL control servers +date: 2026-11-13 +description: | + ## Impact + + An attacker on the network path between pumactl and a Puma control + server reached over ssl:// can steal the control token and use it + to stop, restart, or inspect Puma. + + pumactl support for ssl:// control URLs was added in v4.3.0 (#2052). + + When pumactl connects to an ssl:// control URL, it accepts any TLS + certificate. It should check that a trusted authority issued the + certificate and that the certificate matches the hostname in the URL. + An attacker who can intercept or redirect the connection can present + their own certificate, and pumactl then sends them the control token. + + With the token, the attacker can send authenticated commands to the + real control server, including stop, restart, and stats. + + Only deployments that run pumactl against an ssl:// control URL + are affected, for example: + + * pumactl --control-url 'ssl://control.example.com:9293' + --control-token "$PUMA_CONTROL_TOKEN" stats + + Puma's default configuration is not affected. The control server + is off by default, and unix:// control URLs are not affected. + + ## Acknowledgements + + Thanks to @ahmedehab-nopr for reporting this issue. +cvss_v4: 8.3 +unaffected_versions: + - "< 4.3.0" +patched_versions: + - "~> 7.2.2" + - ">= 8.0.3" +related: + url: + - https://rubygems.org/gems/puma/versions/8.0.3 + - https://github.com/puma/puma/blob/main/History.md#803--2026-11-13 + - https://rubygems.org/gems/puma/versions/7.2.2 + - https://github.com/puma/puma/blob/main/History.md#722--2026-11-13 + - https://github.com/puma/puma/pull/2052 + - https://github.com/puma/puma/security/advisories/GHSA-86xc-wrr9-xmmq +notes: | + - No CVE from GHSA URL. + - cvss_v4 from GHSA URL. + - No gap between 7.2.2 and 8.0.0 releases. diff --git a/gems/puma/GHSA-jm4r-w4xx-qxw7.yml b/gems/puma/GHSA-jm4r-w4xx-qxw7.yml new file mode 100644 index 0000000000..ce4af56082 --- /dev/null +++ b/gems/puma/GHSA-jm4r-w4xx-qxw7.yml @@ -0,0 +1,63 @@ +--- +gem: puma +ghsa: jm4r-w4xx-qxw7 +url: https://github.com/puma/puma/security/advisories/GHSA-jm4r-w4xx-qxw7 +title: Graceful shutdown waits indefinitely on trickled request bodies +date: 2026-11-13 +description: | + ## Impact + + Puma servers running in single mode with the default force_shutdown_after + :forever can be prevented from shutting down or restarting by an + unauthenticated client. + + When Puma begins a graceful shutdown, it hands any request that is + still being received to the thread pool so that it can finish. The + thread pool then waits for that request with no deadline. Puma's + read timeout restarts every time a byte arrives, so an attacker who + sends a POST with a large Content-Length and then trickles one body + byte every few seconds can keep that request "in progress" for as + long as they like. The same works with chunked bodies and with + trickled headers. Puma stops accepting new connections, but the + process does not exit until the attacker closes the socket. + + This delays or blocks deploys, restarts, and operational recovery. + Process supervisors that eventually send SIGKILL will end the + process, but any other in-flight requests are lost when they do. + Many trickling connections can be used together; each holds a + file descriptor but uses very little memory. + + Deployments using Puma's default single-mode configuration are affected. + + Unaffected: + + * Cluster mode (workers N) bounds the delay: the master kills a + stuck worker after worker_shutdown_timeout (30 seconds by default). + + * A finite force_shutdown_after bounds the delay. + + * Deployments behind a proxy that buffers request bodies before + forwarding them are not affected. For example, nginx's default + proxy_request_buffering on keeps a trickled body from reaching Puma. + + ## Acknowledgements + + Thanks to Zheng Yu of depthfirst (@cla7aye15I4nd) for reporting + this issue and providing a proposed fix. +cvss_v4: 6.9 +unaffected_versions: + - "< 5.0.0" +patched_versions: + - "~> 7.2.2" + - ">= 8.0.3" +related: + url: + - https://rubygems.org/gems/puma/versions/8.0.3 + - https://github.com/puma/puma/blob/main/History.md#803--2026-11-13 + - https://rubygems.org/gems/puma/versions/7.2.2 + - https://github.com/puma/puma/blob/main/History.md#722--2026-11-13 + - https://github.com/puma/puma/security/advisories/GHSA-jm4r-w4xx-qxw7 +notes: | + - No CVE from GHSA URL. + - cvss_v4 from GHSA URL. + - No gap between 7.2.2 and 8.0.0 releases. diff --git a/gems/puma/GHSA-m5xw-3hrw-qcmf.yml b/gems/puma/GHSA-m5xw-3hrw-qcmf.yml new file mode 100644 index 0000000000..4861e86e01 --- /dev/null +++ b/gems/puma/GHSA-m5xw-3hrw-qcmf.yml @@ -0,0 +1,50 @@ +--- +gem: puma +ghsa: m5xw-3hrw-qcmf +url: https://github.com/puma/puma/security/advisories/GHSA-m5xw-3hrw-qcmf +title: Reactor timeout queue loses order, letting a slow client + keep other connections open past their timeout +date: 2026-11-13 +description: | + ## Impact + + An unauthenticated client can stop Puma from closing other connections + when their first_data_timeout or persistent_timeout expires. + + The reactor keeps waiting connections in a queue sorted by deadline. + Puma sorted the queue only when new connections arrived, but it + extended a connection's deadline every time that connection sent + data. A client that connects and then sends about one byte per + timeout interval, without completing its request, stays at the + front of the queue with a deadline that is always in the future. + Every expired connection behind it stays open for as long as the + client keeps sending, or until a new connection arrives and causes a sort. + + Connections held this way cost Puma one file descriptor and a small + amount of memory each. Puma does not crash, and the effect ends + when the slow client stops or new traffic arrives. + This bug was introduced in v5.0.3 by the reactor refactor (#2279). + + The default configuration is affected. + + ## Acknowledgements + + Thanks to @0xMigoo for the report and the controlled proof of + concept that isolated the missing sort. +cvss_v4: 6.9 +unaffected_versions: + - "< 5.0.3" +patched_versions: + - ">= 7.2.2" + - ">= 8.0.3" +related: + url: + - https://rubygems.org/gems/puma/versions/8.0.3 + - https://github.com/puma/puma/blob/main/History.md#803--2026-11-13 + - https://rubygems.org/gems/puma/versions/7.2.2 + - https://github.com/puma/puma/blob/main/History.md#722--2026-11-13 + - https://github.com/puma/puma/security/advisories/GHSA-m5xw-3hrw-qcmf +notes: | + - No CVE from GHSA URL. + - cvss_v4 from GHSA URL. + - No gap between 7.2.2 and 8.0.0 releases. diff --git a/gems/puma/GHSA-r885-m39c-939g.yml b/gems/puma/GHSA-r885-m39c-939g.yml new file mode 100644 index 0000000000..61dde85802 --- /dev/null +++ b/gems/puma/GHSA-r885-m39c-939g.yml @@ -0,0 +1,59 @@ +--- +gem: puma +ghsa: r885-m39c-939g +url: https://github.com/puma/puma/security/advisories/GHSA-r885-m39c-939g +title: ssl_cipher_filter values containing `+` are decoded + to a wider cipher list +date: 2026-11-13 +description: | + ## Impact + + Puma servers that set an ssl_cipher_filter containing + accept TLS + cipher suites that the filter is meant to exclude. Any client that + can reach the SSL listener can negotiate those suites. + + Support for ssl_cipher_filter in ssl:// bind URLs was added in + v3.12.0 (#1478), and in the ssl_bind DSL in v4.0.0 (#1676). + + In an OpenSSL cipher string, + means "both": AES+SHA selects suites + that are AES and SHA. Puma decodes the query of a bind URL as HTML + form data, where + means a space, and a space in a cipher string + means "either". So the filter !aNULL:AES+SHA, which is the example + in Puma's README, reaches OpenSSL as !aNULL:AES SHA: every AES + suite plus every SHA suite. On OpenSSL 3.x that is 94 suites in + place of 27, including suites without forward secrecy. + + Puma logs nothing, and the startup output shows the filter as written, + so operators cannot see that their filter is not applied. The filter + only gets wider: explicit exclusions such as !aNULL or !3DES still apply. + + Deployments that set ssl_cipher_filter with a + are affected, in either form: + + * ssl_bind "0.0.0.0", "9292", ssl_cipher_filter: "!aNULL:AES+SHA", + cert: "...", key: "..." + + * puma -b 'ssl://0.0.0.0:9292?key=...&cert=...&ssl_cipher_filter=!aNULL:AES+SHA' + + The default configuration is not affected. Filters without +, + ssl_ciphersuites (TLS 1.3), and JRuby's ssl_cipher_list are not affected. + + ## Acknowledgements + + Thanks to @b84330808 for reporting this issue. +cvss_v4: 6.3 +unaffected_versions: + - "< 3.12.0" +patched_versions: + - "~> 7.2.2" + - ">= 8.0.3" +related: + url: + - https://rubygems.org/gems/puma/versions/8.0.3 + - https://github.com/puma/puma/blob/main/History.md#803--2026-11-13 + - https://rubygems.org/gems/puma/versions/7.2.2 + - https://github.com/puma/puma/blob/main/History.md#722--2026-11-13 + - https://github.com/puma/puma/security/advisories/GHSA-r885-m39c-939g +notes: | + - No CVE from GHSA URL. + - cvss_v4 from GHSA URL. + - No gap between 7.2.2 and 8.0.0 releases. diff --git a/gems/puma/GHSA-rf6g-xhmp-gm7v.yml b/gems/puma/GHSA-rf6g-xhmp-gm7v.yml new file mode 100644 index 0000000000..456297d862 --- /dev/null +++ b/gems/puma/GHSA-rf6g-xhmp-gm7v.yml @@ -0,0 +1,59 @@ +--- +gem: puma +ghsa: rf6g-xhmp-gm7v +url: https://github.com/puma/puma/security/advisories/GHSA-rf6g-xhmp-gm7v +title: Accept loop retries without pausing when out of file + descriptors, flooding the log +date: 2026-11-13 +description: | + ## Impact + + An unauthenticated client can make Puma: + + 1. write log lines as fast as it can... + 2. ...which eventually may fill the disk... + 3. ...and when the disk fills, Puma will stop accepting connections. + + The client can do this by opening enough idle connections to use up + the process's file descriptors. + + When accept fails because the process is out of file descriptors + (EMFILE), the connection stays in the listen backlog, so the listening + socket stays readable. Puma logs the error and calls accept again + at once, with no pause. Each attempt fails the same way and writes + another "Listen loop" error line. An attacker who opens about as + many idle connections as the file descriptor limit, and sends + nothing on them, keeps Puma in this loop. The attacker reconnects + when Puma closes idle connections after first_data_timeout, so + the loop does not end. + + If the log destination then raises an error, such as ENOSPC when + the disk is full, that error escapes the listen loop and ends + the thread that accepts connections. + + Deployments that accept connections directly from clients are + affected, including the default configuration. + + Deployments behind a reverse proxy or load balancer that limits + connections to Puma are not affected, because the client cannot + open enough connections to Puma itself. + + ## Acknowledgements + + Found by Puma's maintainers during an internal security review.x +cvss_v4: 8.7 +patched_versions: + - "~> 7.2.2" + - ">= 8.0.3" +related: + url: + - https://rubygems.org/gems/puma/versions/8.0.3 + - https://github.com/puma/puma/blob/main/History.md#803--2026-11-13 + - https://rubygems.org/gems/puma/versions/7.2.2 + - https://github.com/puma/puma/blob/main/History.md#722--2026-11-13 + - https://github.com/puma/puma/pull/2052 + - https://github.com/puma/puma/security/advisories/GHSA-rf6g-xhmp-gm7v +notes: | + - No CVE from GHSA URL. + - cvss_v4 from GHSA URL. + - No gap between 7.2.2 and 8.0.0 releases. diff --git a/gems/puma/GHSA-x646-wp7c-h9mg.yml b/gems/puma/GHSA-x646-wp7c-h9mg.yml new file mode 100644 index 0000000000..f91aada1f9 --- /dev/null +++ b/gems/puma/GHSA-x646-wp7c-h9mg.yml @@ -0,0 +1,70 @@ +--- +gem: puma +ghsa: x646-wp7c-h9mg +url: https://github.com/puma/puma/security/advisories/GHSA-x646-wp7c-h9mg +title: Client that stops reading a TLS or file response holds + a worker thread indefinitely +date: 2026-11-13 +description: | + ## Impact + + An unauthenticated client can hold a Puma worker thread indefinitely + when Puma sends it a TLS response, or a file response larger than + 64 KB. With enough connections, it can take the server offline. + + The client sends a request and then stops reading the response. + Puma's write timeout (10 seconds) should then free the worker + thread. In two cases it never fires, and the thread stays blocked + until the client disconnects: + + * TLS: MiniSSL::Socket#write_nonblock performs a blocking write. + This has been the case since v3.9.0 (#1274). + + * File bodies over 64 KB, on any bind: Puma sends File bodies, and + bodies that respond to to_path, with IO.copy_stream, which has no + write timeout. This has been the case since v6.0.0 (#2896). + Rack::Files, Rails static file serving, and Rails send_file return + these bodies. + + An attacker who opens max_threads such connections per worker leaves + no threads to serve requests. The attack needs very little bandwidth + and no CPU on the server. + + Two kinds of deployment are affected: + + * Deployments where Puma terminates TLS itself, with an ssl:// + bind or ssl_bind: + + * ssl_bind "0.0.0.0", "9292", cert: "/path/to/cert.pem", key: + "/path/to/key.pem" + + * Applications that serve a file larger than 64 KB through Puma, on + any bind. Puma's default configuration is affected in this case. + + Deployments behind a proxy that buffers responses are not affected + for files that fit in the proxy's buffer. nginx buffers by default + (proxy_buffering on), up to 1 GB per response in a temporary file. + + ## Acknowledgements + + Thanks to @wtn and @SounLabs, who reported this issue + independently, and to @wtn for the fix. +cvss_v4: 8.7 +unaffected_versions: + - "< 3.9.0" +patched_versions: + - ">= 7.2.2" + - ">= 8.0.3" +related: + url: + - https://rubygems.org/gems/puma/versions/8.0.3 + - https://github.com/puma/puma/blob/main/History.md#803--2026-11-13 + - https://rubygems.org/gems/puma/versions/7.2.2 + - https://github.com/puma/puma/blob/main/History.md#722--2026-11-13 + - https://github.com/puma/puma/pull/1274 + - https://github.com/puma/puma/pull/2896 + - https://github.com/puma/puma/security/advisories/GHSA-x646-wp7c-h9mg +notes: | + - No CVE from GHSA URL. + - cvss_v4 from GHSA URL. + - No gap between 7.2.2 and 8.0.0 releases. diff --git a/gems/puma/GHSA-xw3h-pgr9-3vg7.yml b/gems/puma/GHSA-xw3h-pgr9-3vg7.yml new file mode 100644 index 0000000000..5b50285b1a --- /dev/null +++ b/gems/puma/GHSA-xw3h-pgr9-3vg7.yml @@ -0,0 +1,60 @@ +--- +gem: puma +ghsa: xw3h-pgr9-3vg7 +url: https://github.com/puma/puma/security/advisories/GHSA-xw3h-pgr9-3vg7 +title: Reactor blocks on `100 Continue` writes to clients that + stop reading, stalling other connections +date: 2026-11-13 +description: | + ## Impact + + An unauthenticated client with one connection per worker process can + stop Puma from serving every connection that waits in the reactor, + for as long as it holds that connection open. + + Each Puma worker has one reactor thread, which holds connections + waiting for more data: keep-alive connections between requests, + requests that arrive in more than one read, and TLS connections + still in the handshake. When a request with Expect: 100-continue + completes its headers on the reactor thread, Puma writes the + HTTP/1.1 100 Continue interim response with a blocking write and + no timeout. An attacker pipelines a request for a large response + before the Expect: 100-continue request and does not read that + response. The server's send buffer for the connection fills, and + the reactor thread blocks on the 25-byte interim write until the + attacker reads or closes. The 408 Request Timeout response and + error responses for requests that fail to parse in the reactor + were written from the reactor thread in the same way. + + While the reactor thread is blocked, Puma does not read from or + time out any connection in the reactor on that worker. Requests + that arrive complete in a single read are still served, so the + server appears to hang only for some clients. + + The default configuration is affected (queue_requests is on by + default) when Puma is directly reachable by clients and the + application has a route whose response size the client can + influence. A reverse proxy in front of Puma that buffers + responses prevents the attack. + + ## Acknowledgements + + Thanks to @wtn for the report, the TCP and Unix socket proofs + of concept, the note about the 408 path, and the original fix. +cvss_v4: 8.2 +unaffected_versions: + - "< 3.8.0" +patched_versions: + - ">= 7.2.2" + - ">= 8.0.3" +related: + url: + - https://rubygems.org/gems/puma/versions/8.0.3 + - https://github.com/puma/puma/blob/main/History.md#803--2026-11-13 + - https://rubygems.org/gems/puma/versions/7.2.2 + - https://github.com/puma/puma/blob/main/History.md#722--2026-11-13 + - https://github.com/puma/puma/security/advisories/GHSA-xw3h-pgr9-3vg7 +notes: | + - No CVE from GHSA URL. + - cvss_v4 from GHSA URL. + - No gap between 7.2.2 and 8.0.0 releases.