carrierwave 1.3.2 → 2.2.5
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 +4 -4
- data/README.md +117 -43
- data/lib/carrierwave/downloader/base.rb +93 -0
- data/lib/carrierwave/downloader/remote_file.rb +65 -0
- data/lib/carrierwave/locale/en.yml +5 -4
- data/lib/carrierwave/mount.rb +25 -19
- data/lib/carrierwave/mounter.rb +71 -48
- data/lib/carrierwave/orm/activerecord.rb +14 -8
- data/lib/carrierwave/processing/mini_magick.rb +100 -117
- data/lib/carrierwave/processing/rmagick.rb +2 -2
- data/lib/carrierwave/processing/vips.rb +284 -0
- data/lib/carrierwave/processing.rb +1 -0
- data/lib/carrierwave/sanitized_file.rb +38 -16
- data/lib/carrierwave/storage/file.rb +2 -2
- data/lib/carrierwave/storage/fog.rb +44 -13
- data/lib/carrierwave/storage.rb +1 -0
- data/lib/carrierwave/uploader/cache.rb +24 -16
- data/lib/carrierwave/uploader/configuration.rb +28 -15
- data/lib/carrierwave/uploader/content_type_blacklist.rb +17 -8
- data/lib/carrierwave/uploader/content_type_whitelist.rb +20 -8
- data/lib/carrierwave/uploader/download.rb +2 -123
- data/lib/carrierwave/uploader/extension_blacklist.rb +18 -10
- data/lib/carrierwave/uploader/extension_whitelist.rb +19 -10
- data/lib/carrierwave/uploader/mountable.rb +6 -0
- data/lib/carrierwave/uploader/processing.rb +11 -1
- data/lib/carrierwave/uploader/proxy.rb +2 -2
- data/lib/carrierwave/uploader/serialization.rb +1 -1
- data/lib/carrierwave/uploader/store.rb +5 -3
- data/lib/carrierwave/uploader/url.rb +6 -3
- data/lib/carrierwave/uploader/versions.rb +43 -13
- data/lib/carrierwave/uploader.rb +0 -9
- data/lib/carrierwave/validations/active_model.rb +3 -3
- data/lib/carrierwave/version.rb +1 -1
- data/lib/carrierwave.rb +4 -0
- data/lib/generators/templates/uploader.rb +2 -2
- metadata +96 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 04ecbb3b2d0424203756bda1f73e89bdf32d52f3fbcda13b979e9904d1d4f2c9
|
4
|
+
data.tar.gz: e7474ccefff230df0f9d6564825c63acd3176c2a37e2a4b66a797a864d610f74
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a2cff0c002d2d31c1a61bf4d7158a4d648a88bb69843092130bfc48cf2026386becdc707e00f6eff379929ff88f7c20929fa67310e874fe3b4ad3197793b269
|
7
|
+
data.tar.gz: fe8c49e5858818fd2e8f3e271ba884a839d627aefe420f1c4e5d63c00601b4c2e71cb07cdb77ccf0758121173b932c4a12b95762b2b60ee6d0930c9a290f375a
|
data/README.md
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
This gem provides a simple and extremely flexible way to upload files from Ruby applications.
|
4
4
|
It works well with Rack based web applications, such as Ruby on Rails.
|
5
5
|
|
6
|
-
[![Build Status](https://
|
6
|
+
[![Build Status](https://github.com/carrierwaveuploader/carrierwave/workflows/Test/badge.svg)](https://github.com/carrierwaveuploader/carrierwave/actions)
|
7
7
|
[![Code Climate](https://codeclimate.com/github/carrierwaveuploader/carrierwave.svg)](https://codeclimate.com/github/carrierwaveuploader/carrierwave)
|
8
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
|
|
@@ -30,13 +30,13 @@ $ gem install carrierwave
|
|
30
30
|
In Rails, add it to your Gemfile:
|
31
31
|
|
32
32
|
```ruby
|
33
|
-
gem 'carrierwave', '~>
|
33
|
+
gem 'carrierwave', '~> 2.0'
|
34
34
|
```
|
35
35
|
|
36
36
|
Finally, restart the server to apply the changes.
|
37
37
|
|
38
|
-
As of version
|
39
|
-
or higher. If you're on Rails
|
38
|
+
As of version 2.0, CarrierWave requires Rails 5.0 or higher and Ruby 2.2
|
39
|
+
or higher. If you're on Rails 4, you should use 1.x.
|
40
40
|
|
41
41
|
## Getting Started
|
42
42
|
|
@@ -94,7 +94,7 @@ a migration:
|
|
94
94
|
Open your model file and mount the uploader:
|
95
95
|
|
96
96
|
```ruby
|
97
|
-
class User <
|
97
|
+
class User < ApplicationRecord
|
98
98
|
mount_uploader :avatar, AvatarUploader
|
99
99
|
end
|
100
100
|
```
|
@@ -157,7 +157,7 @@ Open your model file and mount the uploader:
|
|
157
157
|
|
158
158
|
|
159
159
|
```ruby
|
160
|
-
class User <
|
160
|
+
class User < ApplicationRecord
|
161
161
|
mount_uploaders :avatars, AvatarUploader
|
162
162
|
serialize :avatars, JSON # If you use SQLite, add this line.
|
163
163
|
end
|
@@ -190,6 +190,17 @@ u.avatars[0].current_path # => 'path/to/file.png'
|
|
190
190
|
u.avatars[0].identifier # => 'file.png'
|
191
191
|
```
|
192
192
|
|
193
|
+
If you want to preserve existing files on uploading new one, you can go like:
|
194
|
+
|
195
|
+
```erb
|
196
|
+
<% user.avatars.each do |avatar| %>
|
197
|
+
<%= hidden_field :user, :avatars, multiple: true, value: avatar.identifier %>
|
198
|
+
<% end %>
|
199
|
+
<%= form.file_field :avatars, multiple: true %>
|
200
|
+
```
|
201
|
+
|
202
|
+
Sorting avatars is supported as well by reordering `hidden_field`, an example using jQuery UI Sortable is available [here](https://github.com/carrierwaveuploader/carrierwave/wiki/How-to%3A-Add%2C-remove-and-reorder-images-using-multiple-file-upload).
|
203
|
+
|
193
204
|
## Changing the storage directory
|
194
205
|
|
195
206
|
In order to change where uploaded files are put, just override the `store_dir`
|
@@ -219,7 +230,7 @@ end
|
|
219
230
|
## Securing uploads
|
220
231
|
|
221
232
|
Certain files might be dangerous if uploaded to the wrong location, such as PHP
|
222
|
-
files or other script files. CarrierWave allows you to specify
|
233
|
+
files or other script files. CarrierWave allows you to specify an allowlist of
|
223
234
|
allowed extensions or content types.
|
224
235
|
|
225
236
|
If you're mounting the uploader, uploading a file with the wrong extension will
|
@@ -227,7 +238,7 @@ make the record invalid instead. Otherwise, an error is raised.
|
|
227
238
|
|
228
239
|
```ruby
|
229
240
|
class MyUploader < CarrierWave::Uploader::Base
|
230
|
-
def
|
241
|
+
def extension_allowlist
|
231
242
|
%w(jpg jpeg gif png)
|
232
243
|
end
|
233
244
|
end
|
@@ -238,29 +249,45 @@ Let's say we need an uploader that accepts only images. This can be done like th
|
|
238
249
|
|
239
250
|
```ruby
|
240
251
|
class MyUploader < CarrierWave::Uploader::Base
|
241
|
-
def
|
252
|
+
def content_type_allowlist
|
242
253
|
/image\//
|
243
254
|
end
|
244
255
|
end
|
245
256
|
```
|
246
257
|
|
247
|
-
You can use a
|
258
|
+
You can use a denylist to reject content types.
|
248
259
|
Let's say we need an uploader that reject JSON files. This can be done like this
|
249
260
|
|
250
261
|
```ruby
|
251
262
|
class NoJsonUploader < CarrierWave::Uploader::Base
|
252
|
-
def
|
263
|
+
def content_type_denylist
|
253
264
|
['application/text', 'application/json']
|
254
265
|
end
|
255
266
|
end
|
256
267
|
```
|
257
268
|
|
269
|
+
### CVE-2016-3714 (ImageTragick)
|
270
|
+
This version of CarrierWave has the ability to mitigate CVE-2016-3714. However, you **MUST** set a content_type_allowlist in your uploaders for this protection to be effective, and you **MUST** either disable ImageMagick's default SVG delegate or use the RSVG delegate for SVG processing.
|
271
|
+
|
272
|
+
|
273
|
+
A valid allowlist that will restrict your uploader to images only, and mitigate the CVE is:
|
274
|
+
|
275
|
+
```ruby
|
276
|
+
class MyUploader < CarrierWave::Uploader::Base
|
277
|
+
def content_type_allowlist
|
278
|
+
[/image\//]
|
279
|
+
end
|
280
|
+
end
|
281
|
+
```
|
282
|
+
|
283
|
+
**WARNING**: A `content_type_allowlist` is the only form of allowlist or denylist supported by CarrierWave that can effectively mitigate against CVE-2016-3714. Use of `extension_allowlist` will not inspect the file headers, and thus still leaves your application open to the vulnerability.
|
284
|
+
|
258
285
|
### Filenames and unicode chars
|
259
286
|
|
260
287
|
Another security issue you should care for is the file names (see
|
261
288
|
[Ruby On Rails Security Guide](http://guides.rubyonrails.org/security.html#file-uploads)).
|
262
289
|
By default, CarrierWave provides only English letters, arabic numerals and some symbols as
|
263
|
-
|
290
|
+
allowlisted characters in the file name. If you want to support local scripts (Cyrillic letters, letters with diacritics and so on), you
|
264
291
|
have to override `sanitize_regexp` method. It should return regular expression which would match
|
265
292
|
all *non*-allowed symbols.
|
266
293
|
|
@@ -280,7 +307,7 @@ You no longer need to do this manually.
|
|
280
307
|
|
281
308
|
Often you'll want to add different versions of the same file. The classic example is image thumbnails. There is built in support for this*:
|
282
309
|
|
283
|
-
*Note:* You must have Imagemagick
|
310
|
+
*Note:* You must have Imagemagick installed to do image resizing.
|
284
311
|
|
285
312
|
Some documentation refers to RMagick instead of MiniMagick but MiniMagick is recommended.
|
286
313
|
|
@@ -305,15 +332,13 @@ end
|
|
305
332
|
|
306
333
|
When this uploader is used, an uploaded image would be scaled to be no larger
|
307
334
|
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
335
|
|
311
|
-
|
312
|
-
|
336
|
+
A version called `:thumb` is then created, which is scaled
|
337
|
+
to exactly 200 by 200 pixels. The thumbnail uses `resize_to_fill` which makes sure
|
313
338
|
that the width and height specified are filled, only cropping
|
314
339
|
if the aspect ratio requires it.
|
315
340
|
|
316
|
-
The uploader could be used like this:
|
341
|
+
The above uploader could be used like this:
|
317
342
|
|
318
343
|
```ruby
|
319
344
|
uploader = AvatarUploader.new
|
@@ -326,6 +351,34 @@ uploader.thumb.url # => '/url/to/thumb_my_file.png' # size: 200x200
|
|
326
351
|
One important thing to remember is that process is called *before* versions are
|
327
352
|
created. This can cut down on processing cost.
|
328
353
|
|
354
|
+
### Processing Methods: mini_magick
|
355
|
+
|
356
|
+
- `convert` - Changes the image encoding format to the given format, eg. jpg
|
357
|
+
- `resize_to_limit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. Will only resize the image if it is larger than the specified dimensions. The resulting image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
|
358
|
+
- `resize_to_fit` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. The image may be shorter or narrower than specified in the smaller dimension but will not be larger than the specified values.
|
359
|
+
- `resize_to_fill` - Resize the image to fit within the specified dimensions while retaining the aspect ratio of the original image. If necessary, crop the image in the larger dimension. Optionally, a "gravity" may be specified, for example "Center", or "NorthEast".
|
360
|
+
- `resize_and_pad` - Resize the image to fit within the specified dimensions while retaining the original aspect ratio. If necessary, will pad the remaining area with the given color, which defaults to transparent (for gif and png, white for jpeg). Optionally, a "gravity" may be specified, as above.
|
361
|
+
|
362
|
+
See `carrierwave/processing/mini_magick.rb` for details.
|
363
|
+
|
364
|
+
### conditional process
|
365
|
+
|
366
|
+
If you want to use conditional process, you can only use `if` statement.
|
367
|
+
|
368
|
+
See `carrierwave/uploader/processing.rb` for details.
|
369
|
+
|
370
|
+
```ruby
|
371
|
+
class MyUploader < CarrierWave::Uploader::Base
|
372
|
+
process :scale => [200, 200], :if => :image?
|
373
|
+
|
374
|
+
def image?(carrier_wave_sanitized_file)
|
375
|
+
true
|
376
|
+
end
|
377
|
+
end
|
378
|
+
```
|
379
|
+
|
380
|
+
### Nested versions
|
381
|
+
|
329
382
|
It is possible to nest versions within versions:
|
330
383
|
|
331
384
|
```ruby
|
@@ -362,7 +415,7 @@ private
|
|
362
415
|
end
|
363
416
|
|
364
417
|
def is_landscape? picture
|
365
|
-
image = MiniMagick::Image.
|
418
|
+
image = MiniMagick::Image.new(picture.path)
|
366
419
|
image[:width] > image[:height]
|
367
420
|
end
|
368
421
|
|
@@ -651,7 +704,6 @@ If you want to use fog you must add in your CarrierWave initializer the
|
|
651
704
|
following lines
|
652
705
|
|
653
706
|
```ruby
|
654
|
-
config.fog_provider = 'fog' # 'fog/aws' etc. Defaults to 'fog'
|
655
707
|
config.fog_credentials = { ... } # Provider specific credentials
|
656
708
|
```
|
657
709
|
|
@@ -669,7 +721,6 @@ You can also pass in additional options, as documented fully in lib/carrierwave/
|
|
669
721
|
|
670
722
|
```ruby
|
671
723
|
CarrierWave.configure do |config|
|
672
|
-
config.fog_provider = 'fog/aws' # required
|
673
724
|
config.fog_credentials = {
|
674
725
|
provider: 'AWS', # required
|
675
726
|
aws_access_key_id: 'xxx', # required unless using use_iam_profile
|
@@ -682,6 +733,9 @@ CarrierWave.configure do |config|
|
|
682
733
|
config.fog_directory = 'name_of_bucket' # required
|
683
734
|
config.fog_public = false # optional, defaults to true
|
684
735
|
config.fog_attributes = { cache_control: "public, max-age=#{365.days.to_i}" } # optional, defaults to {}
|
736
|
+
# For an application which utilizes multiple servers but does not need caches persisted across requests,
|
737
|
+
# uncomment the line :file instead of the default :storage. Otherwise, it will use AWS as the temp cache store.
|
738
|
+
# config.cache_storage = :file
|
685
739
|
end
|
686
740
|
```
|
687
741
|
|
@@ -695,6 +749,14 @@ end
|
|
695
749
|
|
696
750
|
That's it! You can still use the `CarrierWave::Uploader#url` method to return the url to the file on Amazon S3.
|
697
751
|
|
752
|
+
**Note**: for Carrierwave to work properly it needs credentials with the following permissions:
|
753
|
+
|
754
|
+
* `s3:ListBucket`
|
755
|
+
* `s3:PutObject`
|
756
|
+
* `s3:GetObject`
|
757
|
+
* `s3:DeleteObject`
|
758
|
+
* `s3:PutObjectAcl`
|
759
|
+
|
698
760
|
## Using Rackspace Cloud Files
|
699
761
|
|
700
762
|
[Fog](http://github.com/fog/fog) is used to support Rackspace Cloud Files. Ensure you have it in your Gemfile:
|
@@ -710,7 +772,6 @@ Using a US-based account:
|
|
710
772
|
|
711
773
|
```ruby
|
712
774
|
CarrierWave.configure do |config|
|
713
|
-
config.fog_provider = "fog/rackspace/storage" # optional, defaults to "fog"
|
714
775
|
config.fog_credentials = {
|
715
776
|
provider: 'Rackspace',
|
716
777
|
rackspace_username: 'xxxxxx',
|
@@ -725,7 +786,6 @@ Using a UK-based account:
|
|
725
786
|
|
726
787
|
```ruby
|
727
788
|
CarrierWave.configure do |config|
|
728
|
-
config.fog_provider = "fog/rackspace/storage" # optional, defaults to "fog"
|
729
789
|
config.fog_credentials = {
|
730
790
|
provider: 'Rackspace',
|
731
791
|
rackspace_username: 'xxxxxx',
|
@@ -756,31 +816,43 @@ end
|
|
756
816
|
That's it! You can still use the `CarrierWave::Uploader#url` method to return
|
757
817
|
the url to the file on Rackspace Cloud Files.
|
758
818
|
|
759
|
-
## Using Google Storage
|
819
|
+
## Using Google Cloud Storage
|
760
820
|
|
761
|
-
[Fog](http://github.com/fog/fog-google) is used to support Google Storage
|
821
|
+
[Fog](http://github.com/fog/fog-google) is used to support Google Cloud Storage. Ensure you have it in your Gemfile:
|
762
822
|
|
763
823
|
```ruby
|
764
824
|
gem "fog-google"
|
765
|
-
gem "google-api-client", "> 0.8.5", "< 0.9"
|
766
|
-
gem "mime-types"
|
767
825
|
```
|
768
826
|
|
769
|
-
You'll need to configure a directory (also known as a bucket)
|
827
|
+
You'll need to configure a directory (also known as a bucket) and the credentials in the initializer.
|
770
828
|
For the sake of performance it is assumed that the directory already exists, so please create it if need be.
|
771
829
|
|
772
830
|
Please read the [fog-google README](https://github.com/fog/fog-google/blob/master/README.md) on how to get credentials.
|
773
831
|
|
832
|
+
For Google Storage JSON API (recommended):
|
833
|
+
```ruby
|
834
|
+
CarrierWave.configure do |config|
|
835
|
+
config.fog_provider = 'fog/google'
|
836
|
+
config.fog_credentials = {
|
837
|
+
provider: 'Google',
|
838
|
+
google_project: 'my-project',
|
839
|
+
google_json_key_string: 'xxxxxx'
|
840
|
+
# or use google_json_key_location if using an actual file
|
841
|
+
}
|
842
|
+
config.fog_directory = 'google_cloud_storage_bucket_name'
|
843
|
+
end
|
844
|
+
```
|
774
845
|
|
846
|
+
For Google Storage XML API:
|
775
847
|
```ruby
|
776
848
|
CarrierWave.configure do |config|
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
781
|
-
|
782
|
-
|
783
|
-
|
849
|
+
config.fog_provider = 'fog/google'
|
850
|
+
config.fog_credentials = {
|
851
|
+
provider: 'Google',
|
852
|
+
google_storage_access_key_id: 'xxxxxx',
|
853
|
+
google_storage_secret_access_key: 'yyyyyy'
|
854
|
+
}
|
855
|
+
config.fog_directory = 'google_cloud_storage_bucket_name'
|
784
856
|
end
|
785
857
|
```
|
786
858
|
|
@@ -871,8 +943,8 @@ manipulation methods.
|
|
871
943
|
|
872
944
|
## Using MiniMagick
|
873
945
|
|
874
|
-
MiniMagick is similar to RMagick but performs all the operations using the '
|
875
|
-
|
946
|
+
MiniMagick is similar to RMagick but performs all the operations using the 'convert'
|
947
|
+
CLI which is part of the standard ImageMagick kit. This allows you to have the power
|
876
948
|
of ImageMagick without having to worry about installing all the RMagick libraries.
|
877
949
|
|
878
950
|
See the MiniMagick site for more details:
|
@@ -924,10 +996,10 @@ errors:
|
|
924
996
|
carrierwave_processing_error: failed to be processed
|
925
997
|
carrierwave_integrity_error: is not of an allowed file type
|
926
998
|
carrierwave_download_error: could not be downloaded
|
927
|
-
|
928
|
-
|
929
|
-
|
930
|
-
|
999
|
+
extension_allowlist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
|
1000
|
+
extension_denylist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
|
1001
|
+
content_type_allowlist_error: "You are not allowed to upload %{content_type} files, allowed types: %{allowed_types}"
|
1002
|
+
content_type_denylist_error: "You are not allowed to upload %{content_type} files"
|
931
1003
|
rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image?"
|
932
1004
|
mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}"
|
933
1005
|
min_size_error: "File size should be greater than %{min_size}"
|
@@ -975,12 +1047,12 @@ end
|
|
975
1047
|
Will add these callbacks:
|
976
1048
|
|
977
1049
|
```ruby
|
978
|
-
after_save :store_avatar!
|
979
1050
|
before_save :write_avatar_identifier
|
1051
|
+
after_save :store_previous_changes_for_avatar
|
980
1052
|
after_commit :remove_avatar!, on: :destroy
|
981
1053
|
after_commit :mark_remove_avatar_false, on: :update
|
982
|
-
after_save :store_previous_changes_for_avatar
|
983
1054
|
after_commit :remove_previously_stored_avatar, on: :update
|
1055
|
+
after_commit :store_avatar!, on: [:create, :update]
|
984
1056
|
```
|
985
1057
|
|
986
1058
|
If you want to skip any of these callbacks (eg. you want to keep the existing
|
@@ -1000,6 +1072,8 @@ See [CONTRIBUTING.md](https://github.com/carrierwaveuploader/carrierwave/blob/ma
|
|
1000
1072
|
|
1001
1073
|
## License
|
1002
1074
|
|
1075
|
+
The MIT License (MIT)
|
1076
|
+
|
1003
1077
|
Copyright (c) 2008-2015 Jonas Nicklas
|
1004
1078
|
|
1005
1079
|
Permission is hereby granted, free of charge, to any person obtaining
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'ssrf_filter'
|
3
|
+
require 'addressable'
|
4
|
+
require 'carrierwave/downloader/remote_file'
|
5
|
+
|
6
|
+
module CarrierWave
|
7
|
+
module Downloader
|
8
|
+
class Base
|
9
|
+
attr_reader :uploader
|
10
|
+
|
11
|
+
def initialize(uploader)
|
12
|
+
@uploader = uploader
|
13
|
+
end
|
14
|
+
|
15
|
+
##
|
16
|
+
# Downloads a file from given URL and returns a RemoteFile.
|
17
|
+
#
|
18
|
+
# === Parameters
|
19
|
+
#
|
20
|
+
# [url (String)] The URL where the remote file is stored
|
21
|
+
# [remote_headers (Hash)] Request headers
|
22
|
+
#
|
23
|
+
def download(url, remote_headers = {})
|
24
|
+
headers = remote_headers.
|
25
|
+
reverse_merge('User-Agent' => "CarrierWave/#{CarrierWave::VERSION}")
|
26
|
+
uri = process_uri(url.to_s)
|
27
|
+
begin
|
28
|
+
if skip_ssrf_protection?(uri)
|
29
|
+
response = OpenURI.open_uri(process_uri(url.to_s), headers)
|
30
|
+
else
|
31
|
+
request = nil
|
32
|
+
if ::SsrfFilter::VERSION.to_f < 1.1
|
33
|
+
response = SsrfFilter.get(uri, headers: headers) do |req|
|
34
|
+
request = req
|
35
|
+
end
|
36
|
+
else
|
37
|
+
response = SsrfFilter.get(uri, headers: headers, request_proc: ->(req) { request = req }) do |res|
|
38
|
+
res.body # ensure to read body
|
39
|
+
end
|
40
|
+
end
|
41
|
+
response.uri = request.uri
|
42
|
+
response.value
|
43
|
+
end
|
44
|
+
rescue StandardError => e
|
45
|
+
raise CarrierWave::DownloadError, "could not download file: #{e.message}"
|
46
|
+
end
|
47
|
+
CarrierWave::Downloader::RemoteFile.new(response)
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Processes the given URL by parsing it, and escaping if necessary. Public to allow overriding.
|
52
|
+
#
|
53
|
+
# === Parameters
|
54
|
+
#
|
55
|
+
# [url (String)] The URL where the remote file is stored
|
56
|
+
#
|
57
|
+
def process_uri(uri)
|
58
|
+
uri_parts = uri.split('?')
|
59
|
+
encoded_uri = Addressable::URI.parse(uri_parts.shift).normalize.to_s
|
60
|
+
query = uri_parts.any? ? "?#{uri_parts.join('?')}" : ''
|
61
|
+
begin
|
62
|
+
URI.parse("#{encoded_uri}#{query}")
|
63
|
+
rescue URI::InvalidURIError
|
64
|
+
URI.parse("#{encoded_uri}#{URI::DEFAULT_PARSER.escape(query)}")
|
65
|
+
end
|
66
|
+
rescue URI::InvalidURIError, Addressable::URI::InvalidURIError
|
67
|
+
raise CarrierWave::DownloadError, "couldn't parse URL: #{uri}"
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# If this returns true, SSRF protection will be bypassed.
|
72
|
+
# You can override this if you want to allow accessing specific local URIs that are not SSRF exploitable.
|
73
|
+
#
|
74
|
+
# === Parameters
|
75
|
+
#
|
76
|
+
# [uri (URI)] The URI where the remote file is stored
|
77
|
+
#
|
78
|
+
# === Examples
|
79
|
+
#
|
80
|
+
# class CarrierWave::Downloader::CustomDownloader < CarrierWave::Downloader::Base
|
81
|
+
# def skip_ssrf_protection?(uri)
|
82
|
+
# uri.hostname == 'localhost' && uri.port == 80
|
83
|
+
# end
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# my_uploader.downloader = CarrierWave::Downloader::CustomDownloader
|
87
|
+
#
|
88
|
+
def skip_ssrf_protection?(uri)
|
89
|
+
false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module CarrierWave
|
2
|
+
module Downloader
|
3
|
+
class RemoteFile
|
4
|
+
attr_reader :file, :uri
|
5
|
+
|
6
|
+
def initialize(file)
|
7
|
+
case file
|
8
|
+
when String
|
9
|
+
@file = StringIO.new(file)
|
10
|
+
when Net::HTTPResponse
|
11
|
+
@file = StringIO.new(file.body)
|
12
|
+
@content_type = file.content_type
|
13
|
+
@headers = file
|
14
|
+
@uri = file.uri
|
15
|
+
else
|
16
|
+
@file = file
|
17
|
+
@content_type = file.content_type
|
18
|
+
@headers = file.meta
|
19
|
+
@uri = file.base_uri
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def content_type
|
24
|
+
@content_type || 'application/octet-stream'
|
25
|
+
end
|
26
|
+
|
27
|
+
def headers
|
28
|
+
@headers || {}
|
29
|
+
end
|
30
|
+
|
31
|
+
def original_filename
|
32
|
+
filename = filename_from_header || filename_from_uri
|
33
|
+
mime_type = MiniMime.lookup_by_content_type(content_type)
|
34
|
+
unless File.extname(filename).present? || mime_type.blank?
|
35
|
+
filename = "#{filename}.#{mime_type.extension}"
|
36
|
+
end
|
37
|
+
filename
|
38
|
+
end
|
39
|
+
|
40
|
+
def respond_to?(*args)
|
41
|
+
super || file.respond_to?(*args)
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def filename_from_header
|
47
|
+
return nil unless headers['content-disposition']
|
48
|
+
|
49
|
+
match = headers['content-disposition'].match(/filename=(?:"([^"]+)"|([^";]+))/)
|
50
|
+
return nil unless match
|
51
|
+
|
52
|
+
match[1].presence || match[2].presence
|
53
|
+
end
|
54
|
+
|
55
|
+
def filename_from_uri
|
56
|
+
CGI.unescape(File.basename(uri.path))
|
57
|
+
end
|
58
|
+
|
59
|
+
def method_missing(*args, &block)
|
60
|
+
file.send(*args, &block)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
@@ -4,11 +4,12 @@ en:
|
|
4
4
|
carrierwave_processing_error: failed to be processed
|
5
5
|
carrierwave_integrity_error: is not of an allowed file type
|
6
6
|
carrierwave_download_error: could not be downloaded
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
extension_allowlist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
|
8
|
+
extension_denylist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
|
9
|
+
content_type_allowlist_error: "You are not allowed to upload %{content_type} files, allowed types: %{allowed_types}"
|
10
|
+
content_type_denylist_error: "You are not allowed to upload %{content_type} files"
|
11
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}"
|
13
|
+
vips_processing_error: "Failed to manipulate with vips, maybe it is not an image? Original Error: %{e}"
|
13
14
|
min_size_error: "File size should be greater than %{min_size}"
|
14
15
|
max_size_error: "File size should be less than %{max_size}"
|
data/lib/carrierwave/mount.rb
CHANGED
@@ -174,17 +174,26 @@ module CarrierWave
|
|
174
174
|
return if frozen?
|
175
175
|
mounter = _mounter(:#{column})
|
176
176
|
|
177
|
-
if mounter.remove?
|
178
|
-
|
179
|
-
elsif mounter.identifiers.first
|
180
|
-
write_uploader(mounter.serialization_column, mounter.identifiers.first)
|
181
|
-
end
|
177
|
+
mounter.clear! if mounter.remove?
|
178
|
+
write_uploader(mounter.serialization_column, mounter.identifiers.first)
|
182
179
|
end
|
183
180
|
|
184
181
|
def #{column}_identifier
|
185
182
|
_mounter(:#{column}).read_identifiers[0]
|
186
183
|
end
|
187
184
|
|
185
|
+
def #{column}_integrity_error
|
186
|
+
#{column}_integrity_errors.last
|
187
|
+
end
|
188
|
+
|
189
|
+
def #{column}_processing_error
|
190
|
+
#{column}_processing_errors.last
|
191
|
+
end
|
192
|
+
|
193
|
+
def #{column}_download_error
|
194
|
+
#{column}_download_errors.last
|
195
|
+
end
|
196
|
+
|
188
197
|
def store_previous_changes_for_#{column}
|
189
198
|
attribute_changes = ::ActiveRecord.version.to_s.to_f >= 5.1 ? saved_changes : changes
|
190
199
|
@_previous_changes_for_#{column} = attribute_changes[_mounter(:#{column}).serialization_column]
|
@@ -240,9 +249,9 @@ module CarrierWave
|
|
240
249
|
# [store_images!] Stores all files that have been assigned with +images=+
|
241
250
|
# [remove_images!] Removes the uploaded file from the filesystem.
|
242
251
|
#
|
243
|
-
# [
|
244
|
-
# [
|
245
|
-
# [
|
252
|
+
# [image_integrity_errors] Returns error objects of files which failed to pass integrity check
|
253
|
+
# [image_processing_errors] Returns error objects of files which failed to be processed
|
254
|
+
# [image_download_errors] Returns error objects of files which failed to be downloaded
|
246
255
|
#
|
247
256
|
# [image_identifiers] Reads out the identifiers of the files
|
248
257
|
#
|
@@ -329,11 +338,8 @@ module CarrierWave
|
|
329
338
|
return if frozen?
|
330
339
|
mounter = _mounter(:#{column})
|
331
340
|
|
332
|
-
if mounter.remove?
|
333
|
-
|
334
|
-
elsif mounter.identifiers.any?
|
335
|
-
write_uploader(mounter.serialization_column, mounter.identifiers)
|
336
|
-
end
|
341
|
+
mounter.clear! if mounter.remove?
|
342
|
+
write_uploader(mounter.serialization_column, mounter.identifiers.presence)
|
337
343
|
end
|
338
344
|
|
339
345
|
def #{column}_identifiers
|
@@ -395,16 +401,16 @@ module CarrierWave
|
|
395
401
|
_mounter(:#{column}).store!
|
396
402
|
end
|
397
403
|
|
398
|
-
def #{column}
|
399
|
-
_mounter(:#{column}).
|
404
|
+
def #{column}_integrity_errors
|
405
|
+
_mounter(:#{column}).integrity_errors
|
400
406
|
end
|
401
407
|
|
402
|
-
def #{column}
|
403
|
-
_mounter(:#{column}).
|
408
|
+
def #{column}_processing_errors
|
409
|
+
_mounter(:#{column}).processing_errors
|
404
410
|
end
|
405
411
|
|
406
|
-
def #{column}
|
407
|
-
_mounter(:#{column}).
|
412
|
+
def #{column}_download_errors
|
413
|
+
_mounter(:#{column}).download_errors
|
408
414
|
end
|
409
415
|
|
410
416
|
def mark_remove_#{column}_false
|