carrierwave 1.2.1 → 1.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 7a985080c1432e2d6d27cbe46b823f4ca5bdbc75
4
- data.tar.gz: e3e60a7159815499f0559f9574480633e73d68ed
2
+ SHA256:
3
+ metadata.gz: d5e6d1dd656203f5e14c43b75b807e793d623126006ddf8c5a4f9f4706faa750
4
+ data.tar.gz: 82281b939837f54716f580a1deb6397d87cc9bb867dc6a6938a4322d795500b1
5
5
  SHA512:
6
- metadata.gz: 9e027080e1e7fcc86a2f6e0b74cb0d4db2d1f955b733c9bf47354fb74da0707c0017a9305fdc6f090825a7203b319f25bcd61ecc2fa84f123da19a9b9d784d6c
7
- data.tar.gz: 2c8d16ca3353952282e9ffb97d37d44477c5a22f4744f99a84d0fde7bc0ee9d6b456698aa386d0664e8b734f9b8edf155a8034b15efb5b60831579a610d09b67
6
+ metadata.gz: 8eca3a53204f520d0cabf6e2384e6670cb634159e4b6c1e8d3915b6bab8473f21fb116047129b142a986588ea27401131d095186657770d7c31b03d6c929a1c9
7
+ data.tar.gz: 316797cffad6d2568e50929862cd80ba3e56c05d557f70f8631b66fc60ffd65fe5862afe4121a1e5e775e55a5f6c9a6c14414bce6a57deab2d12a8a127ed5b0d
data/README.md CHANGED
@@ -4,8 +4,8 @@ This gem provides a simple and extremely flexible way to upload files from Ruby
4
4
  It works well with Rack based web applications, such as Ruby on Rails.
5
5
 
6
6
  [![Build Status](https://travis-ci.org/carrierwaveuploader/carrierwave.svg?branch=master)](http://travis-ci.org/carrierwaveuploader/carrierwave)
7
- [![Code Climate](http://img.shields.io/codeclimate/github/carrierwaveuploader/carrierwave.svg)](https://codeclimate.com/github/carrierwaveuploader/carrierwave)
8
- [![git.legal](https://git.legal/projects/1363/badge.svg "Number of libraries approved")](https://git.legal/projects/1363)
7
+ [![Code Climate](https://codeclimate.com/github/carrierwaveuploader/carrierwave.svg)](https://codeclimate.com/github/carrierwaveuploader/carrierwave)
8
+ [![SemVer](https://api.dependabot.com/badges/compatibility_score?dependency-name=carrierwave&package-manager=bundler&version-scheme=semver)](https://dependabot.com/compatibility-score.html?dependency-name=carrierwave&package-manager=bundler&version-scheme=semver)
9
9
 
10
10
 
11
11
  ## Information
@@ -24,7 +24,7 @@ It works well with Rack based web applications, such as Ruby on Rails.
24
24
  Install the latest release:
25
25
 
26
26
  ```
27
- $ gem install carrierwave -v "1.0.0"
27
+ $ gem install carrierwave
28
28
  ```
29
29
 
30
30
  In Rails, add it to your Gemfile:
@@ -89,7 +89,7 @@ a migration:
89
89
 
90
90
 
91
91
  rails g migration add_avatar_to_users avatar:string
92
- rake db:migrate
92
+ rails db:migrate
93
93
 
94
94
  Open your model file and mount the uploader:
95
95
 
@@ -144,12 +144,12 @@ example, create a migration like this:
144
144
  #### For databases with ActiveRecord json data type support (e.g. PostgreSQL, MySQL)
145
145
 
146
146
  rails g migration add_avatars_to_users avatars:json
147
- rake db:migrate
147
+ rails db:migrate
148
148
 
149
149
  #### For database without ActiveRecord json data type support (e.g. SQLite)
150
150
 
151
151
  rails g migration add_avatars_to_users avatars:string
152
- rake db:migrate
152
+ rails db:migrate
153
153
 
154
154
  __Note__: JSON datatype doesn't exists in SQLite adapter, that's why you can use a string datatype which will be serialized in model.
155
155
 
@@ -163,6 +163,9 @@ class User < ActiveRecord::Base
163
163
  end
164
164
  ```
165
165
 
166
+ Make sure that you mount the uploader with write (mount_uploaders) with `s` not (mount_uploader)
167
+ in order to avoid errors when uploading multiple files
168
+
166
169
  Make sure your file input fields are set up as multiple file fields. For
167
170
  example in Rails you'll want to do something like this:
168
171
 
@@ -301,8 +304,16 @@ end
301
304
  ```
302
305
 
303
306
  When this uploader is used, an uploaded image would be scaled to be no larger
304
- than 800 by 800 pixels. A version called thumb is then created, which is scaled
305
- and cropped to exactly 200 by 200 pixels. The uploader could be used like this:
307
+ than 800 by 800 pixels. The original aspect ratio will be kept.
308
+ A version called thumb is then created, which is scaled
309
+ to exactly 200 by 200 pixels.
310
+
311
+ If you would like to crop images to a specific height and width you
312
+ can use the alternative option of '''resize_to_fill'''. It will make sure
313
+ that the width and height specified are filled, only cropping
314
+ if the aspect ratio requires it.
315
+
316
+ The uploader could be used like this:
306
317
 
307
318
  ```ruby
308
319
  uploader = AvatarUploader.new
@@ -625,6 +636,8 @@ describe MyUploader do
625
636
  end
626
637
  ```
627
638
 
639
+ If you're looking for minitest asserts, checkout [carrierwave_asserts](https://github.com/hcfairbanks/carrierwave_asserts).
640
+
628
641
  Setting the enable_processing flag on an uploader will prevent any of the versions from processing as well.
629
642
  Processing can be enabled for a single version by setting the processing flag on the version like so:
630
643
 
@@ -659,15 +672,16 @@ CarrierWave.configure do |config|
659
672
  config.fog_provider = 'fog/aws' # required
660
673
  config.fog_credentials = {
661
674
  provider: 'AWS', # required
662
- aws_access_key_id: 'xxx', # required
663
- aws_secret_access_key: 'yyy', # required
675
+ aws_access_key_id: 'xxx', # required unless using use_iam_profile
676
+ aws_secret_access_key: 'yyy', # required unless using use_iam_profile
677
+ use_iam_profile: true, # optional, defaults to false
664
678
  region: 'eu-west-1', # optional, defaults to 'us-east-1'
665
679
  host: 's3.example.com', # optional, defaults to nil
666
680
  endpoint: 'https://s3.example.com:8080' # optional, defaults to nil
667
681
  }
668
- config.fog_directory = 'name_of_directory' # required
669
- config.fog_public = false # optional, defaults to true
670
- config.fog_attributes = { cache_control: "public, max-age=#{365.day.to_i}" } # optional, defaults to {}
682
+ config.fog_directory = 'name_of_bucket' # required
683
+ config.fog_public = false # optional, defaults to true
684
+ config.fog_attributes = { cache_control: "public, max-age=#{365.days.to_i}" } # optional, defaults to {}
671
685
  end
672
686
  ```
673
687
 
@@ -912,7 +926,7 @@ errors:
912
926
  carrierwave_download_error: could not be downloaded
913
927
  extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
914
928
  extension_blacklist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
915
- content_type_whitelist_error: "You are not allowed to upload %{content_type} files"
929
+ content_type_whitelist_error: "You are not allowed to upload %{content_type} files, allowed types: %{allowed_types}"
916
930
  content_type_blacklist_error: "You are not allowed to upload %{content_type} files"
917
931
  rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image?"
918
932
  mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}"
data/lib/carrierwave.rb CHANGED
@@ -34,6 +34,26 @@ if defined?(Merb)
34
34
  Dir.glob(File.join(Merb.load_paths[:uploaders])).each {|f| require f }
35
35
  end
36
36
 
37
+ elsif defined?(Jets)
38
+
39
+ module CarrierWave
40
+ class Turbine < Jets::Turbine
41
+ initializer "carrierwave.setup_paths" do |app|
42
+ CarrierWave.root = Jets.root.to_s
43
+ CarrierWave.tmp_path = "/tmp/carrierwave"
44
+ CarrierWave.configure do |config|
45
+ config.cache_dir = "/tmp/carrierwave/uploads/tmp"
46
+ end
47
+ end
48
+
49
+ initializer "carrierwave.active_record" do
50
+ ActiveSupport.on_load :active_record do
51
+ require 'carrierwave/orm/activerecord'
52
+ end
53
+ end
54
+ end
55
+ end
56
+
37
57
  elsif defined?(Rails)
38
58
 
39
59
  module CarrierWave
@@ -6,7 +6,7 @@ en:
6
6
  carrierwave_download_error: could not be downloaded
7
7
  extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
8
8
  extension_blacklist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
9
- content_type_whitelist_error: "You are not allowed to upload %{content_type} files"
9
+ content_type_whitelist_error: "You are not allowed to upload %{content_type} files, allowed types: %{allowed_types}"
10
10
  content_type_blacklist_error: "You are not allowed to upload %{content_type} files"
11
11
  rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image?"
12
12
  mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}"
@@ -61,6 +61,13 @@ module CarrierWave
61
61
  e.message << " (You may need to install the mini_magick gem)"
62
62
  raise e
63
63
  end
64
+
65
+ prepend Module.new {
66
+ def initialize(*)
67
+ super
68
+ @format = nil
69
+ end
70
+ }
64
71
  end
65
72
 
66
73
  module ClassMethods
@@ -303,6 +310,7 @@ module CarrierWave
303
310
 
304
311
  if @format
305
312
  move_to = current_path.chomp(File.extname(current_path)) + ".#{@format}"
313
+ file.content_type = ::MIME::Types.type_for(move_to).first.to_s
306
314
  file.move_to(move_to, permissions, directory_permissions)
307
315
  end
308
316
 
@@ -333,11 +341,7 @@ module CarrierWave
333
341
  end
334
342
 
335
343
  def mini_magick_image
336
- if url
337
- ::MiniMagick::Image.open(url)
338
- else
339
- ::MiniMagick::Image.open(current_path)
340
- end
344
+ ::MiniMagick::Image.read(read)
341
345
  end
342
346
 
343
347
  end # MiniMagick
@@ -67,6 +67,13 @@ module CarrierWave
67
67
  e.message << " (You may need to install the rmagick gem)"
68
68
  raise e
69
69
  end
70
+
71
+ prepend Module.new {
72
+ def initialize(*)
73
+ super
74
+ @format = nil
75
+ end
76
+ }
70
77
  end
71
78
 
72
79
  module ClassMethods
@@ -346,7 +353,7 @@ module CarrierWave
346
353
  frames = ::Magick::ImageList.new
347
354
 
348
355
  image.each_with_index do |frame, index|
349
- frame = yield *[frame, index, options].take(block.arity) if block_given?
356
+ frame = yield(*[frame, index, options].take(block.arity)) if block_given?
350
357
  frames << frame if frame
351
358
  end
352
359
  frames.append(true) if block_given?
@@ -356,6 +363,7 @@ module CarrierWave
356
363
  if options[:format] || @format
357
364
  frames.write("#{options[:format] || @format}:#{current_path}", &write_block)
358
365
  move_to = current_path.chomp(File.extname(current_path)) + ".#{options[:format] || @format}"
366
+ file.content_type = ::MIME::Types.type_for(move_to).first.to_s
359
367
  file.move_to(move_to, permissions, directory_permissions)
360
368
  else
361
369
  frames.write(current_path, &write_block)
@@ -370,9 +378,15 @@ module CarrierWave
370
378
 
371
379
  def create_info_block(options)
372
380
  return nil unless options
373
- assignments = options.map { |k, v| "self.#{k} = #{v}" }
374
- code = "lambda { |img| " + assignments.join(";") + "}"
375
- eval code
381
+ proc do |img|
382
+ options.each do |k, v|
383
+ if v.is_a?(String) && (matches = v.match(/^["'](.+)["']/))
384
+ ActiveSupport::Deprecation.warn "Passing quoted strings like #{v} to #manipulate! is deprecated, pass them without quoting."
385
+ v = matches[1]
386
+ end
387
+ img.public_send(:"#{k}=", v)
388
+ end
389
+ end
376
390
  end
377
391
 
378
392
  def destroy_image(image)
@@ -20,7 +20,7 @@ module CarrierWave
20
20
  #
21
21
  class SanitizedFile
22
22
 
23
- attr_accessor :file
23
+ attr_reader :file
24
24
 
25
25
  class << self
26
26
  attr_writer :sanitize_regexp
@@ -32,6 +32,7 @@ module CarrierWave
32
32
 
33
33
  def initialize(file)
34
34
  self.file = file
35
+ @content = nil
35
36
  end
36
37
 
37
38
  ##
@@ -113,12 +114,11 @@ module CarrierWave
113
114
  # [String, nil] the path where the file is located.
114
115
  #
115
116
  def path
116
- unless @file.blank?
117
- if is_path?
118
- File.expand_path(@file)
119
- elsif @file.respond_to?(:path) and not @file.path.blank?
120
- File.expand_path(@file.path)
121
- end
117
+ return if @file.blank?
118
+ if is_path?
119
+ File.expand_path(@file)
120
+ elsif @file.respond_to?(:path) && !@file.path.blank?
121
+ File.expand_path(@file.path)
122
122
  end
123
123
  end
124
124
 
@@ -312,7 +312,7 @@ module CarrierWave
312
312
  def mkdir!(path, directory_permissions)
313
313
  options = {}
314
314
  options[:mode] = directory_permissions if directory_permissions
315
- FileUtils.mkdir_p(File.dirname(path), options) unless File.exist?(File.dirname(path))
315
+ FileUtils.mkdir_p(File.dirname(path), **options) unless File.exist?(File.dirname(path))
316
316
  end
317
317
 
318
318
  def chmod!(path, permissions)
@@ -7,6 +7,10 @@ module CarrierWave
7
7
  # pretty much it.
8
8
  #
9
9
  class File < Abstract
10
+ def initialize(*)
11
+ super
12
+ @cache_called = nil
13
+ end
10
14
 
11
15
  ##
12
16
  # Move the file to the uploader's store path.
@@ -62,7 +66,7 @@ module CarrierWave
62
66
  #
63
67
  def cache!(new_file)
64
68
  new_file.move_to(::File.expand_path(uploader.cache_path, uploader.root), uploader.permissions, uploader.directory_permissions, true)
65
- rescue Errno::EMLINK => e
69
+ rescue Errno::EMLINK, Errno::ENOSPC => e
66
70
  raise(e) if @cache_called
67
71
  @cache_called = true
68
72
 
@@ -177,7 +177,7 @@ module CarrierWave
177
177
 
178
178
  ##
179
179
  # Return a temporary authenticated url to a private file, if available
180
- # Only supported for AWS, Rackspace and Google providers
180
+ # Only supported for AWS, Rackspace, Google and AzureRM providers
181
181
  #
182
182
  # === Returns
183
183
  #
@@ -186,18 +186,22 @@ module CarrierWave
186
186
  # [NilClass] no authenticated url available
187
187
  #
188
188
  def authenticated_url(options = {})
189
- if ['AWS', 'Google', 'Rackspace', 'OpenStack'].include?(@uploader.fog_credentials[:provider])
189
+ if ['AWS', 'Google', 'Rackspace', 'OpenStack', 'AzureRM'].include?(@uploader.fog_credentials[:provider])
190
190
  # avoid a get by using local references
191
191
  local_directory = connection.directories.new(:key => @uploader.fog_directory)
192
192
  local_file = local_directory.files.new(:key => path)
193
193
  expire_at = ::Fog::Time.now + @uploader.fog_authenticated_url_expiration
194
194
  case @uploader.fog_credentials[:provider]
195
- when 'AWS'
196
- local_file.url(expire_at, options)
197
- when 'Rackspace'
195
+ when 'AWS', 'Google'
196
+ # Older versions of fog-google do not support options as a parameter
197
+ if url_options_supported?(local_file)
198
+ local_file.url(expire_at, options)
199
+ else
200
+ warn "Options hash not supported in #{local_file.class}. You may need to upgrade your Fog provider."
201
+ local_file.url(expire_at)
202
+ end
203
+ when 'Rackspace', 'OpenStack'
198
204
  connection.get_object_https_url(@uploader.fog_directory, path, expire_at, options)
199
- when 'OpenStack'
200
- connection.get_object_https_url(@uploader.fog_directory, path, expire_at)
201
205
  else
202
206
  local_file.url(expire_at)
203
207
  end
@@ -266,7 +270,7 @@ module CarrierWave
266
270
  end
267
271
 
268
272
  def initialize(uploader, base, path)
269
- @uploader, @base, @path = uploader, base, path
273
+ @uploader, @base, @path, @content_type = uploader, base, path, nil
270
274
  end
271
275
 
272
276
  ##
@@ -276,6 +280,16 @@ module CarrierWave
276
280
  #
277
281
  # [String] contents of file
278
282
  def read
283
+ file_body = file.body
284
+
285
+ return if file_body.nil?
286
+ return file_body unless file_body.is_a?(::File)
287
+
288
+ # Fog::Storage::XXX::File#body could return the source file which was upoloaded to the remote server.
289
+ read_source_file(file_body) if ::File.exist?(file_body.path)
290
+
291
+ # If the source file doesn't exist, the remote content is read
292
+ @file = nil # rubocop:disable Gitlab/ModuleWithInstanceVariables
279
293
  file.body
280
294
  end
281
295
 
@@ -313,7 +327,7 @@ module CarrierWave
313
327
  fog_file = new_file.to_file
314
328
  @content_type ||= new_file.content_type
315
329
  @file = directory.files.create({
316
- :body => (fog_file ? fog_file : new_file).read,
330
+ :body => fog_file ? fog_file : new_file.read,
317
331
  :content_type => @content_type,
318
332
  :key => path,
319
333
  :public => @uploader.fog_public
@@ -342,15 +356,19 @@ module CarrierWave
342
356
  end
343
357
  else
344
358
  # AWS/Google optimized for speed over correctness
345
- case @uploader.fog_credentials[:provider].to_s
359
+ case fog_provider
346
360
  when 'AWS'
347
361
  # check if some endpoint is set in fog_credentials
348
362
  if @uploader.fog_credentials.has_key?(:endpoint)
349
363
  "#{@uploader.fog_credentials[:endpoint]}/#{@uploader.fog_directory}/#{encoded_path}"
350
364
  else
351
365
  protocol = @uploader.fog_use_ssl_for_aws ? "https" : "http"
366
+
367
+ subdomain_regex = /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
368
+ valid_subdomain = @uploader.fog_directory.to_s =~ subdomain_regex && !(protocol == 'https' && @uploader.fog_directory =~ /\./)
369
+
352
370
  # if directory is a valid subdomain, use that style for access
353
- if @uploader.fog_directory.to_s =~ /^(?:[a-z]|\d(?!\d{0,2}(?:\d{1,3}){3}$))(?:[a-z0-9\.]|(?![\-])|\-(?![\.])){1,61}[a-z0-9]$/
371
+ if valid_subdomain
354
372
  s3_subdomain = @uploader.fog_aws_accelerate ? "s3-accelerate" : "s3"
355
373
  "#{protocol}://#{@uploader.fog_directory}.#{s3_subdomain}.amazonaws.com/#{encoded_path}"
356
374
  else
@@ -456,7 +474,31 @@ module CarrierWave
456
474
  end
457
475
 
458
476
  def acl_header
459
- {'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private'}
477
+ if fog_provider == 'AWS'
478
+ { 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
479
+ else
480
+ {}
481
+ end
482
+ end
483
+
484
+ def fog_provider
485
+ @uploader.fog_credentials[:provider].to_s
486
+ end
487
+
488
+ def read_source_file(file_body)
489
+ return unless ::File.exist?(file_body.path)
490
+
491
+ begin
492
+ file_body = ::File.open(file_body.path) if file_body.closed? # Reopen if it's already closed
493
+ file_body.read
494
+ ensure
495
+ file_body.close
496
+ end
497
+ end
498
+
499
+ def url_options_supported?(local_file)
500
+ parameters = local_file.method(:url).parameters
501
+ parameters.count == 2 && parameters[1].include?(:options)
460
502
  end
461
503
  end
462
504
 
@@ -43,6 +43,15 @@ module CarrierWave
43
43
  class Base
44
44
  attr_reader :file
45
45
 
46
+ ##
47
+ # Workaround for class_attribute malfunction when used with Module#prepend
48
+ #
49
+ if RUBY_VERSION < '2.1.0'
50
+ def self.singleton_class?
51
+ !ancestors.include? self
52
+ end
53
+ end
54
+
46
55
  include CarrierWave::Uploader::Configuration
47
56
  include CarrierWave::Uploader::Callbacks
48
57
  include CarrierWave::Uploader::Proxy
@@ -117,12 +117,14 @@ module CarrierWave
117
117
 
118
118
  def add_config(name)
119
119
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
120
+ @#{name} = nil
121
+
120
122
  def self.eager_load_fog(fog_credentials)
121
123
  # see #1198. This will hopefully no longer be necessary after fog 2.0
122
124
  require self.fog_provider
123
125
  require 'carrierwave/storage/fog'
124
126
  Fog::Storage.new(fog_credentials) if fog_credentials.present?
125
- end
127
+ end unless defined? eager_load_fog
126
128
 
127
129
  def self.#{name}(value=nil)
128
130
  @#{name} = value if value
@@ -35,7 +35,7 @@ module CarrierWave
35
35
  def check_content_type_whitelist!(new_file)
36
36
  content_type = new_file.content_type
37
37
  if content_type_whitelist && !whitelisted_content_type?(content_type)
38
- raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_whitelist_error", content_type: content_type)
38
+ raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_whitelist_error", content_type: content_type, allowed_types: Array(content_type_whitelist).join(", "))
39
39
  end
40
40
  end
41
41
 
@@ -1,4 +1,5 @@
1
1
  require 'open-uri'
2
+ require 'ssrf_filter'
2
3
 
3
4
  module CarrierWave
4
5
  module Uploader
@@ -10,14 +11,18 @@ module CarrierWave
10
11
  include CarrierWave::Uploader::Cache
11
12
 
12
13
  class RemoteFile
13
- def initialize(uri, remote_headers = {})
14
+ attr_reader :uri
15
+
16
+ def initialize(uri, remote_headers = {}, skip_ssrf_protection: false)
14
17
  @uri = uri
15
- @remote_headers = remote_headers
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
16
21
  end
17
22
 
18
23
  def original_filename
19
24
  filename = filename_from_header || filename_from_uri
20
- mime_type = MIME::Types[file.content_type].first
25
+ mime_type = MIME::Types[content_type].first
21
26
  unless File.extname(filename).present? || mime_type.blank?
22
27
  filename = "#{filename}.#{mime_type.extensions.first}"
23
28
  end
@@ -32,15 +37,35 @@ module CarrierWave
32
37
  @uri.scheme =~ /^https?$/
33
38
  end
34
39
 
35
- private
40
+ def content_type
41
+ @content_type || 'application/octet-stream'
42
+ end
43
+
44
+ def headers
45
+ @headers || {}
46
+ end
47
+
48
+ private
36
49
 
37
50
  def file
38
51
  if @file.blank?
39
- headers = @remote_headers.
40
- reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}")
41
-
42
- @file = Kernel.open(@uri.to_s, headers)
43
- @file = @file.is_a?(String) ? StringIO.new(@file) : @file
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
44
69
  end
45
70
  @file
46
71
 
@@ -49,14 +74,14 @@ module CarrierWave
49
74
  end
50
75
 
51
76
  def filename_from_header
52
- if file.meta.include? 'content-disposition'
53
- match = file.meta['content-disposition'].match(/filename="?([^"]+)/)
77
+ if headers['content-disposition']
78
+ match = headers['content-disposition'].match(/filename="?([^"]+)/)
54
79
  return match[1] unless match.nil? || match[1].empty?
55
80
  end
56
81
  end
57
82
 
58
83
  def filename_from_uri
59
- URI.decode(File.basename(file.base_uri.path))
84
+ URI::DEFAULT_PARSER.unescape(File.basename(@uri.path))
60
85
  end
61
86
 
62
87
  def method_missing(*args, &block)
@@ -74,7 +99,7 @@ module CarrierWave
74
99
  #
75
100
  def download!(uri, remote_headers = {})
76
101
  processed_uri = process_uri(uri)
77
- file = RemoteFile.new(processed_uri, remote_headers)
102
+ file = RemoteFile.new(processed_uri, remote_headers, skip_ssrf_protection: skip_ssrf_protection?(processed_uri))
78
103
  raise CarrierWave::DownloadError, "trying to download a file which is not served over HTTP" unless file.http?
79
104
  cache!(file)
80
105
  end
@@ -91,11 +116,30 @@ module CarrierWave
91
116
  rescue URI::InvalidURIError
92
117
  uri_parts = uri.split('?')
93
118
  # regexp from Ruby's URI::Parser#regexp[:UNSAFE], with [] specifically removed
94
- encoded_uri = URI.encode(uri_parts.shift, /[^\-_.!~*'()a-zA-Z\d;\/?:@&=+$,]/)
95
- encoded_uri << '?' << URI.encode(uri_parts.join('?')) if uri_parts.any?
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?
96
121
  URI.parse(encoded_uri) rescue raise CarrierWave::DownloadError, "couldn't parse URL"
97
122
  end
98
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
99
143
  end # Download
100
144
  end # Uploader
101
145
  end # CarrierWave
@@ -7,6 +7,15 @@ module CarrierWave
7
7
  include CarrierWave::Uploader::Configuration
8
8
  include CarrierWave::Uploader::Cache
9
9
 
10
+ included do
11
+ prepend Module.new {
12
+ def initialize(*)
13
+ super
14
+ @file, @filename, @cache_id = nil
15
+ end
16
+ }
17
+ end
18
+
10
19
  ##
11
20
  # Override this in your Uploader to change the filename.
12
21
  #
@@ -19,6 +19,13 @@ module CarrierWave
19
19
  after :remove, :remove_versions!
20
20
  after :retrieve_from_cache, :retrieve_versions_from_cache!
21
21
  after :retrieve_from_store, :retrieve_versions_from_store!
22
+
23
+ prepend Module.new {
24
+ def initialize(*)
25
+ super
26
+ @versions = nil
27
+ end
28
+ }
22
29
  end
23
30
 
24
31
  module ClassMethods
@@ -77,7 +84,7 @@ module CarrierWave
77
84
  # value from the parent class unless explicitly overwritten
78
85
  def self.enable_processing(value=nil)
79
86
  self.enable_processing = value if value
80
- if !@enable_processing.nil?
87
+ if defined?(@enable_processing) && !@enable_processing.nil?
81
88
  @enable_processing
82
89
  else
83
90
  superclass.enable_processing
@@ -1,3 +1,3 @@
1
1
  module CarrierWave
2
- VERSION = "1.2.1"
2
+ VERSION = "1.3.2"
3
3
  end
@@ -1,5 +1,4 @@
1
1
  class <%= class_name %>Uploader < CarrierWave::Uploader::Base
2
-
3
2
  # Include RMagick or MiniMagick support:
4
3
  # include CarrierWave::RMagick
5
4
  # include CarrierWave::MiniMagick
@@ -45,5 +44,4 @@ class <%= class_name %>Uploader < CarrierWave::Uploader::Base
45
44
  # def filename
46
45
  # "something.jpg" if original_filename
47
46
  # end
48
-
49
47
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: carrierwave
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jonas Nicklas
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-10-04 00:00:00.000000000 Z
11
+ date: 2021-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '1.16'
55
+ - !ruby/object:Gem::Dependency
56
+ name: ssrf_filter
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: pg
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -123,35 +137,49 @@ dependencies:
123
137
  - !ruby/object:Gem::Version
124
138
  version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
- name: fog
140
+ name: fog-aws
127
141
  requirement: !ruby/object:Gem::Requirement
128
142
  requirements:
129
143
  - - ">="
130
144
  - !ruby/object:Gem::Version
131
- version: 1.28.0
145
+ version: '0'
132
146
  type: :development
133
147
  prerelease: false
134
148
  version_requirements: !ruby/object:Gem::Requirement
135
149
  requirements:
136
150
  - - ">="
137
151
  - !ruby/object:Gem::Version
138
- version: 1.28.0
152
+ version: '0'
139
153
  - !ruby/object:Gem::Dependency
140
- name: mini_magick
154
+ name: fog-google
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - "~>"
158
+ - !ruby/object:Gem::Version
159
+ version: 1.7.1
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - "~>"
165
+ - !ruby/object:Gem::Version
166
+ version: 1.7.1
167
+ - !ruby/object:Gem::Dependency
168
+ name: fog-local
141
169
  requirement: !ruby/object:Gem::Requirement
142
170
  requirements:
143
171
  - - ">="
144
172
  - !ruby/object:Gem::Version
145
- version: 3.6.0
173
+ version: '0'
146
174
  type: :development
147
175
  prerelease: false
148
176
  version_requirements: !ruby/object:Gem::Requirement
149
177
  requirements:
150
178
  - - ">="
151
179
  - !ruby/object:Gem::Version
152
- version: 3.6.0
180
+ version: '0'
153
181
  - !ruby/object:Gem::Dependency
154
- name: rmagick
182
+ name: fog-rackspace
155
183
  requirement: !ruby/object:Gem::Requirement
156
184
  requirements:
157
185
  - - ">="
@@ -164,6 +192,34 @@ dependencies:
164
192
  - - ">="
165
193
  - !ruby/object:Gem::Version
166
194
  version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: mini_magick
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: 3.6.0
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: 3.6.0
209
+ - !ruby/object:Gem::Dependency
210
+ name: rmagick
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - "~>"
214
+ - !ruby/object:Gem::Version
215
+ version: '2.16'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - "~>"
221
+ - !ruby/object:Gem::Version
222
+ version: '2.16'
167
223
  - !ruby/object:Gem::Dependency
168
224
  name: timecop
169
225
  requirement: !ruby/object:Gem::Requirement
@@ -243,8 +299,6 @@ files:
243
299
  - lib/carrierwave/uploader/extension_blacklist.rb
244
300
  - lib/carrierwave/uploader/extension_whitelist.rb
245
301
  - lib/carrierwave/uploader/file_size.rb
246
- - lib/carrierwave/uploader/magic_mime_blacklist.rb
247
- - lib/carrierwave/uploader/magic_mime_whitelist.rb
248
302
  - lib/carrierwave/uploader/mountable.rb
249
303
  - lib/carrierwave/uploader/processing.rb
250
304
  - lib/carrierwave/uploader/proxy.rb
@@ -263,7 +317,7 @@ homepage: https://github.com/carrierwaveuploader/carrierwave
263
317
  licenses:
264
318
  - MIT
265
319
  metadata: {}
266
- post_install_message:
320
+ post_install_message:
267
321
  rdoc_options:
268
322
  - "--main"
269
323
  require_paths:
@@ -279,9 +333,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
279
333
  - !ruby/object:Gem::Version
280
334
  version: '0'
281
335
  requirements: []
282
- rubyforge_project:
283
- rubygems_version: 2.5.2
284
- signing_key:
336
+ rubygems_version: 3.1.2
337
+ signing_key:
285
338
  specification_version: 4
286
339
  summary: Ruby file upload library
287
340
  test_files: []
@@ -1,94 +0,0 @@
1
- module CarrierWave
2
- module Uploader
3
-
4
- ##
5
- # This modules validates the content type of a file with the use of
6
- # ruby-filemagic gem and a blacklist regular expression. If you want
7
- # to use this, you'll need to require this file:
8
- #
9
- # require 'carrierwave/uploader/magic_mime_blacklist'
10
- #
11
- # And then include it in your uploader:
12
- #
13
- # class MyUploader < CarrierWave::Uploader::Base
14
- # include CarrierWave::Uploader::MagicMimeBlacklist
15
- #
16
- # def blacklist_mime_type_pattern
17
- # /image\//
18
- # end
19
- # end
20
- #
21
- module MagicMimeBlacklist
22
- extend ActiveSupport::Concern
23
-
24
- included do
25
- begin
26
- require "filemagic"
27
- rescue LoadError => e
28
- e.message << " (You may need to install the ruby-filemagic gem)"
29
- raise e
30
- end
31
-
32
- before :cache, :check_blacklist_pattern!
33
- end
34
-
35
- ##
36
- # Override this method in your uploader to provide a black list pattern (regexp)
37
- # of content-types which are prohibited to be uploaded.
38
- # Compares the file's content-type.
39
- #
40
- # === Returns
41
- #
42
- # [Regexp] a black list regexp to match the content_type
43
- #
44
- # === Examples
45
- #
46
- # def blacklist_mime_type_pattern
47
- # /(text|application)\/json/
48
- # end
49
- #
50
- def blacklist_mime_type_pattern; end
51
-
52
- private
53
-
54
- def check_blacklist_pattern!(new_file)
55
- return if blacklist_mime_type_pattern.nil?
56
-
57
- content_type = extract_content_type(new_file)
58
-
59
- if content_type.match(blacklist_mime_type_pattern)
60
- raise CarrierWave::IntegrityError,
61
- I18n.translate(:"errors.messages.mime_type_pattern_black_list_error",
62
- :content_type => content_type)
63
- end
64
- end
65
-
66
- ##
67
- # Extracts the content type of the given file
68
- #
69
- # === Returns
70
- #
71
- # [String] the extracted content type
72
- #
73
- def extract_content_type(new_file)
74
- content_type = nil
75
-
76
- File.open(new_file.path) do |fd|
77
- data = fd.read(1024) || ""
78
- content_type = filemagic.buffer(data)
79
- end
80
-
81
- content_type
82
- end
83
-
84
- ##
85
- # FileMagic object with the MAGIC_MIME_TYPE flag set
86
- #
87
- # @return [FileMagic] a filemagic object
88
- def filemagic
89
- @filemagic ||= FileMagic.new(FileMagic::MAGIC_MIME_TYPE)
90
- end
91
-
92
- end # MagicMimeblackList
93
- end # Uploader
94
- end # CarrierWave
@@ -1,94 +0,0 @@
1
- module CarrierWave
2
- module Uploader
3
-
4
- ##
5
- # This modules validates the content type of a file with the use of
6
- # ruby-filemagic gem and a whitelist regular expression. If you want
7
- # to use this, you'll need to require this file:
8
- #
9
- # require 'carrierwave/uploader/magic_mime_whitelist'
10
- #
11
- # And then include it in your uploader:
12
- #
13
- # class MyUploader < CarrierWave::Uploader::Base
14
- # include CarrierWave::Uploader::MagicMimeWhitelist
15
- #
16
- # def whitelist_mime_type_pattern
17
- # /image\//
18
- # end
19
- # end
20
- #
21
- module MagicMimeWhitelist
22
- extend ActiveSupport::Concern
23
-
24
- included do
25
- begin
26
- require "filemagic"
27
- rescue LoadError => e
28
- e.message << " (You may need to install the ruby-filemagic gem)"
29
- raise e
30
- end
31
-
32
- before :cache, :check_whitelist_pattern!
33
- end
34
-
35
- ##
36
- # Override this method in your uploader to provide a white list pattern (regexp)
37
- # of content-types which are allowed to be uploaded.
38
- # Compares the file's content-type.
39
- #
40
- # === Returns
41
- #
42
- # [Regexp] a white list regexp to match the content_type
43
- #
44
- # === Examples
45
- #
46
- # def whitelist_mime_type_pattern
47
- # /(text|application)\/json/
48
- # end
49
- #
50
- def whitelist_mime_type_pattern; end
51
-
52
- private
53
-
54
- def check_whitelist_pattern!(new_file)
55
- return if whitelist_mime_type_pattern.nil?
56
-
57
- content_type = extract_content_type(new_file)
58
-
59
- if !content_type.match(whitelist_mime_type_pattern)
60
- raise CarrierWave::IntegrityError,
61
- I18n.translate(:"errors.messages.mime_type_pattern_white_list_error",
62
- :content_type => content_type)
63
- end
64
- end
65
-
66
- ##
67
- # Extracts the content type of the given file
68
- #
69
- # === Returns
70
- #
71
- # [String] the extracted content type
72
- #
73
- def extract_content_type(new_file)
74
- content_type = nil
75
-
76
- File.open(new_file.path) do |fd|
77
- data = fd.read(1024) || ""
78
- content_type = filemagic.buffer(data)
79
- end
80
-
81
- content_type
82
- end
83
-
84
- ##
85
- # FileMagic object with the MAGIC_MIME_TYPE flag set
86
- #
87
- # @return [FileMagic] a filemagic object
88
- def filemagic
89
- @filemagic ||= FileMagic.new(FileMagic::MAGIC_MIME_TYPE)
90
- end
91
-
92
- end # MagicMimeWhiteList
93
- end # Uploader
94
- end # CarrierWave