CVE-2022-2815: Insecure Storage of Sensitive Information (CWE-922)
value: Insecure Storage of Sensitive Information in GitHub repository publify/publify prior to 9.2.10.
CWE description: The product stores sensitive information without properly limiting read or write access by unauthorized actors.
CVSS v3 base score: 6.5
Published: 2023-01-14T14:15Z
Repository: https://github.com/publify/publify
Language: Ruby
Files changed: publify_core/Manifest.txt, publify_core/app/uploaders/resource_uploader.rb, publify_core/spec/controllers/admin/resources_controller_spec.rb
Commit date: 2022-08-15 07:19:23 +0200
Commit message: Strip EXIF data from resource uploads

Diff: diff --git a/publify_core/Manifest.txt b/publify_core/Manifest.txt
index 835f7832b9..2c077ed4ea 100644
--- a/publify_core/Manifest.txt
+++ b/publify_core/Manifest.txt
@@ -419,10 +419,11 @@ lib/publify_core/testing_support/factories.rb
 lib/publify_core/testing_support/feed_assertions.rb
 lib/publify_core/testing_support/fixtures/exploit.svg
 lib/publify_core/testing_support/fixtures/fakepng.png
 lib/publify_core/testing_support/fixtures/just_some.html
 lib/publify_core/testing_support/fixtures/otherfile.txt
+lib/publify_core/testing_support/fixtures/testfile.jpg
 lib/publify_core/testing_support/fixtures/testfile.png
 lib/publify_core/testing_support/fixtures/testfile.txt
 lib/publify_core/testing_support/upload_fixtures.rb
 lib/publify_core/version.rb
 lib/publify_guid.rb
diff --git a/publify_core/app/uploaders/resource_uploader.rb b/publify_core/app/uploaders/resource_uploader.rb
index 36d5d5a5e4..99ddfc05c5 100644
--- a/publify_core/app/uploaders/resource_uploader.rb
+++ b/publify_core/app/uploaders/resource_uploader.rb
@@ -2,11 +2,14 @@
 
 require "marcel"
 
 class ResourceUploader < CarrierWave::Uploader::Base
   include CarrierWave::MiniMagick
-  before :cache, :check_content_type!
+  before :process, :check_content_type!
+
+  process :fix_exif_rotation, if: :image?
+  process :strip, if: :image?
 
   def content_type_allowlist
     [%r{image/}, %r{audio/}, %r{video/}, "text/plain"]
   end
 
@@ -30,10 +33,26 @@ class ResourceUploader < CarrierWave::Uploader::Base
     resize_setting = model.blog.send("image_#{size}_size").to_i
 
     resize_to_fit(resize_setting, resize_setting)
   end
 
+  def strip
+    manipulate! do |img|
+      img.strip
+      img = yield(img) if block_given?
+      img
+    end
+  end
+
+  def fix_exif_rotation
+    manipulate! do |img|
+      img.auto_orient
+      img = yield(img) if block_given?
+      img
+    end
+  end
+
   def image?(new_file)
     content_type = new_file.content_type
     content_type&.include?("image")
   end
 
diff --git a/publify_core/lib/publify_core/testing_support/fixtures/testfile.jpg b/publify_core/lib/publify_core/testing_support/fixtures/testfile.jpg
new file mode 100644
index 0000000000..4fcda8103d
Binary files /dev/null and b/publify_core/lib/publify_core/testing_support/fixtures/testfile.jpg differ
diff --git a/publify_core/spec/controllers/admin/resources_controller_spec.rb b/publify_core/spec/controllers/admin/resources_controller_spec.rb
index 2b7fc066e5..9cacf7136a 100644
--- a/publify_core/spec/controllers/admin/resources_controller_spec.rb
+++ b/publify_core/spec/controllers/admin/resources_controller_spec.rb
@@ -86,10 +86,39 @@ describe Admin::ResourcesController, type: :controller do
           expect(flash[:warning]).to be_nil
         end
       end
     end
 
+    context "when uploading an image file with exif data" do
+      let(:upload) { file_upload("testfile.jpg", "image/jpeg") }
+
+      it "creates a new Resource" do
+        expect { post :upload, params: { upload: upload } }.
+          to change(Resource, :count).by(1)
+      end
+
+      it "strips EXIF data" do
+        post :upload, params: { upload: upload }
+        resource = Resource.last
+        img = MiniMagick::Image.open resource.upload.file.file
+        expect(img.exif).to be_empty
+      end
+
+      it "sets the content type correctly" do
+        post :upload, params: { upload: upload }
+        expect(Resource.last.mime).to eq "image/jpeg"
+      end
+
+      it "sets the flash to success" do
+        post :upload, params: { upload: upload }
+        aggregate_failures do
+          expect(flash[:success]).not_to be_nil
+          expect(flash[:warning]).to be_nil
+        end
+      end
+    end
+
     context "when attempting to upload a dangerous svg" do
       let(:upload) { file_upload("exploit.svg", "image/svg") }
 
       it "does not create a new image Resource" do
         expect { post :upload, params: { upload: upload } }.

Vulnerable code:   before :cache, :check_content_type!
Fixed code: lib/publify_core/testing_support/fixtures/testfile.jpg
  before :process, :check_content_type!

  process :fix_exif_rotation, if: :image?
  process :strip, if: :image?
  def strip
    manipulate! do |img|
      img.strip
      img = yield(img) if block_given?
      img
    end
  end

  def fix_exif_rotation
    manipulate! do |img|
      img.auto_orient
      img = yield(img) if block_given?
      img
    end
  end

    context "when uploading an image file with exif data" do
      let(:upload) { file_upload("testfile.jpg", "image/jpeg") }

      it "creates a new Resource" do
        expect { post :upload, params: { upload: upload } }.
          to change(Resource, :count).by(1)
      end

      it "strips EXIF data" do
        post :upload, params: { upload: upload }
        resource = Resource.last
        img = MiniMagick::Image.open resource.upload.file.file
        expect(img.exif).to be_empty
      end

      it "sets the content type correctly" do
        post :upload, params: { upload: upload }
        expect(Resource.last.mime).to eq "image/jpeg"
      end

      it "sets the flash to success" do
        post :upload, params: { upload: upload }
        aggregate_failures do
          expect(flash[:success]).not_to be_nil
          expect(flash[:warning]).to be_nil
        end
      end
    end