From 39b282db5c1303899b3d3381ce8a837840f983b5 Mon Sep 17 00:00:00 2001 From: Mitsuhiro Shibuya Date: Tue, 28 Nov 2023 13:42:21 +0900 Subject: [PATCH] Fix Content-Type allowlist bypass vulnerability Refs. https://github.com/carrierwaveuploader/carrierwave/security/advisories/GHSA-gxhx-g4fq-49hj --- .../uploader/content_type_whitelist.rb | 2 +- spec/uploader/content_type_whitelist_spec.rb | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/carrierwave/uploader/content_type_whitelist.rb b/lib/carrierwave/uploader/content_type_whitelist.rb index 2d723c5a0..131c6871a 100644 --- a/lib/carrierwave/uploader/content_type_whitelist.rb +++ b/lib/carrierwave/uploader/content_type_whitelist.rb @@ -51,7 +51,7 @@ def check_content_type_whitelist!(new_file) def whitelisted_content_type?(content_type) Array(content_type_allowlist).any? do |item| item = Regexp.quote(item) if item.class != Regexp - content_type =~ /#{item}/ + content_type =~ /\A#{item}/ end end diff --git a/spec/uploader/content_type_whitelist_spec.rb b/spec/uploader/content_type_whitelist_spec.rb index e5b7ebecf..428802465 100644 --- a/spec/uploader/content_type_whitelist_spec.rb +++ b/spec/uploader/content_type_whitelist_spec.rb @@ -76,6 +76,33 @@ expect { uploader.cache!(ruby_file) }.not_to raise_error end end + + context "with a crafted content type" do + before do + allow(bork_file).to receive(:content_type).and_return('text/plain; image/png') + allow(uploader).to receive(:content_type_allowlist).and_return('image/png') + end + + it "does not allow spoofing" do + expect { uploader.cache!(bork_file) }.to raise_error(CarrierWave::IntegrityError) + end + end + + context "when the allowlist contains charset" do + before do + allow(uploader).to receive(:content_type_allowlist).and_return(%r{text/plain;\s*charset=utf-8}) + end + + it "accepts the content with allowed charset" do + allow(bork_file).to receive(:content_type).and_return('text/plain; charset=utf-8') + expect { uploader.cache!(bork_file) }.not_to raise_error + end + + it "rejects the content without charset" do + allow(bork_file).to receive(:content_type).and_return('text/plain') + expect { uploader.cache!(bork_file) }.to raise_error(CarrierWave::IntegrityError) + end + end end context "when there is a whitelist" do