diff --git a/gems/carrierwave/CVE-2026-44587.yml b/gems/carrierwave/CVE-2026-44587.yml new file mode 100644 index 0000000000..d05fa7cb85 --- /dev/null +++ b/gems/carrierwave/CVE-2026-44587.yml @@ -0,0 +1,79 @@ +--- +gem: carrierwave +cve: 2026-44587 +ghsa: 7g26-2qgj-chfg +url: https://www.cve.org/CVERecord?id=CVE-2026-44587 +title: CarrierWave has a denylisted_content_type bypass via + Unescaped Regex Metacharacters +date: 2026-05-27 +description: | + ### Summary + + CarrierWave's content_type_denylist check fails to escape regex + metacharacters in string entries, causing the denylist to silently + not match the content types it is intended to block. + + **Note**: CarrierWave is aware `#content_type_denylist is deprecated + for the security reason`, but it still used by developers, and the + problem here isn't denylist allows any filetype, and thats not a + vulnerability in carrierwave, its an implementation problem in + developers using CarrierWave, the problem is its denylist entries + are interpolated directly into a regex without `Regexp.quote` or + anchoring. The denylist is still useful when developers want to + ban specific content types but allow everything else. + + ### Details + + In `lib/carrierwave/uploader/content_type_denylist.rb:57`, string + denylist entries are interpolated directly into a regex without + `Regexp.quote` or anchoring: + + ```ruby + def denylisted_content_type?(denylist, content_type) + Array(denylist).any? { |item| content_type =~ /#{item}/ } + end + + The entry "image/svg+xml" becomes the regex /image\/svg+xml/ where + + is a quantifier meaning "one or more g", not a literal +. This + regex never matches the real MIME type "image/svg+xml" which contains + a literal +. This is inconsistent with the allowlist implementation + at lib/carrierwave/uploader/content_type_allowlist.rb:53-57, which + correctly applies both Regexp.quote and a \A anchor: + + rubydef allowlisted_content_type?(allowlist, content_type) + Array(allowlist).any? do |item| + item = Regexp.quote(item) if item.class != Regexp + content_type =~ /\A#{item}/ + end + end + ``` + + Other affected MIME types include `application/xhtml+xml` and any + type containing regex metacharacters. + + Fix: Apply Regexp.quote for string entries and anchor with \A, + matching the existing allowlist implementation: + + ``` + rubydef denylisted_content_type?(denylist, content_type) + Array(denylist).any? do |item| + item = Regexp.quote(item) if item.class != Regexp + content_type =~ /\A#{item}/ + end + end + ``` + ### Impact + + Any application that uses content_type_denylist to block image/svg+xml + — the most common use case, specifically to prevent stored XSS — is + silently unprotected. An attacker can upload an SVG file containing arbitrary +cvss_v3: 4.7 +patched_versions: + - "~> 2.2.7" + - ">= 3.1.3" +related: + url: + - https://www.cve.org/CVERecord?id=CVE-2026-44587 + - https://github.com/carrierwaveuploader/carrierwave/releases + - https://github.com/carrierwaveuploader/carrierwave/security/advisories/GHSA-7g26-2qgj-chfg + - https://github.com/advisories/GHSA-7g26-2qgj-chfg