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.

Files changed (35) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +120 -43
  3. data/lib/carrierwave/downloader/base.rb +93 -0
  4. data/lib/carrierwave/downloader/remote_file.rb +65 -0
  5. data/lib/carrierwave/locale/en.yml +5 -4
  6. data/lib/carrierwave/mount.rb +25 -19
  7. data/lib/carrierwave/mounter.rb +71 -48
  8. data/lib/carrierwave/orm/activerecord.rb +14 -8
  9. data/lib/carrierwave/processing/mini_magick.rb +100 -117
  10. data/lib/carrierwave/processing/rmagick.rb +11 -5
  11. data/lib/carrierwave/processing/vips.rb +284 -0
  12. data/lib/carrierwave/processing.rb +1 -0
  13. data/lib/carrierwave/sanitized_file.rb +44 -23
  14. data/lib/carrierwave/storage/file.rb +2 -2
  15. data/lib/carrierwave/storage/fog.rb +46 -17
  16. data/lib/carrierwave/storage.rb +1 -0
  17. data/lib/carrierwave/uploader/cache.rb +24 -16
  18. data/lib/carrierwave/uploader/configuration.rb +28 -15
  19. data/lib/carrierwave/uploader/content_type_blacklist.rb +17 -8
  20. data/lib/carrierwave/uploader/content_type_whitelist.rb +20 -8
  21. data/lib/carrierwave/uploader/download.rb +2 -80
  22. data/lib/carrierwave/uploader/extension_blacklist.rb +18 -10
  23. data/lib/carrierwave/uploader/extension_whitelist.rb +19 -10
  24. data/lib/carrierwave/uploader/mountable.rb +6 -0
  25. data/lib/carrierwave/uploader/proxy.rb +2 -2
  26. data/lib/carrierwave/uploader/serialization.rb +1 -1
  27. data/lib/carrierwave/uploader/store.rb +5 -3
  28. data/lib/carrierwave/uploader/url.rb +6 -3
  29. data/lib/carrierwave/uploader/versions.rb +43 -13
  30. data/lib/carrierwave/uploader.rb +0 -9
  31. data/lib/carrierwave/validations/active_model.rb +3 -3
  32. data/lib/carrierwave/version.rb +1 -1
  33. data/lib/carrierwave.rb +4 -0
  34. data/lib/generators/templates/uploader.rb +2 -2
  35. metadata +113 -21
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 80780b748c8964a6099525d3c094c95b0c39902a76a7be6c4a1f091fca8675bc
4
- data.tar.gz: 13a35e088f2b6c4ca84c1b58848b0127ddf3bd39c96c7838eda68f98acc4ec48
3
+ metadata.gz: a6a0cdcecad532a99cca81fbb28113f5ebc67e4392c9c69fd4b1b93b84864eb2
4
+ data.tar.gz: 923a9e4630fd3771e9401be7a2cbfee521df983396c3a5a72f58bd23f1812a87
5
5
  SHA512:
6
- metadata.gz: cd8c38e8b598c781b532cefeaf4ca21dcc0309a0ce7dc89817ccc9464777dae7e3a9afda1b308c9013b6d145df1b250d231401d291d1efde4e150dcc53c76dac
7
- data.tar.gz: d578c1b64632ae62ccae75f2c1f0f4f0941104a88ce0745c2de0de016105f05436f33111fe8a9b79eadcb68b0bc3890c9b735da5db6bdd24b5662ad24e3dcdfb
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://travis-ci.org/carrierwaveuploader/carrierwave.svg?branch=master)](http://travis-ci.org/carrierwaveuploader/carrierwave)
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', '~> 1.0'
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 1.0, CarrierWave requires Rails 4.0 or higher and Ruby 2.0
39
- or higher. If you're on Rails 3, you should use v0.11.0.
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 < ActiveRecord::Base
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 < ActiveRecord::Base
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 a whitelist of
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 extension_whitelist
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 content_type_whitelist
252
+ def content_type_allowlist
239
253
  /image\//
240
254
  end
241
255
  end
242
256
  ```
243
257
 
244
- You can use a blacklist to reject content types.
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 content_type_blacklist
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
- white-listed characters in the file name. If you want to support local scripts (Cyrillic letters, letters with diacritics and so on), you
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 and MiniMagick installed to do image resizing. MiniMagick is a Ruby interface for Imagemagick which is a C program. This is why MiniMagick fails on 'bundle install' without Imagemagick installed.
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
- If you would like to crop images to a specific height and width you
309
- can use the alternative option of '''resize_to_fill'''. It will make sure
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.open(picture.path)
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 for Developers
819
+ ## Using Google Cloud Storage
757
820
 
758
- [Fog](http://github.com/fog/fog-google) is used to support Google Storage for Developers. Ensure you have it in your Gemfile:
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), access key id and secret access key in the initializer.
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
- config.fog_provider = 'fog/google' # required
775
- config.fog_credentials = {
776
- provider: 'Google',
777
- google_storage_access_key_id: 'xxxxxx',
778
- google_storage_secret_access_key: 'yyyyyy'
779
- }
780
- config.fog_directory = 'name_of_directory'
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 'mogrify'
872
- command which is part of the standard ImageMagick kit. This allows you to have the power
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
- extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
925
- extension_blacklist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
926
- content_type_whitelist_error: "You are not allowed to upload %{content_type} files, allowed types: %{allowed_types}"
927
- content_type_blacklist_error: "You are not allowed to upload %{content_type} files"
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
- extension_whitelist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
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, allowed types: %{allowed_types}"
10
- content_type_blacklist_error: "You are not allowed to upload %{content_type} files"
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}"
@@ -174,17 +174,26 @@ module CarrierWave
174
174
  return if frozen?
175
175
  mounter = _mounter(:#{column})
176
176
 
177
- if mounter.remove?
178
- write_uploader(mounter.serialization_column, nil)
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
- # [images_integrity_error] Returns an error object if the last files to be assigned caused an integrity error
244
- # [images_processing_error] Returns an error object if the last files to be assigned caused a processing error
245
- # [images_download_error] Returns an error object if the last files to be remotely assigned caused a download error
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
- write_uploader(mounter.serialization_column, nil)
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}_integrity_error
399
- _mounter(:#{column}).integrity_error
404
+ def #{column}_integrity_errors
405
+ _mounter(:#{column}).integrity_errors
400
406
  end
401
407
 
402
- def #{column}_processing_error
403
- _mounter(:#{column}).processing_error
408
+ def #{column}_processing_errors
409
+ _mounter(:#{column}).processing_errors
404
410
  end
405
411
 
406
- def #{column}_download_error
407
- _mounter(:#{column}).download_error
412
+ def #{column}_download_errors
413
+ _mounter(:#{column}).download_errors
408
414
  end
409
415
 
410
416
  def mark_remove_#{column}_false