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.

Files changed (56) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +508 -158
  3. data/lib/carrierwave/compatibility/paperclip.rb +31 -21
  4. data/lib/carrierwave/downloader/base.rb +101 -0
  5. data/lib/carrierwave/downloader/remote_file.rb +68 -0
  6. data/lib/carrierwave/error.rb +1 -0
  7. data/lib/carrierwave/locale/en.yml +11 -5
  8. data/lib/carrierwave/mount.rb +220 -187
  9. data/lib/carrierwave/mounter.rb +255 -0
  10. data/lib/carrierwave/orm/activerecord.rb +24 -34
  11. data/lib/carrierwave/processing/mini_magick.rb +142 -79
  12. data/lib/carrierwave/processing/rmagick.rb +76 -35
  13. data/lib/carrierwave/processing/vips.rb +284 -0
  14. data/lib/carrierwave/processing.rb +1 -1
  15. data/lib/carrierwave/sanitized_file.rb +89 -70
  16. data/lib/carrierwave/storage/abstract.rb +16 -3
  17. data/lib/carrierwave/storage/file.rb +71 -3
  18. data/lib/carrierwave/storage/fog.rb +215 -58
  19. data/lib/carrierwave/storage.rb +1 -7
  20. data/lib/carrierwave/test/matchers.rb +88 -19
  21. data/lib/carrierwave/uploader/cache.rb +88 -44
  22. data/lib/carrierwave/uploader/callbacks.rb +1 -3
  23. data/lib/carrierwave/uploader/configuration.rb +81 -9
  24. data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
  25. data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
  26. data/lib/carrierwave/uploader/default_url.rb +3 -5
  27. data/lib/carrierwave/uploader/dimension.rb +66 -0
  28. data/lib/carrierwave/uploader/download.rb +5 -69
  29. data/lib/carrierwave/uploader/extension_allowlist.rb +63 -0
  30. data/lib/carrierwave/uploader/extension_denylist.rb +64 -0
  31. data/lib/carrierwave/uploader/file_size.rb +43 -0
  32. data/lib/carrierwave/uploader/mountable.rb +13 -8
  33. data/lib/carrierwave/uploader/processing.rb +54 -21
  34. data/lib/carrierwave/uploader/proxy.rb +30 -8
  35. data/lib/carrierwave/uploader/remove.rb +0 -2
  36. data/lib/carrierwave/uploader/serialization.rb +3 -5
  37. data/lib/carrierwave/uploader/store.rb +59 -28
  38. data/lib/carrierwave/uploader/url.rb +8 -7
  39. data/lib/carrierwave/uploader/versions.rb +173 -124
  40. data/lib/carrierwave/uploader.rb +12 -6
  41. data/lib/carrierwave/utilities/file_name.rb +47 -0
  42. data/lib/carrierwave/utilities/uri.rb +14 -12
  43. data/lib/carrierwave/utilities.rb +2 -3
  44. data/lib/carrierwave/validations/active_model.rb +7 -13
  45. data/lib/carrierwave/version.rb +1 -1
  46. data/lib/carrierwave.rb +41 -16
  47. data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +5 -9
  48. data/lib/generators/uploader_generator.rb +3 -3
  49. metadata +224 -100
  50. data/lib/carrierwave/locale/cs.yml +0 -11
  51. data/lib/carrierwave/locale/de.yml +0 -11
  52. data/lib/carrierwave/locale/nl.yml +0 -11
  53. data/lib/carrierwave/locale/sk.yml +0 -11
  54. data/lib/carrierwave/processing/mime_types.rb +0 -73
  55. data/lib/carrierwave/uploader/extension_blacklist.rb +0 -47
  56. 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, for_file)
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
- private
72
-
73
- def interpolate_paperclip_path(path, filename)
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
- def mappings
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
@@ -4,4 +4,5 @@ module CarrierWave
4
4
  class InvalidParameter < UploadError; end
5
5
  class ProcessingError < UploadError; end
6
6
  class DownloadError < UploadError; end
7
+ class UnknownStorageError < StandardError; end
7
8
  end
@@ -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
- extension_white_list_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
8
- extension_black_list_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
9
- rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image? Original Error: %{e}"
10
- mime_types_processing_error: "Failed to process file with MIME::Types, maybe not valid content-type? Original Error: %{e}"
11
- mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}"
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"