file_validators 2.0.2 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ff6c87fab7ee11b1ce0fb5770332f0f1b22c5b96
4
- data.tar.gz: c5b54b8b7b01b685b0f9ee3097c3f8cecbe8ddc0
3
+ metadata.gz: 451e981eb130a49dc6c51834bd7ed4dd30652c9a
4
+ data.tar.gz: e6b28941ccf9ced999b0e201ad17738efb8bc767
5
5
  SHA512:
6
- metadata.gz: 87bf7a8b288777610e8365b486ba806715ee155cc2c5b808aadea4b4f95e1bb18398d394b10af215ffde63fe0babbbc9ea70d265005a727cb31d7b01966ad69a
7
- data.tar.gz: d05f373a6c43907891cbdc0358e3f0389027b2b10b41d788a83db65cb6edca23b3bdf3910bd82c8ebb45b350c368b94884033a0f98f5d65093bf00ff7b0d034d
6
+ metadata.gz: 8c40fa1dae6faf187cc4d3ba77db40691b9bfe58fe9fa99bc88b9fc36477b49ef06bd360ffa8a8af4e2d6f4d56421259b9d9623ab3732a06883b8e64c3f57eb4
7
+ data.tar.gz: 4c6aed01f6c15c42c3f917dcdbbbb4c28907ed662a16f823e33d7416005122e159f2504d4991f5fd4b7b20405b332566a1d52137164f224ce5c1504df1c45407
data/.gitignore CHANGED
@@ -10,3 +10,4 @@ Gemfile.lock
10
10
  gemfiles/*.lock
11
11
  coverage/
12
12
  /.idea
13
+ .ruby-version
data/.travis.yml CHANGED
@@ -1,7 +1,11 @@
1
1
  language: ruby
2
+
2
3
  rvm:
3
- - 1.9.3
4
- - 2.2.2
4
+ - 2.0
5
+ - 2.2.3
6
+ - ruby-head
7
+ - jruby-9.0.4.0
8
+
5
9
  gemfile:
6
10
  - gemfiles/activemodel_4.2.gemfile
7
11
  - gemfiles/activemodel_4.1.gemfile
@@ -9,3 +13,7 @@ gemfile:
9
13
  - gemfiles/activemodel_3.2.gemfile
10
14
  - gemfiles/activemodel_3.1.gemfile
11
15
  - gemfiles/activemodel_3.0.gemfile
16
+
17
+ matrix:
18
+ allow_failures:
19
+ - rvm: ruby-head
data/CHANGELOG.mod ADDED
@@ -0,0 +1,6 @@
1
+ # 2.1.0
2
+
3
+ * Use autoload for lazy loading of libraries
4
+ * Media type spoof valiation is moved to content type detector
5
+ * spoofed_file_media_type message isn't needed anymore
6
+ * Show logger info and warning
data/README.md CHANGED
@@ -191,8 +191,6 @@ File Size Errors
191
191
  * `file_size_is_greater_than_or_equal_to`: takes `count` as replacement
192
192
 
193
193
  Content Type Errors
194
- * `spoofed_file_media_type`: generated when file media type from its extension doesn't match the media type of its
195
- content. learn more from [security](#Security).
196
194
  * `allowed_file_content_types`: generated when you have specified allowed types but the content type
197
195
  of the file doesn't match. takes `types` as replacement.
198
196
  * `excluded_file_content_types`: generated when you have specified excluded types and the content type
@@ -249,10 +247,14 @@ uploaders start processing a file immediately after its assignment (even before
249
247
 
250
248
  ## Tests
251
249
 
252
- ```ruby
253
- rake
254
- rake test:unit
255
- rake test:integration
250
+ ```Shell
251
+ $ rake
252
+ $ rake test:unit
253
+ $ rake test:integration
254
+
255
+ # test different active model versions
256
+ $ appraisal install
257
+ $ appraisal rake
256
258
  ```
257
259
 
258
260
  ## Problems
@@ -1,6 +1,16 @@
1
1
  require 'active_model'
2
- require 'file_validators/validators/file_size_validator'
3
- require 'file_validators/validators/file_content_type_validator'
2
+ require 'ostruct'
3
+
4
+ module FileValidators
5
+ module Utils
6
+ extend ActiveSupport::Autoload
7
+
8
+ autoload :ContentTypeDetector
9
+ autoload :MediaTypeSpoofDetector
10
+ end
11
+ end
12
+
13
+ Dir[File.dirname(__FILE__) + "/file_validators/validators/*.rb"].each { |file| require file }
4
14
 
5
15
  locale_path = Dir.glob(File.dirname(__FILE__) + '/file_validators/locale/*.yml')
6
16
  I18n.load_path += locale_path unless I18n.load_path.include?(locale_path)
@@ -7,7 +7,5 @@ en:
7
7
  file_size_is_greater_than: ! 'file size must be greater than %{count}'
8
8
  file_size_is_greater_than_or_equal_to: ! 'file size must be greater than or equal to %{count}'
9
9
 
10
- spoofed_file_media_type: file has an extension that does not match its contents
11
10
  allowed_file_content_types: ! 'file should be one of %{types}'
12
11
  excluded_file_content_types: ! 'file cannot be %{types}'
13
-
@@ -1,6 +1,9 @@
1
+ require 'logger'
2
+
1
3
  begin
2
4
  require 'cocaine'
3
5
  rescue LoadError
6
+ puts "file_validators requires 'cocaine' gem as you are using file content type validations in strict mode"
4
7
  end
5
8
 
6
9
  module FileValidators
@@ -10,8 +13,11 @@ module FileValidators
10
13
  EMPTY_CONTENT_TYPE = 'inode/x-empty'
11
14
  DEFAULT_CONTENT_TYPE = 'application/octet-stream'
12
15
 
13
- def initialize(file_path)
16
+ attr_accessor :file_path, :file_name
17
+
18
+ def initialize(file_path, file_name)
14
19
  @file_path = file_path
20
+ @file_name = file_name
15
21
  end
16
22
 
17
23
  # content type detection strategy:
@@ -21,24 +27,36 @@ module FileValidators
21
27
  # 3. invalid file: file command raises error and returns 'application/octet-stream'
22
28
 
23
29
  def detect
24
- empty_file? ? EMPTY_CONTENT_TYPE : content_type_from_file_command
30
+ empty_file? ? EMPTY_CONTENT_TYPE : content_type_from_content
25
31
  end
26
32
 
27
33
  private
28
34
 
29
35
  def empty_file?
30
- File.exists?(@file_path) && File.size(@file_path) == 0
36
+ File.exists?(file_path) && File.size(file_path) == 0
31
37
  end
32
38
 
33
- def content_type_from_file_command
34
- type = begin
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"
39
+ def content_type_from_content
40
+ content_type = type_from_file_command
41
+
42
+ if FileValidators::Utils::MediaTypeSpoofDetector.new(content_type, file_name).spoofed?
43
+ logger.warn('A file with a spoofed media type has been detected by the file validators.')
44
+ else
45
+ content_type
46
+ end
47
+ end
48
+
49
+ def type_from_file_command
50
+ begin
51
+ Cocaine::CommandLine.new('file', '-b --mime-type :file').run(file: @file_path).strip
38
52
  rescue Cocaine::CommandLineError => e
39
- # TODO: log command failure
53
+ logger.info(e.message)
40
54
  DEFAULT_CONTENT_TYPE
41
- end.strip
55
+ end
56
+ end
57
+
58
+ def logger
59
+ Logger.new(STDOUT)
42
60
  end
43
61
  end
44
62
 
@@ -1,6 +1,3 @@
1
- require 'file_validators/utils/content_type_detector'
2
- require 'file_validators/utils/media_type_spoof_detector'
3
-
4
1
  module ActiveModel
5
2
  module Validations
6
3
 
@@ -19,7 +16,6 @@ module ActiveModel
19
16
  allowed_types = option_content_types(record, :allow)
20
17
  forbidden_types = option_content_types(record, :exclude)
21
18
 
22
- validate_media_type(record, attribute, content_type, get_file_name(value)) if mode == :strict
23
19
  validate_whitelist(record, attribute, content_type, allowed_types)
24
20
  validate_blacklist(record, attribute, content_type, forbidden_types)
25
21
  end
@@ -59,7 +55,8 @@ module ActiveModel
59
55
  case mode
60
56
  when :strict
61
57
  file_path = get_file_path(value)
62
- FileValidators::Utils::ContentTypeDetector.new(file_path).detect
58
+ file_name = get_file_name(value)
59
+ FileValidators::Utils::ContentTypeDetector.new(file_path, file_name).detect
63
60
  when :relaxed
64
61
  file_name = get_file_name(value)
65
62
  MIME::Types.type_for(file_name).first
@@ -77,12 +74,6 @@ module ActiveModel
77
74
  options[key].is_a?(Proc) ? options[key].call(record) : options[key]
78
75
  end
79
76
 
80
- def validate_media_type(record, attribute, content_type, file_name)
81
- if FileValidators::Utils::MediaTypeSpoofDetector.new(content_type, file_name).spoofed?
82
- record.errors.add attribute, :spoofed_file_media_type
83
- end
84
- end
85
-
86
77
  def validate_whitelist(record, attribute, content_type, allowed_types)
87
78
  if allowed_types.present? and allowed_types.none? { |type| type === content_type }
88
79
  mark_invalid record, attribute, :allowed_file_content_types, allowed_types
@@ -1,3 +1,3 @@
1
1
  module FileValidators
2
- VERSION = '2.0.2'
2
+ VERSION = '2.1.0'
3
3
  end
@@ -4,7 +4,7 @@ require 'tempfile'
4
4
  describe FileValidators::Utils::ContentTypeDetector do
5
5
  it 'returns the empty content type when the file is empty' do
6
6
  tempfile = Tempfile.new('empty')
7
- expect(FileValidators::Utils::ContentTypeDetector.new(tempfile).detect).to eql('inode/x-empty')
7
+ expect(described_class.new(tempfile.path, tempfile.path).detect).to eql('inode/x-empty')
8
8
  tempfile.close
9
9
  end
10
10
 
@@ -12,16 +12,16 @@ describe FileValidators::Utils::ContentTypeDetector do
12
12
  tempfile = Tempfile.new('something')
13
13
  tempfile.write('This is a file.')
14
14
  tempfile.rewind
15
- expect(FileValidators::Utils::ContentTypeDetector.new(tempfile.path).detect).to eql('text/plain')
15
+ expect(described_class.new(tempfile.path, tempfile.path).detect).to eql('text/plain')
16
16
  tempfile.close
17
17
  end
18
18
 
19
19
  it 'returns a sensible default when the file path is empty' do
20
- expect(FileValidators::Utils::ContentTypeDetector.new('').detect).to eql('application/octet-stream')
20
+ expect(described_class.new('', '').detect).to eql('application/octet-stream')
21
21
  end
22
22
 
23
23
  it 'returns a sensible default if the file path is invalid' do
24
- @filename = '/path/to/nothing'
25
- expect(FileValidators::Utils::ContentTypeDetector.new(@filename).detect).to eql('application/octet-stream')
24
+ file_path = '/path/to/nothing'
25
+ expect(described_class.new(file_path, file_path).detect).to eql('application/octet-stream')
26
26
  end
27
27
  end
@@ -2,30 +2,30 @@ require 'spec_helper'
2
2
 
3
3
  describe FileValidators::Utils::MediaTypeSpoofDetector do
4
4
  it 'rejects a file with an extension .html and identifies as jpeg' do
5
- expect(FileValidators::Utils::MediaTypeSpoofDetector.new('image/jpeg', 'sample.html')).to be_spoofed
5
+ expect(described_class.new('image/jpeg', 'sample.html')).to be_spoofed
6
6
  end
7
7
 
8
8
  it 'does not reject a file with an extension .jpg and identifies as png' do
9
- expect(FileValidators::Utils::MediaTypeSpoofDetector.new('image/png', 'sample.jpg')).not_to be_spoofed
9
+ expect(described_class.new('image/png', 'sample.jpg')).not_to be_spoofed
10
10
  end
11
11
 
12
12
  it 'does not reject a file with an extension .txt and identifies as text' do
13
- expect(FileValidators::Utils::MediaTypeSpoofDetector.new('text/plain', 'sample.txt')).not_to be_spoofed
13
+ expect(described_class.new('text/plain', 'sample.txt')).not_to be_spoofed
14
14
  end
15
15
 
16
16
  it 'does not reject a file that does not have any name' do
17
- expect(FileValidators::Utils::MediaTypeSpoofDetector.new('text/plain', '')).not_to be_spoofed
17
+ expect(described_class.new('text/plain', '')).not_to be_spoofed
18
18
  end
19
19
 
20
20
  it 'does not reject a file that does not have any extension' do
21
- expect(FileValidators::Utils::MediaTypeSpoofDetector.new('text/plain', 'sample')).not_to be_spoofed
21
+ expect(described_class.new('text/plain', 'sample')).not_to be_spoofed
22
22
  end
23
23
 
24
24
  it 'rejects a file that does not have a basename but has an extension with mismatched media type' do
25
- expect(FileValidators::Utils::MediaTypeSpoofDetector.new('image/jpeg', '.html')).to be_spoofed
25
+ expect(described_class.new('image/jpeg', '.html')).to be_spoofed
26
26
  end
27
27
 
28
28
  it 'does not reject a file that does not have a basename but has an extension with valid media type' do
29
- expect(FileValidators::Utils::MediaTypeSpoofDetector.new('image/png', '.jpg')).not_to be_spoofed
29
+ expect(described_class.new('image/png', '.jpg')).not_to be_spoofed
30
30
  end
31
31
  end
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'file_validators/validators/file_content_type_validator'
3
2
 
4
3
  describe ActiveModel::Validations::FileContentTypeValidator do
5
4
  class Dummy
@@ -9,7 +8,7 @@ describe ActiveModel::Validations::FileContentTypeValidator do
9
8
  subject { Dummy }
10
9
 
11
10
  def build_validator(options)
12
- @validator = ActiveModel::Validations::FileContentTypeValidator.new(options.merge(attributes: :avatar))
11
+ @validator = described_class.new(options.merge(attributes: :avatar))
13
12
  end
14
13
 
15
14
  context 'whitelist format' do
@@ -138,7 +137,7 @@ describe ActiveModel::Validations::FileContentTypeValidator do
138
137
  before { Dummy.validates_file_content_type :avatar, allow: 'image/jpg' }
139
138
 
140
139
  it 'adds the validator to the class' do
141
- expect(Dummy.validators_on(:avatar)).to include(ActiveModel::Validations::FileContentTypeValidator)
140
+ expect(Dummy.validators_on(:avatar)).to include(described_class)
142
141
  end
143
142
  end
144
143
 
@@ -147,7 +146,7 @@ describe ActiveModel::Validations::FileContentTypeValidator do
147
146
  expect { build_validator message: 'Some message' }.to raise_error(ArgumentError)
148
147
  end
149
148
 
150
- ActiveModel::Validations::FileContentTypeValidator::CHECKS.each do |argument|
149
+ described_class::CHECKS.each do |argument|
151
150
  it "does not raise error if :#{argument} is string, array, regexp or a proc" do
152
151
  expect { build_validator argument => 'image/jpg' }.not_to raise_error
153
152
  expect { build_validator argument => ['image/jpg'] }.not_to raise_error
@@ -1,5 +1,4 @@
1
1
  require 'spec_helper'
2
- require 'file_validators/validators/file_size_validator'
3
2
 
4
3
  describe ActiveModel::Validations::FileSizeValidator do
5
4
  class Dummy
@@ -21,7 +20,7 @@ describe ActiveModel::Validations::FileSizeValidator do
21
20
  subject { Dummy }
22
21
 
23
22
  def build_validator(options)
24
- @validator = ActiveModel::Validations::FileSizeValidator.new(options.merge(attributes: :avatar))
23
+ @validator = described_class.new(options.merge(attributes: :avatar))
25
24
  end
26
25
 
27
26
  context 'with :in option' do
@@ -174,7 +173,7 @@ describe ActiveModel::Validations::FileSizeValidator do
174
173
  before { Dummy.validates_file_size :avatar, in: (5.kilobytes..10.kilobytes) }
175
174
 
176
175
  it 'adds the validator to the class' do
177
- expect(Dummy.validators_on(:avatar)).to include(ActiveModel::Validations::FileSizeValidator)
176
+ expect(Dummy.validators_on(:avatar)).to include(described_class)
178
177
  end
179
178
  end
180
179
 
@@ -183,7 +182,7 @@ describe ActiveModel::Validations::FileSizeValidator do
183
182
  expect { build_validator message: 'Some message' }.to raise_error(ArgumentError)
184
183
  end
185
184
 
186
- (ActiveModel::Validations::FileSizeValidator::CHECKS.keys - [:in]).each do |argument|
185
+ (described_class::CHECKS.keys - [:in]).each do |argument|
187
186
  it "does not raise argument error if :#{argument} is numeric or a proc" do
188
187
  expect { build_validator argument => 5.kilobytes }.not_to raise_error
189
188
  expect { build_validator argument => lambda { |record| 5.kilobytes } }.not_to raise_error
data/spec/spec_helper.rb CHANGED
@@ -2,7 +2,7 @@ ENV['RAILS_ENV'] ||= 'test'
2
2
 
3
3
  require 'active_support'
4
4
  require 'active_support/core_ext'
5
- require_relative '../lib/file_validators'
5
+ require 'file_validators'
6
6
  require 'rspec'
7
7
  require 'coveralls'
8
8
 
@@ -10,8 +10,11 @@ Coveralls.wear!
10
10
 
11
11
  locale_path = Dir.glob(File.dirname(__FILE__) + '/locale/*.yml')
12
12
  I18n.load_path += locale_path unless I18n.load_path.include?(locale_path)
13
+ I18n.enforce_available_locales = false
13
14
 
14
15
  Dir[File.join(File.dirname(__FILE__), 'support/**/*.rb')].each { |f| require f }
15
16
 
16
17
  RSpec.configure do |config|
18
+ # Suppress stdout in the console
19
+ config.before { allow($stdout).to receive(:write) }
17
20
  end
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: 2.0.2
4
+ version: 2.1.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: 2015-09-08 00:00:00.000000000 Z
11
+ date: 2016-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -119,6 +119,7 @@ files:
119
119
  - ".rspec"
120
120
  - ".travis.yml"
121
121
  - Appraisals
122
+ - CHANGELOG.mod
122
123
  - Gemfile
123
124
  - MIT-LICENSE
124
125
  - README.md
@@ -174,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
174
175
  version: '0'
175
176
  requirements: []
176
177
  rubyforge_project:
177
- rubygems_version: 2.4.5.1
178
+ rubygems_version: 2.5.1
178
179
  signing_key:
179
180
  specification_version: 4
180
181
  summary: ActiveModel file validators