carrierwave 0.9.0 → 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of carrierwave might be problematic. Click here for more details.
- checksums.yaml +7 -0
- data/README.md +508 -158
- data/lib/carrierwave/compatibility/paperclip.rb +31 -21
- data/lib/carrierwave/downloader/base.rb +101 -0
- data/lib/carrierwave/downloader/remote_file.rb +68 -0
- data/lib/carrierwave/error.rb +1 -0
- data/lib/carrierwave/locale/en.yml +11 -5
- data/lib/carrierwave/mount.rb +220 -187
- data/lib/carrierwave/mounter.rb +255 -0
- data/lib/carrierwave/orm/activerecord.rb +24 -34
- data/lib/carrierwave/processing/mini_magick.rb +142 -79
- data/lib/carrierwave/processing/rmagick.rb +76 -35
- data/lib/carrierwave/processing/vips.rb +284 -0
- data/lib/carrierwave/processing.rb +1 -1
- data/lib/carrierwave/sanitized_file.rb +89 -70
- data/lib/carrierwave/storage/abstract.rb +16 -3
- data/lib/carrierwave/storage/file.rb +71 -3
- data/lib/carrierwave/storage/fog.rb +215 -58
- data/lib/carrierwave/storage.rb +1 -7
- data/lib/carrierwave/test/matchers.rb +88 -19
- data/lib/carrierwave/uploader/cache.rb +88 -44
- data/lib/carrierwave/uploader/callbacks.rb +1 -3
- data/lib/carrierwave/uploader/configuration.rb +81 -9
- data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
- data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
- data/lib/carrierwave/uploader/default_url.rb +3 -5
- data/lib/carrierwave/uploader/dimension.rb +66 -0
- data/lib/carrierwave/uploader/download.rb +5 -69
- data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
- data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
- data/lib/carrierwave/uploader/file_size.rb +43 -0
- data/lib/carrierwave/uploader/mountable.rb +13 -8
- data/lib/carrierwave/uploader/processing.rb +54 -21
- data/lib/carrierwave/uploader/proxy.rb +30 -8
- data/lib/carrierwave/uploader/remove.rb +0 -2
- data/lib/carrierwave/uploader/serialization.rb +3 -5
- data/lib/carrierwave/uploader/store.rb +59 -28
- data/lib/carrierwave/uploader/url.rb +8 -7
- data/lib/carrierwave/uploader/versions.rb +173 -124
- data/lib/carrierwave/uploader.rb +12 -6
- data/lib/carrierwave/utilities/file_name.rb +47 -0
- data/lib/carrierwave/utilities/uri.rb +14 -12
- data/lib/carrierwave/utilities.rb +2 -3
- data/lib/carrierwave/validations/active_model.rb +7 -13
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +41 -16
- data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +5 -9
- data/lib/generators/uploader_generator.rb +3 -3
- metadata +224 -100
- data/lib/carrierwave/locale/cs.yml +0 -11
- data/lib/carrierwave/locale/de.yml +0 -11
- data/lib/carrierwave/locale/nl.yml +0 -11
- data/lib/carrierwave/locale/sk.yml +0 -11
- data/lib/carrierwave/processing/mime_types.rb +0 -73
- data/lib/carrierwave/uploader/extension_blacklist.rb +0 -47
- data/lib/carrierwave/uploader/extension_whitelist.rb +0 -49
@@ -1,5 +1,3 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
|
3
1
|
module CarrierWave
|
4
2
|
module Compatibility
|
5
3
|
|
@@ -46,11 +44,32 @@ module CarrierWave
|
|
46
44
|
# THE SOFTWARE.
|
47
45
|
#
|
48
46
|
module Paperclip
|
47
|
+
extend ActiveSupport::Concern
|
48
|
+
|
49
|
+
DEFAULT_MAPPINGS = {
|
50
|
+
:rails_root => lambda{|u, f| Rails.root.to_s },
|
51
|
+
:rails_env => lambda{|u, f| Rails.env },
|
52
|
+
:id_partition => lambda{|u, f| ("%09d" % u.model.id).scan(/\d{3}/).join("/")},
|
53
|
+
:id => lambda{|u, f| u.model.id },
|
54
|
+
:attachment => lambda{|u, f| u.mounted_as.to_s.downcase.pluralize },
|
55
|
+
:style => lambda{|u, f| u.paperclip_style },
|
56
|
+
:basename => lambda{|u, f| u.filename.gsub(/#{File.extname(u.filename)}$/, "") },
|
57
|
+
:extension => lambda{|u, d| File.extname(u.filename).gsub(/^\.+/, "")},
|
58
|
+
:class => lambda{|u, f| u.model.class.name.underscore.pluralize}
|
59
|
+
}.freeze
|
60
|
+
|
61
|
+
included do
|
62
|
+
attr_accessor :filename
|
63
|
+
|
64
|
+
class_attribute :mappings
|
65
|
+
self.mappings ||= DEFAULT_MAPPINGS.dup
|
66
|
+
end
|
49
67
|
|
50
68
|
def store_path(for_file=filename)
|
51
69
|
path = paperclip_path
|
70
|
+
self.filename = for_file
|
52
71
|
path ||= File.join(*[store_dir, paperclip_style.to_s, for_file].compact)
|
53
|
-
interpolate_paperclip_path(path
|
72
|
+
interpolate_paperclip_path(path)
|
54
73
|
end
|
55
74
|
|
56
75
|
def store_dir
|
@@ -68,28 +87,19 @@ module CarrierWave
|
|
68
87
|
version_name || paperclip_default_style
|
69
88
|
end
|
70
89
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
mappings.inject(path) do |agg, pair|
|
75
|
-
agg.gsub(":#{pair[0]}") { pair[1].call(self, filename).to_s }
|
90
|
+
module ClassMethods
|
91
|
+
def interpolate(sym, &block)
|
92
|
+
mappings[sym] = block
|
76
93
|
end
|
77
94
|
end
|
78
95
|
|
79
|
-
|
80
|
-
[
|
81
|
-
[:rails_root , lambda{|u, f| Rails.root }],
|
82
|
-
[:rails_env , lambda{|u, f| Rails.env }],
|
83
|
-
[:class , lambda{|u, f| u.model.class.name.underscore.pluralize}],
|
84
|
-
[:id_partition , lambda{|u, f| ("%09d" % u.model.id).scan(/\d{3}/).join("/")}],
|
85
|
-
[:id , lambda{|u, f| u.model.id }],
|
86
|
-
[:attachment , lambda{|u, f| u.mounted_as.to_s.downcase.pluralize }],
|
87
|
-
[:style , lambda{|u, f| u.paperclip_style }],
|
88
|
-
[:basename , lambda{|u, f| f.gsub(/#{File.extname(f)}$/, "") }],
|
89
|
-
[:extension , lambda{|u, f| File.extname(f).gsub(/^\.+/, "")}]
|
90
|
-
]
|
91
|
-
end
|
96
|
+
private
|
92
97
|
|
98
|
+
def interpolate_paperclip_path(path)
|
99
|
+
mappings.each_pair.inject(path) do |agg, pair|
|
100
|
+
agg.gsub(":#{pair[0]}") { pair[1].call(self, self.paperclip_style).to_s }
|
101
|
+
end
|
102
|
+
end
|
93
103
|
end # Paperclip
|
94
104
|
end # Compatibility
|
95
105
|
end # CarrierWave
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'ssrf_filter'
|
3
|
+
require 'addressable'
|
4
|
+
require 'carrierwave/downloader/remote_file'
|
5
|
+
|
6
|
+
module CarrierWave
|
7
|
+
module Downloader
|
8
|
+
class Base
|
9
|
+
include CarrierWave::Utilities::Uri
|
10
|
+
|
11
|
+
attr_reader :uploader
|
12
|
+
|
13
|
+
def initialize(uploader)
|
14
|
+
@uploader = uploader
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Downloads a file from given URL and returns a RemoteFile.
|
19
|
+
#
|
20
|
+
# === Parameters
|
21
|
+
#
|
22
|
+
# [url (String)] The URL where the remote file is stored
|
23
|
+
# [remote_headers (Hash)] Request headers
|
24
|
+
#
|
25
|
+
def download(url, remote_headers = {})
|
26
|
+
@current_download_retry_count = 0
|
27
|
+
headers = remote_headers.
|
28
|
+
reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}")
|
29
|
+
uri = process_uri(url.to_s)
|
30
|
+
begin
|
31
|
+
if skip_ssrf_protection?(uri)
|
32
|
+
response = OpenURI.open_uri(process_uri(url.to_s), headers)
|
33
|
+
else
|
34
|
+
request = nil
|
35
|
+
if ::SsrfFilter::VERSION.to_f < 1.1
|
36
|
+
response = SsrfFilter.get(uri, headers: headers) do |req|
|
37
|
+
request = req
|
38
|
+
end
|
39
|
+
else
|
40
|
+
response = SsrfFilter.get(uri, headers: headers, request_proc: ->(req) { request = req }) do |res|
|
41
|
+
res.body # ensure to read body
|
42
|
+
end
|
43
|
+
end
|
44
|
+
response.uri = request.uri
|
45
|
+
response.value
|
46
|
+
end
|
47
|
+
rescue StandardError => e
|
48
|
+
if @current_download_retry_count < @uploader.download_retry_count
|
49
|
+
@current_download_retry_count += 1
|
50
|
+
sleep @uploader.download_retry_wait_time
|
51
|
+
retry
|
52
|
+
else
|
53
|
+
raise CarrierWave::DownloadError, "could not download file: #{e.message}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
CarrierWave::Downloader::RemoteFile.new(response)
|
57
|
+
end
|
58
|
+
|
59
|
+
##
|
60
|
+
# Processes the given URL by parsing it, and escaping if necessary. Public to allow overriding.
|
61
|
+
#
|
62
|
+
# === Parameters
|
63
|
+
#
|
64
|
+
# [url (String)] The URL where the remote file is stored
|
65
|
+
#
|
66
|
+
def process_uri(source)
|
67
|
+
uri = Addressable::URI.parse(source)
|
68
|
+
uri.host = uri.normalized_host
|
69
|
+
# Perform decode first, as the path is likely to be already encoded
|
70
|
+
uri.path = encode_path(decode_uri(uri.path)) if uri.path =~ CarrierWave::Utilities::Uri::PATH_UNSAFE
|
71
|
+
uri.query = encode_non_ascii(uri.query) if uri.query
|
72
|
+
uri.fragment = encode_non_ascii(uri.fragment) if uri.fragment
|
73
|
+
URI.parse(uri.to_s)
|
74
|
+
rescue URI::InvalidURIError, Addressable::URI::InvalidURIError
|
75
|
+
raise CarrierWave::DownloadError, "couldn't parse URL: #{source}"
|
76
|
+
end
|
77
|
+
|
78
|
+
##
|
79
|
+
# If this returns true, SSRF protection will be bypassed.
|
80
|
+
# You can override this if you want to allow accessing specific local URIs that are not SSRF exploitable.
|
81
|
+
#
|
82
|
+
# === Parameters
|
83
|
+
#
|
84
|
+
# [uri (URI)] The URI where the remote file is stored
|
85
|
+
#
|
86
|
+
# === Examples
|
87
|
+
#
|
88
|
+
# class CarrierWave::Downloader::CustomDownloader < CarrierWave::Downloader::Base
|
89
|
+
# def skip_ssrf_protection?(uri)
|
90
|
+
# uri.hostname == 'localhost' && uri.port == 80
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
#
|
94
|
+
# my_uploader.downloader = CarrierWave::Downloader::CustomDownloader
|
95
|
+
#
|
96
|
+
def skip_ssrf_protection?(uri)
|
97
|
+
false
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module CarrierWave
|
2
|
+
module Downloader
|
3
|
+
class RemoteFile
|
4
|
+
attr_reader :file, :uri
|
5
|
+
|
6
|
+
def initialize(file)
|
7
|
+
case file
|
8
|
+
when String
|
9
|
+
@file = StringIO.new(file)
|
10
|
+
when Net::HTTPResponse
|
11
|
+
body = file.body
|
12
|
+
raise CarrierWave::DownloadError, 'could not download file: No Content' if body.nil?
|
13
|
+
|
14
|
+
@file = StringIO.new(body)
|
15
|
+
@content_type = file.content_type
|
16
|
+
@headers = file
|
17
|
+
@uri = file.uri
|
18
|
+
else
|
19
|
+
@file = file
|
20
|
+
@content_type = file.content_type
|
21
|
+
@headers = file.meta
|
22
|
+
@uri = file.base_uri
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def content_type
|
27
|
+
@content_type || 'application/octet-stream'
|
28
|
+
end
|
29
|
+
|
30
|
+
def headers
|
31
|
+
@headers || {}
|
32
|
+
end
|
33
|
+
|
34
|
+
def original_filename
|
35
|
+
filename = filename_from_header || filename_from_uri
|
36
|
+
mime_type = Marcel::TYPES[content_type]
|
37
|
+
unless File.extname(filename).present? || mime_type.blank?
|
38
|
+
extension = mime_type[0].first
|
39
|
+
filename = "#{filename}.#{extension}"
|
40
|
+
end
|
41
|
+
filename
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def filename_from_header
|
47
|
+
return nil unless headers['content-disposition']
|
48
|
+
|
49
|
+
match = headers['content-disposition'].match(/filename=(?:"([^"]+)"|([^";]+))/)
|
50
|
+
return nil unless match
|
51
|
+
|
52
|
+
match[1].presence || match[2].presence
|
53
|
+
end
|
54
|
+
|
55
|
+
def filename_from_uri
|
56
|
+
CGI.unescape(File.basename(uri.path))
|
57
|
+
end
|
58
|
+
|
59
|
+
def method_missing(*args, &block)
|
60
|
+
file.send(*args, &block)
|
61
|
+
end
|
62
|
+
|
63
|
+
def respond_to_missing?(*args)
|
64
|
+
super || file.respond_to?(*args)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/carrierwave/error.rb
CHANGED
@@ -4,8 +4,14 @@ en:
|
|
4
4
|
carrierwave_processing_error: failed to be processed
|
5
5
|
carrierwave_integrity_error: is not of an allowed file type
|
6
6
|
carrierwave_download_error: could not be downloaded
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
extension_allowlist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
|
8
|
+
extension_denylist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
|
9
|
+
content_type_allowlist_error: "You are not allowed to upload %{content_type} files, allowed types: %{allowed_types}"
|
10
|
+
content_type_denylist_error: "You are not allowed to upload %{content_type} files"
|
11
|
+
processing_error: "Failed to manipulate, maybe it is not an image?"
|
12
|
+
min_size_error: "File size should be greater than %{min_size}"
|
13
|
+
max_size_error: "File size should be less than %{max_size}"
|
14
|
+
min_width_error: "Image width should be greater than %{min_width}px"
|
15
|
+
max_width_error: "Image width should be less than %{max_width}px"
|
16
|
+
min_height_error: "Image height should be greater than %{min_height}px"
|
17
|
+
max_height_error: "Image height should be less than %{max_height}px"
|