ratonvirus 0.1.1 → 0.2.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
  SHA256:
3
- metadata.gz: 4b47a31e5d403f39f0b7496a292532b2dbaa5c38fe624368b09a2cb28c949623
4
- data.tar.gz: 7a37d2f6d7b20e45c417b87ea22bbadbaf434714c1ce7082c1b4a4d87b646845
3
+ metadata.gz: 7fa55b1025e3d8099f86b8b9e9157cbd4473980441aac2c7acc98b92cf2c3ff6
4
+ data.tar.gz: f219700545adb3593677af4fad1bb9d198892fc94bd128101a817169118254a0
5
5
  SHA512:
6
- metadata.gz: a52088960f46b6e4aad10616e9f2b65ad082490bd7158484178f88cc6aa68fa76397e471fa4e791e38cd51bcca799aabdbe868656c03226a9157bb2d82be4aaa
7
- data.tar.gz: 62b103a30f7711cad63d9c9095eec42f98baf372b7fe2308ee03dc3ae063a74bf458afa8026e36ac10aa184df40302a9d721eafbbd93f6fc92efcc090efd9c4c
6
+ metadata.gz: 5fcd623e174c648393cc5489c27ce685ee0da9282a7289122a537daacff67950323123e7698da610af6a77b19b63753accb41ca22d06098dc95f4f8d41a109f9
7
+ data.tar.gz: 85100d78a0e6a476cb4c4e3dde833381e7ac2219d4c9127cef4f07458b8ca56ba3a0977275f5d715f64cbab7989bbcb047c733dade35e6d6a95192b48005d0bd
@@ -1,3 +1,9 @@
1
+ # v0.1.1
2
+
3
+ Fixed:
4
+
5
+ - Rescue file not found exception for blob.download [#2](https://github.com/mainio/ratonvirus/pull/2)
6
+
1
7
  # v0.1.0
2
8
 
3
9
  Initial public release.
@@ -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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Ratonvirus
4
- VERSION = "0.1.1"
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.1
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: 2019-11-27 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