Merge pull request #3551 from evazion/fix-3528

Fix #3528: Add method to prevent image-hosting CloudFlare sites from altering the image
This commit is contained in:
Albert Yi
2018-02-21 14:52:43 -08:00
committed by GitHub
2 changed files with 24 additions and 12 deletions

View File

@@ -26,20 +26,23 @@ module Downloads
end end
def size def size
@source, headers, @data = before_download(@source, @data) url, headers, _ = before_download(@source, @data)
options = { timeout: 3, headers: headers }.deep_merge(Danbooru.config.httparty_options) options = { timeout: 3, headers: headers }.deep_merge(Danbooru.config.httparty_options)
res = HTTParty.head(@source, options) res = HTTParty.head(url, options)
res.content_length res.content_length
end end
def download! def download!
url, headers, @data = before_download(@source, @data)
::File.open(@file_path, "wb") do |out| ::File.open(@file_path, "wb") do |out|
@source, @data = http_get_streaming(@source, @data) do |response| http_get_streaming(uncached_url(url), headers) do |response|
out.write(response) out.write(response)
end end
end end
@downloaded_source = @source
@source = after_download(@source) @downloaded_source = url
@source = after_download(url)
end end
def before_download(url, datums) def before_download(url, datums)
@@ -52,6 +55,13 @@ module Downloads
return [url, headers, datums] return [url, headers, datums]
end end
# Prevent transparent proxies (namely Cloudflare) from potentially mangling the image. See issue #3528.
def uncached_url(url)
url = Addressable::URI.parse(url)
url.query_values = (url.query_values || {}).merge(danbooru_no_cache: SecureRandom.uuid)
url
end
def after_download(src) def after_download(src)
src = fix_twitter_sources(src) src = fix_twitter_sources(src)
if options[:referer_url].present? if options[:referer_url].present?
@@ -67,7 +77,7 @@ module Downloads
end end
end end
def http_get_streaming(src, datums = {}, options = {}, &block) def http_get_streaming(src, headers = {}, options = {}, &block)
max_size = options[:max_size] || Danbooru.config.max_file_size max_size = options[:max_size] || Danbooru.config.max_file_size
max_size = nil if max_size == 0 # unlimited max_size = nil if max_size == 0 # unlimited
limit = 4 limit = 4
@@ -79,9 +89,6 @@ module Downloads
raise Error.new("URL must be HTTP or HTTPS") raise Error.new("URL must be HTTP or HTTPS")
end end
src, headers, datums = before_download(src, datums)
url = URI.parse(src)
validate_local_hosts(url) validate_local_hosts(url)
begin begin
@@ -96,7 +103,7 @@ module Downloads
@content_type = res["Content-Type"] @content_type = res["Content-Type"]
return [src, datums] return
else else
raise Error.new("HTTP error code: #{res.code} #{res.message}") raise Error.new("HTTP error code: #{res.code} #{res.message}")
end end
@@ -109,8 +116,6 @@ module Downloads
end end
end end
end # while end # while
[src, datums]
end # def end # def
def fix_twitter_sources(src) def fix_twitter_sources(src)

View File

@@ -28,6 +28,13 @@ module Downloads
end end
end end
context "a download for an ArtStation image hosted on CloudFlare" do
should "return the original file, not the polished file" do
@source = "https://cdnb.artstation.com/p/assets/images/images/003/716/071/large/aoi-ogata-hate-city.jpg?1476754974"
assert_downloaded(517_706, @source) # polished size: 502_052
end
end
context "a download for a https://$artist.artstation.com/projects/$id page" do context "a download for a https://$artist.artstation.com/projects/$id page" do
setup do setup do
@source = "https://dantewontdie.artstation.com/projects/YZK5q" @source = "https://dantewontdie.artstation.com/projects/YZK5q"