{"id":"CVE-2026-47737","kind":"vulnerability","aliases":["CVE-2026-47737","GHSA-2vqw-3mp8-cgmx"],"related":[],"title":"Puma PROXY Protocol v1 Accepts Repeated Protocol Headers on Persistent Connections","description":"### Impact\n\nPuma is vulnerable to source IP spoofing when `set_remote_address proxy_protocol: :v1` is enabled and persistent connections are used.\n\nPROXY protocol v1 is a connection-level protocol. [Support was added to Puma in v5.5.0](https://github.com/puma/puma/issues/2651). A proxy sends one PROXY header at the beginning of a TCP connection, before any HTTP data. Puma incorrectly re-parsed PROXY protocol headers after each keep-alive request on the same connection. An attacker able to send HTTP requests through a trusted proxy could therefore inject a second PROXY header between HTTP requests. Puma would treat the injected header as authoritative for the next request and overwrite `REMOTE_ADDR`.\n\nThis can mislead applications or middleware that use `REMOTE_ADDR` for security decisions, rate limiting, auditing, or allow/deny lists.\n\n**Only deployments that explicitly enable PROXY protocol v1 are affected**, and will have set:\n\n```ruby\nset_remote_address proxy_protocol: :v1\n```\n\nPuma's default configuration is not affected. Deployments that do not use persistent connections to Puma are also not expected to be affected by this issue.\n\n### Patches\n\nUsers should upgrade to versions 7.2.1 or 8.0.2.\n\n### Workarounds\n\nDisable PROXY protocol v1 parsing if it is not required:\n\n ```ruby\n   # remove/comment this:\n   # set_remote_address proxy_protocol: :v1\n ```\n\nUsers can also disable persistent connections to Puma, for example:\n\n```ruby\nenable_keep_alives false\n```\n\n### References\n\n- [HAProxy PROXY protocol specification](https://www.haproxy.org/download/1.8/doc/proxy-protocol.txt)\n- [CVE-2025-31135 / GHSA-c2c3-pqw5-5p7c: go-guerrilla repeated PROXY command source IP spoofing](https://github.com/phires/go-guerrilla/security/advisories/GHSA-c2c3-pqw5-5p7c)\n- [Puma `set_remote_address` documentation](https://github.com/puma/puma/blob/master/lib/puma/dsl.rb)","summary":"Puma accepted a second PROXY protocol v1 header sent on a persistent connection and overwrote REMOTE_ADDR with it. An attacker who can send requests through a trusted proxy can make applications see a forged client IP for security checks, rate limits, audit logs and allow/deny lists.","severity":"high","cvss":7.5,"epss":0.00177,"exploited":false,"exploited_at":null,"products":[{"name":"puma","fixed":["8.0.2"],"affected":">= 8.0.0, < 8.0.2","ecosystem":"rubygems"},{"name":"puma","fixed":["7.2.1"],"affected":">= 5.5.0, < 7.2.1","ecosystem":"rubygems"}],"ecosystems":["rubygems"],"tags":["cwe-290","cwe-345","package","puma","rails","ruby","self-hosted"],"not_affected_if":["default-config","no-keep-alive","no-proxy-protocol"],"how_to_check":null,"fix":"- puma `8.0.2` for the 8.0 line\n- puma `7.2.1` for the 7.x line\n- Workaround stated by the source: remove `set_remote_address proxy_protocol: :v1` if PROXY protocol v1 is not required, or set `enable_keep_alives false`.","status":"open","published_at":"2026-06-09T00:09:44+00:00","first_seen_at":"2026-11-02T22:13:20+00:00","triaged_at":"2026-11-02T22:14:46+00:00","updated_at":"2026-11-02T22:14:46+00:00","category":"package","confidence":0.85,"triage":{"at":"2026-11-02T22:14:46Z","model":"claude-haiku-5-5","output":{"fix":"- puma `8.0.2` for the 8.0 line\n- puma `7.2.1` for the 7.x line\n- Workaround stated by the source: remove `set_remote_address proxy_protocol: :v1` if PROXY protocol v1 is not required, or set `enable_keep_alives false`.","reach":"network","title":"Puma: repeated PROXY protocol headers let a client spoof its source address","impact":"medium","summary":"Puma accepted a second PROXY protocol v1 header sent on a persistent connection and overwrote REMOTE_ADDR with it. An attacker who can send requests through a trusted proxy can make applications see a forged client IP for security checks, rate limits, audit logs and allow/deny lists.","audience":"developers","category":"package","nicknames":[],"who_cares":["ruby","puma","rails","self-hosted"],"confidence":0.85,"fix_status":"available","needs_auth":"unknown","worst_case":"Client source IP is spoofed in REMOTE_ADDR for security decisions","analyst_note":"","how_to_check":"- Check the Puma version in the Gemfile.lock: `puma` at `8.0.0` to `8.0.1`, or `5.5.0` to `7.2.0`, is affected.\n- Search the Puma config (usually `config/puma.rb`) for `set_remote_address proxy_protocol: :v1`; only deployments that set this are affected.\n- Confirm whether keep-alive connections reach Puma through the proxy; deployments without persistent connections are not expected to be affected.","plain_action":"","needs_analyst":false,"not_affected_if":["default-config","no-proxy-protocol","no-keep-alive"],"who_is_affected":"Only Ruby applications served by Puma that explicitly enable `set_remote_address proxy_protocol: :v1` and accept persistent connections through a proxy are exposed. The default Puma configuration is not affected, and deployments without persistent connections are not expected to be affected.","user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"unknown"},"cost_usd":0.000859,"input_tokens":5002,"output_tokens":717,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-2025':315C '-2026':13A,21A '-290':26B '-31135':316C '-345':28B '-47737':14A,22A '/download/1.8/doc/proxy-protocol.txt)':313C '/phires/go-guerrilla/security/advisories/ghsa-c2c3-pqw5-5p7c)':333C '/puma/puma/blob/master/lib/puma/dsl.rb)':341C '/puma/puma/issues/2651).':123C '2vqw':17A '3mp8':18A '5p7c':321C '7.2.1':268C '8.0.2':270C 'abl':161C 'accept':5A,37B 'add':116C 'addr':52B,195C,205C 'address':94C,232C,287C,337C 'affect':224C,242C,258C 'aliv':153C,304C 'allow/deny':81B,213C 'also':253C,293C 'applic':67B,199C 'attack':56B,160C 'audit':78B,211C 'authorit':187C 'begin':132C 'c2c3':319C 'cgmx':19A 'check':75B 'client':71B 'command':327C 'configur':239C 'connect':11A,48B,102C,111C,136C,158C,249C,296C 'connection-level':110C 'could':170C 'cve':12A,20A,314C 'cwe':25B,27B 'data':140C 'decis':208C 'default':238C 'deploy':216C,243C 'disabl':272C,294C 'document':338C 'enabl':99C,219C,302C 'exampl':300C 'expect':255C 'explicit':218C 'fals':305C 'forg':70B 'ghsa':16A,318C 'ghsa-2vqw-3mp8-cgmx':15A 'ghsa-c2c3-pqw5-5p7c':317C 'github.com':122C,332C,340C 'github.com/phires/go-guerrilla/security/advisories/ghsa-c2c3-pqw5-5p7c)':331C 'github.com/puma/puma/blob/master/lib/puma/dsl.rb)':339C 'github.com/puma/puma/issues/2651).':121C 'go':323C 'go-guerrilla':322C 'guerrilla':324C 'haproxi':307C 'header':8A,43B,129C,148C,176C,185C 'host':35B 'http':139C,164C,178C 'impact':83C 'incorrect':142C 'inject':172C,184C 'ip':72B,89C,329C 'issu':261C 'keep':152C,303C 'keep-al':151C 'level':112C 'limit':77B,210C 'list':82B,214C 'log':79B 'make':66B 'middlewar':201C 'mislead':198C 'next':190C 'one':127C 'overwrit':193C 'overwrot':50B 'packag':29B 'pars':145C,276C 'patch':262C 'persist':10A,47B,101C,248C,295C 'pqw5':320C 'protocol':3A,7A,41B,96C,106C,113C,147C,221C,234C,274C,289C,309C 'proxi':2A,40B,64B,95C,105C,125C,128C,146C,169C,175C,220C,233C,273C,288C,308C,326C 'puma':1A,23A,24A,30B,36B,84C,118C,141C,180C,236C,251C,298C,334C 'rail':31B 'rate':76B,209C 're':144C 're-pars':143C 'refer':306C 'remot':51B,93C,194C,204C,231C,286C,336C 'remove/comment':283C 'repeat':6A,325C 'request':60B,154C,165C,179C,191C 'requir':281C 'rubi':32B,229C,282C,301C 'second':39B,174C 'secur':74B,207C 'see':68B 'self':34B 'self-host':33B 'send':59B,126C,163C 'sent':44B 'set':92C,228C,230C,285C,335C 'sourc':88C,328C 'specif':310C 'spoof':90C,330C 'support':114C 'tcp':135C 'therefor':171C 'treat':182C 'trust':63B,168C 'upgrad':265C 'use':104C,203C,247C 'user':263C,291C 'v1':4A,42B,97C,107C,222C,235C,275C,290C 'v5.5.0':120C 'version':267C 'vulner':86C 'workaround':271C 'would':181C 'www.haproxy.org':312C 'www.haproxy.org/download/1.8/doc/proxy-protocol.txt)':311C","nicknames":[],"mitigations":[],"who_affected":"Only Ruby applications served by Puma that explicitly enable `set_remote_address proxy_protocol: :v1` and accept persistent connections through a proxy are exposed. The default Puma configuration is not affected, and deployments without persistent connections are not expected to be affected.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["puma"],"ptokens":["puma"],"labels":["cwe290","cwe345","high","package","puma","rails","ruby","rubygems","selfhosted"],"first_public_at":"2026-05-27T00:19:53+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-2vqw-3mp8-cgmx","url":"https://github.com/advisories/GHSA-2vqw-3mp8-cgmx","severity":"high","published_at":"2026-06-09T00:09:44+00:00","modified_at":"2026-11-02T22:08:42+00:00","first_seen_at":"2026-11-02T22:13:20+00:00","fetched_at":"2026-11-02T22:13:20+00:00","note":null},{"source":"nvd","external_id":"CVE-2026-47737","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-47737","severity":null,"published_at":"2026-07-14T20:17:03+00:00","modified_at":"2026-07-15T18:15:13+00:00","first_seen_at":"2026-11-02T22:35:43+00:00","fetched_at":"2026-11-02T22:35:43+00:00","note":null}],"documents":[{"id":54118,"url":"https://api.github.com/repos/puma/puma/security-advisories/GHSA-2vqw-3mp8-cgmx","title":"Maintainer's advisory: api.github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: api.github.com"},{"id":54119,"url":"https://github.com/puma/puma/security/advisories/GHSA-2vqw-3mp8-cgmx","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa","publisher":"vendor","text":null,"published_at":"2026-05-27T00:19:53+00:00","relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"},{"id":54120,"url":"https://github.com/puma/puma/pull/3944","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":54121,"url":"https://github.com/puma/puma/pull/3947","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":54114,"url":"https://github.com/puma/puma/commit/439c6136d9c2275721b7864db3ee78af7c80889f","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":54115,"url":"https://github.com/puma/puma/commit/ebe9db3929ab8299d19c8f5b41e8ef4f4b22fa58","title":"Patch: github.com","kind":"other","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Patch: github.com"},{"id":54116,"url":"https://github.com/puma/puma/releases/tag/v7.2.1","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"},{"id":54117,"url":"https://github.com/puma/puma/releases/tag/v8.0.2","title":"Release: github.com","kind":"release","source":"ghsa","publisher":"vendor","text":null,"published_at":null,"relation":"fix","confidence":null,"note":"Release: github.com"}],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}