carrierwave 2.1.1 → 2.2.0
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 +46 -30
- data/lib/carrierwave/downloader/base.rb +7 -3
- data/lib/carrierwave/locale/en.yml +5 -4
- data/lib/carrierwave/processing.rb +1 -0
- data/lib/carrierwave/processing/rmagick.rb +1 -1
- data/lib/carrierwave/processing/vips.rb +284 -0
- data/lib/carrierwave/sanitized_file.rb +12 -4
- data/lib/carrierwave/storage/fog.rb +11 -2
- data/lib/carrierwave/uploader/cache.rb +1 -1
- data/lib/carrierwave/uploader/content_type_blacklist.rb +17 -8
- data/lib/carrierwave/uploader/content_type_whitelist.rb +20 -8
- data/lib/carrierwave/uploader/extension_blacklist.rb +18 -10
- data/lib/carrierwave/uploader/extension_whitelist.rb +19 -10
- data/lib/carrierwave/uploader/url.rb +6 -3
- data/lib/carrierwave/uploader/versions.rb +1 -1
- data/lib/carrierwave/version.rb +1 -1
- data/lib/generators/templates/uploader.rb +2 -2
- metadata +19 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6e511db9d3eebc43bd42613884cf42ba2ad5f236a438a609cccc67c6ce3d192
|
4
|
+
data.tar.gz: 20cef424394b5a40d27e73e8677161870c6d35f60734fd05cf2d0666f461b834
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28aeace46926db2716ac4600a67cb3a318c553f8f04f533fcf19afaddbafc46489bc3df5d523bc878f5b77164d4110c69ddfe346944caeb46b0dd83df39d7ac9
|
7
|
+
data.tar.gz: dc271f6fdfd5a515295185265a53b0b894ecef7e1d72bdcaa062357dacd5c7e9bca54a818ffcd344a95b341628451f5b6e579b62587845148075f9db9de949f7
|
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
|
|
@@ -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
|
@@ -230,7 +230,7 @@ end
|
|
230
230
|
## Securing uploads
|
231
231
|
|
232
232
|
Certain files might be dangerous if uploaded to the wrong location, such as PHP
|
233
|
-
files or other script files. CarrierWave allows you to specify
|
233
|
+
files or other script files. CarrierWave allows you to specify an allowlist of
|
234
234
|
allowed extensions or content types.
|
235
235
|
|
236
236
|
If you're mounting the uploader, uploading a file with the wrong extension will
|
@@ -238,7 +238,7 @@ make the record invalid instead. Otherwise, an error is raised.
|
|
238
238
|
|
239
239
|
```ruby
|
240
240
|
class MyUploader < CarrierWave::Uploader::Base
|
241
|
-
def
|
241
|
+
def extension_allowlist
|
242
242
|
%w(jpg jpeg gif png)
|
243
243
|
end
|
244
244
|
end
|
@@ -249,45 +249,45 @@ Let's say we need an uploader that accepts only images. This can be done like th
|
|
249
249
|
|
250
250
|
```ruby
|
251
251
|
class MyUploader < CarrierWave::Uploader::Base
|
252
|
-
def
|
252
|
+
def content_type_allowlist
|
253
253
|
/image\//
|
254
254
|
end
|
255
255
|
end
|
256
256
|
```
|
257
257
|
|
258
|
-
You can use a
|
258
|
+
You can use a denylist to reject content types.
|
259
259
|
Let's say we need an uploader that reject JSON files. This can be done like this
|
260
260
|
|
261
261
|
```ruby
|
262
262
|
class NoJsonUploader < CarrierWave::Uploader::Base
|
263
|
-
def
|
263
|
+
def content_type_denylist
|
264
264
|
['application/text', 'application/json']
|
265
265
|
end
|
266
266
|
end
|
267
267
|
```
|
268
268
|
|
269
269
|
### CVE-2016-3714 (ImageTragick)
|
270
|
-
This version of CarrierWave has the ability to mitigate CVE-2016-3714. However, you **MUST** set a
|
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
271
|
|
272
272
|
|
273
|
-
A valid
|
273
|
+
A valid allowlist that will restrict your uploader to images only, and mitigate the CVE is:
|
274
274
|
|
275
275
|
```ruby
|
276
276
|
class MyUploader < CarrierWave::Uploader::Base
|
277
|
-
def
|
277
|
+
def content_type_allowlist
|
278
278
|
[/image\//]
|
279
279
|
end
|
280
280
|
end
|
281
281
|
```
|
282
282
|
|
283
|
-
**WARNING**: A `
|
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
284
|
|
285
285
|
### Filenames and unicode chars
|
286
286
|
|
287
287
|
Another security issue you should care for is the file names (see
|
288
288
|
[Ruby On Rails Security Guide](http://guides.rubyonrails.org/security.html#file-uploads)).
|
289
289
|
By default, CarrierWave provides only English letters, arabic numerals and some symbols as
|
290
|
-
|
290
|
+
allowlisted characters in the file name. If you want to support local scripts (Cyrillic letters, letters with diacritics and so on), you
|
291
291
|
have to override `sanitize_regexp` method. It should return regular expression which would match
|
292
292
|
all *non*-allowed symbols.
|
293
293
|
|
@@ -717,6 +717,9 @@ CarrierWave.configure do |config|
|
|
717
717
|
config.fog_directory = 'name_of_bucket' # required
|
718
718
|
config.fog_public = false # optional, defaults to true
|
719
719
|
config.fog_attributes = { cache_control: "public, max-age=#{365.days.to_i}" } # optional, defaults to {}
|
720
|
+
# For an application which utilizes multiple servers but does not need caches persisted across requests,
|
721
|
+
# uncomment the line :file instead of the default :storage. Otherwise, it will use AWS as the temp cache store.
|
722
|
+
# config.cache_storage = :file
|
720
723
|
end
|
721
724
|
```
|
722
725
|
|
@@ -797,30 +800,43 @@ end
|
|
797
800
|
That's it! You can still use the `CarrierWave::Uploader#url` method to return
|
798
801
|
the url to the file on Rackspace Cloud Files.
|
799
802
|
|
800
|
-
## Using Google Storage
|
803
|
+
## Using Google Cloud Storage
|
801
804
|
|
802
|
-
[Fog](http://github.com/fog/fog-google) is used to support Google Storage
|
805
|
+
[Fog](http://github.com/fog/fog-google) is used to support Google Cloud Storage. Ensure you have it in your Gemfile:
|
803
806
|
|
804
807
|
```ruby
|
805
808
|
gem "fog-google"
|
806
|
-
gem "google-api-client", "> 0.8.5", "< 0.9"
|
807
|
-
gem "mime-types"
|
808
809
|
```
|
809
810
|
|
810
|
-
You'll need to configure a directory (also known as a bucket)
|
811
|
+
You'll need to configure a directory (also known as a bucket) and the credentials in the initializer.
|
811
812
|
For the sake of performance it is assumed that the directory already exists, so please create it if need be.
|
812
813
|
|
813
814
|
Please read the [fog-google README](https://github.com/fog/fog-google/blob/master/README.md) on how to get credentials.
|
814
815
|
|
816
|
+
For Google Storage JSON API (recommended):
|
817
|
+
```ruby
|
818
|
+
CarrierWave.configure do |config|
|
819
|
+
config.fog_provider = 'fog/google'
|
820
|
+
config.fog_credentials = {
|
821
|
+
provider: 'Google',
|
822
|
+
google_project: 'my-project',
|
823
|
+
google_json_key_string: 'xxxxxx'
|
824
|
+
# or use google_json_key_location if using an actual file
|
825
|
+
}
|
826
|
+
config.fog_directory = 'google_cloud_storage_bucket_name'
|
827
|
+
end
|
828
|
+
```
|
815
829
|
|
830
|
+
For Google Storage XML API:
|
816
831
|
```ruby
|
817
832
|
CarrierWave.configure do |config|
|
818
|
-
|
819
|
-
|
820
|
-
|
821
|
-
|
822
|
-
|
823
|
-
|
833
|
+
config.fog_provider = 'fog/google'
|
834
|
+
config.fog_credentials = {
|
835
|
+
provider: 'Google',
|
836
|
+
google_storage_access_key_id: 'xxxxxx',
|
837
|
+
google_storage_secret_access_key: 'yyyyyy'
|
838
|
+
}
|
839
|
+
config.fog_directory = 'google_cloud_storage_bucket_name'
|
824
840
|
end
|
825
841
|
```
|
826
842
|
|
@@ -964,10 +980,10 @@ errors:
|
|
964
980
|
carrierwave_processing_error: failed to be processed
|
965
981
|
carrierwave_integrity_error: is not of an allowed file type
|
966
982
|
carrierwave_download_error: could not be downloaded
|
967
|
-
|
968
|
-
|
969
|
-
|
970
|
-
|
983
|
+
extension_allowlist_error: "You are not allowed to upload %{extension} files, allowed types: %{allowed_types}"
|
984
|
+
extension_denylist_error: "You are not allowed to upload %{extension} files, prohibited types: %{prohibited_types}"
|
985
|
+
content_type_allowlist_error: "You are not allowed to upload %{content_type} files, allowed types: %{allowed_types}"
|
986
|
+
content_type_denylist_error: "You are not allowed to upload %{content_type} files"
|
971
987
|
rmagick_processing_error: "Failed to manipulate with rmagick, maybe it is not an image?"
|
972
988
|
mini_magick_processing_error: "Failed to manipulate with MiniMagick, maybe it is not an image? Original Error: %{e}"
|
973
989
|
min_size_error: "File size should be greater than %{min_size}"
|
@@ -1015,12 +1031,12 @@ end
|
|
1015
1031
|
Will add these callbacks:
|
1016
1032
|
|
1017
1033
|
```ruby
|
1018
|
-
after_save :store_avatar!
|
1019
1034
|
before_save :write_avatar_identifier
|
1035
|
+
after_save :store_previous_changes_for_avatar
|
1020
1036
|
after_commit :remove_avatar!, on: :destroy
|
1021
1037
|
after_commit :mark_remove_avatar_false, on: :update
|
1022
|
-
after_save :store_previous_changes_for_avatar
|
1023
1038
|
after_commit :remove_previously_stored_avatar, on: :update
|
1039
|
+
after_commit :store_avatar!, on: [:create, :update]
|
1024
1040
|
```
|
1025
1041
|
|
1026
1042
|
If you want to skip any of these callbacks (eg. you want to keep the existing
|
@@ -42,7 +42,7 @@ module CarrierWave
|
|
42
42
|
end
|
43
43
|
|
44
44
|
##
|
45
|
-
# Processes the given URL by parsing and escaping
|
45
|
+
# Processes the given URL by parsing it, and escaping if necessary. Public to allow overriding.
|
46
46
|
#
|
47
47
|
# === Parameters
|
48
48
|
#
|
@@ -51,8 +51,12 @@ module CarrierWave
|
|
51
51
|
def process_uri(uri)
|
52
52
|
uri_parts = uri.split('?')
|
53
53
|
encoded_uri = Addressable::URI.parse(uri_parts.shift).normalize.to_s
|
54
|
-
|
55
|
-
|
54
|
+
query = uri_parts.any? ? "?#{uri_parts.join('?')}" : ''
|
55
|
+
begin
|
56
|
+
URI.parse("#{encoded_uri}#{query}")
|
57
|
+
rescue URI::InvalidURIError
|
58
|
+
URI.parse("#{encoded_uri}#{URI::DEFAULT_PARSER.escape(query)}")
|
59
|
+
end
|
56
60
|
rescue URI::InvalidURIError, Addressable::URI::InvalidURIError
|
57
61
|
raise CarrierWave::DownloadError, "couldn't parse URL: #{uri}"
|
58
62
|
end
|
@@ -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}"
|
@@ -228,7 +228,7 @@ module CarrierWave
|
|
228
228
|
height = dimension_from height
|
229
229
|
manipulate! do |img|
|
230
230
|
img.resize_to_fit!(width, height)
|
231
|
-
new_img = ::Magick::Image.new(width, height) {
|
231
|
+
new_img = ::Magick::Image.new(width, height) { |img| img.background_color = background == :transparent ? 'rgba(255,255,255,0)' : background.to_s }
|
232
232
|
if background == :transparent
|
233
233
|
filled = new_img.matte_floodfill(1, 1)
|
234
234
|
else
|
@@ -0,0 +1,284 @@
|
|
1
|
+
module CarrierWave
|
2
|
+
|
3
|
+
##
|
4
|
+
# This module simplifies manipulation with vips by providing a set
|
5
|
+
# of convenient helper methods. If you want to use them, you'll need to
|
6
|
+
# require this file:
|
7
|
+
#
|
8
|
+
# require 'carrierwave/processing/vips'
|
9
|
+
#
|
10
|
+
# And then include it in your uploader:
|
11
|
+
#
|
12
|
+
# class MyUploader < CarrierWave::Uploader::Base
|
13
|
+
# include CarrierWave::Vips
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# You can now use the provided helpers:
|
17
|
+
#
|
18
|
+
# class MyUploader < CarrierWave::Uploader::Base
|
19
|
+
# include CarrierWave::Vips
|
20
|
+
#
|
21
|
+
# process :resize_to_fit => [200, 200]
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# Or create your own helpers with the powerful vips! method, which
|
25
|
+
# yields an ImageProcessing::Builder object. Check out the ImageProcessing
|
26
|
+
# docs at http://github.com/janko-m/image_processing and the list of all
|
27
|
+
# available Vips options at
|
28
|
+
# https://libvips.github.io/libvips/API/current/using-cli.html for more info.
|
29
|
+
#
|
30
|
+
# class MyUploader < CarrierWave::Uploader::Base
|
31
|
+
# include CarrierWave::Vips
|
32
|
+
#
|
33
|
+
# process :radial_blur => 10
|
34
|
+
#
|
35
|
+
# def radial_blur(amount)
|
36
|
+
# vips! do |builder|
|
37
|
+
# builder.radial_blur(amount)
|
38
|
+
# builder = yield(builder) if block_given?
|
39
|
+
# builder
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# === Note
|
45
|
+
#
|
46
|
+
# The ImageProcessing gem uses ruby-vips, a binding for the vips image
|
47
|
+
# library. You can find more information here:
|
48
|
+
#
|
49
|
+
# https://github.com/libvips/ruby-vips
|
50
|
+
#
|
51
|
+
#
|
52
|
+
module Vips
|
53
|
+
extend ActiveSupport::Concern
|
54
|
+
|
55
|
+
included do
|
56
|
+
require "image_processing/vips"
|
57
|
+
# We need to disable caching since we're editing images in place.
|
58
|
+
::Vips.cache_set_max(0)
|
59
|
+
end
|
60
|
+
|
61
|
+
module ClassMethods
|
62
|
+
def convert(format)
|
63
|
+
process :convert => format
|
64
|
+
end
|
65
|
+
|
66
|
+
def resize_to_limit(width, height)
|
67
|
+
process :resize_to_limit => [width, height]
|
68
|
+
end
|
69
|
+
|
70
|
+
def resize_to_fit(width, height)
|
71
|
+
process :resize_to_fit => [width, height]
|
72
|
+
end
|
73
|
+
|
74
|
+
def resize_to_fill(width, height, gravity='centre')
|
75
|
+
process :resize_to_fill => [width, height, gravity]
|
76
|
+
end
|
77
|
+
|
78
|
+
def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil)
|
79
|
+
process :resize_and_pad => [width, height, background, gravity, alpha]
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
##
|
84
|
+
# Changes the image encoding format to the given format
|
85
|
+
#
|
86
|
+
# See https://libvips.github.io/libvips/API/current/using-cli.html#using-command-line-conversion
|
87
|
+
#
|
88
|
+
# === Parameters
|
89
|
+
#
|
90
|
+
# [format (#to_s)] an abbreviation of the format
|
91
|
+
#
|
92
|
+
# === Yields
|
93
|
+
#
|
94
|
+
# [Vips::Image] additional manipulations to perform
|
95
|
+
#
|
96
|
+
# === Examples
|
97
|
+
#
|
98
|
+
# image.convert(:png)
|
99
|
+
#
|
100
|
+
def convert(format, page=nil)
|
101
|
+
vips! do |builder|
|
102
|
+
builder = builder.convert(format)
|
103
|
+
builder = builder.loader(page: page) if page
|
104
|
+
builder
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
##
|
109
|
+
# Resize the image to fit within the specified dimensions while retaining
|
110
|
+
# the original aspect ratio. Will only resize the image if it is larger than the
|
111
|
+
# specified dimensions. The resulting image may be shorter or narrower than specified
|
112
|
+
# in the smaller dimension but will not be larger than the specified values.
|
113
|
+
#
|
114
|
+
# === Parameters
|
115
|
+
#
|
116
|
+
# [width (Integer)] the width to scale the image to
|
117
|
+
# [height (Integer)] the height to scale the image to
|
118
|
+
# [combine_options (Hash)] additional Vips options to apply before resizing
|
119
|
+
#
|
120
|
+
# === Yields
|
121
|
+
#
|
122
|
+
# [Vips::Image] additional manipulations to perform
|
123
|
+
#
|
124
|
+
def resize_to_limit(width, height, combine_options: {})
|
125
|
+
width, height = resolve_dimensions(width, height)
|
126
|
+
|
127
|
+
vips! do |builder|
|
128
|
+
builder.resize_to_limit(width, height)
|
129
|
+
.apply(combine_options)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
##
|
134
|
+
# Resize the image to fit within the specified dimensions while retaining
|
135
|
+
# the original aspect ratio. The image may be shorter or narrower than
|
136
|
+
# specified in the smaller dimension but will not be larger than the specified values.
|
137
|
+
#
|
138
|
+
# === Parameters
|
139
|
+
#
|
140
|
+
# [width (Integer)] the width to scale the image to
|
141
|
+
# [height (Integer)] the height to scale the image to
|
142
|
+
# [combine_options (Hash)] additional Vips options to apply before resizing
|
143
|
+
#
|
144
|
+
# === Yields
|
145
|
+
#
|
146
|
+
# [Vips::Image] additional manipulations to perform
|
147
|
+
#
|
148
|
+
def resize_to_fit(width, height, combine_options: {})
|
149
|
+
width, height = resolve_dimensions(width, height)
|
150
|
+
|
151
|
+
vips! do |builder|
|
152
|
+
builder.resize_to_fit(width, height)
|
153
|
+
.apply(combine_options)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
##
|
158
|
+
# Resize the image to fit within the specified dimensions while retaining
|
159
|
+
# the aspect ratio of the original image. If necessary, crop the image in the
|
160
|
+
# larger dimension.
|
161
|
+
#
|
162
|
+
# === Parameters
|
163
|
+
#
|
164
|
+
# [width (Integer)] the width to scale the image to
|
165
|
+
# [height (Integer)] the height to scale the image to
|
166
|
+
# [combine_options (Hash)] additional vips options to apply before resizing
|
167
|
+
#
|
168
|
+
# === Yields
|
169
|
+
#
|
170
|
+
# [Vips::Image] additional manipulations to perform
|
171
|
+
#
|
172
|
+
def resize_to_fill(width, height, _gravity = nil, combine_options: {})
|
173
|
+
width, height = resolve_dimensions(width, height)
|
174
|
+
|
175
|
+
vips! do |builder|
|
176
|
+
builder.resize_to_fill(width, height).apply(combine_options)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
##
|
181
|
+
# Resize the image to fit within the specified dimensions while retaining
|
182
|
+
# the original aspect ratio. If necessary, will pad the remaining area
|
183
|
+
# with the given color, which defaults to transparent (for gif and png,
|
184
|
+
# white for jpeg).
|
185
|
+
#
|
186
|
+
# See https://libvips.github.io/libvips/API/current/libvips-conversion.html#VipsCompassDirection
|
187
|
+
# for gravity options.
|
188
|
+
#
|
189
|
+
# === Parameters
|
190
|
+
#
|
191
|
+
# [width (Integer)] the width to scale the image to
|
192
|
+
# [height (Integer)] the height to scale the image to
|
193
|
+
# [background (List, nil)] the color of the background as a RGB, like [0, 255, 255], nil indicates transparent
|
194
|
+
# [gravity (String)] how to position the image
|
195
|
+
# [alpha (Boolean, nil)] pad the image with the alpha channel if supported
|
196
|
+
# [combine_options (Hash)] additional vips options to apply before resizing
|
197
|
+
#
|
198
|
+
# === Yields
|
199
|
+
#
|
200
|
+
# [Vips::Image] additional manipulations to perform
|
201
|
+
#
|
202
|
+
def resize_and_pad(width, height, background=nil, gravity='centre', alpha=nil, combine_options: {})
|
203
|
+
width, height = resolve_dimensions(width, height)
|
204
|
+
|
205
|
+
vips! do |builder|
|
206
|
+
builder.resize_and_pad(width, height, background: background, gravity: gravity, alpha: alpha)
|
207
|
+
.apply(combine_options)
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
##
|
212
|
+
# Returns the width of the image in pixels.
|
213
|
+
#
|
214
|
+
# === Returns
|
215
|
+
#
|
216
|
+
# [Integer] the image's width in pixels
|
217
|
+
#
|
218
|
+
def width
|
219
|
+
vips_image.width
|
220
|
+
end
|
221
|
+
|
222
|
+
##
|
223
|
+
# Returns the height of the image in pixels.
|
224
|
+
#
|
225
|
+
# === Returns
|
226
|
+
#
|
227
|
+
# [Integer] the image's height in pixels
|
228
|
+
#
|
229
|
+
def height
|
230
|
+
vips_image.height
|
231
|
+
end
|
232
|
+
|
233
|
+
# Process the image with vip, using the ImageProcessing gem. This
|
234
|
+
# method will build a "convert" vips command and execute it on the
|
235
|
+
# current image.
|
236
|
+
#
|
237
|
+
# === Gotcha
|
238
|
+
#
|
239
|
+
# This method assumes that the object responds to +current_path+.
|
240
|
+
# Any class that this module is mixed into must have a +current_path+ method.
|
241
|
+
# CarrierWave::Uploader does, so you won't need to worry about this in
|
242
|
+
# most cases.
|
243
|
+
#
|
244
|
+
# === Yields
|
245
|
+
#
|
246
|
+
# [ImageProcessing::Builder] use it to define processing to be performed
|
247
|
+
#
|
248
|
+
# === Raises
|
249
|
+
#
|
250
|
+
# [CarrierWave::ProcessingError] if processing failed.
|
251
|
+
def vips!
|
252
|
+
builder = ImageProcessing::Vips.source(current_path)
|
253
|
+
builder = yield(builder)
|
254
|
+
|
255
|
+
result = builder.call
|
256
|
+
result.close
|
257
|
+
|
258
|
+
FileUtils.mv result.path, current_path
|
259
|
+
|
260
|
+
if File.extname(result.path) != File.extname(current_path)
|
261
|
+
move_to = current_path.chomp(File.extname(current_path)) + File.extname(result.path)
|
262
|
+
file.content_type = ::MiniMime.lookup_by_filename(move_to).content_type
|
263
|
+
file.move_to(move_to, permissions, directory_permissions)
|
264
|
+
end
|
265
|
+
rescue ::Vips::Error => e
|
266
|
+
message = I18n.translate(:"errors.messages.vips_processing_error", :e => e)
|
267
|
+
raise CarrierWave::ProcessingError, message
|
268
|
+
end
|
269
|
+
|
270
|
+
private
|
271
|
+
|
272
|
+
def resolve_dimensions(*dimensions)
|
273
|
+
dimensions.map do |value|
|
274
|
+
next value unless value.instance_of?(Proc)
|
275
|
+
value.arity >= 1 ? value.call(self) : value.call
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
def vips_image
|
280
|
+
::Vips::Image.new_from_buffer(read, "")
|
281
|
+
end
|
282
|
+
|
283
|
+
end # Vips
|
284
|
+
end # CarrierWave
|
@@ -2,6 +2,7 @@ require 'pathname'
|
|
2
2
|
require 'active_support/core_ext/string/multibyte'
|
3
3
|
require 'mini_mime'
|
4
4
|
require 'mimemagic'
|
5
|
+
require 'mimemagic/overlay'
|
5
6
|
|
6
7
|
module CarrierWave
|
7
8
|
|
@@ -181,9 +182,9 @@ module CarrierWave
|
|
181
182
|
move!(new_path)
|
182
183
|
chmod!(new_path, permissions)
|
183
184
|
if keep_filename
|
184
|
-
self.file = {:tempfile => new_path, :filename => original_filename, :content_type => content_type}
|
185
|
+
self.file = {:tempfile => new_path, :filename => original_filename, :content_type => @content_type}
|
185
186
|
else
|
186
|
-
self.file = {:tempfile => new_path, :content_type => content_type}
|
187
|
+
self.file = {:tempfile => new_path, :content_type => @content_type}
|
187
188
|
end
|
188
189
|
self
|
189
190
|
end
|
@@ -330,9 +331,16 @@ module CarrierWave
|
|
330
331
|
|
331
332
|
def mime_magic_content_type
|
332
333
|
if path
|
333
|
-
File.open(path) do |file|
|
334
|
-
MimeMagic.by_magic(file).try(:type)
|
334
|
+
type = File.open(path) do |file|
|
335
|
+
MimeMagic.by_magic(file).try(:type)
|
335
336
|
end
|
337
|
+
|
338
|
+
if type.nil?
|
339
|
+
type = ::MiniMime.lookup_by_filename(path).try(:content_type)
|
340
|
+
type = 'invalid/invalid' unless type.nil? || type.start_with?('text/')
|
341
|
+
end
|
342
|
+
|
343
|
+
type
|
336
344
|
end
|
337
345
|
rescue Errno::ENOENT
|
338
346
|
nil
|
@@ -200,7 +200,7 @@ module CarrierWave
|
|
200
200
|
# avoid a get by using local references
|
201
201
|
local_directory = connection.directories.new(:key => @uploader.fog_directory)
|
202
202
|
local_file = local_directory.files.new(:key => path)
|
203
|
-
expire_at = options[:expire_at] || ::Fog::Time.now
|
203
|
+
expire_at = options[:expire_at] || ::Fog::Time.now.since(@uploader.fog_authenticated_url_expiration.to_i)
|
204
204
|
case @uploader.fog_credentials[:provider]
|
205
205
|
when 'AWS', 'Google'
|
206
206
|
# Older versions of fog-google do not support options as a parameter
|
@@ -450,7 +450,7 @@ module CarrierWave
|
|
450
450
|
# @return [CarrierWave::Storage::Fog::File] the location where the file will be stored.
|
451
451
|
#
|
452
452
|
def copy_to(new_path)
|
453
|
-
connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path,
|
453
|
+
connection.copy_object(@uploader.fog_directory, file.key, @uploader.fog_directory, new_path, copy_options)
|
454
454
|
CarrierWave::Storage::Fog::File.new(@uploader, @base, new_path)
|
455
455
|
end
|
456
456
|
|
@@ -494,9 +494,18 @@ module CarrierWave
|
|
494
494
|
@file ||= directory.files.head(path)
|
495
495
|
end
|
496
496
|
|
497
|
+
def copy_options
|
498
|
+
options = {}
|
499
|
+
options.merge!(acl_header) if acl_header.present?
|
500
|
+
options['Content-Type'] ||= content_type if content_type
|
501
|
+
options.merge(@uploader.fog_attributes)
|
502
|
+
end
|
503
|
+
|
497
504
|
def acl_header
|
498
505
|
if fog_provider == 'AWS'
|
499
506
|
{ 'x-amz-acl' => @uploader.fog_public ? 'public-read' : 'private' }
|
507
|
+
elsif fog_provider == "Google"
|
508
|
+
@uploader.fog_public ? { destination_predefined_acl: "publicRead" } : {}
|
500
509
|
else
|
501
510
|
{}
|
502
511
|
end
|
@@ -8,39 +8,48 @@ module CarrierWave
|
|
8
8
|
end
|
9
9
|
|
10
10
|
##
|
11
|
-
# Override this method in your uploader to provide a
|
11
|
+
# Override this method in your uploader to provide a denylist of files content types
|
12
12
|
# which are not allowed to be uploaded.
|
13
13
|
# Not only strings but Regexp are allowed as well.
|
14
14
|
#
|
15
15
|
# === Returns
|
16
16
|
#
|
17
|
-
# [NilClass, String, Regexp, Array[String, Regexp]] a
|
17
|
+
# [NilClass, String, Regexp, Array[String, Regexp]] a denylist of content types which are not allowed to be uploaded
|
18
18
|
#
|
19
19
|
# === Examples
|
20
20
|
#
|
21
|
-
# def
|
21
|
+
# def content_type_denylist
|
22
22
|
# %w(text/json application/json)
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
# Basically the same, but using a Regexp:
|
26
26
|
#
|
27
|
-
# def
|
27
|
+
# def content_type_denylist
|
28
28
|
# [/(text|application)\/json/]
|
29
29
|
# end
|
30
30
|
#
|
31
|
-
def
|
31
|
+
def content_type_denylist
|
32
|
+
if respond_to?(:content_type_blacklist)
|
33
|
+
ActiveSupport::Deprecation.warn "#content_type_blacklist is deprecated, use #content_type_denylist instead." unless instance_variable_defined?(:@content_type_blacklist_warned)
|
34
|
+
@content_type_blacklist_warned = true
|
35
|
+
content_type_blacklist
|
36
|
+
end
|
37
|
+
end
|
32
38
|
|
33
39
|
private
|
34
40
|
|
35
41
|
def check_content_type_blacklist!(new_file)
|
42
|
+
return unless content_type_denylist
|
43
|
+
|
36
44
|
content_type = new_file.content_type
|
37
|
-
if
|
38
|
-
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_blacklist_error",
|
45
|
+
if blacklisted_content_type?(content_type)
|
46
|
+
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_blacklist_error",
|
47
|
+
content_type: content_type, default: :"errors.messages.content_type_denylist_error")
|
39
48
|
end
|
40
49
|
end
|
41
50
|
|
42
51
|
def blacklisted_content_type?(content_type)
|
43
|
-
Array(
|
52
|
+
Array(content_type_denylist).any? { |item| content_type =~ /#{item}/ }
|
44
53
|
end
|
45
54
|
|
46
55
|
end # ContentTypeBlacklist
|
@@ -8,39 +8,51 @@ module CarrierWave
|
|
8
8
|
end
|
9
9
|
|
10
10
|
##
|
11
|
-
# Override this method in your uploader to provide
|
11
|
+
# Override this method in your uploader to provide an allowlist of files content types
|
12
12
|
# which are allowed to be uploaded.
|
13
13
|
# Not only strings but Regexp are allowed as well.
|
14
14
|
#
|
15
15
|
# === Returns
|
16
16
|
#
|
17
|
-
# [NilClass, String, Regexp, Array[String, Regexp]]
|
17
|
+
# [NilClass, String, Regexp, Array[String, Regexp]] an allowlist of content types which are allowed to be uploaded
|
18
18
|
#
|
19
19
|
# === Examples
|
20
20
|
#
|
21
|
-
# def
|
21
|
+
# def content_type_allowlist
|
22
22
|
# %w(text/json application/json)
|
23
23
|
# end
|
24
24
|
#
|
25
25
|
# Basically the same, but using a Regexp:
|
26
26
|
#
|
27
|
-
# def
|
27
|
+
# def content_type_allowlist
|
28
28
|
# [/(text|application)\/json/]
|
29
29
|
# end
|
30
30
|
#
|
31
|
-
def
|
31
|
+
def content_type_allowlist
|
32
|
+
if respond_to?(:content_type_whitelist)
|
33
|
+
ActiveSupport::Deprecation.warn "#content_type_whitelist is deprecated, use #content_type_allowlist instead." unless instance_variable_defined?(:@content_type_whitelist_warned)
|
34
|
+
@content_type_whitelist_warned = true
|
35
|
+
content_type_whitelist
|
36
|
+
end
|
37
|
+
end
|
32
38
|
|
33
39
|
private
|
34
40
|
|
35
41
|
def check_content_type_whitelist!(new_file)
|
42
|
+
return unless content_type_allowlist
|
43
|
+
|
36
44
|
content_type = new_file.content_type
|
37
|
-
if
|
38
|
-
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_whitelist_error", content_type: content_type,
|
45
|
+
if !whitelisted_content_type?(content_type)
|
46
|
+
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.content_type_whitelist_error", content_type: content_type,
|
47
|
+
allowed_types: Array(content_type_allowlist).join(", "), default: :"errors.messages.content_type_allowlist_error")
|
39
48
|
end
|
40
49
|
end
|
41
50
|
|
42
51
|
def whitelisted_content_type?(content_type)
|
43
|
-
Array(
|
52
|
+
Array(content_type_allowlist).any? do |item|
|
53
|
+
item = Regexp.quote(item) if item.class != Regexp
|
54
|
+
content_type =~ /#{item}/
|
55
|
+
end
|
44
56
|
end
|
45
57
|
|
46
58
|
end # ContentTypeWhitelist
|
@@ -8,43 +8,51 @@ module CarrierWave
|
|
8
8
|
end
|
9
9
|
|
10
10
|
##
|
11
|
-
# Override this method in your uploader to provide a
|
11
|
+
# Override this method in your uploader to provide a denylist of extensions which
|
12
12
|
# are prohibited to be uploaded. Compares the file's extension case insensitive.
|
13
13
|
# Furthermore, not only strings but Regexp are allowed as well.
|
14
14
|
#
|
15
|
-
# When using a Regexp in the
|
15
|
+
# When using a Regexp in the denylist, `\A` and `\z` are automatically added to
|
16
16
|
# the Regexp expression, also case insensitive.
|
17
17
|
#
|
18
18
|
# === Returns
|
19
19
|
|
20
|
-
# [NilClass, String, Regexp, Array[String, Regexp]] a
|
20
|
+
# [NilClass, String, Regexp, Array[String, Regexp]] a deny list of extensions which are prohibited to be uploaded
|
21
21
|
#
|
22
22
|
# === Examples
|
23
23
|
#
|
24
|
-
# def
|
24
|
+
# def extension_denylist
|
25
25
|
# %w(swf tiff)
|
26
26
|
# end
|
27
27
|
#
|
28
28
|
# Basically the same, but using a Regexp:
|
29
29
|
#
|
30
|
-
# def
|
30
|
+
# def extension_denylist
|
31
31
|
# [/swf/, 'tiff']
|
32
32
|
# end
|
33
33
|
#
|
34
|
-
|
35
|
-
|
34
|
+
def extension_denylist
|
35
|
+
if respond_to?(:extension_blacklist)
|
36
|
+
ActiveSupport::Deprecation.warn "#extension_blacklist is deprecated, use #extension_denylist instead." unless instance_variable_defined?(:@extension_blacklist_warned)
|
37
|
+
@extension_blacklist_warned = true
|
38
|
+
extension_blacklist
|
39
|
+
end
|
40
|
+
end
|
36
41
|
|
37
42
|
private
|
38
43
|
|
39
44
|
def check_extension_blacklist!(new_file)
|
45
|
+
return unless extension_denylist
|
46
|
+
|
40
47
|
extension = new_file.extension.to_s
|
41
|
-
if
|
42
|
-
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_blacklist_error", extension: new_file.extension.inspect,
|
48
|
+
if blacklisted_extension?(extension)
|
49
|
+
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_blacklist_error", extension: new_file.extension.inspect,
|
50
|
+
prohibited_types: Array(extension_denylist).join(", "), default: :"errors.messages.extension_denylist_error")
|
43
51
|
end
|
44
52
|
end
|
45
53
|
|
46
54
|
def blacklisted_extension?(extension)
|
47
|
-
Array(
|
55
|
+
Array(extension_denylist).any? { |item| extension =~ /\A#{item}\z/i }
|
48
56
|
end
|
49
57
|
end
|
50
58
|
end
|
@@ -8,45 +8,54 @@ module CarrierWave
|
|
8
8
|
end
|
9
9
|
|
10
10
|
##
|
11
|
-
# Override this method in your uploader to provide
|
11
|
+
# Override this method in your uploader to provide an allowlist of extensions which
|
12
12
|
# are allowed to be uploaded. Compares the file's extension case insensitive.
|
13
13
|
# Furthermore, not only strings but Regexp are allowed as well.
|
14
14
|
#
|
15
|
-
# When using a Regexp in the
|
15
|
+
# When using a Regexp in the allowlist, `\A` and `\z` are automatically added to
|
16
16
|
# the Regexp expression, also case insensitive.
|
17
17
|
#
|
18
18
|
# === Returns
|
19
19
|
#
|
20
|
-
# [NilClass, String, Regexp, Array[String, Regexp]]
|
20
|
+
# [NilClass, String, Regexp, Array[String, Regexp]] an allowlist of extensions which are allowed to be uploaded
|
21
21
|
#
|
22
22
|
# === Examples
|
23
23
|
#
|
24
|
-
# def
|
24
|
+
# def extension_allowlist
|
25
25
|
# %w(jpg jpeg gif png)
|
26
26
|
# end
|
27
27
|
#
|
28
28
|
# Basically the same, but using a Regexp:
|
29
29
|
#
|
30
|
-
# def
|
30
|
+
# def extension_allowlist
|
31
31
|
# [/jpe?g/, 'gif', 'png']
|
32
32
|
# end
|
33
33
|
#
|
34
|
-
def
|
34
|
+
def extension_allowlist
|
35
|
+
if respond_to?(:extension_whitelist)
|
36
|
+
ActiveSupport::Deprecation.warn "#extension_whitelist is deprecated, use #extension_allowlist instead." unless instance_variable_defined?(:@extension_whitelist_warned)
|
37
|
+
@extension_whitelist_warned = true
|
38
|
+
extension_whitelist
|
39
|
+
end
|
40
|
+
end
|
35
41
|
|
36
42
|
private
|
37
43
|
|
38
44
|
def check_extension_whitelist!(new_file)
|
45
|
+
return unless extension_allowlist
|
46
|
+
|
39
47
|
extension = new_file.extension.to_s
|
40
|
-
if
|
41
|
-
|
48
|
+
if !whitelisted_extension?(extension)
|
49
|
+
# Look for whitelist first, then fallback to allowlist
|
50
|
+
raise CarrierWave::IntegrityError, I18n.translate(:"errors.messages.extension_whitelist_error", extension: new_file.extension.inspect,
|
51
|
+
allowed_types: Array(extension_allowlist).join(", "), default: :"errors.messages.extension_allowlist_error")
|
42
52
|
end
|
43
53
|
end
|
44
54
|
|
45
55
|
def whitelisted_extension?(extension)
|
46
56
|
downcase_extension = extension.downcase
|
47
|
-
Array(
|
57
|
+
Array(extension_allowlist).any? { |item| downcase_extension =~ /\A#{item}\z/i }
|
48
58
|
end
|
49
|
-
|
50
59
|
end # ExtensionWhitelist
|
51
60
|
end # Uploader
|
52
61
|
end # CarrierWave
|
@@ -15,9 +15,12 @@ module CarrierWave
|
|
15
15
|
# [String] the location where this file is accessible via a url
|
16
16
|
#
|
17
17
|
def url(options = {})
|
18
|
-
if file.respond_to?(:url)
|
19
|
-
file.method(:url).arity.zero? ?
|
20
|
-
|
18
|
+
if file.respond_to?(:url)
|
19
|
+
tmp_url = file.method(:url).arity.zero? ? file.url : file.url(options)
|
20
|
+
return tmp_url if tmp_url.present?
|
21
|
+
end
|
22
|
+
|
23
|
+
if file.respond_to?(:path)
|
21
24
|
path = encode_path(file.path.sub(File.expand_path(root), ''))
|
22
25
|
|
23
26
|
if host = asset_host
|
data/lib/carrierwave/version.rb
CHANGED
@@ -33,9 +33,9 @@ class <%= class_name %>Uploader < CarrierWave::Uploader::Base
|
|
33
33
|
# process resize_to_fit: [50, 50]
|
34
34
|
# end
|
35
35
|
|
36
|
-
# Add
|
36
|
+
# Add an allowlist of extensions which are allowed to be uploaded.
|
37
37
|
# For images you might use something like this:
|
38
|
-
# def
|
38
|
+
# def extension_allowlist
|
39
39
|
# %w(jpg jpeg gif png)
|
40
40
|
# end
|
41
41
|
|
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: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jonas Nicklas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-02-
|
11
|
+
date: 2021-02-23 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -252,14 +252,14 @@ dependencies:
|
|
252
252
|
name: rmagick
|
253
253
|
requirement: !ruby/object:Gem::Requirement
|
254
254
|
requirements:
|
255
|
-
- - "
|
255
|
+
- - ">="
|
256
256
|
- !ruby/object:Gem::Version
|
257
257
|
version: '2.16'
|
258
258
|
type: :development
|
259
259
|
prerelease: false
|
260
260
|
version_requirements: !ruby/object:Gem::Requirement
|
261
261
|
requirements:
|
262
|
-
- - "
|
262
|
+
- - ">="
|
263
263
|
- !ruby/object:Gem::Version
|
264
264
|
version: '2.16'
|
265
265
|
- !ruby/object:Gem::Dependency
|
@@ -304,6 +304,20 @@ dependencies:
|
|
304
304
|
- - ">="
|
305
305
|
- !ruby/object:Gem::Version
|
306
306
|
version: '0'
|
307
|
+
- !ruby/object:Gem::Dependency
|
308
|
+
name: pry-byebug
|
309
|
+
requirement: !ruby/object:Gem::Requirement
|
310
|
+
requirements:
|
311
|
+
- - ">="
|
312
|
+
- !ruby/object:Gem::Version
|
313
|
+
version: '0'
|
314
|
+
type: :development
|
315
|
+
prerelease: false
|
316
|
+
version_requirements: !ruby/object:Gem::Requirement
|
317
|
+
requirements:
|
318
|
+
- - ">="
|
319
|
+
- !ruby/object:Gem::Version
|
320
|
+
version: '0'
|
307
321
|
description: Upload files in your Ruby applications, map them to a range of ORMs,
|
308
322
|
store them on different backends.
|
309
323
|
email:
|
@@ -326,6 +340,7 @@ files:
|
|
326
340
|
- lib/carrierwave/processing.rb
|
327
341
|
- lib/carrierwave/processing/mini_magick.rb
|
328
342
|
- lib/carrierwave/processing/rmagick.rb
|
343
|
+
- lib/carrierwave/processing/vips.rb
|
329
344
|
- lib/carrierwave/sanitized_file.rb
|
330
345
|
- lib/carrierwave/storage.rb
|
331
346
|
- lib/carrierwave/storage/abstract.rb
|