carrierwave 1.2.2 → 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 +5 -5
- data/README.md +17 -11
- data/lib/carrierwave/locale/en.yml +1 -1
- data/lib/carrierwave/processing/mini_magick.rb +8 -5
- data/lib/carrierwave/processing/rmagick.rb +17 -4
- data/lib/carrierwave/sanitized_file.rb +8 -8
- data/lib/carrierwave/storage/file.rb +5 -1
- data/lib/carrierwave/storage/fog.rb +54 -12
- data/lib/carrierwave/uploader/configuration.rb +3 -1
- data/lib/carrierwave/uploader/content_type_whitelist.rb +1 -1
- data/lib/carrierwave/uploader/download.rb +59 -15
- data/lib/carrierwave/uploader/store.rb +9 -0
- data/lib/carrierwave/uploader/versions.rb +8 -1
- data/lib/carrierwave/uploader.rb +9 -0
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +20 -0
- metadata +69 -16
- data/lib/carrierwave/uploader/magic_mime_blacklist.rb +0 -94
- data/lib/carrierwave/uploader/magic_mime_whitelist.rb +0 -94
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d5e6d1dd656203f5e14c43b75b807e793d623126006ddf8c5a4f9f4706faa750
|
4
|
+
data.tar.gz: 82281b939837f54716f580a1deb6397d87cc9bb867dc6a6938a4322d795500b1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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](
|
8
|
-
[![
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
|
@@ -307,7 +310,7 @@ to exactly 200 by 200 pixels.
|
|
307
310
|
|
308
311
|
If you would like to crop images to a specific height and width you
|
309
312
|
can use the alternative option of '''resize_to_fill'''. It will make sure
|
310
|
-
that the width and height specified are filled, only
|
313
|
+
that the width and height specified are filled, only cropping
|
311
314
|
if the aspect ratio requires it.
|
312
315
|
|
313
316
|
The uploader could be used like this:
|
@@ -633,6 +636,8 @@ describe MyUploader do
|
|
633
636
|
end
|
634
637
|
```
|
635
638
|
|
639
|
+
If you're looking for minitest asserts, checkout [carrierwave_asserts](https://github.com/hcfairbanks/carrierwave_asserts).
|
640
|
+
|
636
641
|
Setting the enable_processing flag on an uploader will prevent any of the versions from processing as well.
|
637
642
|
Processing can be enabled for a single version by setting the processing flag on the version like so:
|
638
643
|
|
@@ -667,13 +672,14 @@ CarrierWave.configure do |config|
|
|
667
672
|
config.fog_provider = 'fog/aws' # required
|
668
673
|
config.fog_credentials = {
|
669
674
|
provider: 'AWS', # required
|
670
|
-
aws_access_key_id: 'xxx', # required
|
671
|
-
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
|
672
678
|
region: 'eu-west-1', # optional, defaults to 'us-east-1'
|
673
679
|
host: 's3.example.com', # optional, defaults to nil
|
674
680
|
endpoint: 'https://s3.example.com:8080' # optional, defaults to nil
|
675
681
|
}
|
676
|
-
config.fog_directory = '
|
682
|
+
config.fog_directory = 'name_of_bucket' # required
|
677
683
|
config.fog_public = false # optional, defaults to true
|
678
684
|
config.fog_attributes = { cache_control: "public, max-age=#{365.days.to_i}" } # optional, defaults to {}
|
679
685
|
end
|
@@ -920,7 +926,7 @@ errors:
|
|
920
926
|
carrierwave_download_error: could not be downloaded
|
921
927
|
extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
|
922
928
|
extension_blacklist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
|
923
|
-
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}"
|
924
930
|
content_type_blacklist_error: "You are not allowed to upload %{content_type} files"
|
925
931
|
rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image?"
|
926
932
|
mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}"
|
@@ -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
|
@@ -334,11 +341,7 @@ module CarrierWave
|
|
334
341
|
end
|
335
342
|
|
336
343
|
def mini_magick_image
|
337
|
-
|
338
|
-
::MiniMagick::Image.open(url)
|
339
|
-
else
|
340
|
-
::MiniMagick::Image.open(current_path)
|
341
|
-
end
|
344
|
+
::MiniMagick::Image.read(read)
|
342
345
|
end
|
343
346
|
|
344
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
|
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?
|
@@ -371,9 +378,15 @@ module CarrierWave
|
|
371
378
|
|
372
379
|
def create_info_block(options)
|
373
380
|
return nil unless options
|
374
|
-
|
375
|
-
|
376
|
-
|
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
|
377
390
|
end
|
378
391
|
|
379
392
|
def destroy_image(image)
|
@@ -20,7 +20,7 @@ module CarrierWave
|
|
20
20
|
#
|
21
21
|
class SanitizedFile
|
22
22
|
|
23
|
-
|
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
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
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
|
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
|
-
|
197
|
-
|
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 =>
|
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
|
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
|
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
|
-
|
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
|
|
@@ -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
|
-
|
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[
|
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
|
-
|
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
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
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
|
53
|
-
match =
|
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.
|
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.
|
95
|
-
encoded_uri << '?' << URI.
|
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
|
data/lib/carrierwave/uploader.rb
CHANGED
@@ -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
|
data/lib/carrierwave/version.rb
CHANGED
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
|
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.
|
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:
|
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:
|
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:
|
152
|
+
version: '0'
|
139
153
|
- !ruby/object:Gem::Dependency
|
140
|
-
name:
|
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:
|
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:
|
180
|
+
version: '0'
|
153
181
|
- !ruby/object:Gem::Dependency
|
154
|
-
name:
|
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
|
-
|
283
|
-
|
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
|