carrierwave 1.3.0 → 2.2.3
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 +120 -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 +11 -5
- data/lib/carrierwave/processing/vips.rb +284 -0
- data/lib/carrierwave/processing.rb +1 -0
- data/lib/carrierwave/sanitized_file.rb +44 -23
- data/lib/carrierwave/storage/file.rb +2 -2
- data/lib/carrierwave/storage/fog.rb +46 -17
- 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 -80
- 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/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 +113 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6a0cdcecad532a99cca81fbb28113f5ebc67e4392c9c69fd4b1b93b84864eb2
|
4
|
+
data.tar.gz: 923a9e4630fd3771e9401be7a2cbfee521df983396c3a5a72f58bd23f1812a87
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dc28940e27fb1b721ae86e98b5bf026409ab4d09ba09394e266470ed93c3fe36bd0a78d57c633b548e2cc3455b6442b6df212b43868d6e4b879719b85976fed0
|
7
|
+
data.tar.gz: 313afd97eb83c72cd84cfdcfd94d6a8eea6affc34884291d40adff251d2dda1d5080555f01bb02496480ce42721ca7e93c48749eaec7a7b09ee52268dd1b6400
|
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,12 +157,15 @@ 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
|
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
|
|
@@ -187,6 +190,17 @@ u.avatars[0].current_path # => 'path/to/file.png'
|
|
187
190
|
u.avatars[0].identifier # => 'file.png'
|
188
191
|
```
|
189
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
|
+
|
190
204
|
## Changing the storage directory
|
191
205
|
|
192
206
|
In order to change where uploaded files are put, just override the `store_dir`
|
@@ -216,7 +230,7 @@ end
|
|
216
230
|
## Securing uploads
|
217
231
|
|
218
232
|
Certain files might be dangerous if uploaded to the wrong location, such as PHP
|
219
|
-
files or other script files. CarrierWave allows you to specify
|
233
|
+
files or other script files. CarrierWave allows you to specify an allowlist of
|
220
234
|
allowed extensions or content types.
|
221
235
|
|
222
236
|
If you're mounting the uploader, uploading a file with the wrong extension will
|
@@ -224,7 +238,7 @@ make the record invalid instead. Otherwise, an error is raised.
|
|
224
238
|
|
225
239
|
```ruby
|
226
240
|
class MyUploader < CarrierWave::Uploader::Base
|
227
|
-
def
|
241
|
+
def extension_allowlist
|
228
242
|
%w(jpg jpeg gif png)
|
229
243
|
end
|
230
244
|
end
|
@@ -235,29 +249,45 @@ Let's say we need an uploader that accepts only images. This can be done like th
|
|
235
249
|
|
236
250
|
```ruby
|
237
251
|
class MyUploader < CarrierWave::Uploader::Base
|
238
|
-
def
|
252
|
+
def content_type_allowlist
|
239
253
|
/image\//
|
240
254
|
end
|
241
255
|
end
|
242
256
|
```
|
243
257
|
|
244
|
-
You can use a
|
258
|
+
You can use a denylist to reject content types.
|
245
259
|
Let's say we need an uploader that reject JSON files. This can be done like this
|
246
260
|
|
247
261
|
```ruby
|
248
262
|
class NoJsonUploader < CarrierWave::Uploader::Base
|
249
|
-
def
|
263
|
+
def content_type_denylist
|
250
264
|
['application/text', 'application/json']
|
251
265
|
end
|
252
266
|
end
|
253
267
|
```
|
254
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
|
+
|
255
285
|
### Filenames and unicode chars
|
256
286
|
|
257
287
|
Another security issue you should care for is the file names (see
|
258
288
|
[Ruby On Rails Security Guide](http://guides.rubyonrails.org/security.html#file-uploads)).
|
259
289
|
By default, CarrierWave provides only English letters, arabic numerals and some symbols as
|
260
|
-
|
290
|
+
allowlisted characters in the file name. If you want to support local scripts (Cyrillic letters, letters with diacritics and so on), you
|
261
291
|
have to override `sanitize_regexp` method. It should return regular expression which would match
|
262
292
|
all *non*-allowed symbols.
|
263
293
|
|
@@ -277,7 +307,7 @@ You no longer need to do this manually.
|
|
277
307
|
|
278
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*:
|
279
309
|
|
280
|
-
*Note:* You must have Imagemagick
|
310
|
+
*Note:* You must have Imagemagick installed to do image resizing.
|
281
311
|
|
282
312
|
Some documentation refers to RMagick instead of MiniMagick but MiniMagick is recommended.
|
283
313
|
|
@@ -302,15 +332,13 @@ end
|
|
302
332
|
|
303
333
|
When this uploader is used, an uploaded image would be scaled to be no larger
|
304
334
|
than 800 by 800 pixels. The original aspect ratio will be kept.
|
305
|
-
A version called thumb is then created, which is scaled
|
306
|
-
to exactly 200 by 200 pixels.
|
307
335
|
|
308
|
-
|
309
|
-
|
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
|
310
338
|
that the width and height specified are filled, only cropping
|
311
339
|
if the aspect ratio requires it.
|
312
340
|
|
313
|
-
The uploader could be used like this:
|
341
|
+
The above uploader could be used like this:
|
314
342
|
|
315
343
|
```ruby
|
316
344
|
uploader = AvatarUploader.new
|
@@ -323,6 +351,34 @@ uploader.thumb.url # => '/url/to/thumb_my_file.png' # size: 200x200
|
|
323
351
|
One important thing to remember is that process is called *before* versions are
|
324
352
|
created. This can cut down on processing cost.
|
325
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
|
+
|
326
382
|
It is possible to nest versions within versions:
|
327
383
|
|
328
384
|
```ruby
|
@@ -359,7 +415,7 @@ private
|
|
359
415
|
end
|
360
416
|
|
361
417
|
def is_landscape? picture
|
362
|
-
image = MiniMagick::Image.
|
418
|
+
image = MiniMagick::Image.new(picture.path)
|
363
419
|
image[:width] > image[:height]
|
364
420
|
end
|
365
421
|
|
@@ -648,7 +704,6 @@ If you want to use fog you must add in your CarrierWave initializer the
|
|
648
704
|
following lines
|
649
705
|
|
650
706
|
```ruby
|
651
|
-
config.fog_provider = 'fog' # 'fog/aws' etc. Defaults to 'fog'
|
652
707
|
config.fog_credentials = { ... } # Provider specific credentials
|
653
708
|
```
|
654
709
|
|
@@ -666,7 +721,6 @@ You can also pass in additional options, as documented fully in lib/carrierwave/
|
|
666
721
|
|
667
722
|
```ruby
|
668
723
|
CarrierWave.configure do |config|
|
669
|
-
config.fog_provider = 'fog/aws' # required
|
670
724
|
config.fog_credentials = {
|
671
725
|
provider: 'AWS', # required
|
672
726
|
aws_access_key_id: 'xxx', # required unless using use_iam_profile
|
@@ -679,6 +733,9 @@ CarrierWave.configure do |config|
|
|
679
733
|
config.fog_directory = 'name_of_bucket' # required
|
680
734
|
config.fog_public = false # optional, defaults to true
|
681
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
|
682
739
|
end
|
683
740
|
```
|
684
741
|
|
@@ -692,6 +749,14 @@ end
|
|
692
749
|
|
693
750
|
That's it! You can still use the `CarrierWave::Uploader#url` method to return the url to the file on Amazon S3.
|
694
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
|
+
|
695
760
|
## Using Rackspace Cloud Files
|
696
761
|
|
697
762
|
[Fog](http://github.com/fog/fog) is used to support Rackspace Cloud Files. Ensure you have it in your Gemfile:
|
@@ -707,7 +772,6 @@ Using a US-based account:
|
|
707
772
|
|
708
773
|
```ruby
|
709
774
|
CarrierWave.configure do |config|
|
710
|
-
config.fog_provider = "fog/rackspace/storage" # optional, defaults to "fog"
|
711
775
|
config.fog_credentials = {
|
712
776
|
provider: 'Rackspace',
|
713
777
|
rackspace_username: 'xxxxxx',
|
@@ -722,7 +786,6 @@ Using a UK-based account:
|
|
722
786
|
|
723
787
|
```ruby
|
724
788
|
CarrierWave.configure do |config|
|
725
|
-
config.fog_provider = "fog/rackspace/storage" # optional, defaults to "fog"
|
726
789
|
config.fog_credentials = {
|
727
790
|
provider: 'Rackspace',
|
728
791
|
rackspace_username: 'xxxxxx',
|
@@ -753,31 +816,43 @@ end
|
|
753
816
|
That's it! You can still use the `CarrierWave::Uploader#url` method to return
|
754
817
|
the url to the file on Rackspace Cloud Files.
|
755
818
|
|
756
|
-
## Using Google Storage
|
819
|
+
## Using Google Cloud Storage
|
757
820
|
|
758
|
-
[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:
|
759
822
|
|
760
823
|
```ruby
|
761
824
|
gem "fog-google"
|
762
|
-
gem "google-api-client", "> 0.8.5", "< 0.9"
|
763
|
-
gem "mime-types"
|
764
825
|
```
|
765
826
|
|
766
|
-
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.
|
767
828
|
For the sake of performance it is assumed that the directory already exists, so please create it if need be.
|
768
829
|
|
769
830
|
Please read the [fog-google README](https://github.com/fog/fog-google/blob/master/README.md) on how to get credentials.
|
770
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
|
+
```
|
771
845
|
|
846
|
+
For Google Storage XML API:
|
772
847
|
```ruby
|
773
848
|
CarrierWave.configure do |config|
|
774
|
-
|
775
|
-
|
776
|
-
|
777
|
-
|
778
|
-
|
779
|
-
|
780
|
-
|
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'
|
781
856
|
end
|
782
857
|
```
|
783
858
|
|
@@ -868,8 +943,8 @@ manipulation methods.
|
|
868
943
|
|
869
944
|
## Using MiniMagick
|
870
945
|
|
871
|
-
MiniMagick is similar to RMagick but performs all the operations using the '
|
872
|
-
|
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
|
873
948
|
of ImageMagick without having to worry about installing all the RMagick libraries.
|
874
949
|
|
875
950
|
See the MiniMagick site for more details:
|
@@ -921,10 +996,10 @@ errors:
|
|
921
996
|
carrierwave_processing_error: failed to be processed
|
922
997
|
carrierwave_integrity_error: is not of an allowed file type
|
923
998
|
carrierwave_download_error: could not be downloaded
|
924
|
-
|
925
|
-
|
926
|
-
|
927
|
-
|
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"
|
928
1003
|
rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image?"
|
929
1004
|
mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}"
|
930
1005
|
min_size_error: "File size should be greater than %{min_size}"
|
@@ -972,12 +1047,12 @@ end
|
|
972
1047
|
Will add these callbacks:
|
973
1048
|
|
974
1049
|
```ruby
|
975
|
-
after_save :store_avatar!
|
976
1050
|
before_save :write_avatar_identifier
|
1051
|
+
after_save :store_previous_changes_for_avatar
|
977
1052
|
after_commit :remove_avatar!, on: :destroy
|
978
1053
|
after_commit :mark_remove_avatar_false, on: :update
|
979
|
-
after_save :store_previous_changes_for_avatar
|
980
1054
|
after_commit :remove_previously_stored_avatar, on: :update
|
1055
|
+
after_commit :store_avatar!, on: [:create, :update]
|
981
1056
|
```
|
982
1057
|
|
983
1058
|
If you want to skip any of these callbacks (eg. you want to keep the existing
|
@@ -997,6 +1072,8 @@ See [CONTRIBUTING.md](https://github.com/carrierwaveuploader/carrierwave/blob/ma
|
|
997
1072
|
|
998
1073
|
## License
|
999
1074
|
|
1075
|
+
The MIT License (MIT)
|
1076
|
+
|
1000
1077
|
Copyright (c) 2008-2015 Jonas Nicklas
|
1001
1078
|
|
1002
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
|