file_validators 1.2.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +2 -2
- data/Appraisals +5 -1
- data/README.md +42 -5
- data/file_validators.gemspec +1 -1
- data/gemfiles/activemodel_4.2.gemfile +8 -0
- data/lib/file_validators/utils/content_type_detector.rb +6 -1
- data/lib/file_validators/utils/media_type_spoof_detector.rb +1 -1
- data/lib/file_validators/validators/file_content_type_validator.rb +22 -6
- data/lib/file_validators/validators/file_size_validator.rb +2 -0
- data/lib/file_validators/version.rb +1 -1
- data/spec/fixtures/spoofed.jpg +1 -0
- data/spec/integration/file_content_type_validation_integration_spec.rb +129 -8
- data/spec/integration/file_size_validator_integration_spec.rb +42 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/matchers/allow_content_type.rb +2 -2
- data/spec/support/matchers/allow_file_size.rb +1 -1
- metadata +7 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b2a185e059287eb886b2f0aca6f8654d0860f97d
|
4
|
+
data.tar.gz: 087b21a3812ec49de9dd0df345521792631b2925
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ba536373220e03c3e4da79095baad18015dfec6f152cca7fca98f8ba0b1c9b64488b6f447497153e959d3eb1980f07015b32f2c1717638fda8002bb99232d1c7
|
7
|
+
data.tar.gz: cf6d97528247cae02d8c42824c42938e37e57b2ff73045cd429ecb601c23784ce058afe3ef8969994ef83f7366482132818b724b2c00c0a28390d52650a5fb4a
|
data/.travis.yml
CHANGED
data/Appraisals
CHANGED
data/README.md
CHANGED
@@ -15,7 +15,7 @@ Any module that uses ActiveModel, for example ActiveRecord, can use these file v
|
|
15
15
|
* ActiveModel versions: 3 and 4.
|
16
16
|
* Rails versions: 3 and 4.
|
17
17
|
|
18
|
-
It has been tested to work with Carrierwave, Paperclip, Dragonfly etc file uploading solutions.
|
18
|
+
It has been tested to work with Carrierwave, Paperclip, Dragonfly, Refile etc file uploading solutions.
|
19
19
|
Validations works both before and after uploads.
|
20
20
|
|
21
21
|
## Installation
|
@@ -135,7 +135,17 @@ validates :attachment, file_content_type: { allow: [/^image\/.*/, 'video/mp4'] }
|
|
135
135
|
# proc/lambda example
|
136
136
|
validates :video, file_content_type: { allow: lambda { |record| record.content_types } }
|
137
137
|
```
|
138
|
-
* `exclude`: Forbidden content types. Can be a single content type or an array.
|
138
|
+
* `exclude`: Forbidden content types. Can be a single content type or an array. Each type
|
139
|
+
can be a String or a Regexp. It also accepts `proc`. See `:allow` options examples.
|
140
|
+
* `mode`: `:strict` or `:relaxed`. `:strict` mode can detect content type based on the contents
|
141
|
+
of the files. It also detects media type spoofing (see more in [security](#security)).
|
142
|
+
`:relaxed` mode uses file name to detect the content type using `mime-types` gem.
|
143
|
+
If mode option is not set then the validator uses form supplied content type.
|
144
|
+
```ruby
|
145
|
+
# string
|
146
|
+
validates :avatar, file_content_type: { allow: 'image/jpeg', mode: :strict }
|
147
|
+
validates :avatar, file_content_type: { allow: 'image/jpeg', mode: :relaxed }
|
148
|
+
```
|
139
149
|
* `message`: The message to display when the uploaded file has an invalid content type.
|
140
150
|
You will get `types` as a replacement. You can write error messages without using any replacement.
|
141
151
|
```ruby
|
@@ -157,15 +167,20 @@ validates :avatar, file_content_type: { allow: /^image\/.*/, exclude: ['image/pn
|
|
157
167
|
|
158
168
|
## Security
|
159
169
|
|
160
|
-
This gem
|
170
|
+
This gem can use Unix file command to get the content type based on the content of the file rather
|
161
171
|
than the extension. This prevents fake content types inserted in the request header.
|
162
172
|
|
163
173
|
It also prevents file media type spoofing. For example, user may upload a .html document as
|
164
174
|
a part of the EXIF header of a valid JPEG file. Content type validator will identify its content type
|
165
175
|
as `image/jpeg` and, without spoof detection, it may pass the validation and be saved as .html document
|
166
|
-
thus exposing your application to a security vulnerability. Media type spoof detector wont let that happen.
|
176
|
+
thus exposing your application to a security vulnerability. Media type spoof detector wont let that happen.
|
177
|
+
It will not allow a file having `image/jpeg` content type to be saved as `text/plain`. It checks only media
|
178
|
+
type mismatch, for example `text` of `text/plain` and `image` of `image/jpeg`. So it will not prevent
|
179
|
+
`image/jpeg` from saving as `image/png` as both have the same `image` media type.
|
167
180
|
|
168
|
-
**note**:
|
181
|
+
**note**: This security feature is disabled by default. To enable it, first add `cocaine` gem in
|
182
|
+
your Gemfile and then add `mode: :strict` option in [content type validations](#file-content-type-validator).
|
183
|
+
`:strict` mode may not work in direct file uploading systems as the file is not passed along with the form.
|
169
184
|
|
170
185
|
## i18n Translations
|
171
186
|
|
@@ -211,6 +226,28 @@ en:
|
|
211
226
|
tb: "TB"
|
212
227
|
```
|
213
228
|
|
229
|
+
## Further Instructions
|
230
|
+
|
231
|
+
If you are using `:strict` or `:relaxed` mode, for content types which are not supported
|
232
|
+
by mime-types gem, you need to register those content types. For example, you can register
|
233
|
+
`.docx` in the initializer:
|
234
|
+
```Ruby
|
235
|
+
# config/initializers/mime_types.rb
|
236
|
+
Mime::Type.register "application/vnd.openxmlformats-officedocument.wordprocessingml.document", :docx
|
237
|
+
```
|
238
|
+
|
239
|
+
If you want to see what content type `:strict` mode returns, run this command in the shell:
|
240
|
+
```Shell
|
241
|
+
$ file -b --mime-type your-file.xxx
|
242
|
+
```
|
243
|
+
|
244
|
+
## Issues
|
245
|
+
|
246
|
+
**Carrierwave** - You are adding file validators to a model, then you are recommended to keep extension_white_list &/
|
247
|
+
extension_black_list in the uploaders (in case you don't have, add that method).
|
248
|
+
As of this writing (see [issue](https://github.com/carrierwaveuploader/carrierwave/issues/361)), Carrierwave
|
249
|
+
uploaders start processing a file immediately after its assignment (even before the validators are called).
|
250
|
+
|
214
251
|
## Tests
|
215
252
|
|
216
253
|
```ruby
|
data/file_validators.gemspec
CHANGED
@@ -19,8 +19,8 @@ Gem::Specification.new do |s|
|
|
19
19
|
|
20
20
|
s.add_dependency 'activemodel', '>= 3.0'
|
21
21
|
s.add_dependency 'mime-types', '>= 1.0'
|
22
|
-
s.add_dependency 'cocaine', '~> 0.5.4'
|
23
22
|
|
23
|
+
s.add_development_dependency 'cocaine', '~> 0.5.4'
|
24
24
|
s.add_development_dependency 'rake'
|
25
25
|
s.add_development_dependency 'rspec', '~> 3.1.0'
|
26
26
|
s.add_development_dependency 'coveralls'
|
@@ -1,4 +1,7 @@
|
|
1
|
-
|
1
|
+
begin
|
2
|
+
require 'cocaine'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
2
5
|
|
3
6
|
module FileValidators
|
4
7
|
module Utils
|
@@ -30,6 +33,8 @@ module FileValidators
|
|
30
33
|
def content_type_from_file_command
|
31
34
|
type = begin
|
32
35
|
Cocaine::CommandLine.new('file', '-b --mime-type :file').run(file: @file_path)
|
36
|
+
rescue NameError => e
|
37
|
+
puts "file_validators: Add 'cocaine' gem as you are using file content type validations in strict mode"
|
33
38
|
rescue Cocaine::CommandLineError => e
|
34
39
|
# TODO: log command failure
|
35
40
|
DEFAULT_CONTENT_TYPE
|
@@ -25,7 +25,7 @@ module FileValidators
|
|
25
25
|
|
26
26
|
def has_extension?
|
27
27
|
# the following code replaced File.extname(@file_name).present? because it cannot
|
28
|
-
# return the extension of a file
|
28
|
+
# return the extension of a extension-only file names, e.g. '.html', '.jpg' etc
|
29
29
|
@file_name.split('.').length > 1
|
30
30
|
end
|
31
31
|
|
@@ -13,13 +13,12 @@ module ActiveModel
|
|
13
13
|
|
14
14
|
def validate_each(record, attribute, value)
|
15
15
|
unless value.blank?
|
16
|
-
|
17
|
-
|
18
|
-
content_type = detect_content_type(file_path)
|
16
|
+
mode = option_value(record, :mode)
|
17
|
+
content_type = get_content_type(value, mode)
|
19
18
|
allowed_types = option_content_types(record, :allow)
|
20
19
|
forbidden_types = option_content_types(record, :exclude)
|
21
20
|
|
22
|
-
validate_media_type(record, attribute, content_type,
|
21
|
+
validate_media_type(record, attribute, content_type, get_file_name(value)) if mode == :strict
|
23
22
|
validate_whitelist(record, attribute, content_type, allowed_types)
|
24
23
|
validate_blacklist(record, attribute, content_type, forbidden_types)
|
25
24
|
end
|
@@ -55,8 +54,19 @@ module ActiveModel
|
|
55
54
|
end
|
56
55
|
end
|
57
56
|
|
58
|
-
def
|
59
|
-
|
57
|
+
def get_content_type(value, mode)
|
58
|
+
case mode
|
59
|
+
when :strict
|
60
|
+
file_path = get_file_path(value)
|
61
|
+
FileValidators::Utils::ContentTypeDetector.new(file_path).detect
|
62
|
+
when :relaxed
|
63
|
+
file_name = get_file_name(value)
|
64
|
+
MIME::Types.type_for(file_name).first
|
65
|
+
else
|
66
|
+
value = JSON.parse(value) if value.is_a?(String)
|
67
|
+
value = OpenStruct.new(value) if value.is_a?(Hash)
|
68
|
+
value.content_type
|
69
|
+
end
|
60
70
|
end
|
61
71
|
|
62
72
|
def option_content_types(record, key)
|
@@ -103,6 +113,12 @@ module ActiveModel
|
|
103
113
|
# * +exclude+: Forbidden content types.
|
104
114
|
# * +message+: The message to display when the uploaded file has an invalid
|
105
115
|
# content type.
|
116
|
+
# * +mode+: :strict or :relaxed.
|
117
|
+
# :strict mode validates the content type based on the actual contents
|
118
|
+
# of the files. Thus it can detect media type spoofing.
|
119
|
+
# :relaxed validates the content type based on the file name using
|
120
|
+
# the mime-types gem. It's only for sanity check.
|
121
|
+
# If mode is not set then it uses form supplied content type.
|
106
122
|
# * +if+: A lambda or name of an instance method. Validation will only
|
107
123
|
# be run is this lambda or method returns true.
|
108
124
|
# * +unless+: Same as +if+ but validates if lambda or method returns false.
|
@@ -15,6 +15,8 @@ module ActiveModel
|
|
15
15
|
def validate_each(record, attribute, value)
|
16
16
|
unless value.blank?
|
17
17
|
options.slice(*CHECKS.keys).each do |option, option_value|
|
18
|
+
value = JSON.parse(value) if value.is_a?(String)
|
19
|
+
value = OpenStruct.new(value) if value.is_a?(Hash)
|
18
20
|
option_value = option_value.call(record) if option_value.is_a?(Proc)
|
19
21
|
unless valid_size?(value.size, option, option_value)
|
20
22
|
record.errors.add(attribute,
|
@@ -0,0 +1 @@
|
|
1
|
+
a sample text
|
@@ -12,6 +12,7 @@ describe 'File Content Type integration with ActiveModel' do
|
|
12
12
|
@chubby_bubble_path = File.join(File.dirname(__FILE__), '../fixtures/chubby_bubble.jpg')
|
13
13
|
@chubby_cute_path = File.join(File.dirname(__FILE__), '../fixtures/chubby_cute.png')
|
14
14
|
@sample_text_path = File.join(File.dirname(__FILE__), '../fixtures/sample.txt')
|
15
|
+
@spoofed_file_path = File.join(File.dirname(__FILE__), '../fixtures/spoofed.jpg')
|
15
16
|
end
|
16
17
|
|
17
18
|
context ':allow option' do
|
@@ -40,7 +41,7 @@ describe 'File Content Type integration with ActiveModel' do
|
|
40
41
|
before :all do
|
41
42
|
Person.class_eval do
|
42
43
|
Person.reset_callbacks(:validate)
|
43
|
-
validates :avatar, file_content_type: { allow: /^image
|
44
|
+
validates :avatar, file_content_type: { allow: /^image\/.*/, mode: :strict }
|
44
45
|
end
|
45
46
|
end
|
46
47
|
|
@@ -68,7 +69,7 @@ describe 'File Content Type integration with ActiveModel' do
|
|
68
69
|
before :all do
|
69
70
|
Person.class_eval do
|
70
71
|
Person.reset_callbacks(:validate)
|
71
|
-
validates :avatar, file_content_type: { allow: ['image/jpeg', 'text/plain'] }
|
72
|
+
validates :avatar, file_content_type: { allow: ['image/jpeg', 'text/plain'], mode: :strict }
|
72
73
|
end
|
73
74
|
end
|
74
75
|
|
@@ -96,7 +97,8 @@ describe 'File Content Type integration with ActiveModel' do
|
|
96
97
|
before :all do
|
97
98
|
Person.class_eval do
|
98
99
|
Person.reset_callbacks(:validate)
|
99
|
-
validates :avatar, file_content_type: { allow: lambda { |record| ['image/jpeg', 'text/plain'] }
|
100
|
+
validates :avatar, file_content_type: { allow: lambda { |record| ['image/jpeg', 'text/plain'] },
|
101
|
+
mode: :strict }
|
100
102
|
end
|
101
103
|
end
|
102
104
|
|
@@ -126,7 +128,7 @@ describe 'File Content Type integration with ActiveModel' do
|
|
126
128
|
before :all do
|
127
129
|
Person.class_eval do
|
128
130
|
Person.reset_callbacks(:validate)
|
129
|
-
validates :avatar, file_content_type: { exclude: 'image/jpeg' }
|
131
|
+
validates :avatar, file_content_type: { exclude: 'image/jpeg', mode: :strict }
|
130
132
|
end
|
131
133
|
end
|
132
134
|
|
@@ -147,7 +149,7 @@ describe 'File Content Type integration with ActiveModel' do
|
|
147
149
|
before :all do
|
148
150
|
Person.class_eval do
|
149
151
|
Person.reset_callbacks(:validate)
|
150
|
-
validates :avatar, file_content_type: { exclude: /^image
|
152
|
+
validates :avatar, file_content_type: { exclude: /^image\/.*/, mode: :strict }
|
151
153
|
end
|
152
154
|
end
|
153
155
|
|
@@ -175,7 +177,7 @@ describe 'File Content Type integration with ActiveModel' do
|
|
175
177
|
before :all do
|
176
178
|
Person.class_eval do
|
177
179
|
Person.reset_callbacks(:validate)
|
178
|
-
validates :avatar, file_content_type: { exclude: ['image/jpeg', 'text/plain'] }
|
180
|
+
validates :avatar, file_content_type: { exclude: ['image/jpeg', 'text/plain'], mode: :strict }
|
179
181
|
end
|
180
182
|
end
|
181
183
|
|
@@ -203,7 +205,7 @@ describe 'File Content Type integration with ActiveModel' do
|
|
203
205
|
before :all do
|
204
206
|
Person.class_eval do
|
205
207
|
Person.reset_callbacks(:validate)
|
206
|
-
validates :avatar, file_content_type: { exclude: lambda { |record| /^image\/.*/ } }
|
208
|
+
validates :avatar, file_content_type: { exclude: lambda { |record| /^image\/.*/ }, mode: :strict }
|
207
209
|
end
|
208
210
|
end
|
209
211
|
|
@@ -227,7 +229,7 @@ describe 'File Content Type integration with ActiveModel' do
|
|
227
229
|
before :all do
|
228
230
|
Person.class_eval do
|
229
231
|
Person.reset_callbacks(:validate)
|
230
|
-
validates :avatar, file_content_type: { allow: /^image\/.*/, exclude: 'image/png' }
|
232
|
+
validates :avatar, file_content_type: { allow: /^image\/.*/, exclude: 'image/png', mode: :strict }
|
231
233
|
end
|
232
234
|
end
|
233
235
|
|
@@ -243,4 +245,123 @@ describe 'File Content Type integration with ActiveModel' do
|
|
243
245
|
it { is_expected.not_to be_valid }
|
244
246
|
end
|
245
247
|
end
|
248
|
+
|
249
|
+
context ':mode option' do
|
250
|
+
context 'strict mode' do
|
251
|
+
before :all do
|
252
|
+
Person.class_eval do
|
253
|
+
Person.reset_callbacks(:validate)
|
254
|
+
validates :avatar, file_content_type: { allow: 'image/jpeg', mode: :strict }
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
subject { Person.new }
|
259
|
+
|
260
|
+
context 'with valid file' do
|
261
|
+
it 'validates the file' do
|
262
|
+
subject.avatar = Rack::Test::UploadedFile.new(@cute_path, 'image/jpeg')
|
263
|
+
expect(subject).to be_valid
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context 'with spoofed file' do
|
268
|
+
it 'invalidates the file' do
|
269
|
+
subject.avatar = Rack::Test::UploadedFile.new(@spoofed_file_path, 'image/jpeg')
|
270
|
+
expect(subject).not_to be_valid
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
context 'relaxed mode' do
|
276
|
+
before :all do
|
277
|
+
Person.class_eval do
|
278
|
+
Person.reset_callbacks(:validate)
|
279
|
+
validates :avatar, file_content_type: { allow: 'image/jpeg', mode: :relaxed }
|
280
|
+
end
|
281
|
+
end
|
282
|
+
|
283
|
+
subject { Person.new }
|
284
|
+
|
285
|
+
context 'with valid file' do
|
286
|
+
it 'validates the file' do
|
287
|
+
subject.avatar = Rack::Test::UploadedFile.new(@cute_path, 'image/jpeg')
|
288
|
+
expect(subject).to be_valid
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
context 'with spoofed file' do
|
293
|
+
it 'validates the file' do
|
294
|
+
subject.avatar = Rack::Test::UploadedFile.new(@spoofed_file_path, 'image/jpeg')
|
295
|
+
expect(subject).to be_valid
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
context 'default mode' do
|
301
|
+
before :all do
|
302
|
+
Person.class_eval do
|
303
|
+
Person.reset_callbacks(:validate)
|
304
|
+
validates :avatar, file_content_type: { allow: 'image/jpeg' }
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
subject { Person.new }
|
309
|
+
|
310
|
+
context 'with valid file' do
|
311
|
+
it 'validates the file' do
|
312
|
+
subject.avatar = Rack::Test::UploadedFile.new(@cute_path, 'image/jpeg')
|
313
|
+
expect(subject).to be_valid
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
context 'with spoofed file' do
|
318
|
+
it 'invalidates the file' do
|
319
|
+
subject.avatar = Rack::Test::UploadedFile.new(@spoofed_file_path, 'image/jpeg')
|
320
|
+
expect(subject).to be_valid
|
321
|
+
end
|
322
|
+
end
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
context 'image data as json string' do
|
327
|
+
before :all do
|
328
|
+
Person.class_eval do
|
329
|
+
Person.reset_callbacks(:validate)
|
330
|
+
validates :avatar, file_content_type: { allow: 'image/jpeg' }
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
subject { Person.new }
|
335
|
+
|
336
|
+
context 'for invalid content type' do
|
337
|
+
before { subject.avatar = "{\"filename\":\"img140910_88338.GIF\",\"content_type\":\"image/gif\",\"size\":13150}" }
|
338
|
+
it { is_expected.not_to be_valid }
|
339
|
+
end
|
340
|
+
|
341
|
+
context 'for valid content type' do
|
342
|
+
before { subject.avatar = "{\"filename\":\"img140910_88338.jpg\",\"content_type\":\"image/jpeg\",\"size\":13150}" }
|
343
|
+
it { is_expected.to be_valid }
|
344
|
+
end
|
345
|
+
end
|
346
|
+
|
347
|
+
context 'image data as hash' do
|
348
|
+
before :all do
|
349
|
+
Person.class_eval do
|
350
|
+
Person.reset_callbacks(:validate)
|
351
|
+
validates :avatar, file_content_type: { allow: 'image/jpeg' }
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
subject { Person.new }
|
356
|
+
|
357
|
+
context 'for invalid content type' do
|
358
|
+
before { subject.avatar = { "filename":"img140910_88338.GIF", "content_type":"image/gif", "size":13150 } }
|
359
|
+
it { is_expected.not_to be_valid }
|
360
|
+
end
|
361
|
+
|
362
|
+
context 'for valid content type' do
|
363
|
+
before { subject.avatar = { "filename":"img140910_88338.jpg", "content_type":"image/jpeg", "size":13150 } }
|
364
|
+
it { is_expected.to be_valid }
|
365
|
+
end
|
366
|
+
end
|
246
367
|
end
|
@@ -206,4 +206,46 @@ describe 'File Size Validator integration with ActiveModel' do
|
|
206
206
|
it { is_expected.to be_valid }
|
207
207
|
end
|
208
208
|
end
|
209
|
+
|
210
|
+
context 'image data as json string' do
|
211
|
+
before :all do
|
212
|
+
Person.class_eval do
|
213
|
+
Person.reset_callbacks(:validate)
|
214
|
+
validates :avatar, file_size: { greater_than: 20.kilobytes }
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
subject { Person.new }
|
219
|
+
|
220
|
+
context 'when file size is less than the specified size' do
|
221
|
+
before { subject.avatar = "{\"filename\":\"img140910_88338.GIF\",\"content_type\":\"image/gif\",\"size\":13150}" }
|
222
|
+
it { is_expected.not_to be_valid }
|
223
|
+
end
|
224
|
+
|
225
|
+
context 'when file size within the specified size' do
|
226
|
+
before { subject.avatar = "{\"filename\":\"img140910_88338.GIF\",\"content_type\":\"image/gif\",\"size\":33150}" }
|
227
|
+
it { is_expected.to be_valid }
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
context 'image data as hash' do
|
232
|
+
before :all do
|
233
|
+
Person.class_eval do
|
234
|
+
Person.reset_callbacks(:validate)
|
235
|
+
validates :avatar, file_size: { greater_than: 20.kilobytes }
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
subject { Person.new }
|
240
|
+
|
241
|
+
context 'when file size is less than the specified size' do
|
242
|
+
before { subject.avatar = { "filename":"img140910_88338.GIF", "content_type":"image/gif", "size":13150 } }
|
243
|
+
it { is_expected.not_to be_valid }
|
244
|
+
end
|
245
|
+
|
246
|
+
context 'when file size within the specified size' do
|
247
|
+
before { subject.avatar = { "filename":"img140910_88338.GIF", "content_type":"image/gif", "size":33150 } }
|
248
|
+
it { is_expected.to be_valid }
|
249
|
+
end
|
250
|
+
end
|
209
251
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
RSpec::Matchers.define :allow_file_content_type do |content_type, validator, message|
|
2
2
|
match do |model|
|
3
3
|
value = double('file', path: content_type, original_filename: content_type)
|
4
|
-
model.
|
5
|
-
validator.
|
4
|
+
allow_any_instance_of(model).to receive(:read_attribute_for_validation).and_return(value)
|
5
|
+
allow(validator).to receive(:get_content_type).and_return(content_type)
|
6
6
|
dummy = model.new
|
7
7
|
validator.validate(dummy)
|
8
8
|
if message.present?
|
@@ -1,7 +1,7 @@
|
|
1
1
|
RSpec::Matchers.define :allow_file_size do |size, validator, message|
|
2
2
|
match do |model|
|
3
3
|
value = double('file', size: size)
|
4
|
-
model.
|
4
|
+
allow_any_instance_of(model).to receive(:read_attribute_for_validation).and_return(value)
|
5
5
|
dummy = model.new
|
6
6
|
validator.validate(dummy)
|
7
7
|
if message.present?
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: file_validators
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ahmad Musaffa
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2015-07-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activemodel
|
@@ -45,7 +45,7 @@ dependencies:
|
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
47
|
version: 0.5.4
|
48
|
-
type: :
|
48
|
+
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
@@ -130,6 +130,7 @@ files:
|
|
130
130
|
- gemfiles/activemodel_3.2.gemfile
|
131
131
|
- gemfiles/activemodel_4.0.gemfile
|
132
132
|
- gemfiles/activemodel_4.1.gemfile
|
133
|
+
- gemfiles/activemodel_4.2.gemfile
|
133
134
|
- lib/file_validators.rb
|
134
135
|
- lib/file_validators/locale/en.yml
|
135
136
|
- lib/file_validators/utils/content_type_detector.rb
|
@@ -141,6 +142,7 @@ files:
|
|
141
142
|
- spec/fixtures/chubby_cute.png
|
142
143
|
- spec/fixtures/cute.jpg
|
143
144
|
- spec/fixtures/sample.txt
|
145
|
+
- spec/fixtures/spoofed.jpg
|
144
146
|
- spec/integration/combined_validators_integration_spec.rb
|
145
147
|
- spec/integration/file_content_type_validation_integration_spec.rb
|
146
148
|
- spec/integration/file_size_validator_integration_spec.rb
|
@@ -172,7 +174,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
172
174
|
version: '0'
|
173
175
|
requirements: []
|
174
176
|
rubyforge_project:
|
175
|
-
rubygems_version: 2.
|
177
|
+
rubygems_version: 2.4.5
|
176
178
|
signing_key:
|
177
179
|
specification_version: 4
|
178
180
|
summary: ActiveModel file validators
|
@@ -181,6 +183,7 @@ test_files:
|
|
181
183
|
- spec/fixtures/chubby_cute.png
|
182
184
|
- spec/fixtures/cute.jpg
|
183
185
|
- spec/fixtures/sample.txt
|
186
|
+
- spec/fixtures/spoofed.jpg
|
184
187
|
- spec/integration/combined_validators_integration_spec.rb
|
185
188
|
- spec/integration/file_content_type_validation_integration_spec.rb
|
186
189
|
- spec/integration/file_size_validator_integration_spec.rb
|