file_validators 2.3.0 → 3.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0353fd8c53123663ec484c7172c2df4d572f95dd
4
- data.tar.gz: b1eb811cfea7257288ff5e588621f78aeff39896
3
+ metadata.gz: 6738e978bc3966d2acd83590b2e77b240e005bed
4
+ data.tar.gz: 9fccf7c9b07a08f867582ed3c65aee9606018bd7
5
5
  SHA512:
6
- metadata.gz: 002f91a5243bf85650572ecb80a5283a25aafd6cdda3d4e9fe19bba988f597f3db228e583dc113bb54fb1c7973bc0caa9fc409592f10ff1c75c075438959fcf7
7
- data.tar.gz: a1860e71ee7054624f2cf368802e1af2ee302886ecfca99c0ff43b948c6cf3af3d9264e62b1c877220fb465c18a5df6e11b507d42dbe03c29612e0e2f8513dd7
6
+ metadata.gz: 59033aade86b442a9bd3eb3f8cbc61cf38c66452af71ba58471998e3c015a8b0ead2eba3275fd23a988c61f5cc8719b0dee7af0b099c0c25f7322d2be7775616
7
+ data.tar.gz: 90e211e12d9f5b3d9dc6aebecb4b3b84b853538d61eeb6ae8c014c9618d06fae927d0af7509aa5e616598034a54995e45a6e10fee8c03ad88c401e4a231c01bb
@@ -0,0 +1,32 @@
1
+ Bundler/OrderedGems:
2
+ Enabled: false
3
+
4
+ Style/Documentation:
5
+ Enabled: false
6
+
7
+ Style/MissingRespondToMissing:
8
+ Enabled: false
9
+
10
+ Style/CaseEquality:
11
+ Enabled: false
12
+
13
+ Style/GuardClause:
14
+ Enabled: false
15
+
16
+ Style/RegexpLiteral:
17
+ Enabled: false
18
+
19
+ Style/Next:
20
+ Enabled: false
21
+
22
+ Metrics/LineLength:
23
+ Max: 110
24
+
25
+ Metrics/ModuleLength:
26
+ Enabled: false
27
+
28
+ Metrics/BlockLength:
29
+ Enabled: false
30
+
31
+ Metrics/MethodLength:
32
+ Enabled: false
data/Appraisals CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  appraise 'activemodel-3.2' do
2
4
  gem 'activemodel', '3.2.22.5'
3
5
  gem 'rack', '1.6.5'
@@ -1,3 +1,13 @@
1
+ # 3.0.0.beta1
2
+
3
+ * [#29](https://github.com/musaffa/file_validators/pull/29) Upgrade cocaine to terrapin
4
+ * Rubocop style guide
5
+
6
+ # 2.3.0
7
+
8
+ * [#19](https://github.com/musaffa/file_validators/pull/19) Return false with blank size
9
+ * [#27](https://github.com/musaffa/file_validators/pull/27) Fix file size validator for ActiveStorage
10
+
1
11
  # 2.2.0-beta.1
2
12
 
3
13
  * [#17](https://github.com/musaffa/file_validators/pull/17) Now Supports multiple file uploads
data/Gemfile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  source 'https://rubygems.org'
2
4
 
3
5
  # Declare your gem's dependencies in file_validators.gemspec.
data/README.md CHANGED
@@ -39,7 +39,7 @@ class Profile
39
39
 
40
40
  attr_accessor :avatar
41
41
  validates :avatar, file_size: { less_than_or_equal_to: 100.kilobytes },
42
- file_content_type: { allow: ['image/jpeg', 'image/png'] }
42
+ file_content_type: { allow: ['image/jpeg', 'image/png'] }
43
43
  end
44
44
  ```
45
45
  ActiveRecord example:
@@ -67,23 +67,23 @@ validates :avatar, file_size: { less_than: 2.gigabytes }
67
67
  ```
68
68
  * `less_than_or_equal_to`: Less than or equal to a number in bytes or a proc that returns a number
69
69
  ```ruby
70
- validates :avatar, file_size: { less_than_or_equal_to: 50.bytes }
70
+ validates :avatar, file_size: { less_than_or_equal_to: 50.bytes }
71
71
  ```
72
72
  * `greater_than`: greater than a number in bytes or a proc that returns a number
73
73
  ```ruby
74
- validates :avatar, file_size: { greater_than: 1.byte }
74
+ validates :avatar, file_size: { greater_than: 1.byte }
75
75
  ```
76
76
  * `greater_than_or_equal_to`: Greater than or equal to a number in bytes or a proc that returns a number
77
77
  ```ruby
78
- validates :avatar, file_size: { greater_than_or_equal_to: 50.bytes }
78
+ validates :avatar, file_size: { greater_than_or_equal_to: 50.bytes }
79
79
  ```
80
- * `message`: Error message to display. With all the options above except `:in`, you will get `count` as a replacement.
81
- With `:in` you will get `min` and `max` as replacements.
80
+ * `message`: Error message to display. With all the options above except `:in`, you will get `count` as a replacement.
81
+ With `:in` you will get `min` and `max` as replacements.
82
82
  `count`, `min` and `max` each will have its value and unit together.
83
83
  You can write error messages without using any replacement.
84
84
  ```ruby
85
85
  validates :avatar, file_size: { less_than: 100.kilobytes,
86
- message: 'avatar should be less than %{count}' }
86
+ message: 'avatar should be less than %{count}' }
87
87
  ```
88
88
  ```ruby
89
89
  validates :document, file_size: { in: 1.kilobyte..1.megabyte,
@@ -172,7 +172,7 @@ validates :avatar, file_content_type: { allow: /^image\/.*/, exclude: ['image/pn
172
172
  This gem can use Unix file command to get the content type based on the content of the file rather
173
173
  than the extension. This prevents fake content types inserted in the request header.
174
174
 
175
- It also prevents file media type spoofing. For example, user may upload a .html document as
175
+ It also prevents file media type spoofing. For example, user may upload a .html document as
176
176
  a part of the EXIF header of a valid JPEG file. Content type validator will identify its content type
177
177
  as `image/jpeg` and, without spoof detection, it may pass the validation and be saved as .html document
178
178
  thus exposing your application to a security vulnerability. Media type spoof detector wont let that happen.
@@ -180,7 +180,7 @@ It will not allow a file having `image/jpeg` content type to be saved as `text/p
180
180
  type mismatch, for example `text` of `text/plain` and `image` of `image/jpeg`. So it will not prevent
181
181
  `image/jpeg` from saving as `image/png` as both have the same `image` media type.
182
182
 
183
- **note**: This security feature is disabled by default. To enable it, first add `cocaine` gem in
183
+ **note**: This security feature is disabled by default. To enable it, first add `terrapin` gem in
184
184
  your Gemfile and then add `mode: :strict` option in [content type validations](#file-content-type-validator).
185
185
  `:strict` mode may not work in direct file uploading systems as the file is not passed along with the form.
186
186
 
@@ -254,6 +254,7 @@ uploaders start processing a file immediately after its assignment (even before
254
254
  $ rake
255
255
  $ rake test:unit
256
256
  $ rake test:integration
257
+ $ rubocop
257
258
 
258
259
  # test different active model versions
259
260
  $ bundle exec appraisal install
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'bundler/setup'
3
5
  rescue LoadError
@@ -16,7 +18,7 @@ namespace :test do
16
18
  end
17
19
  end
18
20
 
19
- task :default => ['test:unit', 'test:integration']
21
+ task default: ['test:unit', 'test:integration']
20
22
 
21
23
  # require 'rdoc/task'
22
24
 
@@ -1,4 +1,6 @@
1
- $:.push File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.push File.expand_path('lib', __dir__)
2
4
 
3
5
  require 'file_validators/version'
4
6
 
@@ -12,17 +14,18 @@ Gem::Specification.new do |s|
12
14
  s.homepage = 'https://github.com/musaffa/file_validators'
13
15
  s.license = 'MIT'
14
16
 
15
- s.files = `git ls-files`.split($/)
17
+ s.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
16
18
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
19
  s.test_files = s.files.grep(%r{^spec/})
18
- s.require_paths = ['lib']
20
+ s.require_paths = ['lib']
19
21
 
20
22
  s.add_dependency 'activemodel', '>= 3.2'
21
23
  s.add_dependency 'mime-types', '>= 1.0'
22
24
 
23
- s.add_development_dependency 'cocaine', '~> 0.5.4'
24
- s.add_development_dependency 'rake'
25
- s.add_development_dependency 'rspec', '~> 3.5.0'
26
25
  s.add_development_dependency 'coveralls'
27
26
  s.add_development_dependency 'rack-test'
27
+ s.add_development_dependency 'rake'
28
+ s.add_development_dependency 'rspec', '~> 3.5.0'
29
+ s.add_development_dependency 'rubocop', '~> 0.58.2'
30
+ s.add_development_dependency 'terrapin', '~> 0.6'
28
31
  end
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by Appraisal
2
4
 
3
- source "https://rubygems.org"
5
+ source 'https://rubygems.org'
4
6
 
5
- gem "appraisal"
6
- gem "activemodel", "3.2.22.5"
7
- gem "rack", "1.6.5"
7
+ gem 'appraisal'
8
+ gem 'activemodel', '3.2.22.5'
9
+ gem 'rack', '1.6.5'
8
10
 
9
- gemspec :path => "../"
11
+ gemspec path: '../'
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by Appraisal
2
4
 
3
- source "https://rubygems.org"
5
+ source 'https://rubygems.org'
4
6
 
5
- gem "appraisal"
6
- gem "activemodel", "4.0.13"
7
- gem "rack", "1.6.5"
7
+ gem 'appraisal'
8
+ gem 'activemodel', '4.0.13'
9
+ gem 'rack', '1.6.5'
8
10
 
9
- gemspec :path => "../"
11
+ gemspec path: '../'
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by Appraisal
2
4
 
3
- source "https://rubygems.org"
5
+ source 'https://rubygems.org'
4
6
 
5
- gem "appraisal"
6
- gem "activemodel", "4.1.6"
7
- gem "rack", "1.6.5"
7
+ gem 'appraisal'
8
+ gem 'activemodel', '4.1.6'
9
+ gem 'rack', '1.6.5'
8
10
 
9
- gemspec :path => "../"
11
+ gemspec path: '../'
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by Appraisal
2
4
 
3
- source "https://rubygems.org"
5
+ source 'https://rubygems.org'
4
6
 
5
- gem "appraisal"
6
- gem "activemodel", "4.2.7.1"
7
- gem "rack", "1.6.5"
7
+ gem 'appraisal'
8
+ gem 'activemodel', '4.2.7.1'
9
+ gem 'rack', '1.6.5'
8
10
 
9
- gemspec :path => "../"
11
+ gemspec path: '../'
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # This file was generated by Appraisal
2
4
 
3
- source "https://rubygems.org"
5
+ source 'https://rubygems.org'
4
6
 
5
- gem "appraisal"
6
- gem "activemodel", "5.0.1"
7
+ gem 'appraisal'
8
+ gem 'activemodel', '5.0.1'
7
9
 
8
- gemspec :path => "../"
10
+ gemspec path: '../'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_model'
2
4
  require 'ostruct'
3
5
 
@@ -10,7 +12,7 @@ module FileValidators
10
12
  end
11
13
  end
12
14
 
13
- Dir[File.dirname(__FILE__) + "/file_validators/validators/*.rb"].each { |file| require file }
15
+ Dir[File.dirname(__FILE__) + '/file_validators/validators/*.rb'].each { |file| require file }
14
16
 
15
17
  locale_path = Dir.glob(File.dirname(__FILE__) + '/file_validators/locale/*.yml')
16
18
  I18n.load_path += locale_path unless I18n.load_path.include?(locale_path)
@@ -1,14 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'logger'
2
4
 
3
5
  begin
4
- require 'cocaine'
6
+ require 'terrapin'
5
7
  rescue LoadError
6
- puts "file_validators requires 'cocaine' gem as you are using file content type validations in strict mode"
8
+ puts "file_validators requires 'terrapin' gem as you are using" \
9
+ ' file content type validations in strict mode'
7
10
  end
8
11
 
9
12
  module FileValidators
10
13
  module Utils
11
-
12
14
  class ContentTypeDetector
13
15
  EMPTY_CONTENT_TYPE = 'inode/x-empty'
14
16
  DEFAULT_CONTENT_TYPE = 'application/octet-stream'
@@ -50,18 +52,15 @@ module FileValidators
50
52
  end
51
53
 
52
54
  def type_from_file_command
53
- begin
54
- Cocaine::CommandLine.new('file', '-b --mime-type :file').run(file: @file_path).strip
55
- rescue Cocaine::CommandLineError => e
56
- logger.info(e.message)
57
- DEFAULT_CONTENT_TYPE
58
- end
55
+ Terrapin::CommandLine.new('file', '-b --mime-type :file').run(file: @file_path).strip
56
+ rescue Terrapin::CommandLineError => e
57
+ logger.info(e.message)
58
+ DEFAULT_CONTENT_TYPE
59
59
  end
60
60
 
61
61
  def logger
62
62
  Logger.new(STDOUT)
63
63
  end
64
64
  end
65
-
66
65
  end
67
66
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'mime/types'
2
4
 
3
5
  module FileValidators
4
6
  module Utils
5
-
6
7
  class MediaTypeSpoofDetector
7
8
  def initialize(content_type, file_name)
8
9
  @content_type = content_type
@@ -18,12 +19,12 @@ module FileValidators
18
19
  # for `image` of `image/png` match with `image` of `image/jpeg`.
19
20
 
20
21
  def spoofed?
21
- has_extension? and media_type_mismatch?
22
+ extension? && media_type_mismatch?
22
23
  end
23
24
 
24
25
  private
25
26
 
26
- def has_extension?
27
+ def extension?
27
28
  # the following code replaced File.extname(@file_name).present? because it cannot
28
29
  # return the extension of a extension-only file names, e.g. '.html', '.jpg' etc
29
30
  @file_name.split('.').length > 1
@@ -41,6 +42,5 @@ module FileValidators
41
42
  @content_type.split('/').first
42
43
  end
43
44
  end
44
-
45
45
  end
46
46
  end
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  module Validations
3
-
4
5
  class FileContentTypeValidator < ActiveModel::EachValidator
5
- CHECKS = [:allow, :exclude].freeze
6
+ CHECKS = %i[allow exclude].freeze
6
7
 
7
8
  def self.helper_method_name
8
9
  :validates_file_content_type
@@ -10,16 +11,16 @@ module ActiveModel
10
11
 
11
12
  def validate_each(record, attribute, value)
12
13
  values = parse_values(value)
13
- unless values.empty?
14
- mode = option_value(record, :mode)
15
- allowed_types = option_content_types(record, :allow)
16
- forbidden_types = option_content_types(record, :exclude)
17
-
18
- values.each do |value|
19
- content_type = get_content_type(value, mode)
20
- validate_whitelist(record, attribute, content_type, allowed_types)
21
- validate_blacklist(record, attribute, content_type, forbidden_types)
22
- end
14
+ return if values.empty?
15
+
16
+ mode = option_value(record, :mode)
17
+ allowed_types = option_content_types(record, :allow)
18
+ forbidden_types = option_content_types(record, :exclude)
19
+
20
+ values.each do |val|
21
+ content_type = get_content_type(val, mode)
22
+ validate_whitelist(record, attribute, content_type, allowed_types)
23
+ validate_blacklist(record, attribute, content_type, forbidden_types)
23
24
  end
24
25
  end
25
26
 
@@ -42,7 +43,7 @@ module ActiveModel
42
43
 
43
44
  value = JSON.parse(value) if value.is_a?(String)
44
45
 
45
- Array.wrap(value).reject { |value| value.blank? }
46
+ Array.wrap(value).reject(&:blank?)
46
47
  end
47
48
 
48
49
  def get_file_path(value)
@@ -85,7 +86,7 @@ module ActiveModel
85
86
  end
86
87
 
87
88
  def validate_whitelist(record, attribute, content_type, allowed_types)
88
- if allowed_types.present? and allowed_types.none? { |type| type === content_type }
89
+ if allowed_types.present? && allowed_types.none? { |type| type === content_type }
89
90
  mark_invalid record, attribute, :allowed_file_content_types, allowed_types
90
91
  end
91
92
  end
@@ -130,6 +131,5 @@ module ActiveModel
130
131
  validates_with FileContentTypeValidator, _merge_attributes(attr_names)
131
132
  end
132
133
  end
133
-
134
134
  end
135
135
  end
@@ -1,6 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveModel
2
4
  module Validations
3
-
4
5
  class FileSizeValidator < ActiveModel::EachValidator
5
6
  CHECKS = { in: :===,
6
7
  less_than: :<,
@@ -14,22 +15,18 @@ module ActiveModel
14
15
 
15
16
  def validate_each(record, attribute, value)
16
17
  values = parse_values(value)
17
- unless values.empty?
18
- options.slice(*CHECKS.keys).each do |option, option_value|
19
- option_value = option_value.call(record) if option_value.is_a?(Proc)
20
- if values.any? { |v| not valid_size?(value_byte_size(v), option, option_value) }
21
- record.errors.add(attribute,
22
- "file_size_is_#{option}".to_sym,
23
- filtered_options(values).merge!(detect_error_options(option_value)))
24
- end
25
- end
18
+ return if values.empty?
19
+
20
+ options.slice(*CHECKS.keys).each do |option, option_value|
21
+ check_errors(record, attribute, values, option, option_value)
26
22
  end
27
23
  end
28
24
 
29
25
  def check_validity!
30
26
  unless (CHECKS.keys & options.keys).present?
31
- raise ArgumentError, 'You must at least pass in one of these options - :in, :less_than,
32
- :less_than_or_equal_to, :greater_than and :greater_than_or_equal_to'
27
+ raise ArgumentError, 'You must at least pass in one of these options' \
28
+ ' - :in, :less_than, :less_than_or_equal_to,' \
29
+ ' :greater_than and :greater_than_or_equal_to'
33
30
  end
34
31
 
35
32
  check_options(Numeric, options.slice(*(CHECKS.keys - [:in])))
@@ -46,7 +43,7 @@ module ActiveModel
46
43
 
47
44
  value = OpenStruct.new(value) if value.is_a?(Hash)
48
45
 
49
- Array.wrap(value).reject { |value| value.blank? }
46
+ Array.wrap(value).reject(&:blank?)
50
47
  end
51
48
 
52
49
  def check_options(klass, options)
@@ -57,6 +54,18 @@ module ActiveModel
57
54
  end
58
55
  end
59
56
 
57
+ def check_errors(record, attribute, values, option, option_value)
58
+ option_value = option_value.call(record) if option_value.is_a?(Proc)
59
+ has_invalid_size = values.any? { |v| !valid_size?(value_byte_size(v), option, option_value) }
60
+ if has_invalid_size
61
+ record.errors.add(
62
+ attribute,
63
+ "file_size_is_#{option}".to_sym,
64
+ filtered_options(values).merge!(detect_error_options(option_value))
65
+ )
66
+ end
67
+ end
68
+
60
69
  def value_byte_size(value)
61
70
  if value.respond_to?(:byte_size)
62
71
  value.byte_size
@@ -82,7 +91,7 @@ module ActiveModel
82
91
 
83
92
  def detect_error_options(option_value)
84
93
  if option_value.is_a?(Range)
85
- { min: human_size(option_value.min), max: human_size(option_value.max) }
94
+ { min: human_size(option_value.min), max: human_size(option_value.max) }
86
95
  else
87
96
  { count: human_size(option_value) }
88
97
  end
@@ -92,12 +101,22 @@ module ActiveModel
92
101
  if defined?(ActiveSupport::NumberHelper) # Rails 4.0+
93
102
  ActiveSupport::NumberHelper.number_to_human_size(size)
94
103
  else
95
- storage_units_format = I18n.translate(:'number.human.storage_units.format', :locale => options[:locale], :raise => true)
96
- unit = I18n.translate(:'number.human.storage_units.units.byte', :locale => options[:locale], :count => size.to_i, :raise => true)
104
+ storage_units_format = I18n.translate(
105
+ :'number.human.storage_units.format',
106
+ locale: options[:locale],
107
+ raise: true
108
+ )
109
+
110
+ unit = I18n.translate(
111
+ :'number.human.storage_units.units.byte',
112
+ locale: options[:locale],
113
+ count: size.to_i,
114
+ raise: true
115
+ )
116
+
97
117
  storage_units_format.gsub(/%n/, size.to_i.to_s).gsub(/%u/, unit).html_safe
98
118
  end
99
119
  end
100
-
101
120
  end
102
121
 
103
122
  module HelperMethods
@@ -116,6 +135,5 @@ module ActiveModel
116
135
  validates_with FileSizeValidator, _merge_attributes(attr_names)
117
136
  end
118
137
  end
119
-
120
138
  end
121
139
  end