{"id":"GHSA-hfr4-8rjc-97m2","kind":"vulnerability","aliases":["GHSA-hfr4-8rjc-97m2"],"related":[],"title":"Possible circumvention of file size limits in Active Storage direct uploads to Google Cloud Storage","description":"### Impact\n`ActiveStorage::Service::GCSService#url_for_direct_upload` does not bind the declared content length into the signed direct-upload URL, and `#headers_for_direct_upload` returns no length constraint, so the URL authorizes an upload body of any size. A direct-upload client could store a file larger than the `byte_size` it declared, and the resulting blob keeps the declared size, possibly bypassing application file size validations and quotas. This is the weakness that CVE-2020-8162 fixed in the S3 service.\n\nApplications that store files in Google Cloud Storage and accept direct uploads are affected.\n\n### Details\nThe fixed releases sign an `x-goog-content-length-range` header that matches the blob's declared `byte_size` and return it from `headers_for_direct_upload`, so Cloud Storage rejects an upload body of any other size. Browsers send this header with the direct upload, so a bucket's CORS configuration must list `x-goog-content-length-range` in `responseHeader`, or browser direct uploads fail after the upgrade. The Active Storage CHANGELOG and the CORS example in the Active Storage guide show the change.\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/e19916cea31cbfb0030b165ae853b741f8dd57a7.patch\n- 8.0: https://github.com/rails/rails/commit/7fe85a72a789eae5ee070ce3ee1dad10a8a5f70c.patch\n\n### Credit\nThis issue was responsibly reported by HackerOne researcher [f4cts](https://hackerone.com/f4cts).","summary":"Active Storage's Google Cloud Storage direct-upload URLs do not bind the declared file size, so a client can upload a larger file than it declared. The stored blob keeps the declared size, which can bypass application size validations and quotas.","severity":"medium","cvss":5.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-602","google-cloud-storage","package","rails","ruby"],"not_affected_if":["no-direct-uploads","no-gcs-service","static-export"],"how_to_check":"- Look for `activestorage` in `Gemfile.lock` and compare its version with the fixed ranges.\n- Check whether the app uses `ActiveStorage` with the `google` service in `config/storage.yml` and accepts direct uploads.\n- Check whether the bucket CORS configuration lists `x-goog-content-length-range` in `responseHeader`.","fix":"- `activestorage` `8.1.4.1` for the 8.1 line\n- `activestorage` `8.0.5.2` for the 8.0 line\n- Patches for teams that cannot upgrade yet are linked in the advisory (commits for 8.1 and 8.0).\n- After upgrading, add `x-goog-content-length-range` to the bucket CORS `responseHeader` list, or browser direct uploads fail.","status":"open","published_at":"2026-11-04T03:02:06+00:00","first_seen_at":"2026-11-09T22:53:29+00:00","triaged_at":"2026-11-09T22:56:27+00:00","updated_at":"2026-11-09T22:56:27+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-11-09T22:56:27Z","model":"claude-haiku-5-5","output":{"fix":"- `activestorage` `8.1.4.1` for the 8.1 line\n- `activestorage` `8.0.5.2` for the 8.0 line\n- Patches for teams that cannot upgrade yet are linked in the advisory (commits for 8.1 and 8.0).\n- After upgrading, add `x-goog-content-length-range` to the bucket CORS `responseHeader` list, or browser direct uploads fail.","reach":"internet","title":"Rails Active Storage: direct uploads to Google Cloud Storage exceed declared size","impact":"low","summary":"Active Storage's Google Cloud Storage direct-upload URLs do not bind the declared file size, so a client can upload a larger file than it declared. The stored blob keeps the declared size, which can bypass application size validations and quotas.","audience":"developers","category":"package","nicknames":[],"who_cares":["rails","activestorage","google-cloud-storage","ruby"],"confidence":0.8,"fix_status":"available","needs_auth":"unknown","worst_case":"Clients store files larger than declared, bypassing size limits and quotas.","analyst_note":"","how_to_check":"- Look for `activestorage` in `Gemfile.lock` and compare its version with the fixed ranges.\n- Check whether the app uses `ActiveStorage` with the `google` service in `config/storage.yml` and accepts direct uploads.\n- Check whether the bucket CORS configuration lists `x-goog-content-length-range` in `responseHeader`.","plain_action":"","product_name":"","needs_analyst":false,"not_affected_if":["no-gcs-service","no-direct-uploads","static-export"],"who_is_affected":"Applications that use Active Storage with Google Cloud Storage and accept direct browser or client uploads are exposed. Apps on S3, Azure or local disk services, or apps that do not use direct uploads, are not affected by this flaw.","user_interaction":"no","product_ecosystem":"","needs_special_setup":"yes","severity_if_unknown":"medium"},"cost_usd":0.000859,"input_tokens":5141,"output_tokens":689,"severity_before":"medium"},"analyst_status":null,"analyst_note":null,"search":"'-2020':160C '-602':30B '-8162':161C '/f4cts).':307C '/rails/rails/commit/7fe85a72a789eae5ee070ce3ee1dad10a8a5f70c.patch':294C '/rails/rails/commit/e19916cea31cbfb0030b165ae853b741f8dd57a7.patch':290C '8.0':291C '8.1':287C '8rjc':19A,24A '97m2':20A,25A 'accept':176C 'activ':8A,38B,254C,263C 'activestorag':28B,82C 'activestorage':26A,27A 'affect':180C 'applic':76B,148C,167C 'author':115C 'avail':274C 'bind':50B,91C 'blob':68B,141C,197C 'bodi':118C,216C 'browser':221C,246C 'bucket':231C 'bypass':75B,147C 'byte':134C,200C 'cannot':283C 'chang':268C 'changelog':256C 'circumvent':2A 'client':57B,126C 'cloud':14A,33B,42B,173C,211C 'configur':234C 'constraint':111C 'content':94C,190C,240C 'cor':233C,259C 'could':127C 'credit':295C 'cve':159C 'cwe':29B 'declar':52B,65B,71B,93C,137C,144C,199C 'detail':181C 'direct':10A,45B,87C,100C,106C,124C,177C,208C,227C,247C 'direct-upload':44B,99C,123C 'exampl':260C 'f4cts':304C 'fail':249C 'file':4A,53B,62B,130C,149C,170C 'fix':162C,183C,271C 'gcsservic':84C 'ghsa':17A,22A 'ghsa-hfr4-8rjc-97m2':16A,21A 'github.com':289C,293C 'github.com/rails/rails/commit/7fe85a72a789eae5ee070ce3ee1dad10a8a5f70c.patch':292C 'github.com/rails/rails/commit/e19916cea31cbfb0030b165ae853b741f8dd57a7.patch':288C 'goog':189C,239C 'googl':13A,32B,41B,172C 'google-cloud-storag':31B 'guid':265C 'hackeron':302C 'hackerone.com':306C 'hackerone.com/f4cts).':305C 'header':104C,193C,206C,224C 'hfr4':18A,23A 'impact':81C 'issu':297C 'keep':69B,142C 'larger':61B,131C 'length':95C,110C,191C,241C 'limit':6A 'list':236C 'locat':278C 'match':195C 'must':235C 'normal':277C 'packag':35B 'patch':279C 'possibl':1A,146C 'quota':80B,153C 'rail':36B 'rang':192C,242C 'reject':213C 'releas':184C,269C,272C 'report':300C 'research':303C 'respons':299C 'responsehead':244C 'result':140C 'return':108C,203C 'rubi':37B 's3':165C 'send':222C 'servic':83C,166C 'show':266C 'sign':98C,185C 'size':5A,54B,72B,77B,121C,135C,145C,150C,201C,220C 'storag':9A,15A,34B,39B,43B,174C,212C,255C,264C 'store':67B,128C,169C 'upgrad':252C,284C 'upload':11A,46B,59B,88C,101C,107C,117C,125C,178C,209C,215C,228C,248C 'url':47B,85C,102C,114C 'valid':78B,151C 'weak':157C 'x':188C,238C 'x-goog-content-length-rang':187C,237C","nicknames":[],"mitigations":[],"who_affected":"Applications that use Active Storage with Google Cloud Storage and accept direct browser or client uploads are exposed. Apps on S3, Azure or local disk services, or apps that do not use direct uploads, are not affected by this flaw.","tier":"C","verified":{},"analyst_revisit_at":null,"fix_status":"available","audience":"developers","plain_action":null,"pkeys":["activestorage"],"ptokens":["activestorage"],"labels":["activestorage","cwe602","googlecloudstorage","medium","package","rails","ruby","rubygems"],"campaign_type":null,"search_a":"'8rjc':19A,24A '97m2':20A,25A 'activ':8A 'activestorage':26A,27A 'circumvent':2A 'cloud':14A 'direct':10A 'file':4A 'ghsa':17A,22A 'ghsa-hfr4-8rjc-97m2':16A,21A 'googl':13A 'hfr4':18A,23A 'limit':6A 'possibl':1A 'size':5A 'storag':9A,15A 'upload':11A","search_b":"'-602':30B 'activ':38B 'activestorag':28B 'applic':76B 'bind':50B 'blob':68B 'bypass':75B 'client':57B 'cloud':33B,42B 'cwe':29B 'declar':52B,65B,71B 'direct':45B 'direct-upload':44B 'file':53B,62B 'googl':32B,41B 'google-cloud-storag':31B 'keep':69B 'larger':61B 'packag':35B 'quota':80B 'rail':36B 'rubi':37B 'size':54B,72B,77B 'storag':34B,39B,43B 'store':67B 'upload':46B,59B 'url':47B 'valid':78B","search_c":"'-2020':160C '-8162':161C '/f4cts).':307C '/rails/rails/commit/7fe85a72a789eae5ee070ce3ee1dad10a8a5f70c.patch':294C '/rails/rails/commit/e19916cea31cbfb0030b165ae853b741f8dd57a7.patch':290C '8.0':291C '8.1':287C 'accept':176C 'activ':254C,263C 'activestorag':82C 'affect':180C 'applic':148C,167C 'author':115C 'avail':274C 'bind':91C 'blob':141C,197C 'bodi':118C,216C 'browser':221C,246C 'bucket':231C 'bypass':147C 'byte':134C,200C 'cannot':283C 'chang':268C 'changelog':256C 'client':126C 'cloud':173C,211C 'configur':234C 'constraint':111C 'content':94C,190C,240C 'cor':233C,259C 'could':127C 'credit':295C 'cve':159C 'declar':93C,137C,144C,199C 'detail':181C 'direct':87C,100C,106C,124C,177C,208C,227C,247C 'direct-upload':99C,123C 'exampl':260C 'f4cts':304C 'fail':249C 'file':130C,149C,170C 'fix':162C,183C,271C 'gcsservic':84C 'github.com':289C,293C 'github.com/rails/rails/commit/7fe85a72a789eae5ee070ce3ee1dad10a8a5f70c.patch':292C 'github.com/rails/rails/commit/e19916cea31cbfb0030b165ae853b741f8dd57a7.patch':288C 'goog':189C,239C 'googl':172C 'guid':265C 'hackeron':302C 'hackerone.com':306C 'hackerone.com/f4cts).':305C 'header':104C,193C,206C,224C 'impact':81C 'issu':297C 'keep':142C 'larger':131C 'length':95C,110C,191C,241C 'list':236C 'locat':278C 'match':195C 'must':235C 'normal':277C 'patch':279C 'possibl':146C 'quota':153C 'rang':192C,242C 'reject':213C 'releas':184C,269C,272C 'report':300C 'research':303C 'respons':299C 'responsehead':244C 'result':140C 'return':108C,203C 's3':165C 'send':222C 'servic':83C,166C 'show':266C 'sign':98C,185C 'size':121C,135C,145C,150C,201C,220C 'storag':174C,212C,255C,264C 'store':128C,169C 'upgrad':252C,284C 'upload':88C,101C,107C,117C,125C,178C,209C,215C,228C,248C 'url':85C,102C,114C 'valid':151C 'weak':157C 'x':188C,238C 'x-goog-content-length-rang':187C,237C","cause_entry":null,"cause_path":null,"disclosures_checked_at":null,"first_public_at":"2026-11-04T03:02:06+00:00","kev":null,"cvss_vector":null,"cvss_by":null,"poc_count":0,"sources":[{"source":"ghsa-repo","external_id":"GHSA-hfr4-8rjc-97m2","url":"https://github.com/rails/rails/security/advisories/GHSA-hfr4-8rjc-97m2","severity":"medium","published_at":"2026-11-04T03:02:06+00:00","modified_at":"2026-11-04T03:02:06+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":110672,"url":"https://github.com/rails/rails/security/advisories/GHSA-hfr4-8rjc-97m2","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"}