carrierwave 1.3.2 → 3.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/README.md +235 -91
 - data/lib/carrierwave/compatibility/paperclip.rb +4 -2
 - data/lib/carrierwave/downloader/base.rb +101 -0
 - data/lib/carrierwave/downloader/remote_file.rb +68 -0
 - data/lib/carrierwave/locale/en.yml +9 -6
 - data/lib/carrierwave/mount.rb +53 -61
 - data/lib/carrierwave/mounter.rb +167 -77
 - data/lib/carrierwave/orm/activerecord.rb +23 -58
 - data/lib/carrierwave/processing/mini_magick.rb +108 -123
 - data/lib/carrierwave/processing/rmagick.rb +11 -15
 - data/lib/carrierwave/processing/vips.rb +284 -0
 - data/lib/carrierwave/processing.rb +1 -0
 - data/lib/carrierwave/sanitized_file.rb +60 -66
 - data/lib/carrierwave/storage/abstract.rb +5 -5
 - data/lib/carrierwave/storage/file.rb +6 -5
 - data/lib/carrierwave/storage/fog.rb +101 -62
 - data/lib/carrierwave/storage.rb +1 -0
 - data/lib/carrierwave/test/matchers.rb +11 -7
 - data/lib/carrierwave/uploader/cache.rb +40 -24
 - data/lib/carrierwave/uploader/callbacks.rb +1 -1
 - data/lib/carrierwave/uploader/configuration.rb +38 -19
 - data/lib/carrierwave/uploader/content_type_allowlist.rb +62 -0
 - data/lib/carrierwave/uploader/content_type_denylist.rb +62 -0
 - data/lib/carrierwave/uploader/dimension.rb +66 -0
 - data/lib/carrierwave/uploader/download.rb +2 -123
 - 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 +2 -2
 - data/lib/carrierwave/uploader/mountable.rb +6 -0
 - data/lib/carrierwave/uploader/processing.rb +42 -7
 - data/lib/carrierwave/uploader/proxy.rb +17 -4
 - data/lib/carrierwave/uploader/serialization.rb +1 -1
 - data/lib/carrierwave/uploader/store.rb +47 -7
 - data/lib/carrierwave/uploader/url.rb +7 -4
 - data/lib/carrierwave/uploader/versions.rb +157 -109
 - data/lib/carrierwave/uploader.rb +10 -17
 - data/lib/carrierwave/utilities/file_name.rb +47 -0
 - data/lib/carrierwave/utilities/uri.rb +14 -11
 - data/lib/carrierwave/utilities.rb +1 -0
 - data/lib/carrierwave/validations/active_model.rb +7 -9
 - data/lib/carrierwave/version.rb +1 -1
 - data/lib/carrierwave.rb +13 -17
 - data/lib/generators/templates/{uploader.rb → uploader.rb.erb} +3 -3
 - data/lib/generators/uploader_generator.rb +3 -3
 - metadata +103 -36
 - data/lib/carrierwave/uploader/content_type_blacklist.rb +0 -48
 - data/lib/carrierwave/uploader/content_type_whitelist.rb +0 -48
 - data/lib/carrierwave/uploader/extension_blacklist.rb +0 -51
 - data/lib/carrierwave/uploader/extension_whitelist.rb +0 -52
 
| 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'securerandom'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module CarrierWave
         
     | 
| 
       2 
4 
     | 
    
         | 
| 
       3 
5 
     | 
    
         
             
              class FormNotMultipart < UploadError
         
     | 
| 
         @@ -22,10 +24,11 @@ module CarrierWave 
     | 
|
| 
       22 
24 
     | 
    
         
             
              # [String] a cache id in the format TIMEINT-PID-COUNTER-RND
         
     | 
| 
       23 
25 
     | 
    
         
             
              #
         
     | 
| 
       24 
26 
     | 
    
         
             
              def self.generate_cache_id
         
     | 
| 
       25 
     | 
    
         
            -
                [ 
     | 
| 
       26 
     | 
    
         
            -
                   
     | 
| 
       27 
     | 
    
         
            -
                   
     | 
| 
       28 
     | 
    
         
            -
                  '%04d' %  
     | 
| 
      
 27 
     | 
    
         
            +
                [
         
     | 
| 
      
 28 
     | 
    
         
            +
                  Time.now.utc.to_i,
         
     | 
| 
      
 29 
     | 
    
         
            +
                  SecureRandom.random_number(1_000_000_000_000_000),
         
     | 
| 
      
 30 
     | 
    
         
            +
                  '%04d' % (CarrierWave::CacheCounter.increment % 10_000),
         
     | 
| 
      
 31 
     | 
    
         
            +
                  '%04d' % SecureRandom.random_number(10_000)
         
     | 
| 
       29 
32 
     | 
    
         
             
                ].map(&:to_s).join('-')
         
     | 
| 
       30 
33 
     | 
    
         
             
              end
         
     | 
| 
       31 
34 
     | 
    
         | 
| 
         @@ -36,6 +39,16 @@ module CarrierWave 
     | 
|
| 
       36 
39 
     | 
    
         
             
                  include CarrierWave::Uploader::Callbacks
         
     | 
| 
       37 
40 
     | 
    
         
             
                  include CarrierWave::Uploader::Configuration
         
     | 
| 
       38 
41 
     | 
    
         | 
| 
      
 42 
     | 
    
         
            +
                  included do
         
     | 
| 
      
 43 
     | 
    
         
            +
                    prepend Module.new {
         
     | 
| 
      
 44 
     | 
    
         
            +
                      def initialize(*)
         
     | 
| 
      
 45 
     | 
    
         
            +
                        super
         
     | 
| 
      
 46 
     | 
    
         
            +
                        @staged = false
         
     | 
| 
      
 47 
     | 
    
         
            +
                      end
         
     | 
| 
      
 48 
     | 
    
         
            +
                    }
         
     | 
| 
      
 49 
     | 
    
         
            +
                    attr_accessor :staged
         
     | 
| 
      
 50 
     | 
    
         
            +
                  end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
       39 
52 
     | 
    
         
             
                  module ClassMethods
         
     | 
| 
       40 
53 
     | 
    
         | 
| 
       41 
54 
     | 
    
         
             
                    ##
         
     | 
| 
         @@ -52,7 +65,7 @@ module CarrierWave 
     | 
|
| 
       52 
65 
     | 
    
         
             
                    # It's recommended that you keep cache files in one place only.
         
     | 
| 
       53 
66 
     | 
    
         
             
                    #
         
     | 
| 
       54 
67 
     | 
    
         
             
                    def clean_cached_files!(seconds=60*60*24)
         
     | 
| 
       55 
     | 
    
         
            -
                      cache_storage.new( 
     | 
| 
      
 68 
     | 
    
         
            +
                      (cache_storage || storage).new(new).clean_cache!(seconds)
         
     | 
| 
       56 
69 
     | 
    
         
             
                    end
         
     | 
| 
       57 
70 
     | 
    
         
             
                  end
         
     | 
| 
       58 
71 
     | 
    
         | 
| 
         @@ -64,7 +77,7 @@ module CarrierWave 
     | 
|
| 
       64 
77 
     | 
    
         
             
                  # [Bool] whether the current file is cached
         
     | 
| 
       65 
78 
     | 
    
         
             
                  #
         
     | 
| 
       66 
79 
     | 
    
         
             
                  def cached?
         
     | 
| 
       67 
     | 
    
         
            -
                     
     | 
| 
      
 80 
     | 
    
         
            +
                    !!@cache_id
         
     | 
| 
       68 
81 
     | 
    
         
             
                  end
         
     | 
| 
       69 
82 
     | 
    
         | 
| 
       70 
83 
     | 
    
         
             
                  ##
         
     | 
| 
         @@ -78,14 +91,8 @@ module CarrierWave 
     | 
|
| 
       78 
91 
     | 
    
         
             
                  end
         
     | 
| 
       79 
92 
     | 
    
         | 
| 
       80 
93 
     | 
    
         
             
                  def sanitized_file
         
     | 
| 
       81 
     | 
    
         
            -
                     
     | 
| 
       82 
     | 
    
         
            -
                     
     | 
| 
       83 
     | 
    
         
            -
                      sanitized = CarrierWave::Storage::Fog.new(self).retrieve!(File.basename(_content.path))
         
     | 
| 
       84 
     | 
    
         
            -
                    else
         
     | 
| 
       85 
     | 
    
         
            -
                      sanitized = SanitizedFile.new :tempfile => StringIO.new(_content),
         
     | 
| 
       86 
     | 
    
         
            -
                        :filename => File.basename(path), :content_type => file.content_type
         
     | 
| 
       87 
     | 
    
         
            -
                    end
         
     | 
| 
       88 
     | 
    
         
            -
                    sanitized
         
     | 
| 
      
 94 
     | 
    
         
            +
                    ActiveSupport::Deprecation.warn('#sanitized_file is deprecated, use #file instead.')
         
     | 
| 
      
 95 
     | 
    
         
            +
                    file
         
     | 
| 
       89 
96 
     | 
    
         
             
                  end
         
     | 
| 
       90 
97 
     | 
    
         | 
| 
       91 
98 
     | 
    
         
             
                  ##
         
     | 
| 
         @@ -96,7 +103,7 @@ module CarrierWave 
     | 
|
| 
       96 
103 
     | 
    
         
             
                  # [String] a cache name, in the format TIMEINT-PID-COUNTER-RND/filename.txt
         
     | 
| 
       97 
104 
     | 
    
         
             
                  #
         
     | 
| 
       98 
105 
     | 
    
         
             
                  def cache_name
         
     | 
| 
       99 
     | 
    
         
            -
                    File.join(cache_id,  
     | 
| 
      
 106 
     | 
    
         
            +
                    File.join(cache_id, original_filename) if cache_id && original_filename
         
     | 
| 
       100 
107 
     | 
    
         
             
                  end
         
     | 
| 
       101 
108 
     | 
    
         | 
| 
       102 
109 
     | 
    
         
             
                  ##
         
     | 
| 
         @@ -104,7 +111,7 @@ module CarrierWave 
     | 
|
| 
       104 
111 
     | 
    
         
             
                  #
         
     | 
| 
       105 
112 
     | 
    
         
             
                  # By default, cache!() uses copy_to(), which operates by copying the file
         
     | 
| 
       106 
113 
     | 
    
         
             
                  # to the cache, then deleting the original file.  If move_to_cache() is
         
     | 
| 
       107 
     | 
    
         
            -
                  #  
     | 
| 
      
 114 
     | 
    
         
            +
                  # overridden to return true, then cache!() uses move_to(), which simply
         
     | 
| 
       108 
115 
     | 
    
         
             
                  # moves the file to the cache.  Useful for large files.
         
     | 
| 
       109 
116 
     | 
    
         
             
                  #
         
     | 
| 
       110 
117 
     | 
    
         
             
                  # === Parameters
         
     | 
| 
         @@ -115,7 +122,7 @@ module CarrierWave 
     | 
|
| 
       115 
122 
     | 
    
         
             
                  #
         
     | 
| 
       116 
123 
     | 
    
         
             
                  # [CarrierWave::FormNotMultipart] if the assigned parameter is a string
         
     | 
| 
       117 
124 
     | 
    
         
             
                  #
         
     | 
| 
       118 
     | 
    
         
            -
                  def cache!(new_file =  
     | 
| 
      
 125 
     | 
    
         
            +
                  def cache!(new_file = file)
         
     | 
| 
       119 
126 
     | 
    
         
             
                    new_file = CarrierWave::SanitizedFile.new(new_file)
         
     | 
| 
       120 
127 
     | 
    
         
             
                    return if new_file.empty?
         
     | 
| 
       121 
128 
     | 
    
         | 
| 
         @@ -123,6 +130,8 @@ module CarrierWave 
     | 
|
| 
       123 
130 
     | 
    
         | 
| 
       124 
131 
     | 
    
         
             
                    self.cache_id = CarrierWave.generate_cache_id unless cache_id
         
     | 
| 
       125 
132 
     | 
    
         | 
| 
      
 133 
     | 
    
         
            +
                    @identifier = nil
         
     | 
| 
      
 134 
     | 
    
         
            +
                    @staged = true
         
     | 
| 
       126 
135 
     | 
    
         
             
                    @filename = new_file.filename
         
     | 
| 
       127 
136 
     | 
    
         
             
                    self.original_filename = new_file.filename
         
     | 
| 
       128 
137 
     | 
    
         | 
| 
         @@ -156,8 +165,9 @@ module CarrierWave 
     | 
|
| 
       156 
165 
     | 
    
         
             
                  def retrieve_from_cache!(cache_name)
         
     | 
| 
       157 
166 
     | 
    
         
             
                    with_callbacks(:retrieve_from_cache, cache_name) do
         
     | 
| 
       158 
167 
     | 
    
         
             
                      self.cache_id, self.original_filename = cache_name.to_s.split('/', 2)
         
     | 
| 
      
 168 
     | 
    
         
            +
                      @staged = true
         
     | 
| 
       159 
169 
     | 
    
         
             
                      @filename = original_filename
         
     | 
| 
       160 
     | 
    
         
            -
                      @file = cache_storage.retrieve_from_cache!( 
     | 
| 
      
 170 
     | 
    
         
            +
                      @file = cache_storage.retrieve_from_cache!(full_original_filename)
         
     | 
| 
       161 
171 
     | 
    
         
             
                    end
         
     | 
| 
       162 
172 
     | 
    
         
             
                  end
         
     | 
| 
       163 
173 
     | 
    
         | 
| 
         @@ -172,20 +182,21 @@ module CarrierWave 
     | 
|
| 
       172 
182 
     | 
    
         
             
                  #
         
     | 
| 
       173 
183 
     | 
    
         
             
                  # [String] the cache path
         
     | 
| 
       174 
184 
     | 
    
         
             
                  #
         
     | 
| 
       175 
     | 
    
         
            -
                  def cache_path(for_file= 
     | 
| 
      
 185 
     | 
    
         
            +
                  def cache_path(for_file=full_original_filename)
         
     | 
| 
       176 
186 
     | 
    
         
             
                    File.join(*[cache_dir, @cache_id, for_file].compact)
         
     | 
| 
       177 
187 
     | 
    
         
             
                  end
         
     | 
| 
       178 
188 
     | 
    
         | 
| 
      
 189 
     | 
    
         
            +
                protected
         
     | 
| 
      
 190 
     | 
    
         
            +
             
     | 
| 
      
 191 
     | 
    
         
            +
                  attr_reader :cache_id
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
       179 
193 
     | 
    
         
             
                private
         
     | 
| 
       180 
194 
     | 
    
         | 
| 
       181 
195 
     | 
    
         
             
                  def workfile_path(for_file=original_filename)
         
     | 
| 
       182 
196 
     | 
    
         
             
                    File.join(CarrierWave.tmp_path, @cache_id, version_name.to_s, for_file)
         
     | 
| 
       183 
197 
     | 
    
         
             
                  end
         
     | 
| 
       184 
198 
     | 
    
         | 
| 
       185 
     | 
    
         
            -
                  attr_reader : 
     | 
| 
       186 
     | 
    
         
            -
             
     | 
| 
       187 
     | 
    
         
            -
                  # We can override the full_original_filename method in other modules
         
     | 
| 
       188 
     | 
    
         
            -
                  alias_method :full_original_filename, :original_filename
         
     | 
| 
      
 199 
     | 
    
         
            +
                  attr_reader :original_filename
         
     | 
| 
       189 
200 
     | 
    
         | 
| 
       190 
201 
     | 
    
         
             
                  def cache_id=(cache_id)
         
     | 
| 
       191 
202 
     | 
    
         
             
                    # Earlier version used 3 part cache_id. Thus we should allow for
         
     | 
| 
         @@ -200,7 +211,12 @@ module CarrierWave 
     | 
|
| 
       200 
211 
     | 
    
         
             
                  end
         
     | 
| 
       201 
212 
     | 
    
         | 
| 
       202 
213 
     | 
    
         
             
                  def cache_storage
         
     | 
| 
       203 
     | 
    
         
            -
                    @cache_storage ||= self.class.cache_storage.new(self)
         
     | 
| 
      
 214 
     | 
    
         
            +
                    @cache_storage ||= (self.class.cache_storage || self.class.storage).new(self)
         
     | 
| 
      
 215 
     | 
    
         
            +
                  end
         
     | 
| 
      
 216 
     | 
    
         
            +
             
     | 
| 
      
 217 
     | 
    
         
            +
                  # We can override the full_original_filename method in other modules
         
     | 
| 
      
 218 
     | 
    
         
            +
                  def full_original_filename
         
     | 
| 
      
 219 
     | 
    
         
            +
                    forcing_extension(original_filename)
         
     | 
| 
       204 
220 
     | 
    
         
             
                  end
         
     | 
| 
       205 
221 
     | 
    
         
             
                end # Cache
         
     | 
| 
       206 
222 
     | 
    
         
             
              end # Uploader
         
     | 
| 
         @@ -1,3 +1,5 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'carrierwave/downloader/base'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
       1 
3 
     | 
    
         
             
            module CarrierWave
         
     | 
| 
       2 
4 
     | 
    
         | 
| 
       3 
5 
     | 
    
         
             
              module Uploader
         
     | 
| 
         @@ -21,9 +23,11 @@ module CarrierWave 
     | 
|
| 
       21 
23 
     | 
    
         
             
                    add_config :move_to_cache
         
     | 
| 
       22 
24 
     | 
    
         
             
                    add_config :move_to_store
         
     | 
| 
       23 
25 
     | 
    
         
             
                    add_config :remove_previously_stored_files_after_update
         
     | 
| 
      
 26 
     | 
    
         
            +
                    add_config :downloader
         
     | 
| 
      
 27 
     | 
    
         
            +
                    add_config :force_extension
         
     | 
| 
       24 
28 
     | 
    
         | 
| 
       25 
29 
     | 
    
         
             
                    # fog
         
     | 
| 
       26 
     | 
    
         
            -
                     
     | 
| 
      
 30 
     | 
    
         
            +
                    add_deprecated_config :fog_provider
         
     | 
| 
       27 
31 
     | 
    
         
             
                    add_config :fog_attributes
         
     | 
| 
       28 
32 
     | 
    
         
             
                    add_config :fog_credentials
         
     | 
| 
       29 
33 
     | 
    
         
             
                    add_config :fog_directory
         
     | 
| 
         @@ -41,6 +45,8 @@ module CarrierWave 
     | 
|
| 
       41 
45 
     | 
    
         
             
                    add_config :validate_download
         
     | 
| 
       42 
46 
     | 
    
         
             
                    add_config :mount_on
         
     | 
| 
       43 
47 
     | 
    
         
             
                    add_config :cache_only
         
     | 
| 
      
 48 
     | 
    
         
            +
                    add_config :download_retry_count
         
     | 
| 
      
 49 
     | 
    
         
            +
                    add_config :download_retry_wait_time
         
     | 
| 
       44 
50 
     | 
    
         | 
| 
       45 
51 
     | 
    
         
             
                    # set default values
         
     | 
| 
       46 
52 
     | 
    
         
             
                    reset_config
         
     | 
| 
         @@ -74,7 +80,7 @@ module CarrierWave 
     | 
|
| 
       74 
80 
     | 
    
         
             
                    def storage(storage = nil)
         
     | 
| 
       75 
81 
     | 
    
         
             
                      case storage
         
     | 
| 
       76 
82 
     | 
    
         
             
                      when Symbol
         
     | 
| 
       77 
     | 
    
         
            -
                        if storage_engine = storage_engines[storage]
         
     | 
| 
      
 83 
     | 
    
         
            +
                        if (storage_engine = storage_engines[storage])
         
     | 
| 
       78 
84 
     | 
    
         
             
                          self._storage = eval storage_engine
         
     | 
| 
       79 
85 
     | 
    
         
             
                        else
         
     | 
| 
       80 
86 
     | 
    
         
             
                          raise CarrierWave::UnknownStorageError, "Unknown storage: #{storage}"
         
     | 
| 
         @@ -107,8 +113,8 @@ module CarrierWave 
     | 
|
| 
       107 
113 
     | 
    
         
             
                    #     cache_storage CarrierWave::Storage::File
         
     | 
| 
       108 
114 
     | 
    
         
             
                    #     cache_storage MyCustomStorageEngine
         
     | 
| 
       109 
115 
     | 
    
         
             
                    #
         
     | 
| 
       110 
     | 
    
         
            -
                    def cache_storage(storage =  
     | 
| 
       111 
     | 
    
         
            -
                       
     | 
| 
      
 116 
     | 
    
         
            +
                    def cache_storage(storage = false)
         
     | 
| 
      
 117 
     | 
    
         
            +
                      unless storage == false
         
     | 
| 
       112 
118 
     | 
    
         
             
                        self._cache_storage = storage.is_a?(Symbol) ? eval(storage_engines[storage]) : storage
         
     | 
| 
       113 
119 
     | 
    
         
             
                      end
         
     | 
| 
       114 
120 
     | 
    
         
             
                      _cache_storage
         
     | 
| 
         @@ -119,16 +125,8 @@ module CarrierWave 
     | 
|
| 
       119 
125 
     | 
    
         
             
                      class_eval <<-RUBY, __FILE__, __LINE__ + 1
         
     | 
| 
       120 
126 
     | 
    
         
             
                        @#{name} = nil
         
     | 
| 
       121 
127 
     | 
    
         | 
| 
       122 
     | 
    
         
            -
                        def self.eager_load_fog(fog_credentials)
         
     | 
| 
       123 
     | 
    
         
            -
                          # see #1198. This will hopefully no longer be necessary after fog 2.0
         
     | 
| 
       124 
     | 
    
         
            -
                          require self.fog_provider
         
     | 
| 
       125 
     | 
    
         
            -
                          require 'carrierwave/storage/fog'
         
     | 
| 
       126 
     | 
    
         
            -
                          Fog::Storage.new(fog_credentials) if fog_credentials.present?
         
     | 
| 
       127 
     | 
    
         
            -
                        end unless defined? eager_load_fog
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
128 
     | 
    
         
             
                        def self.#{name}(value=nil)
         
     | 
| 
       130 
     | 
    
         
            -
                          @#{name} = value  
     | 
| 
       131 
     | 
    
         
            -
                          eager_load_fog(value) if value && '#{name}' == 'fog_credentials'
         
     | 
| 
      
 129 
     | 
    
         
            +
                          @#{name} = value unless value.nil?
         
     | 
| 
       132 
130 
     | 
    
         
             
                          return @#{name} if self.object_id == #{self.object_id} || defined?(@#{name})
         
     | 
| 
       133 
131 
     | 
    
         
             
                          name = superclass.#{name}
         
     | 
| 
       134 
132 
     | 
    
         
             
                          return nil if name.nil? && !instance_variable_defined?(:@#{name})
         
     | 
| 
         @@ -136,12 +134,10 @@ module CarrierWave 
     | 
|
| 
       136 
134 
     | 
    
         
             
                        end
         
     | 
| 
       137 
135 
     | 
    
         | 
| 
       138 
136 
     | 
    
         
             
                        def self.#{name}=(value)
         
     | 
| 
       139 
     | 
    
         
            -
                          eager_load_fog(value) if '#{name}' == 'fog_credentials' && value.present?
         
     | 
| 
       140 
137 
     | 
    
         
             
                          @#{name} = value
         
     | 
| 
       141 
138 
     | 
    
         
             
                        end
         
     | 
| 
       142 
139 
     | 
    
         | 
| 
       143 
140 
     | 
    
         
             
                        def #{name}=(value)
         
     | 
| 
       144 
     | 
    
         
            -
                          self.class.eager_load_fog(value) if '#{name}' == 'fog_credentials' && value.present?
         
     | 
| 
       145 
141 
     | 
    
         
             
                          @#{name} = value
         
     | 
| 
       146 
142 
     | 
    
         
             
                        end
         
     | 
| 
       147 
143 
     | 
    
         | 
| 
         @@ -157,6 +153,26 @@ module CarrierWave 
     | 
|
| 
       157 
153 
     | 
    
         
             
                      RUBY
         
     | 
| 
       158 
154 
     | 
    
         
             
                    end
         
     | 
| 
       159 
155 
     | 
    
         | 
| 
      
 156 
     | 
    
         
            +
                    def add_deprecated_config(name)
         
     | 
| 
      
 157 
     | 
    
         
            +
                      class_eval <<-RUBY, __FILE__, __LINE__ + 1
         
     | 
| 
      
 158 
     | 
    
         
            +
                        def self.#{name}(value=nil)
         
     | 
| 
      
 159 
     | 
    
         
            +
                          ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
         
     | 
| 
      
 160 
     | 
    
         
            +
                        end
         
     | 
| 
      
 161 
     | 
    
         
            +
             
     | 
| 
      
 162 
     | 
    
         
            +
                        def self.#{name}=(value)
         
     | 
| 
      
 163 
     | 
    
         
            +
                          ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
         
     | 
| 
      
 164 
     | 
    
         
            +
                        end
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
                        def #{name}=(value)
         
     | 
| 
      
 167 
     | 
    
         
            +
                          ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
         
     | 
| 
      
 168 
     | 
    
         
            +
                        end
         
     | 
| 
      
 169 
     | 
    
         
            +
             
     | 
| 
      
 170 
     | 
    
         
            +
                        def #{name}
         
     | 
| 
      
 171 
     | 
    
         
            +
                          ActiveSupport::Deprecation.warn "##{name} is deprecated and has no effect"
         
     | 
| 
      
 172 
     | 
    
         
            +
                        end
         
     | 
| 
      
 173 
     | 
    
         
            +
                      RUBY
         
     | 
| 
      
 174 
     | 
    
         
            +
                    end
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
       160 
176 
     | 
    
         
             
                    def configure
         
     | 
| 
       161 
177 
     | 
    
         
             
                      yield self
         
     | 
| 
       162 
178 
     | 
    
         
             
                    end
         
     | 
| 
         @@ -166,15 +182,14 @@ module CarrierWave 
     | 
|
| 
       166 
182 
     | 
    
         
             
                    #
         
     | 
| 
       167 
183 
     | 
    
         
             
                    def reset_config
         
     | 
| 
       168 
184 
     | 
    
         
             
                      configure do |config|
         
     | 
| 
       169 
     | 
    
         
            -
                        config.permissions =  
     | 
| 
       170 
     | 
    
         
            -
                        config.directory_permissions =  
     | 
| 
      
 185 
     | 
    
         
            +
                        config.permissions = 0o644
         
     | 
| 
      
 186 
     | 
    
         
            +
                        config.directory_permissions = 0o755
         
     | 
| 
       171 
187 
     | 
    
         
             
                        config.storage_engines = {
         
     | 
| 
       172 
188 
     | 
    
         
             
                          :file => "CarrierWave::Storage::File",
         
     | 
| 
       173 
189 
     | 
    
         
             
                          :fog  => "CarrierWave::Storage::Fog"
         
     | 
| 
       174 
190 
     | 
    
         
             
                        }
         
     | 
| 
       175 
191 
     | 
    
         
             
                        config.storage = :file
         
     | 
| 
       176 
     | 
    
         
            -
                        config.cache_storage =  
     | 
| 
       177 
     | 
    
         
            -
                        config.fog_provider = 'fog'
         
     | 
| 
      
 192 
     | 
    
         
            +
                        config.cache_storage = nil
         
     | 
| 
       178 
193 
     | 
    
         
             
                        config.fog_attributes = {}
         
     | 
| 
       179 
194 
     | 
    
         
             
                        config.fog_credentials = {}
         
     | 
| 
       180 
195 
     | 
    
         
             
                        config.fog_public = true
         
     | 
| 
         @@ -187,6 +202,8 @@ module CarrierWave 
     | 
|
| 
       187 
202 
     | 
    
         
             
                        config.move_to_cache = false
         
     | 
| 
       188 
203 
     | 
    
         
             
                        config.move_to_store = false
         
     | 
| 
       189 
204 
     | 
    
         
             
                        config.remove_previously_stored_files_after_update = true
         
     | 
| 
      
 205 
     | 
    
         
            +
                        config.downloader = CarrierWave::Downloader::Base
         
     | 
| 
      
 206 
     | 
    
         
            +
                        config.force_extension = false
         
     | 
| 
       190 
207 
     | 
    
         
             
                        config.ignore_integrity_errors = true
         
     | 
| 
       191 
208 
     | 
    
         
             
                        config.ignore_processing_errors = true
         
     | 
| 
       192 
209 
     | 
    
         
             
                        config.ignore_download_errors = true
         
     | 
| 
         @@ -197,6 +214,8 @@ module CarrierWave 
     | 
|
| 
       197 
214 
     | 
    
         
             
                        config.base_path = CarrierWave.base_path
         
     | 
| 
       198 
215 
     | 
    
         
             
                        config.enable_processing = true
         
     | 
| 
       199 
216 
     | 
    
         
             
                        config.ensure_multipart_form = true
         
     | 
| 
      
 217 
     | 
    
         
            +
                        config.download_retry_count = 0
         
     | 
| 
      
 218 
     | 
    
         
            +
                        config.download_retry_wait_time = 5
         
     | 
| 
       200 
219 
     | 
    
         
             
                      end
         
     | 
| 
       201 
220 
     | 
    
         
             
                    end
         
     | 
| 
       202 
221 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module CarrierWave
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Uploader
         
     | 
| 
      
 3 
     | 
    
         
            +
                module ContentTypeAllowlist
         
     | 
| 
      
 4 
     | 
    
         
            +
                  extend ActiveSupport::Concern
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  included do
         
     | 
| 
      
 7 
     | 
    
         
            +
                    before :cache, :check_content_type_allowlist!
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # Override this method in your uploader to provide an allowlist of files content types
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # which are allowed to be uploaded.
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # Not only strings but Regexp are allowed as well.
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # === Returns
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # [NilClass, String, Regexp, Array[String, Regexp]] an allowlist of content types which are allowed to be uploaded
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # === Examples
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #     def content_type_allowlist
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #       %w(text/json application/json)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # Basically the same, but using a Regexp:
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #     def content_type_allowlist
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #       [/(text|application)\/json/]
         
     | 
| 
      
 29 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 30 
     | 
    
         
            +
                  #
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def content_type_allowlist
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                private
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  def check_content_type_allowlist!(new_file)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    allowlist = content_type_allowlist
         
     | 
| 
      
 38 
     | 
    
         
            +
                    if !allowlist && respond_to?(:content_type_whitelist) && content_type_whitelist
         
     | 
| 
      
 39 
     | 
    
         
            +
                      ActiveSupport::Deprecation.warn "#content_type_whitelist is deprecated, use #content_type_allowlist instead." unless instance_variable_defined?(:@content_type_whitelist_warned)
         
     | 
| 
      
 40 
     | 
    
         
            +
                      @content_type_whitelist_warned = true
         
     | 
| 
      
 41 
     | 
    
         
            +
                      allowlist = content_type_whitelist
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    return unless allowlist
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    content_type = new_file.content_type
         
     | 
| 
      
 47 
     | 
    
         
            +
                    if !allowlisted_content_type?(allowlist, content_type)
         
     | 
| 
      
 48 
     | 
    
         
            +
                      raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_allowlist_error", content_type: content_type,
         
     | 
| 
      
 49 
     | 
    
         
            +
                                                                        allowed_types: Array(allowlist).join(", "), default: :"errors.messages.content_type_whitelist_error")
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                  def allowlisted_content_type?(allowlist, content_type)
         
     | 
| 
      
 54 
     | 
    
         
            +
                    Array(allowlist).any? do |item|
         
     | 
| 
      
 55 
     | 
    
         
            +
                      item = Regexp.quote(item) if item.class != Regexp
         
     | 
| 
      
 56 
     | 
    
         
            +
                      content_type =~ /\A#{item}/
         
     | 
| 
      
 57 
     | 
    
         
            +
                    end
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                end # ContentTypeAllowlist
         
     | 
| 
      
 61 
     | 
    
         
            +
              end # Uploader
         
     | 
| 
      
 62 
     | 
    
         
            +
            end # CarrierWave
         
     | 
| 
         @@ -0,0 +1,62 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module CarrierWave
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Uploader
         
     | 
| 
      
 3 
     | 
    
         
            +
                module ContentTypeDenylist
         
     | 
| 
      
 4 
     | 
    
         
            +
                  extend ActiveSupport::Concern
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                  included do
         
     | 
| 
      
 7 
     | 
    
         
            +
                    before :cache, :check_content_type_denylist!
         
     | 
| 
      
 8 
     | 
    
         
            +
                  end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # Override this method in your uploader to provide a denylist of files content types
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # which are not allowed to be uploaded.
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # Not only strings but Regexp are allowed as well.
         
     | 
| 
      
 14 
     | 
    
         
            +
                  #
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # === Returns
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # [NilClass, String, Regexp, Array[String, Regexp]] a denylist of content types which are not allowed to be uploaded
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # === Examples
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #     def content_type_denylist
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #       %w(text/json application/json)
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #
         
     | 
| 
      
 25 
     | 
    
         
            +
                  # Basically the same, but using a Regexp:
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #     def content_type_denylist
         
     | 
| 
      
 28 
     | 
    
         
            +
                  #       [/(text|application)\/json/]
         
     | 
| 
      
 29 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 30 
     | 
    
         
            +
                  #
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def content_type_denylist
         
     | 
| 
      
 32 
     | 
    
         
            +
                  end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                private
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
      
 36 
     | 
    
         
            +
                  def check_content_type_denylist!(new_file)
         
     | 
| 
      
 37 
     | 
    
         
            +
                    denylist = content_type_denylist
         
     | 
| 
      
 38 
     | 
    
         
            +
                    if !denylist && respond_to?(:content_type_blacklist) && content_type_blacklist
         
     | 
| 
      
 39 
     | 
    
         
            +
                      ActiveSupport::Deprecation.warn "#content_type_blacklist is deprecated, use #content_type_denylist instead." unless instance_variable_defined?(:@content_type_blacklist_warned)
         
     | 
| 
      
 40 
     | 
    
         
            +
                      @content_type_blacklist_warned = true
         
     | 
| 
      
 41 
     | 
    
         
            +
                      denylist = content_type_blacklist
         
     | 
| 
      
 42 
     | 
    
         
            +
                    end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                    return unless denylist
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                    ActiveSupport::Deprecation.warn "Use of #content_type_denylist is deprecated for the security reason, use #content_type_allowlist instead to explicitly state what are safe to accept" unless instance_variable_defined?(:@content_type_denylist_warned)
         
     | 
| 
      
 47 
     | 
    
         
            +
                    @content_type_denylist_warned = true
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    content_type = new_file.content_type
         
     | 
| 
      
 50 
     | 
    
         
            +
                    if denylisted_content_type?(denylist, content_type)
         
     | 
| 
      
 51 
     | 
    
         
            +
                      raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_denylist_error",
         
     | 
| 
      
 52 
     | 
    
         
            +
                                                                        content_type: content_type, default: :"errors.messages.content_type_blacklist_error")
         
     | 
| 
      
 53 
     | 
    
         
            +
                    end
         
     | 
| 
      
 54 
     | 
    
         
            +
                  end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  def denylisted_content_type?(denylist, content_type)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    Array(denylist).any? { |item| content_type =~ /#{item}/ }
         
     | 
| 
      
 58 
     | 
    
         
            +
                  end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                end # ContentTypeDenylist
         
     | 
| 
      
 61 
     | 
    
         
            +
              end # Uploader
         
     | 
| 
      
 62 
     | 
    
         
            +
            end # CarrierWave
         
     | 
| 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'active_support'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            module CarrierWave
         
     | 
| 
      
 4 
     | 
    
         
            +
              module Uploader
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Dimension
         
     | 
| 
      
 6 
     | 
    
         
            +
                  extend ActiveSupport::Concern
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                  included do
         
     | 
| 
      
 9 
     | 
    
         
            +
                    before :cache, :check_dimensions!
         
     | 
| 
      
 10 
     | 
    
         
            +
                  end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # Override this method in your uploader to provide a Range of width which
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # are allowed to be uploaded.
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # === Returns
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # [NilClass, Range] a width range which are permitted to be uploaded
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #
         
     | 
| 
      
 19 
     | 
    
         
            +
                  # === Examples
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #
         
     | 
| 
      
 21 
     | 
    
         
            +
                  #     def width_range
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #       1000..2000
         
     | 
| 
      
 23 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #
         
     | 
| 
      
 25 
     | 
    
         
            +
                  def width_range; end
         
     | 
| 
      
 26 
     | 
    
         
            +
             
     | 
| 
      
 27 
     | 
    
         
            +
                  ##
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # Override this method in your uploader to provide a Range of height which
         
     | 
| 
      
 29 
     | 
    
         
            +
                  # are allowed to be uploaded.
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # === Returns
         
     | 
| 
      
 31 
     | 
    
         
            +
                  #
         
     | 
| 
      
 32 
     | 
    
         
            +
                  # [NilClass, Range] a height range which are permitted to be uploaded
         
     | 
| 
      
 33 
     | 
    
         
            +
                  #
         
     | 
| 
      
 34 
     | 
    
         
            +
                  # === Examples
         
     | 
| 
      
 35 
     | 
    
         
            +
                  #
         
     | 
| 
      
 36 
     | 
    
         
            +
                  #     def height_range
         
     | 
| 
      
 37 
     | 
    
         
            +
                  #       1000..
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #     end
         
     | 
| 
      
 39 
     | 
    
         
            +
                  #
         
     | 
| 
      
 40 
     | 
    
         
            +
                  def height_range; end
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                private
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                  def check_dimensions!(new_file)
         
     | 
| 
      
 45 
     | 
    
         
            +
                    # NOTE: Skip the check for resized images
         
     | 
| 
      
 46 
     | 
    
         
            +
                    return if version_name.present?
         
     | 
| 
      
 47 
     | 
    
         
            +
                    return unless width_range || height_range
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
                    unless respond_to?(:width) || respond_to?(:height)
         
     | 
| 
      
 50 
     | 
    
         
            +
                      raise 'You need to include one of CarrierWave::MiniMagick, CarrierWave::RMagick, or CarrierWave::Vips to perform image dimension validation'
         
     | 
| 
      
 51 
     | 
    
         
            +
                    end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    if width_range&.begin && width < width_range.begin
         
     | 
| 
      
 54 
     | 
    
         
            +
                      raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.min_width_error", :min_width => ActiveSupport::NumberHelper.number_to_delimited(width_range.begin))
         
     | 
| 
      
 55 
     | 
    
         
            +
                    elsif width_range&.end && width > width_range.end
         
     | 
| 
      
 56 
     | 
    
         
            +
                      raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.max_width_error", :max_width => ActiveSupport::NumberHelper.number_to_delimited(width_range.end))
         
     | 
| 
      
 57 
     | 
    
         
            +
                    elsif height_range&.begin && height < height_range.begin
         
     | 
| 
      
 58 
     | 
    
         
            +
                      raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.min_height_error", :min_height => ActiveSupport::NumberHelper.number_to_delimited(height_range.begin))
         
     | 
| 
      
 59 
     | 
    
         
            +
                    elsif height_range&.end && height > height_range.end
         
     | 
| 
      
 60 
     | 
    
         
            +
                      raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.max_height_error", :max_height => ActiveSupport::NumberHelper.number_to_delimited(height_range.end))
         
     | 
| 
      
 61 
     | 
    
         
            +
                    end
         
     | 
| 
      
 62 
     | 
    
         
            +
                  end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                end # Dimension
         
     | 
| 
      
 65 
     | 
    
         
            +
              end # Uploader
         
     | 
| 
      
 66 
     | 
    
         
            +
            end # CarrierWave
         
     | 
| 
         @@ -1,6 +1,3 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'open-uri'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'ssrf_filter'
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
1 
     | 
    
         
             
            module CarrierWave
         
     | 
| 
       5 
2 
     | 
    
         
             
              module Uploader
         
     | 
| 
       6 
3 
     | 
    
         
             
                module Download
         
     | 
| 
         @@ -10,87 +7,8 @@ module CarrierWave 
     | 
|
| 
       10 
7 
     | 
    
         
             
                  include CarrierWave::Uploader::Configuration
         
     | 
| 
       11 
8 
     | 
    
         
             
                  include CarrierWave::Uploader::Cache
         
     | 
| 
       12 
9 
     | 
    
         | 
| 
       13 
     | 
    
         
            -
                  class RemoteFile
         
     | 
| 
       14 
     | 
    
         
            -
                    attr_reader :uri
         
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
                    def initialize(uri, remote_headers = {}, skip_ssrf_protection: false)
         
     | 
| 
       17 
     | 
    
         
            -
                      @uri = uri
         
     | 
| 
       18 
     | 
    
         
            -
                      @remote_headers = remote_headers.reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}")
         
     | 
| 
       19 
     | 
    
         
            -
                      @file, @content_type, @headers = nil
         
     | 
| 
       20 
     | 
    
         
            -
                      @skip_ssrf_protection = skip_ssrf_protection
         
     | 
| 
       21 
     | 
    
         
            -
                    end
         
     | 
| 
       22 
     | 
    
         
            -
             
     | 
| 
       23 
     | 
    
         
            -
                    def original_filename
         
     | 
| 
       24 
     | 
    
         
            -
                      filename = filename_from_header || filename_from_uri
         
     | 
| 
       25 
     | 
    
         
            -
                      mime_type = MIME::Types[content_type].first
         
     | 
| 
       26 
     | 
    
         
            -
                      unless File.extname(filename).present? || mime_type.blank?
         
     | 
| 
       27 
     | 
    
         
            -
                        filename = "#{filename}.#{mime_type.extensions.first}"
         
     | 
| 
       28 
     | 
    
         
            -
                      end
         
     | 
| 
       29 
     | 
    
         
            -
                      filename
         
     | 
| 
       30 
     | 
    
         
            -
                    end
         
     | 
| 
       31 
     | 
    
         
            -
             
     | 
| 
       32 
     | 
    
         
            -
                    def respond_to?(*args)
         
     | 
| 
       33 
     | 
    
         
            -
                      super or file.respond_to?(*args)
         
     | 
| 
       34 
     | 
    
         
            -
                    end
         
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
                    def http?
         
     | 
| 
       37 
     | 
    
         
            -
                      @uri.scheme =~ /^https?$/
         
     | 
| 
       38 
     | 
    
         
            -
                    end
         
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
                    def content_type
         
     | 
| 
       41 
     | 
    
         
            -
                      @content_type || 'application/octet-stream'
         
     | 
| 
       42 
     | 
    
         
            -
                    end
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                    def headers
         
     | 
| 
       45 
     | 
    
         
            -
                      @headers || {}
         
     | 
| 
       46 
     | 
    
         
            -
                    end
         
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
                    private
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                    def file
         
     | 
| 
       51 
     | 
    
         
            -
                      if @file.blank?
         
     | 
| 
       52 
     | 
    
         
            -
                        if @skip_ssrf_protection
         
     | 
| 
       53 
     | 
    
         
            -
                          @file = (URI.respond_to?(:open) ? URI : Kernel).open(@uri.to_s, @remote_headers)
         
     | 
| 
       54 
     | 
    
         
            -
                          @file = @file.is_a?(String) ? StringIO.new(@file) : @file
         
     | 
| 
       55 
     | 
    
         
            -
                          @content_type = @file.content_type
         
     | 
| 
       56 
     | 
    
         
            -
                          @headers = @file.meta
         
     | 
| 
       57 
     | 
    
         
            -
                          @uri = @file.base_uri
         
     | 
| 
       58 
     | 
    
         
            -
                        else
         
     | 
| 
       59 
     | 
    
         
            -
                          request = nil
         
     | 
| 
       60 
     | 
    
         
            -
                          response = SsrfFilter.get(@uri, headers: @remote_headers) do |req|
         
     | 
| 
       61 
     | 
    
         
            -
                            request = req
         
     | 
| 
       62 
     | 
    
         
            -
                          end
         
     | 
| 
       63 
     | 
    
         
            -
                          response.value
         
     | 
| 
       64 
     | 
    
         
            -
                          @file = StringIO.new(response.body)
         
     | 
| 
       65 
     | 
    
         
            -
                          @content_type = response.content_type
         
     | 
| 
       66 
     | 
    
         
            -
                          @headers = response
         
     | 
| 
       67 
     | 
    
         
            -
                          @uri = request.uri
         
     | 
| 
       68 
     | 
    
         
            -
                        end
         
     | 
| 
       69 
     | 
    
         
            -
                      end
         
     | 
| 
       70 
     | 
    
         
            -
                      @file
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                    rescue StandardError => e
         
     | 
| 
       73 
     | 
    
         
            -
                      raise CarrierWave::DownloadError, "could not download file: #{e.message}"
         
     | 
| 
       74 
     | 
    
         
            -
                    end
         
     | 
| 
       75 
     | 
    
         
            -
             
     | 
| 
       76 
     | 
    
         
            -
                    def filename_from_header
         
     | 
| 
       77 
     | 
    
         
            -
                      if headers['content-disposition']
         
     | 
| 
       78 
     | 
    
         
            -
                        match = headers['content-disposition'].match(/filename="?([^"]+)/)
         
     | 
| 
       79 
     | 
    
         
            -
                        return match[1] unless match.nil? || match[1].empty?
         
     | 
| 
       80 
     | 
    
         
            -
                      end
         
     | 
| 
       81 
     | 
    
         
            -
                    end
         
     | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
                    def filename_from_uri
         
     | 
| 
       84 
     | 
    
         
            -
                      URI::DEFAULT_PARSER.unescape(File.basename(@uri.path))
         
     | 
| 
       85 
     | 
    
         
            -
                    end
         
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
                    def method_missing(*args, &block)
         
     | 
| 
       88 
     | 
    
         
            -
                      file.send(*args, &block)
         
     | 
| 
       89 
     | 
    
         
            -
                    end
         
     | 
| 
       90 
     | 
    
         
            -
                  end
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
10 
     | 
    
         
             
                  ##
         
     | 
| 
       93 
     | 
    
         
            -
                  # Caches the file by downloading it from the given URL.
         
     | 
| 
      
 11 
     | 
    
         
            +
                  # Caches the file by downloading it from the given URL, using downloader.
         
     | 
| 
       94 
12 
     | 
    
         
             
                  #
         
     | 
| 
       95 
13 
     | 
    
         
             
                  # === Parameters
         
     | 
| 
       96 
14 
     | 
    
         
             
                  #
         
     | 
| 
         @@ -98,48 +16,9 @@ module CarrierWave 
     | 
|
| 
       98 
16 
     | 
    
         
             
                  # [remote_headers (Hash)] Request headers
         
     | 
| 
       99 
17 
     | 
    
         
             
                  #
         
     | 
| 
       100 
18 
     | 
    
         
             
                  def download!(uri, remote_headers = {})
         
     | 
| 
       101 
     | 
    
         
            -
                     
     | 
| 
       102 
     | 
    
         
            -
                    file = RemoteFile.new(processed_uri, remote_headers, skip_ssrf_protection: skip_ssrf_protection?(processed_uri))
         
     | 
| 
       103 
     | 
    
         
            -
                    raise CarrierWave::DownloadError, "trying to download a file which is not served over HTTP" unless file.http?
         
     | 
| 
      
 19 
     | 
    
         
            +
                    file = downloader.new(self).download(uri, remote_headers)
         
     | 
| 
       104 
20 
     | 
    
         
             
                    cache!(file)
         
     | 
| 
       105 
21 
     | 
    
         
             
                  end
         
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
       107 
     | 
    
         
            -
                  ##
         
     | 
| 
       108 
     | 
    
         
            -
                  # Processes the given URL by parsing and escaping it. Public to allow overriding.
         
     | 
| 
       109 
     | 
    
         
            -
                  #
         
     | 
| 
       110 
     | 
    
         
            -
                  # === Parameters
         
     | 
| 
       111 
     | 
    
         
            -
                  #
         
     | 
| 
       112 
     | 
    
         
            -
                  # [url (String)] The URL where the remote file is stored
         
     | 
| 
       113 
     | 
    
         
            -
                  #
         
     | 
| 
       114 
     | 
    
         
            -
                  def process_uri(uri)
         
     | 
| 
       115 
     | 
    
         
            -
                    URI.parse(uri)
         
     | 
| 
       116 
     | 
    
         
            -
                  rescue URI::InvalidURIError
         
     | 
| 
       117 
     | 
    
         
            -
                    uri_parts = uri.split('?')
         
     | 
| 
       118 
     | 
    
         
            -
                    # regexp from Ruby's URI::Parser#regexp[:UNSAFE], with [] specifically removed
         
     | 
| 
       119 
     | 
    
         
            -
                    encoded_uri = URI::DEFAULT_PARSER.escape(uri_parts.shift, /[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,]/)
         
     | 
| 
       120 
     | 
    
         
            -
                    encoded_uri << '?' << URI::DEFAULT_PARSER.escape(uri_parts.join('?')) if uri_parts.any?
         
     | 
| 
       121 
     | 
    
         
            -
                    URI.parse(encoded_uri) rescue raise CarrierWave::DownloadError, "couldn't parse URL"
         
     | 
| 
       122 
     | 
    
         
            -
                  end
         
     | 
| 
       123 
     | 
    
         
            -
             
     | 
| 
       124 
     | 
    
         
            -
                  ##
         
     | 
| 
       125 
     | 
    
         
            -
                  # If this returns true, SSRF protection will be bypassed.
         
     | 
| 
       126 
     | 
    
         
            -
                  # You can override this if you want to allow accessing specific local URIs that are not SSRF exploitable.
         
     | 
| 
       127 
     | 
    
         
            -
                  #
         
     | 
| 
       128 
     | 
    
         
            -
                  # === Parameters
         
     | 
| 
       129 
     | 
    
         
            -
                  #
         
     | 
| 
       130 
     | 
    
         
            -
                  # [uri (URI)] The URI where the remote file is stored
         
     | 
| 
       131 
     | 
    
         
            -
                  #
         
     | 
| 
       132 
     | 
    
         
            -
                  # === Examples
         
     | 
| 
       133 
     | 
    
         
            -
                  #
         
     | 
| 
       134 
     | 
    
         
            -
                  #     class MyUploader < CarrierWave::Uploader::Base
         
     | 
| 
       135 
     | 
    
         
            -
                  #       def skip_ssrf_protection?(uri)
         
     | 
| 
       136 
     | 
    
         
            -
                  #         uri.hostname == 'localhost' && uri.port == 80
         
     | 
| 
       137 
     | 
    
         
            -
                  #       end
         
     | 
| 
       138 
     | 
    
         
            -
                  #     end
         
     | 
| 
       139 
     | 
    
         
            -
                  #
         
     | 
| 
       140 
     | 
    
         
            -
                  def skip_ssrf_protection?(uri)
         
     | 
| 
       141 
     | 
    
         
            -
                    false
         
     | 
| 
       142 
     | 
    
         
            -
                  end
         
     | 
| 
       143 
22 
     | 
    
         
             
                end # Download
         
     | 
| 
       144 
23 
     | 
    
         
             
              end # Uploader
         
     | 
| 
       145 
24 
     | 
    
         
             
            end # CarrierWave
         
     |