file_validators 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|