diff --git a/app/models/upload.rb b/app/models/upload.rb index cf95ec5aa28..d6119abc13c 100644 --- a/app/models/upload.rb +++ b/app/models/upload.rb @@ -95,6 +95,8 @@ class Upload < ActiveRecord::Base # - image_type ("avatar", "profile_background", "card_background") # - is_attachment_for_group_message (boolean) def self.create_for(user_id, file, filename, filesize, options = {}) + upload = Upload.new + DistributedMutex.synchronize("upload_#{user_id}_#{filename}") do # do some work on images if FileHelper.is_image?(filename) && is_actual_image?(file) @@ -105,13 +107,19 @@ class Upload < ActiveRecord::Base File.write(file.path, doc.to_s) file.rewind else + # ensure image isn't huge + w, h = FastImage.size(file) || [0, 0] + if w * h >= SiteSetting.max_image_megapixels * 1_000_000 + upload.errors.add(:base, I18n.t("upload.images.larger_than_x_megapixels", max_image_megapixels: SiteSetting.max_image_megapixels)) + return upload + end + # fix orientation first fix_image_orientation(file.path) if should_optimize?(file.path) end # retrieve image info - image_info = FastImage.new(file) - w, h = *(image_info.try(:size) || [0, 0]) + w, h = FastImage.size(file) || [0, 0] # default size width, height = ImageSizer.resize(w, h) @@ -214,8 +222,7 @@ class Upload < ActiveRecord::Base # don't optimize GIFs or SVGs return false if path =~ /\.(gif|svg)$/i return true if path !~ /\.png$/i - image_info = FastImage.new(path) rescue nil - w, h = *(image_info.try(:size) || [0, 0]) + w, h = FastImage.size(path) || [0, 0] # don't optimize large PNGs w > 0 && h > 0 && w * h < LARGE_PNG_SIZE end diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 808beed3794..6468a337a07 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1213,6 +1213,8 @@ en: authorized_extensions: "A list of file extensions allowed for upload (use '*' to enable all file types)" max_similar_results: "How many similar topics to show above the editor when composing a new topic. Comparison is based on title and body." + max_image_megapixels: "Maximum number of megapixels allowed for an image." + title_prettify: "Prevent common title typos and errors, including all caps, lowercase first character, multiple ! and ?, extra . at end, etc." topic_views_heat_low: "After this many views, the views field is slightly highlighted." @@ -2640,6 +2642,7 @@ en: too_large: "Sorry, the file you are trying to upload is too big (maximum size is %{max_size_kb}KB)." images: too_large: "Sorry, the image you are trying to upload is too big (maximum size is %{max_size_kb}KB), please resize it and try again." + larger_than_x_megapixels: "Sorry, the image you are trying to upload is too large (maximum dimension is %{max_image_megapixels}-megapixels), please resize it and try again." size_not_found: "Sorry, but we couldn't determine the size of the image. Maybe your image is corrupted?" avatar: diff --git a/config/site_settings.yml b/config/site_settings.yml index 4344fab89e5..ae668d64e45 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -699,6 +699,10 @@ files: max_attachment_size_kb: client: true default: 3072 + max_image_megapixels: + default: 40 + min: 5 + max: 100 authorized_extensions: client: true default: 'jpg|jpeg|png|gif' diff --git a/spec/fixtures/images/huge.jpg b/spec/fixtures/images/huge.jpg new file mode 100644 index 00000000000..25f2d5c6fb4 Binary files /dev/null and b/spec/fixtures/images/huge.jpg differ diff --git a/spec/models/upload_spec.rb b/spec/models/upload_spec.rb index 6412be89428..4cf0ad64e05 100644 --- a/spec/models/upload_spec.rb +++ b/spec/models/upload_spec.rb @@ -17,6 +17,10 @@ describe Upload do let(:image_svg) { file_from_fixtures(image_svg_filename) } let(:image_svg_filesize) { File.size(image_svg) } + let(:huge_image_filename) { "huge.jpg" } + let(:huge_image) { file_from_fixtures(huge_image_filename) } + let(:huge_image_filesize) { File.size(huge_image) } + let(:attachment_path) { __FILE__ } let(:attachment) { File.new(attachment_path) } let(:attachment_filename) { File.basename(attachment_path) } @@ -55,6 +59,12 @@ describe Upload do expect(Upload.create_for(user_id, image, image_filename, image_filesize)).to eq(upload) end + it "ensures images isn't huge before processing it" do + Upload.expects(:fix_image_orientation).never + upload = Upload.create_for(user_id, huge_image, huge_image_filename, huge_image_filesize) + expect(upload.errors.size).to be > 0 + end + it "fix image orientation" do Upload.expects(:fix_image_orientation).with(image.path) Upload.create_for(user_id, image, image_filename, image_filesize) @@ -62,7 +72,6 @@ describe Upload do it "computes width & height for images" do ImageSizer.expects(:resize) - image.expects(:rewind).times(3) Upload.create_for(user_id, image, image_filename, image_filesize) end