{"id":"GHSA-8474-xrp6-wh69","kind":"vulnerability","aliases":["GHSA-8474-xrp6-wh69"],"related":[],"title":"Possible DoS vulnerability in Active Support safe_constantize","description":"### Impact\nWhen constant lookup raises `LoadError`, `ActiveSupport::Inflector#safe_constantize` (and `String#safe_constantize`) builds a regular expression from the requested constant name without escaping it, so a caller that passes untrusted input lets the attacker choose the source of a pattern that the application compiles. Crafted patterns can trigger defects in Ruby's regular expression compiler, possibly leading to a crash of the server process, and repeated requests could keep the application unavailable.\n\nAction Cable passes the `channel` value of each `subscribe` command to `safe_constantize` before any channel lookup, so in the default configuration an unauthenticated client of the Action Cable endpoint can reach this code.\n\n### Details\nThe check that decides whether `safe_constantize` returns `nil` or re-raises the `LoadError` now matches the first segment of the constant name against the error message as a literal string, and no longer compiles a regular expression. It returns the same result as before for constant names that contain no regular expression syntax.\n\n### Affected applications\nAn application is affected if it meets all of these requirements:\n- Mounts Action Cable, which a new Rails application does by default at `/cable` (`config.action_cable.mount_path`), or otherwise passes untrusted input to `safe_constantize`.\n- Accepts Action Cable connections from untrusted clients. `ApplicationCable::Connection#connect` authenticates nobody unless the application adds a check.\n\n### Workarounds\nApplications that do not use Action Cable can set `config.action_cable.mount_path = nil` to remove the endpoint. Applications that use Action Cable can call `reject_unauthorized_connection` in `ApplicationCable::Connection#connect` for unauthenticated clients, which limits exposure to authenticated users but does not remove it.\n\n### Releases\nThe fixed releases are available at the normal locations.\n\nPatches for those who cannot upgrade at once:\n- 8.1: https://github.com/rails/rails/commit/95aa9e416016524956d4904fed96eaed079580bb.patch\n- 8.0: https://github.com/rails/rails/commit/210ac22d446c8e2625b091e79256806b836eee20.patch\n\n### Versions affected\n\n- activesupport < 8.0.5.2\n- activesupport >= 8.1, < 8.1.4.1\n\n### Credit\nThis issue was responsibly reported by Felix Tscheulin ([flixt](https://hackerone.com/flixt)) through HackerOne researcher [scheja](https://hackerone.com/scheja), and independently by Bucky Wolfe ([Igneous](https://github.com/Igneous)), who provided a proof of concept that crashes the server. It was also found by the Anthropic OSS Scanner (ANT-2026-ZUN5KU3Q).","summary":"ActiveSupport's safe_constantize builds a regular expression from an unescaped constant name, so untrusted input can supply a crafted pattern. Crafted patterns can crash the server process, and repeated requests can keep the application unavailable.","severity":"high","cvss":7.5,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"activesupport","fixed":["8.1.4.1"],"affected":">= 8.1, < 8.1.4.1","ecosystem":"rubygems"},{"name":"activesupport","fixed":["8.0.5.2"],"affected":"< 8.0.5.2","ecosystem":"rubygems"}],"ecosystems":["rubygems"],"tags":["action-cable","activesupport","cwe-74","package","rails","ruby"],"not_affected_if":["auth-required","no-action-cable","untrusted-input-never-reaches-safe-constantize"],"how_to_check":"- Check the installed version with `bundle exec gem list activesupport` and compare it to the fixed versions.\n- Look for Action Cable mounted at `/cable` (`config.action_cable.mount_path`) and for `subscribe` commands reaching `safe_constantize`.\n- Check whether `ApplicationCable::Connection#connect` rejects unauthenticated clients.","fix":"- activesupport `8.1.4.1` for the 8.1 line\n- activesupport `8.0.5.2` for the 8.0 line","status":"open","published_at":"2026-11-04T03:02:04+00:00","first_seen_at":"2026-11-09T22:53:29+00:00","triaged_at":"2026-11-09T22:54:20+00:00","updated_at":"2026-11-09T22:54:20+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:54:20Z","model":"claude-haiku-5-5","output":{"fix":"- activesupport `8.1.4.1` for the 8.1 line\n- activesupport `8.0.5.2` for the 8.0 line","reach":"internet","title":"Rails ActiveSupport: safe_constantize regex flaw lets a client crash the server","impact":"medium","summary":"ActiveSupport's safe_constantize builds a regular expression from an unescaped constant name, so untrusted input can supply a crafted pattern. Crafted patterns can crash the server process, and repeated requests can keep the application unavailable.","audience":"developers","category":"package","nicknames":[],"who_cares":["rails","activesupport","action-cable","ruby"],"confidence":0.8,"fix_status":"available","needs_auth":"no","worst_case":"unauthenticated clients crash the server process repeatedly","analyst_note":"","how_to_check":"- Check the installed version with `bundle exec gem list activesupport` and compare it to the fixed versions.\n- Look for Action Cable mounted at `/cable` (`config.action_cable.mount_path`) and for `subscribe` commands reaching `safe_constantize`.\n- Check whether `ApplicationCable::Connection#connect` rejects unauthenticated clients.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["no-action-cable","auth-required","untrusted-input-never-reaches-safe-constantize"],"who_is_affected":"Applications that mount Action Cable, which a new Rails application does by default at /cable, or that pass untrusted input to safe_constantize are exposed. Action Cable connections from untrusted clients are the main path, since the default connect method authenticates nobody unless the application adds a check. Applications that do not use Action Cable and do not pass untrusted input to safe_constantize can skip it.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"high"},"cost_usd":0.00088,"input_tokens":5481,"output_tokens":664,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-2026':411C '-74':26B '-8474':10A,15A '/cable':255C '/flixt))':374C '/igneous)),':390C '/rails/rails/commit/210ac22d446c8e2625b091e79256806b836eee20.patch':354C '/rails/rails/commit/95aa9e416016524956d4904fed96eaed079580bb.patch':350C '/scheja),':381C '8.0':351C '8.0.5.2':358C '8.1':347C,360C '8.1.4.1':361C 'accept':266C 'action':22B,140C,167C,244C,267C,290C,304C 'action-c':21B 'activ':5A 'activesupport':19A,20A,24B,30B,72C,357C,359C 'add':281C 'affect':230C,235C,356C 'also':403C 'ant':410C 'anthrop':407C 'applic':64B,110C,138C,231C,233C,250C,280C,285C,301C 'applicationc':273C,312C 'attack':101C 'authent':276C,322C 'avail':334C 'bucki':385C 'build':34B,80C 'cabl':23B,141C,168C,245C,268C,291C,305C 'call':307C 'caller':94C 'cannot':343C 'channel':144C,155C 'check':176C,283C 'choos':102C 'client':164C,272C,317C 'code':173C 'command':149C 'compil':111C,122C,210C 'concept':396C 'config.action_cable.mount':256C,294C 'configur':161C 'connect':269C,274C,275C,310C,313C,314C 'constant':8A,33B,41B,68C,75C,79C,87C,152C,181C,197C,222C,265C 'contain':225C 'could':135C 'craft':49B,51B,112C 'crash':54B,127C,398C 'credit':362C 'cwe':25B 'decid':178C 'default':160C,253C 'defect':116C 'detail':174C 'dos':2A 'endpoint':169C,300C 'error':201C 'escap':90C 'exposur':320C 'express':37B,83C,121C,213C,228C 'felix':369C 'first':193C 'fix':331C 'flixt':371C 'found':404C 'ghsa':9A,14A 'github.com':349C,353C,389C 'github.com/igneous)),':388C 'github.com/rails/rails/commit/210ac22d446c8e2625b091e79256806b836eee20.patch':352C 'github.com/rails/rails/commit/95aa9e416016524956d4904fed96eaed079580bb.patch':348C 'hackeron':376C 'hackerone.com':373C,380C 'hackerone.com/flixt))':372C 'hackerone.com/scheja),':379C 'igneous':387C 'impact':66C 'independ':383C 'inflector':73C 'input':45B,98C,262C 'issu':364C 'keep':62B,136C 'lead':124C 'let':99C 'limit':319C 'liter':205C 'loaderror':71C,189C 'locat':338C 'longer':209C 'lookup':69C,156C 'match':191C 'meet':238C 'messag':202C 'mount':243C 'name':42B,88C,198C,223C 'new':248C 'nil':183C,296C 'nobodi':277C 'normal':337C 'oss':408C 'otherwis':259C 'packag':27B 'pass':96C,142C,260C 'patch':339C 'path':257C,295C 'pattern':50B,52B,107C,113C 'possibl':1A,123C 'process':57B,131C 'proof':394C 'provid':392C 'rail':28B,249C 'rais':70C,187C 're':186C 're-rais':185C 'reach':171C 'regular':36B,82C,120C,212C,227C 'reject':308C 'releas':329C,332C 'remov':298C,327C 'repeat':59B,133C 'report':367C 'request':60B,86C,134C 'requir':242C 'research':377C 'respons':366C 'result':218C 'return':182C,215C 'rubi':29B,118C 'safe':7A,32B,74C,78C,151C,180C,264C 'scanner':409C 'scheja':378C 'segment':194C 'server':56B,130C,400C 'set':293C 'sourc':104C 'string':77C,206C 'subscrib':148C 'suppli':47B 'support':6A 'syntax':229C 'trigger':115C 'tscheulin':370C 'unauthent':163C,316C 'unauthor':309C 'unavail':65B,139C 'unescap':40B 'unless':278C 'untrust':44B,97C,261C,271C 'upgrad':344C 'use':289C,303C 'user':323C 'valu':145C 'version':355C 'vulner':3A 'wh69':13A,18A 'whether':179C 'without':89C 'wolf':386C 'workaround':284C 'xrp6':12A,17A 'xrp6-wh69':11A,16A 'zun5ku3q':412C","nicknames":[],"mitigations":[],"who_affected":"Applications that mount Action Cable, which a new Rails application does by default at /cable, or that pass untrusted input to safe_constantize are exposed. Action Cable connections from untrusted clients are the main path, since the default connect method authenticates nobody unless the application adds a check. Applications that do not use Action Cable and do not pass untrusted input to safe_constantize can skip it.","tier":"B","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["activesupport"],"ptokens":["activesupport"],"labels":["actioncable","activesupport","cwe74","high","package","rails","ruby","rubygems"],"campaign_type":null,"search_a":"'-8474':10A,15A 'activ':5A 'activesupport':19A,20A 'constant':8A 'dos':2A 'ghsa':9A,14A 'possibl':1A 'safe':7A 'support':6A 'vulner':3A 'wh69':13A,18A 'xrp6':12A,17A 'xrp6-wh69':11A,16A","search_b":"'-74':26B 'action':22B 'action-c':21B 'activesupport':24B,30B 'applic':64B 'build':34B 'cabl':23B 'constant':33B,41B 'craft':49B,51B 'crash':54B 'cwe':25B 'express':37B 'input':45B 'keep':62B 'name':42B 'packag':27B 'pattern':50B,52B 'process':57B 'rail':28B 'regular':36B 'repeat':59B 'request':60B 'rubi':29B 'safe':32B 'server':56B 'suppli':47B 'unavail':65B 'unescap':40B 'untrust':44B","search_c":"'-2026':411C '/cable':255C '/flixt))':374C '/igneous)),':390C '/rails/rails/commit/210ac22d446c8e2625b091e79256806b836eee20.patch':354C '/rails/rails/commit/95aa9e416016524956d4904fed96eaed079580bb.patch':350C '/scheja),':381C '8.0':351C '8.0.5.2':358C '8.1':347C,360C '8.1.4.1':361C 'accept':266C 'action':140C,167C,244C,267C,290C,304C 'activesupport':72C,357C,359C 'add':281C 'affect':230C,235C,356C 'also':403C 'ant':410C 'anthrop':407C 'applic':110C,138C,231C,233C,250C,280C,285C,301C 'applicationc':273C,312C 'attack':101C 'authent':276C,322C 'avail':334C 'bucki':385C 'build':80C 'cabl':141C,168C,245C,268C,291C,305C 'call':307C 'caller':94C 'cannot':343C 'channel':144C,155C 'check':176C,283C 'choos':102C 'client':164C,272C,317C 'code':173C 'command':149C 'compil':111C,122C,210C 'concept':396C 'config.action_cable.mount':256C,294C 'configur':161C 'connect':269C,274C,275C,310C,313C,314C 'constant':68C,75C,79C,87C,152C,181C,197C,222C,265C 'contain':225C 'could':135C 'craft':112C 'crash':127C,398C 'credit':362C 'decid':178C 'default':160C,253C 'defect':116C 'detail':174C 'endpoint':169C,300C 'error':201C 'escap':90C 'exposur':320C 'express':83C,121C,213C,228C 'felix':369C 'first':193C 'fix':331C 'flixt':371C 'found':404C 'github.com':349C,353C,389C 'github.com/igneous)),':388C 'github.com/rails/rails/commit/210ac22d446c8e2625b091e79256806b836eee20.patch':352C 'github.com/rails/rails/commit/95aa9e416016524956d4904fed96eaed079580bb.patch':348C 'hackeron':376C 'hackerone.com':373C,380C 'hackerone.com/flixt))':372C 'hackerone.com/scheja),':379C 'igneous':387C 'impact':66C 'independ':383C 'inflector':73C 'input':98C,262C 'issu':364C 'keep':136C 'lead':124C 'let':99C 'limit':319C 'liter':205C 'loaderror':71C,189C 'locat':338C 'longer':209C 'lookup':69C,156C 'match':191C 'meet':238C 'messag':202C 'mount':243C 'name':88C,198C,223C 'new':248C 'nil':183C,296C 'nobodi':277C 'normal':337C 'oss':408C 'otherwis':259C 'pass':96C,142C,260C 'patch':339C 'path':257C,295C 'pattern':107C,113C 'possibl':123C 'process':131C 'proof':394C 'provid':392C 'rail':249C 'rais':70C,187C 're':186C 're-rais':185C 'reach':171C 'regular':82C,120C,212C,227C 'reject':308C 'releas':329C,332C 'remov':298C,327C 'repeat':133C 'report':367C 'request':86C,134C 'requir':242C 'research':377C 'respons':366C 'result':218C 'return':182C,215C 'rubi':118C 'safe':74C,78C,151C,180C,264C 'scanner':409C 'scheja':378C 'segment':194C 'server':130C,400C 'set':293C 'sourc':104C 'string':77C,206C 'subscrib':148C 'syntax':229C 'trigger':115C 'tscheulin':370C 'unauthent':163C,316C 'unauthor':309C 'unavail':139C 'unless':278C 'untrust':97C,261C,271C 'upgrad':344C 'use':289C,303C 'user':323C 'valu':145C 'version':355C 'whether':179C 'without':89C 'wolf':386C 'workaround':284C 'zun5ku3q':412C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-04T03:02:04+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-8474-xrp6-wh69","url":"https://github.com/rails/rails/security/advisories/GHSA-8474-xrp6-wh69","severity":"high","published_at":"2026-11-04T03:02:04+00:00","modified_at":"2026-11-04T03:02:04+00:00","first_seen_at":"2026-11-09T22:53:29+00:00","fetched_at":"2026-11-09T22:53:29+00:00","note":null}],"documents":[{"id":110674,"url":"https://github.com/rails/rails/security/advisories/GHSA-8474-xrp6-wh69","title":"Maintainer's advisory: github.com","kind":"vendor","source":"ghsa-repo","publisher":"vendor","text":null,"published_at":null,"relation":"advisory","confidence":null,"note":"Maintainer's advisory: github.com"}],"disclosures":[],"exploit_sources":[],"notice":"Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai"}