From 863d425c76eba12c3294227b39018f6b2dccbbf3 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_allowlist.rb | 2 +- spec/uploader/content_type_allowlist_spec.rb | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/lib/carrierwave/uploader/content_type_allowlist.rb b/lib/carrierwave/uploader/content_type_allowlist.rb index 1a1498cad..0a6d69947 100644 --- a/lib/carrierwave/uploader/content_type_allowlist.rb +++ b/lib/carrierwave/uploader/content_type_allowlist.rb @@ -53,7 +53,7 @@ def check_content_type_allowlist!(new_file) def allowlisted_content_type?(allowlist, content_type) Array(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_allowlist_spec.rb b/spec/uploader/content_type_allowlist_spec.rb index f6b9baf2b..e38def1c0 100644 --- a/spec/uploader/content_type_allowlist_spec.rb +++ b/spec/uploader/content_type_allowlist_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