ratonvirus 0.1.2 → 0.2.0

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
  SHA256:
3
- metadata.gz: f4ccf906781401ec2e9126acf96165d278b0dce5ecf2b10cfa71373490433d98
4
- data.tar.gz: dc56500ae5839a3987c47e96d05ceb9e4627c412edd953d0dbf1fc2c02069c52
3
+ metadata.gz: 7fa55b1025e3d8099f86b8b9e9157cbd4473980441aac2c7acc98b92cf2c3ff6
4
+ data.tar.gz: f219700545adb3593677af4fad1bb9d198892fc94bd128101a817169118254a0
5
5
  SHA512:
6
- metadata.gz: 830a364f9355592158d7580c8755b576ea6a591951e48832ec6d83a308d5d5b0bdcaec3822d7dd47a5120eebc264c2037762202e0f53c42c3d10781d8c67f081
7
- data.tar.gz: 68562672a90dafe0d4705378cc94549d2cf64858987a14901b73607da595b747e72f0d45c52a06f9b1595aba6ab3118998ee5e23901ff438f31bdf6aa399852f
6
+ metadata.gz: 5fcd623e174c648393cc5489c27ce685ee0da9282a7289122a537daacff67950323123e7698da610af6a77b19b63753accb41ca22d06098dc95f4f8d41a109f9
7
+ data.tar.gz: 85100d78a0e6a476cb4c4e3dde833381e7ac2219d4c9127cef4f07458b8ca56ba3a0977275f5d715f64cbab7989bbcb047c733dade35e6d6a95192b48005d0bd
data/CHANGELOG.md CHANGED
@@ -1,11 +1,3 @@
1
- # v0.1.2
2
-
3
- Fixed:
4
-
5
- - Backport: Issue related with scanning files with CarrierWave storage engine using remote storage engines such as Fog.
6
- Related to #9
7
-
8
-
9
1
  # v0.1.1
10
2
 
11
3
  Fixed:
data/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  Rails antivirus made easy.
4
4
  Developed by [Mainio Tech](https://www.mainiotech.fi/).
5
5
 
6
- [![Build Status](https://travis-ci.com/mainio/ratonvirus.svg?branch=master)](https://travis-ci.com/mainio/ratonvirus)
6
+ [![Build Status](https://travis-ci.org/mainio/ratonvirus.svg?branch=master)](https://travis-ci.org/mainio/ratonvirus)
7
7
  [![codecov](https://codecov.io/gh/mainio/ratonvirus/branch/master/graph/badge.svg)](https://codecov.io/gh/mainio/ratonvirus)
8
8
 
9
9
  Ratonvirus allows your Rails application to rat on the viruses that your users
data/lib/ratonvirus.rb CHANGED
@@ -12,7 +12,6 @@ require_relative "ratonvirus/scanner/support/callbacks"
12
12
  require_relative "ratonvirus/scanner/base"
13
13
  require_relative "ratonvirus/scanner/eicar"
14
14
  require_relative "ratonvirus/scanner/addon/remove_infected"
15
- require_relative "ratonvirus/storage/support/io_handling"
16
15
  require_relative "ratonvirus/storage/base"
17
16
  require_relative "ratonvirus/storage/filepath"
18
17
  require_relative "ratonvirus/storage/active_storage"
@@ -3,21 +3,9 @@
3
3
  module Ratonvirus
4
4
  module Storage
5
5
  class ActiveStorage < Base
6
- def changed?(_record, _attribute)
7
- # With Active Storage we assume the record is always changed because
8
- # there is currently no way to know if the attribute has actually
9
- # changed.
10
- #
11
- # Calling record.changed? will not also work because it is not marked
12
- # as dirty in case the Active Storage attachment has changed.
13
- #
14
- # NOTE:
15
- # This should be changed in the future as the `attachment_changes` was
16
- # introduced to Rails by this commit:
17
- # https://github.com/rails/rails/commit/e8682c5bf051517b0b265e446aa1a7eccfd47bf7
18
- #
19
- # However, it is still not available in Rails 5.2.x.
20
- true
6
+ def changed?(record, attribute)
7
+ resource = record.public_send attribute
8
+ !resource.record.attachment_changes[resource.name].nil?
21
9
  end
22
10
 
23
11
  def accept?(resource)
@@ -25,65 +13,114 @@ module Ratonvirus
25
13
  resource.is_a?(::ActiveStorage::Attached::Many)
26
14
  end
27
15
 
28
- def process(resource, &_block)
16
+ def process(resource, &block)
29
17
  return unless block_given?
30
18
  return if resource.nil?
31
-
32
19
  return unless resource.attached?
33
20
 
34
- if resource.is_a?(::ActiveStorage::Attached::One)
35
- yield processable(resource.attachment) if resource.attachment
36
- elsif resource.is_a?(::ActiveStorage::Attached::Many)
37
- resource.attachments.each do |attachment|
38
- yield processable(attachment)
39
- end
21
+ change = resource.record.attachment_changes[resource.name]
22
+
23
+ if change.is_a?(::ActiveStorage::Attached::Changes::CreateOne)
24
+ handle_create_one(change, &block)
25
+ elsif change.is_a?(::ActiveStorage::Attached::Changes::CreateMany)
26
+ handle_create_many(change, &block)
40
27
  end
41
28
  end
42
29
 
43
30
  def asset_path(asset, &block)
44
31
  return unless block_given?
45
- return if asset.nil?
46
- return unless asset.blob
32
+ return unless asset.is_a?(Array)
47
33
 
48
- blob_path asset.blob, &block
34
+ ext = asset[0].filename.extension_with_delimiter
35
+ case asset[1]
36
+ when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
37
+ # These files should be already locally stored but their permissions
38
+ # can prevent the virus scanner executable from accessing them.
39
+ # Therefore, a temporary file is created for them as well.
40
+ io_path(asset[1], ext, &block)
41
+ when Hash
42
+ io = asset[1].fetch(:io)
43
+ io_path(io, ext, &block) if io
44
+ when ::ActiveStorage::Blob
45
+ asset[1].open do |tempfile|
46
+ prepare_for_scanner tempfile.path
47
+ yield tempfile.path
48
+ end
49
+ end
49
50
  end
50
51
 
52
+ # This is actually only required for the dyncamic blob uploads but for
53
+ # consistency, it is handled for all the cases accordingly either by
54
+ # closing the tempfile of the upload which also removes the file when
55
+ # called with the bang method. For the IO references, the IO is closed
56
+ # which should trigger the file deletion by latest at the Rack or Ruby
57
+ # level during garbage collection. There is no guarantee that the file
58
+ # for which the IO was opened would be deleted beause the IO itself is
59
+ # not necessarily associated with an actual file.
51
60
  def asset_remove(asset)
52
- asset.purge
61
+ return unless asset.is_a?(Array)
62
+
63
+ case asset[1]
64
+ when ActionDispatch::Http::UploadedFile, Rack::Test::UploadedFile
65
+ # This removes the temp file from the system.
66
+ asset[1].tempfile.close!
67
+ when Hash
68
+ # No guarantee all references for the file are deleted.
69
+ io = asset[1].fetch(:io)
70
+ io.close
71
+ when ::ActiveStorage::Blob
72
+ # This deletes the dynamically uploaded blobs that might not be
73
+ # associated with any record at this point. This ensures the blobs are
74
+ # not left "hanging" in the storage system and the database in case
75
+ # automatic file deletion is applied.
76
+ asset[1].purge
77
+ end
53
78
  end
54
79
 
55
80
  private
56
81
 
57
- # This creates a local copy of the blob for the scanning process. A
58
- # local copy is needed for processing because the actual blob may be
59
- # stored at a remote storage service (such as Amazon S3), meaning it
60
- # cannot be otherwise processed locally.
61
- #
62
- # NOTE:
63
- # Later implementations of Active Storage have the blob.open method that
64
- # provides similar functionality. However, Rails 5.2.x still does not
65
- # include this functionality, so we need to take care of it ourselves.
66
- #
67
- # This was introduced in the following commit:
68
- # https://github.com/rails/rails/commit/ee21b7c2eb64def8f00887a9fafbd77b85f464f1
69
- #
70
- # SEE:
71
- # https://edgeguides.rubyonrails.org/active_storage_overview.html#downloading-files
72
- def blob_path(blob)
82
+ def handle_create_one(change, &block)
83
+ yield_processable_from(change, &block)
84
+ end
85
+
86
+ def handle_create_many(change, &block)
87
+ change.send(:subchanges).each do |subchange|
88
+ yield_processable_from(subchange, &block)
89
+ end
90
+ end
91
+
92
+ def yield_processable_from(change, &_block)
93
+ attachable = change.attachable
94
+ return unless attachable
95
+ return if attachable.is_a?(::ActiveStorage::Blob)
96
+
97
+ # If the attachable is a string, it is a reference to an already
98
+ # existing blob. This can happen e.g. when the file blob is uploaded
99
+ # dynamically before the form is submitted.
100
+ attachable = change.attachment.blob if attachable.is_a?(String)
101
+
102
+ yield processable([change.attachment, attachable])
103
+ end
104
+
105
+ # This creates a local copy of the io contents for the scanning process. A
106
+ # local copy is needed for processing because the io object may be a file
107
+ # stream in the memory which may not have a path associated with it on the
108
+ # filesystem.
109
+ def io_path(io, extension)
73
110
  tempfile = Tempfile.open(
74
- ["Ratonvirus", blob.filename.extension_with_delimiter],
111
+ ["Ratonvirus", extension],
75
112
  tempdir
76
113
  )
114
+ # Important for the scanner to be able to access the file.
115
+ prepare_for_scanner tempfile.path
77
116
 
78
117
  begin
79
118
  tempfile.binmode
80
- blob.download { |chunk| tempfile.write(chunk) }
119
+ IO.copy_stream(io, tempfile)
81
120
  tempfile.flush
82
121
  tempfile.rewind
83
122
 
84
123
  yield tempfile.path
85
- rescue StandardError
86
- return
87
124
  ensure
88
125
  tempfile.close!
89
126
  end
@@ -92,6 +129,11 @@ module Ratonvirus
92
129
  def tempdir
93
130
  Dir.tmpdir
94
131
  end
132
+
133
+ def prepare_for_scanner(filepath)
134
+ # Important for the scanner to be able to access the file.
135
+ File.chmod(0o644, filepath)
136
+ end
95
137
  end
96
138
  end
97
139
  end
@@ -3,8 +3,6 @@
3
3
  module Ratonvirus
4
4
  module Storage
5
5
  class Carrierwave < Base
6
- include Ratonvirus::Storage::Support::IoHandling
7
-
8
6
  def changed?(record, attribute)
9
7
  record.public_send :"#{attribute}_changed?"
10
8
  end
@@ -17,22 +15,12 @@ module Ratonvirus
17
15
  end
18
16
  end
19
17
 
20
- def asset_path(asset, &block)
18
+ def asset_path(asset)
21
19
  return unless block_given?
22
20
  return if asset.nil?
23
21
  return if asset.file.nil?
24
22
 
25
- # If the file is a local SanitizedFile, it is faster to run the scan
26
- # directly against that file instead of copying it to a tempfile first
27
- # as below for external file storages.
28
- return yield asset.file.path if asset.file.is_a?(::CarrierWave::SanitizedFile)
29
-
30
- # The file could be externally stored, so we need to read it to memory
31
- # in order to create a temporary file for the scanner to perform the
32
- # scan on.
33
- io = StringIO.new(asset.file.read)
34
- ext = File.extname(asset.file.path)
35
- io_path(io, ext, &block)
23
+ yield asset.file.path
36
24
  end
37
25
 
38
26
  def asset_remove(asset)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ratonvirus
4
- VERSION = "0.1.2"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -3,19 +3,17 @@
3
3
  namespace :ratonvirus do
4
4
  desc "Tests if the antivirus scanner is available and properly configured"
5
5
  task test: :environment do
6
- begin
7
- if Ratonvirus.scanner.available?
8
- puts "Ratonvirus correctly configured."
9
- else
10
- puts "Ratonvirus scanner is not available!"
11
- puts ""
12
- puts "Please refer to Ratonvirus documentation for proper configuration."
13
- end
14
- rescue StandardError
15
- puts "Ratonvirus scanner is not configured."
6
+ if Ratonvirus.scanner.available?
7
+ puts "Ratonvirus correctly configured."
8
+ else
9
+ puts "Ratonvirus scanner is not available!"
16
10
  puts ""
17
11
  puts "Please refer to Ratonvirus documentation for proper configuration."
18
12
  end
13
+ rescue StandardError
14
+ puts "Ratonvirus scanner is not configured."
15
+ puts ""
16
+ puts "Please refer to Ratonvirus documentation for proper configuration."
19
17
  end
20
18
 
21
19
  desc "Scans the given file through the antivirus scanner"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ratonvirus
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Antti Hukkanen
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-05 00:00:00.000000000 Z
11
+ date: 2020-06-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: '6.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '5.0'
26
+ version: '6.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '12.3'
33
+ version: '13.0'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '12.3'
40
+ version: '13.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -58,98 +58,98 @@ dependencies:
58
58
  requirements:
59
59
  - - "~>"
60
60
  - !ruby/object:Gem::Version
61
- version: '3.0'
61
+ version: '4.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
- version: '3.0'
68
+ version: '4.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: simplecov
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
73
  - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: 0.16.0
75
+ version: 0.18.0
76
76
  type: :development
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
80
  - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: 0.16.0
82
+ version: 0.18.0
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: activemodel
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: '5.0'
89
+ version: '6.0'
90
90
  type: :development
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: '5.0'
96
+ version: '6.0'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: activestorage
99
99
  requirement: !ruby/object:Gem::Requirement
100
100
  requirements:
101
101
  - - "~>"
102
102
  - !ruby/object:Gem::Version
103
- version: '5.0'
103
+ version: '6.0'
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
- version: '5.0'
110
+ version: '6.0'
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: carrierwave
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - "~>"
116
116
  - !ruby/object:Gem::Version
117
- version: '1.2'
117
+ version: '2.1'
118
118
  type: :development
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: '1.2'
124
+ version: '2.1'
125
125
  - !ruby/object:Gem::Dependency
126
126
  name: rubocop
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
129
  - - "~>"
130
130
  - !ruby/object:Gem::Version
131
- version: 0.64.0
131
+ version: 0.86.0
132
132
  type: :development
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
- version: 0.64.0
138
+ version: 0.86.0
139
139
  - !ruby/object:Gem::Dependency
140
140
  name: rubocop-rspec
141
141
  requirement: !ruby/object:Gem::Requirement
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '1.32'
145
+ version: '1.40'
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: '1.32'
152
+ version: '1.40'
153
153
  description: Adds antivirus check capability for Rails applications.
154
154
  email:
155
155
  - antti.hukkanen@mainiotech.fi
@@ -177,7 +177,6 @@ files:
177
177
  - lib/ratonvirus/storage/carrierwave.rb
178
178
  - lib/ratonvirus/storage/filepath.rb
179
179
  - lib/ratonvirus/storage/multi.rb
180
- - lib/ratonvirus/storage/support/io_handling.rb
181
180
  - lib/ratonvirus/support/backend.rb
182
181
  - lib/ratonvirus/version.rb
183
182
  - lib/tasks/ratonvirus.rake
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "tempfile"
4
-
5
- module Ratonvirus
6
- module Storage
7
- module Support
8
- module IoHandling
9
- private
10
-
11
- # This creates a local copy of the io contents for the scanning process.
12
- # A local copy is needed for processing because the io object may be a
13
- # file stream in the memory which may not have a path associated with it
14
- # on the filesystem.
15
- def io_path(io, extension)
16
- tempfile = Tempfile.open(
17
- ["Ratonvirus", extension],
18
- tempdir
19
- )
20
- # Important for the scanner to be able to access the file.
21
- prepare_for_scanner tempfile.path
22
-
23
- begin
24
- tempfile.binmode
25
- IO.copy_stream(io, tempfile)
26
- tempfile.flush
27
- tempfile.rewind
28
-
29
- yield tempfile.path
30
- ensure
31
- tempfile.close!
32
- end
33
- end
34
-
35
- def tempdir
36
- Dir.tmpdir
37
- end
38
-
39
- def prepare_for_scanner(filepath)
40
- # Important for the scanner to be able to access the file.
41
- File.chmod(0o644, filepath)
42
- end
43
- end
44
- end
45
- end
46
- end