{"id":"GHSA-p2pm-g2h6-7j5j","kind":"vulnerability","aliases":["GHSA-p2pm-g2h6-7j5j"],"related":[],"title":"Possible DoS vulnerability in Active Storage filename handling","description":"### Impact\n`ActiveStorage::Filename#base` and `#extension_with_delimiter`, and therefore `#extension`, pass the stored filename unmodified to `File.basename` and `File.extname`, which raise `ArgumentError` when the name contains a NUL byte. Active Storage stores the client-supplied filename verbatim, so a user who is permitted to upload an attachment could persist such a filename on databases that accept NUL bytes in string columns, such as SQLite and MySQL/MariaDB. Each subsequent request that builds a variant or preview of the blob, opens it, or renders it as an Action Text attachment then raises, possibly resulting in a persistent DoS vulnerability for the pages that display the blob until it is purged or its filename is corrected. Applications that use PostgreSQL are not affected, because PostgreSQL rejects the row.\n\n### Details\n`Filename#base` and `#extension_with_delimiter` now derive from `Filename#sanitized`, the form that `Filename#to_s` and the `Content-Disposition` header already use. For a filename that `#sanitized` changes, such as one containing a NUL byte, an invalid byte sequence, a path separator or another replaced character, the base and extension now agree with `#to_s`; the variant format inferred from such a name may change, and an existing variant may be regenerated once. Stored filenames are not modified, and no data migration is required.\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/60f22af88b71eae251f112d8c0237a08a8832692.patch\n- 8.0: https://github.com/rails/rails/commit/9b0d4bef2211a2c08cab2e0ea17fa1d0efd5d47a.patch\n\n### Credit\nThis issue was found by the Anthropic OSS Scanner (ANT-2026-UQM5Z4YL).","summary":"Active Storage raises an error when a stored attachment filename contains a NUL byte, because File.basename and File.extname reject it. A user allowed to upload a file can store such a name on SQLite or MySQL/MariaDB, and each later request that renders that blob fails, which is a persistent denial of service until the blob is purged or renamed.","severity":"medium","cvss":4.3,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"activestorage","fixed":["8.1.4.1"],"affected":">= 8.1, < 8.1.4.1","ecosystem":"rubygems"},{"name":"activestorage","fixed":["8.0.5.2"],"affected":"< 8.0.5.2","ecosystem":"rubygems"}],"ecosystems":["rubygems"],"tags":["activestorage","cwe-248","package","rails","ruby","self-hosted"],"not_affected_if":["no-user-uploads","postgresql"],"how_to_check":"- Check the Rails version in `Gemfile.lock` for the `activestorage` gem.\n- Confirm whether the app uses Active Storage with SQLite or MySQL/MariaDB; PostgreSQL is not affected.\n- Confirm whether untrusted users can upload attachments.","fix":"- activestorage `8.1.x` below `8.1.4.1`: move to `8.1.4.1`\n- activestorage `8.0.x` below `8.0.5.2`: move to `8.0.5.2`\n- A patch exists for those who cannot upgrade at once: the commit at https://github.com/rails/rails/commit/60f22af88b71eae251f112d8c0237a08a8832692.patch for 8.1 and https://github.com/rails/rails/commit/9b0d4bef2211a2c08cab2e0ea17fa1d0efd5d47a.patch for 8.0.","status":"open","published_at":"2026-11-04T03:02:07+00:00","first_seen_at":"2026-11-09T22:53:29+00:00","triaged_at":"2026-11-09T22:57:13+00:00","updated_at":"2026-11-09T22:57:13+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:57:13Z","model":"claude-haiku-5-5","output":{"fix":"- activestorage `8.1.x` below `8.1.4.1`: move to `8.1.4.1`\n- activestorage `8.0.x` below `8.0.5.2`: move to `8.0.5.2`\n- A patch exists for those who cannot upgrade at once: the commit at https://github.com/rails/rails/commit/60f22af88b71eae251f112d8c0237a08a8832692.patch for 8.1 and https://github.com/rails/rails/commit/9b0d4bef2211a2c08cab2e0ea17fa1d0efd5d47a.patch for 8.0.","reach":"internet","title":"Rails Active Storage: crafted filename crashes pages showing an attachment","impact":"low","summary":"Active Storage raises an error when a stored attachment filename contains a NUL byte, because File.basename and File.extname reject it. A user allowed to upload a file can store such a name on SQLite or MySQL/MariaDB, and each later request that renders that blob fails, which is a persistent denial of service until the blob is purged or renamed.","audience":"businesses","category":"package","nicknames":[],"who_cares":["rails","activestorage","ruby","self-hosted"],"confidence":0.8,"fix_status":"available","needs_auth":"yes","worst_case":"pages rendering an attachment fail until the blob is purged","analyst_note":"","how_to_check":"- Check the Rails version in `Gemfile.lock` for the `activestorage` gem.\n- Confirm whether the app uses Active Storage with SQLite or MySQL/MariaDB; PostgreSQL is not affected.\n- Confirm whether untrusted users can upload attachments.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["postgresql","no-user-uploads"],"who_is_affected":"Applications that use Active Storage with a database that accepts NUL bytes in string columns, such as SQLite or MySQL/MariaDB, and that let users upload attachments. Applications on PostgreSQL are not affected because the database rejects the row. Apps without user-uploaded attachments have little exposure.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"no","severity_if_unknown":"medium"},"cost_usd":0.000908,"input_tokens":5211,"output_tokens":773,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-2026':341C '-248':23B '/rails/rails/commit/60f22af88b71eae251f112d8c0237a08a8832692.patch':325C '/rails/rails/commit/9b0d4bef2211a2c08cab2e0ea17fa1d0efd5d47a.patch':329C '7j5j':13A,18A '8.0':326C '8.1':322C 'accept':146C 'action':176C 'activ':5A,30B,119C 'activestorag':21B,90C 'activestorage':19A,20A 'affect':210C 'agre':271C 'allow':52B 'alreadi':240C 'anoth':263C 'ant':340C 'anthrop':337C 'applic':204C 'argumenterror':111C 'attach':38B,137C,178C 'avail':309C 'base':92C,218C,267C 'blob':73B,84B,168C,194C 'build':161C 'byte':43B,118C,148C,254C,257C 'cannot':318C 'chang':247C,284C 'charact':265C 'client':124C 'client-suppli':123C 'column':151C 'contain':40B,115C,251C 'content':237C 'content-disposit':236C 'correct':203C 'could':138C 'credit':330C 'cwe':22B 'data':300C 'databas':144C 'delimit':96C,222C 'denial':79B 'deriv':224C 'detail':216C 'display':192C 'disposit':238C 'dos':2A,186C 'error':34B 'exist':287C 'extens':94C,99C,220C,269C 'fail':74B 'file':56B 'file.basename':45B,106C 'file.extname':47B,108C 'filenam':7A,39B,91C,103C,126C,142C,201C,217C,226C,231C,244C,294C 'fix':306C 'form':229C 'format':277C 'found':334C 'g2h6':12A,17A 'ghsa':10A,15A 'ghsa-p2pm-g2h6-7j5j':9A,14A 'github.com':324C,328C 'github.com/rails/rails/commit/60f22af88b71eae251f112d8c0237a08a8832692.patch':323C 'github.com/rails/rails/commit/9b0d4bef2211a2c08cab2e0ea17fa1d0efd5d47a.patch':327C 'handl':8A 'header':239C 'host':29B 'impact':89C 'infer':278C 'invalid':256C 'issu':332C 'later':68B 'locat':313C 'may':283C,289C 'migrat':301C 'modifi':297C 'mysql/mariadb':65B,156C 'name':61B,114C,282C 'normal':312C 'nul':42B,117C,147C,253C 'one':250C 'open':169C 'oss':338C 'p2pm':11A,16A 'packag':24B 'page':190C 'pass':100C 'patch':314C 'path':260C 'permit':133C 'persist':78B,139C,185C 'possibl':1A,181C 'postgresql':207C,212C 'preview':165C 'purg':86B,198C 'rail':25B 'rais':32B,110C,180C 'regener':291C 'reject':48B,213C 'releas':304C,307C 'renam':88B 'render':71B,172C 'replac':264C 'request':69B,159C 'requir':303C 'result':182C 'row':215C 'rubi':26B 'sanit':227C,246C 'scanner':339C 'self':28B 'self-host':27B 'separ':261C 'sequenc':258C 'servic':81B 'sqlite':63B,154C 'storag':6A,31B,120C 'store':37B,58B,102C,121C,293C 'string':150C 'subsequ':158C 'suppli':125C 'text':177C 'therefor':98C 'unmodifi':104C 'upgrad':319C 'upload':54B,135C 'uqm5z4yl':342C 'use':206C,241C 'user':51B,130C 'variant':163C,276C,288C 'verbatim':127C 'vulner':3A,187C","nicknames":[],"mitigations":[],"who_affected":"Applications that use Active Storage with a database that accepts NUL bytes in string columns, such as SQLite or MySQL/MariaDB, and that let users upload attachments. Applications on PostgreSQL are not affected because the database rejects the row. Apps without user-uploaded attachments have little exposure.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"businesses","plain_action":null,"pkeys":["activestorage"],"ptokens":["activestorage"],"labels":["activestorage","cwe248","medium","package","rails","ruby","rubygems","selfhosted"],"campaign_type":null,"search_a":"'7j5j':13A,18A 'activ':5A 'activestorage':19A,20A 'dos':2A 'filenam':7A 'g2h6':12A,17A 'ghsa':10A,15A 'ghsa-p2pm-g2h6-7j5j':9A,14A 'handl':8A 'p2pm':11A,16A 'possibl':1A 'storag':6A 'vulner':3A","search_b":"'-248':23B 'activ':30B 'activestorag':21B 'allow':52B 'attach':38B 'blob':73B,84B 'byte':43B 'contain':40B 'cwe':22B 'denial':79B 'error':34B 'fail':74B 'file':56B 'file.basename':45B 'file.extname':47B 'filenam':39B 'host':29B 'later':68B 'mysql/mariadb':65B 'name':61B 'nul':42B 'packag':24B 'persist':78B 'purg':86B 'rail':25B 'rais':32B 'reject':48B 'renam':88B 'render':71B 'request':69B 'rubi':26B 'self':28B 'self-host':27B 'servic':81B 'sqlite':63B 'storag':31B 'store':37B,58B 'upload':54B 'user':51B","search_c":"'-2026':341C '/rails/rails/commit/60f22af88b71eae251f112d8c0237a08a8832692.patch':325C '/rails/rails/commit/9b0d4bef2211a2c08cab2e0ea17fa1d0efd5d47a.patch':329C '8.0':326C '8.1':322C 'accept':146C 'action':176C 'activ':119C 'activestorag':90C 'affect':210C 'agre':271C 'alreadi':240C 'anoth':263C 'ant':340C 'anthrop':337C 'applic':204C 'argumenterror':111C 'attach':137C,178C 'avail':309C 'base':92C,218C,267C 'blob':168C,194C 'build':161C 'byte':118C,148C,254C,257C 'cannot':318C 'chang':247C,284C 'charact':265C 'client':124C 'client-suppli':123C 'column':151C 'contain':115C,251C 'content':237C 'content-disposit':236C 'correct':203C 'could':138C 'credit':330C 'data':300C 'databas':144C 'delimit':96C,222C 'deriv':224C 'detail':216C 'display':192C 'disposit':238C 'dos':186C 'exist':287C 'extens':94C,99C,220C,269C 'file.basename':106C 'file.extname':108C 'filenam':91C,103C,126C,142C,201C,217C,226C,231C,244C,294C 'fix':306C 'form':229C 'format':277C 'found':334C 'github.com':324C,328C 'github.com/rails/rails/commit/60f22af88b71eae251f112d8c0237a08a8832692.patch':323C 'github.com/rails/rails/commit/9b0d4bef2211a2c08cab2e0ea17fa1d0efd5d47a.patch':327C 'header':239C 'impact':89C 'infer':278C 'invalid':256C 'issu':332C 'locat':313C 'may':283C,289C 'migrat':301C 'modifi':297C 'mysql/mariadb':156C 'name':114C,282C 'normal':312C 'nul':117C,147C,253C 'one':250C 'open':169C 'oss':338C 'page':190C 'pass':100C 'patch':314C 'path':260C 'permit':133C 'persist':139C,185C 'possibl':181C 'postgresql':207C,212C 'preview':165C 'purg':198C 'rais':110C,180C 'regener':291C 'reject':213C 'releas':304C,307C 'render':172C 'replac':264C 'request':159C 'requir':303C 'result':182C 'row':215C 'sanit':227C,246C 'scanner':339C 'separ':261C 'sequenc':258C 'sqlite':154C 'storag':120C 'store':102C,121C,293C 'string':150C 'subsequ':158C 'suppli':125C 'text':177C 'therefor':98C 'unmodifi':104C 'upgrad':319C 'upload':135C 'uqm5z4yl':342C 'use':206C,241C 'user':130C 'variant':163C,276C,288C 'verbatim':127C 'vulner':187C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-04T03:02:07+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-p2pm-g2h6-7j5j","url":"https://github.com/rails/rails/security/advisories/GHSA-p2pm-g2h6-7j5j","severity":"medium","published_at":"2026-11-04T03:02:07+00:00","modified_at":"2026-11-04T03:02:07+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":110671,"url":"https://github.com/rails/rails/security/advisories/GHSA-p2pm-g2h6-7j5j","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"}