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 +4 -4
- data/CHANGELOG.md +6 -0
- data/lib/ratonvirus/storage/active_storage.rb +89 -47
- data/lib/ratonvirus/version.rb +1 -1
- data/lib/tasks/ratonvirus.rake +8 -10
- metadata +20 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 7fa55b1025e3d8099f86b8b9e9157cbd4473980441aac2c7acc98b92cf2c3ff6
|
|
4
|
+
data.tar.gz: f219700545adb3593677af4fad1bb9d198892fc94bd128101a817169118254a0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5fcd623e174c648393cc5489c27ce685ee0da9282a7289122a537daacff67950323123e7698da610af6a77b19b63753accb41ca22d06098dc95f4f8d41a109f9
|
|
7
|
+
data.tar.gz: 85100d78a0e6a476cb4c4e3dde833381e7ac2219d4c9127cef4f07458b8ca56ba3a0977275f5d715f64cbab7989bbcb047c733dade35e6d6a95192b48005d0bd
|
data/CHANGELOG.md
CHANGED
|
@@ -3,21 +3,9 @@
|
|
|
3
3
|
module Ratonvirus
|
|
4
4
|
module Storage
|
|
5
5
|
class ActiveStorage < Base
|
|
6
|
-
def changed?(
|
|
7
|
-
|
|
8
|
-
|
|
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, &
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
|
46
|
-
return unless asset.blob
|
|
32
|
+
return unless asset.is_a?(Array)
|
|
47
33
|
|
|
48
|
-
|
|
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.
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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",
|
|
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
|
-
|
|
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
|
data/lib/ratonvirus/version.rb
CHANGED
data/lib/tasks/ratonvirus.rake
CHANGED
|
@@ -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
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
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.
|
|
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:
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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.
|
|
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.
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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: '
|
|
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
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
152
|
+
version: '1.40'
|
|
153
153
|
description: Adds antivirus check capability for Rails applications.
|
|
154
154
|
email:
|
|
155
155
|
- antti.hukkanen@mainiotech.fi
|