file_validators 2.1.0 → 3.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.
Files changed (40) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +32 -0
  4. data/.tool-versions +1 -0
  5. data/.travis.yml +43 -7
  6. data/Appraisals +13 -13
  7. data/CHANGELOG.md +31 -0
  8. data/Gemfile +2 -0
  9. data/README.md +26 -18
  10. data/Rakefile +3 -1
  11. data/file_validators.gemspec +13 -7
  12. data/gemfiles/activemodel_3.2.gemfile +2 -1
  13. data/gemfiles/activemodel_4.0.gemfile +2 -1
  14. data/gemfiles/{activemodel_4.2.gemfile → activemodel_5.0.gemfile} +1 -1
  15. data/gemfiles/{activemodel_4.1.gemfile → activemodel_6.0.gemfile} +1 -1
  16. data/gemfiles/{activemodel_3.0.gemfile → activemodel_6.1.gemfile} +1 -1
  17. data/lib/file_validators.rb +6 -7
  18. data/lib/file_validators/error.rb +6 -0
  19. data/lib/file_validators/mime_type_analyzer.rb +106 -0
  20. data/lib/file_validators/validators/file_content_type_validator.rb +37 -33
  21. data/lib/file_validators/validators/file_size_validator.rb +62 -19
  22. data/lib/file_validators/version.rb +3 -1
  23. data/spec/integration/combined_validators_integration_spec.rb +3 -1
  24. data/spec/integration/file_content_type_validation_integration_spec.rb +117 -11
  25. data/spec/integration/file_size_validator_integration_spec.rb +100 -10
  26. data/spec/lib/file_validators/mime_type_analyzer_spec.rb +139 -0
  27. data/spec/lib/file_validators/validators/file_content_type_validator_spec.rb +90 -32
  28. data/spec/lib/file_validators/validators/file_size_validator_spec.rb +84 -30
  29. data/spec/spec_helper.rb +5 -0
  30. data/spec/support/fakeio.rb +17 -0
  31. data/spec/support/helpers.rb +7 -0
  32. data/spec/support/matchers/allow_content_type.rb +2 -0
  33. data/spec/support/matchers/allow_file_size.rb +2 -0
  34. metadata +86 -28
  35. data/CHANGELOG.mod +0 -6
  36. data/gemfiles/activemodel_3.1.gemfile +0 -8
  37. data/lib/file_validators/utils/content_type_detector.rb +0 -64
  38. data/lib/file_validators/utils/media_type_spoof_detector.rb +0 -46
  39. data/spec/lib/file_validators/utils/content_type_detector_spec.rb +0 -27
  40. data/spec/lib/file_validators/utils/media_type_spoof_detector_spec.rb +0 -31
@@ -1,6 +0,0 @@
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
@@ -1,8 +0,0 @@
1
- # This file was generated by Appraisal
2
-
3
- source "https://rubygems.org"
4
-
5
- gem "appraisal"
6
- gem "activemodel", "3.1.12"
7
-
8
- gemspec :path => "../"
@@ -1,64 +0,0 @@
1
- require 'logger'
2
-
3
- begin
4
- require 'cocaine'
5
- rescue LoadError
6
- puts "file_validators requires 'cocaine' gem as you are using file content type validations in strict mode"
7
- end
8
-
9
- module FileValidators
10
- module Utils
11
-
12
- class ContentTypeDetector
13
- EMPTY_CONTENT_TYPE = 'inode/x-empty'
14
- DEFAULT_CONTENT_TYPE = 'application/octet-stream'
15
-
16
- attr_accessor :file_path, :file_name
17
-
18
- def initialize(file_path, file_name)
19
- @file_path = file_path
20
- @file_name = file_name
21
- end
22
-
23
- # content type detection strategy:
24
- #
25
- # 1. empty file: returns 'inode/x-empty'
26
- # 2. nonempty file: if the file is not empty then returns the content type using file command
27
- # 3. invalid file: file command raises error and returns 'application/octet-stream'
28
-
29
- def detect
30
- empty_file? ? EMPTY_CONTENT_TYPE : content_type_from_content
31
- end
32
-
33
- private
34
-
35
- def empty_file?
36
- File.exists?(file_path) && File.size(file_path) == 0
37
- end
38
-
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
52
- rescue Cocaine::CommandLineError => e
53
- logger.info(e.message)
54
- DEFAULT_CONTENT_TYPE
55
- end
56
- end
57
-
58
- def logger
59
- Logger.new(STDOUT)
60
- end
61
- end
62
-
63
- end
64
- end
@@ -1,46 +0,0 @@
1
- require 'mime/types'
2
-
3
- module FileValidators
4
- module Utils
5
-
6
- class MediaTypeSpoofDetector
7
- def initialize(content_type, file_name)
8
- @content_type = content_type
9
- @file_name = file_name
10
- end
11
-
12
- # media type spoof detection strategy:
13
- #
14
- # 1. it will not identify as spoofed if file name doesn't have any extension
15
- # 2. it will identify as spoofed if any of the file extension's media types
16
- # matches the media type of the content type. So it will return true for
17
- # `text` of `text/plain` mismatch with `image` of `image/jpeg`, but return false
18
- # for `image` of `image/png` match with `image` of `image/jpeg`.
19
-
20
- def spoofed?
21
- has_extension? and media_type_mismatch?
22
- end
23
-
24
- private
25
-
26
- def has_extension?
27
- # the following code replaced File.extname(@file_name).present? because it cannot
28
- # return the extension of a extension-only file names, e.g. '.html', '.jpg' etc
29
- @file_name.split('.').length > 1
30
- end
31
-
32
- def media_type_mismatch?
33
- supplied_media_types.none? { |type| type == detected_media_type }
34
- end
35
-
36
- def supplied_media_types
37
- MIME::Types.type_for(@file_name).collect(&:media_type)
38
- end
39
-
40
- def detected_media_type
41
- @content_type.split('/').first
42
- end
43
- end
44
-
45
- end
46
- end
@@ -1,27 +0,0 @@
1
- require 'spec_helper'
2
- require 'tempfile'
3
-
4
- describe FileValidators::Utils::ContentTypeDetector do
5
- it 'returns the empty content type when the file is empty' do
6
- tempfile = Tempfile.new('empty')
7
- expect(described_class.new(tempfile.path, tempfile.path).detect).to eql('inode/x-empty')
8
- tempfile.close
9
- end
10
-
11
- it 'returns a content type based on the content of the file' do
12
- tempfile = Tempfile.new('something')
13
- tempfile.write('This is a file.')
14
- tempfile.rewind
15
- expect(described_class.new(tempfile.path, tempfile.path).detect).to eql('text/plain')
16
- tempfile.close
17
- end
18
-
19
- it 'returns a sensible default when the file path is empty' do
20
- expect(described_class.new('', '').detect).to eql('application/octet-stream')
21
- end
22
-
23
- it 'returns a sensible default if the file path is invalid' do
24
- file_path = '/path/to/nothing'
25
- expect(described_class.new(file_path, file_path).detect).to eql('application/octet-stream')
26
- end
27
- end
@@ -1,31 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe FileValidators::Utils::MediaTypeSpoofDetector do
4
- it 'rejects a file with an extension .html and identifies as jpeg' do
5
- expect(described_class.new('image/jpeg', 'sample.html')).to be_spoofed
6
- end
7
-
8
- it 'does not reject a file with an extension .jpg and identifies as png' do
9
- expect(described_class.new('image/png', 'sample.jpg')).not_to be_spoofed
10
- end
11
-
12
- it 'does not reject a file with an extension .txt and identifies as text' do
13
- expect(described_class.new('text/plain', 'sample.txt')).not_to be_spoofed
14
- end
15
-
16
- it 'does not reject a file that does not have any name' do
17
- expect(described_class.new('text/plain', '')).not_to be_spoofed
18
- end
19
-
20
- it 'does not reject a file that does not have any extension' do
21
- expect(described_class.new('text/plain', 'sample')).not_to be_spoofed
22
- end
23
-
24
- it 'rejects a file that does not have a basename but has an extension with mismatched media type' do
25
- expect(described_class.new('image/jpeg', '.html')).to be_spoofed
26
- end
27
-
28
- it 'does not reject a file that does not have a basename but has an extension with valid media type' do
29
- expect(described_class.new('image/png', '.jpg')).not_to be_spoofed
30
- end
31
- end