SECURITY: force IM decoder based on file extension
This commit is contained in:
parent
0e84024958
commit
4bf3bf6786
|
@ -118,6 +118,14 @@ class OptimizedImage < ActiveRecord::Base
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
IM_DECODERS ||= /\A(jpe?g|png|tiff?|bmp|ico)\z/i
|
||||||
|
|
||||||
|
def self.prepend_decoder!(path)
|
||||||
|
extension = File.extname(path)[1..-1]
|
||||||
|
raise Discourse::InvalidAccess unless extension[IM_DECODERS]
|
||||||
|
"#{extension}:#{path}"
|
||||||
|
end
|
||||||
|
|
||||||
def self.thumbnail_or_resize
|
def self.thumbnail_or_resize
|
||||||
SiteSetting.strip_image_metadata ? "thumbnail" : "resize"
|
SiteSetting.strip_image_metadata ? "thumbnail" : "resize"
|
||||||
end
|
end
|
||||||
|
@ -125,6 +133,9 @@ class OptimizedImage < ActiveRecord::Base
|
||||||
def self.resize_instructions(from, to, dimensions, opts = {})
|
def self.resize_instructions(from, to, dimensions, opts = {})
|
||||||
ensure_safe_paths!(from, to)
|
ensure_safe_paths!(from, to)
|
||||||
|
|
||||||
|
prepend_decoder!(from)
|
||||||
|
prepend_decoder!(to)
|
||||||
|
|
||||||
# NOTE: ORDER is important!
|
# NOTE: ORDER is important!
|
||||||
%W{
|
%W{
|
||||||
convert
|
convert
|
||||||
|
@ -159,6 +170,9 @@ class OptimizedImage < ActiveRecord::Base
|
||||||
def self.crop_instructions(from, to, dimensions, opts = {})
|
def self.crop_instructions(from, to, dimensions, opts = {})
|
||||||
ensure_safe_paths!(from, to)
|
ensure_safe_paths!(from, to)
|
||||||
|
|
||||||
|
prepend_decoder!(from)
|
||||||
|
prepend_decoder!(to)
|
||||||
|
|
||||||
%W{
|
%W{
|
||||||
convert
|
convert
|
||||||
#{from}[0]
|
#{from}[0]
|
||||||
|
@ -191,6 +205,9 @@ class OptimizedImage < ActiveRecord::Base
|
||||||
def self.downsize_instructions(from, to, dimensions, opts = {})
|
def self.downsize_instructions(from, to, dimensions, opts = {})
|
||||||
ensure_safe_paths!(from, to)
|
ensure_safe_paths!(from, to)
|
||||||
|
|
||||||
|
prepend_decoder!(from)
|
||||||
|
prepend_decoder!(to)
|
||||||
|
|
||||||
%W{
|
%W{
|
||||||
convert
|
convert
|
||||||
#{from}[0]
|
#{from}[0]
|
||||||
|
|
|
@ -135,13 +135,19 @@ class UploadCreator
|
||||||
def convert_to_jpeg!
|
def convert_to_jpeg!
|
||||||
jpeg_tempfile = Tempfile.new(["image", ".jpg"])
|
jpeg_tempfile = Tempfile.new(["image", ".jpg"])
|
||||||
|
|
||||||
OptimizedImage.ensure_safe_paths!(@file.path, jpeg_tempfile.path)
|
from = @file.path
|
||||||
|
to = jpeg_tempfile.path
|
||||||
|
|
||||||
|
OptimizedImage.ensure_safe_paths!(from, to)
|
||||||
|
|
||||||
|
OptimizedImage.prepend_decoder!(from)
|
||||||
|
OptimizedImage.prepend_decoder!(to)
|
||||||
|
|
||||||
begin
|
begin
|
||||||
execute_convert(@file, jpeg_tempfile)
|
execute_convert(from, to)
|
||||||
rescue
|
rescue
|
||||||
# retry with debugging enabled
|
# retry with debugging enabled
|
||||||
execute_convert(@file, jpeg_tempfile, true)
|
execute_convert(from, to, true)
|
||||||
end
|
end
|
||||||
|
|
||||||
# keep the JPEG if it's at least 15% smaller
|
# keep the JPEG if it's at least 15% smaller
|
||||||
|
@ -155,15 +161,18 @@ class UploadCreator
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def execute_convert(input_file, output_file, debug = false)
|
def execute_convert(from, to, debug = false)
|
||||||
command = ['convert', input_file.path,
|
command = [
|
||||||
'-auto-orient',
|
"convert",
|
||||||
'-background', 'white',
|
from,
|
||||||
'-interlace', 'none',
|
"-auto-orient",
|
||||||
'-flatten',
|
"-background white",
|
||||||
'-quality', SiteSetting.png_to_jpg_quality.to_s]
|
"-interlace none",
|
||||||
command << '-debug' << 'all' if debug
|
"-flatten",
|
||||||
command << output_file.path
|
"-quality #{SiteSetting.png_to_jpg_quality}"
|
||||||
|
]
|
||||||
|
command << "-debug all" if debug
|
||||||
|
command << to
|
||||||
|
|
||||||
Discourse::Utils.execute_command(*command, failure_message: I18n.t("upload.png_to_jpg_conversion_failure_message"))
|
Discourse::Utils.execute_command(*command, failure_message: I18n.t("upload.png_to_jpg_conversion_failure_message"))
|
||||||
end
|
end
|
||||||
|
@ -208,8 +217,13 @@ class UploadCreator
|
||||||
end
|
end
|
||||||
|
|
||||||
def fix_orientation!
|
def fix_orientation!
|
||||||
OptimizedImage.ensure_safe_paths!(@file.path)
|
path = @file.path
|
||||||
Discourse::Utils.execute_command('convert', @file.path, '-auto-orient', @file.path)
|
|
||||||
|
OptimizedImage.ensure_safe_paths!(path)
|
||||||
|
OptimizedImage.prepend_decoder!(path)
|
||||||
|
|
||||||
|
Discourse::Utils.execute_command('convert', path, '-auto-orient', path)
|
||||||
|
|
||||||
extract_image_info!
|
extract_image_info!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ describe OptimizedImage do
|
||||||
}.not_to raise_error
|
}.not_to raise_error
|
||||||
end
|
end
|
||||||
|
|
||||||
it "raises nothing on paths" do
|
it "raises InvalidAccess error on paths" do
|
||||||
expect {
|
expect {
|
||||||
OptimizedImage.ensure_safe_paths!("/a.png", "/b.png", "c.png")
|
OptimizedImage.ensure_safe_paths!("/a.png", "/b.png", "c.png")
|
||||||
}.to raise_error(Discourse::InvalidAccess)
|
}.to raise_error(Discourse::InvalidAccess)
|
||||||
|
|
Loading…
Reference in New Issue