s3-antivirus 0.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.
@@ -0,0 +1,142 @@
1
+ require "aws-sdk-sqs"
2
+ require "json"
3
+ require "securerandom"
4
+ require "syslog/logger"
5
+ require "uri"
6
+ require "yaml"
7
+
8
+ module S3Antivirus
9
+ class Scan
10
+ extend Memoist
11
+ include AwsServices
12
+ include Logger
13
+ include Conf
14
+
15
+ def initialize(options={})
16
+ @options = options
17
+ end
18
+
19
+ def run
20
+ logger.info "Polling SQS queue for S3 antivirus findings. Started #{Time.now}..."
21
+ return if @options[:noop]
22
+
23
+ poller = Aws::SQS::QueuePoller.new(conf['queue'])
24
+ poller.poll do |msg|
25
+ begin
26
+ body = JSON.parse(msg.body)
27
+
28
+ logger.debug "body #{body}"
29
+ if body.key?('Records')
30
+ body['Records'].each do |record|
31
+ # Instance variables change on each iteration
32
+ @s3_record = S3Record.new(record)
33
+ @notifier = Notifier.new(@s3_record)
34
+ @tagger = Tagger.new(@s3_record)
35
+
36
+ logger.info("Checking #{@s3_record.human_key}")
37
+ within_size_limit = check_file_size
38
+ next unless within_size_limit # continue to next file because oversized
39
+ filename = "/tmp/#{SecureRandom.uuid}"
40
+ success = download_file(filename)
41
+ next unless success # continue to next file because unable to download
42
+ scan_file(filename)
43
+ end
44
+ end
45
+ rescue Exception => e
46
+ logger.error "message failed: #{e.inspect} #{msg.inspect}"
47
+ raise e
48
+ end
49
+ # Sometimes stdout doesnt get shown but the message is processed file.
50
+ # Know this because the SNS message gets sent.
51
+ # Even this $stdout.flush doesn't seem to be enough.
52
+ $stdout.flush
53
+ end
54
+ end
55
+
56
+ private
57
+ def scan_file(filename)
58
+ logger.info "Scanning #{human_key}..."
59
+ sh("clamdscan #{filename}")
60
+ exitstatus = $?.exitstatus
61
+ if exitstatus == 0 # No virus found.
62
+ clean_file_found
63
+ elsif exitstatus == 1 # Virus(es) found.
64
+ virus_found
65
+ else # An error occured.
66
+ raise "#{human_key} could not be scanned, clamdscan exit status was #{exitstatus}, retry"
67
+ end
68
+ ensure
69
+ system("rm #{filename}")
70
+ end
71
+
72
+ def clean_file_found
73
+ if conf['tag_files']
74
+ logger.info "#{human_key} is clean (tagging)"
75
+ @tagger.tag("clean");
76
+ else
77
+ logger.info "#{human_key} is clean"
78
+ end
79
+ if conf['report_clean']
80
+ @notifier.notify(status: "clean", action: "no");
81
+ end
82
+ end
83
+
84
+ def virus_found
85
+ if conf['delete']
86
+ logger.info "#{human_key} is infected (deleting)"
87
+ s3.delete_object(
88
+ bucket: @s3_record.bucket,
89
+ key: @s3_record.key,
90
+ )
91
+ @notifier.notify(status: "infected", action: "delete");
92
+ elsif conf['tag_files']
93
+ logger.info "#{human_key} is infected (tagging)"
94
+ @tagger.tag("infected");
95
+ @notifier.notify(status: "infected", action: "tag");
96
+ else
97
+ logger.info "#{human_key} is infected"
98
+ @notifier.notify(status: "infected", action: "no");
99
+ end
100
+ end
101
+
102
+ def human_key
103
+ @s3_record.human_key
104
+ end
105
+
106
+ def download_file(filename)
107
+ logger.info "Downloading #{@s3_record.human_key} to #{filename}..."
108
+ params = {
109
+ response_target: filename,
110
+ bucket: @s3_record.bucket,
111
+ key: @s3_record.key,
112
+ }
113
+ params[:version_id] = @s3_record.version if @s3_record.version
114
+
115
+ begin
116
+ s3.get_object(params)
117
+ rescue Aws::S3::Errors::NoSuchKey
118
+ logger.info "#{@s3_record.human_key} no longer exist, skipping."
119
+ return false
120
+ end
121
+ true # successful download
122
+ end
123
+
124
+ def check_file_size
125
+ within_size_limit = true
126
+ if @s3_record.oversized?
127
+ logger.info "#{@s3_record.human_key} is too large. Bigger than half of the EBS volume, skipping."
128
+ if conf['tag_files']
129
+ @tagger.tag("oversized")
130
+ end
131
+ @notifier.notify(status: "oversized", action: "no")
132
+ within_size_limit = false
133
+ end
134
+ within_size_limit
135
+ end
136
+
137
+ def sh(command)
138
+ logger.info("=> #{command}")
139
+ system(command)
140
+ end
141
+ end
142
+ end
@@ -0,0 +1,28 @@
1
+ module S3Antivirus
2
+ class Tagger
3
+ include AwsServices
4
+ include Conf
5
+
6
+ def initialize(s3_record)
7
+ @s3_record = s3_record
8
+ @bucket, @key, @version = s3_record.bucket, s3_record.key, s3_record.version
9
+ @tag_key = conf['tag_key']
10
+ end
11
+
12
+ # Different tag values:
13
+ #
14
+ # clean
15
+ # inflected
16
+ # oversized
17
+ #
18
+ def tag(value)
19
+ params = {
20
+ bucket: @bucket,
21
+ key: @key,
22
+ tagging: {tag_set: [{key: @tag_key, value: value}]}
23
+ }
24
+ params[:version_id] = @version if @version
25
+ s3.put_object_tagging(params)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,32 @@
1
+ require "syslog/logger"
2
+
3
+ module S3Antivirus
4
+ class Tee
5
+ extend Memoist
6
+
7
+ def initialize(path, options={})
8
+ @path, @options = path, options
9
+ end
10
+
11
+ def method_missing(name, message, &block)
12
+ if logger.respond_to?(name)
13
+ # Interesting note about level mapping: http://bit.ly/2PP5Y6Z
14
+ # Messages from Ruby applications are not considered as critical as messages
15
+ # from other system daemons using syslog(3), so most messages are reduced by one level.
16
+ #
17
+ # Will mimic behavior for puts stdout.
18
+ # Interestingly, the default logger.level is ::Logger::DEBUG which is 0.
19
+ # So can't check against that, so will check against the method name.
20
+ puts message unless name == :debug
21
+ logger.send(name, message, &block)
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def logger
28
+ Syslog::Logger.new(@path)
29
+ end
30
+ memoize :logger
31
+ end
32
+ end
@@ -0,0 +1,3 @@
1
+ module S3Antivirus
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,13 @@
1
+ $stdout.sync = true unless ENV["S3_ANTIVIRUS_STDOUT_SYNC"] == "0"
2
+
3
+ $:.unshift(File.expand_path("../", __FILE__))
4
+ require "memoist"
5
+ require "rainbow/ext/string"
6
+ require "s3_antivirus/version"
7
+
8
+ require "s3_antivirus/autoloader"
9
+ S3Antivirus::Autoloader.setup
10
+
11
+ module S3Antivirus
12
+ class Error < StandardError; end
13
+ end
@@ -0,0 +1,49 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "s3_antivirus/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "s3-antivirus"
8
+ spec.version = S3Antivirus::VERSION
9
+ spec.authors = ["Tung Nguyen"]
10
+ spec.email = ["tongueroo@gmail.com"]
11
+ spec.summary = "Detects if files uploaded to s3 contain a virus with ClamAV and auto-deletes or tags them"
12
+ spec.homepage = "https://github.com/tongueroo/s3-antivirus"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activesupport"
22
+ spec.add_dependency "aws-sdk-s3"
23
+ spec.add_dependency "aws-sdk-sns"
24
+ spec.add_dependency "aws-sdk-sqs"
25
+ spec.add_dependency "memoist"
26
+ spec.add_dependency "rainbow"
27
+ spec.add_dependency "recursive-open-struct"
28
+ spec.add_dependency "thor"
29
+ spec.add_dependency "zeitwerk"
30
+
31
+ spec.add_development_dependency "bundler"
32
+ spec.add_development_dependency "byebug"
33
+ spec.add_development_dependency "cli_markdown"
34
+ spec.add_development_dependency "rake"
35
+ spec.add_development_dependency "rspec"
36
+
37
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
38
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
39
+ if spec.respond_to?(:metadata)
40
+ # spec.metadata["allowed_push_host"] = ""
41
+
42
+ spec.metadata["homepage_uri"] = spec.homepage
43
+
44
+ # spec.metadata["source_code_uri"] = ""
45
+ # spec.metadata["changelog_uri"] = ""
46
+ else
47
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
48
+ end
49
+ end
@@ -0,0 +1,8 @@
1
+ delete: true
2
+ report_clean: true
3
+ tag_files: true
4
+ tag_key: clamav-status
5
+ region: us-west-2
6
+ queue: https://sqs.us-west-2.amazonaws.com/112233445566/s3-ScanQueue-1BRQO2VOPIUM8
7
+ topic: arn:aws:sns:us-west-2:112233445566:s3-FindingsTopic-J1E10FB4G914
8
+ volume_size: 8
@@ -0,0 +1,38 @@
1
+ {
2
+ "Records": [
3
+ {
4
+ "eventVersion": "2.1",
5
+ "eventSource": "aws:s3",
6
+ "awsRegion": "us-west-2",
7
+ "eventTime": "2019-12-14T22:56:24.066Z",
8
+ "eventName": "ObjectCreated:Put",
9
+ "userIdentity": {
10
+ "principalId": "AWS:AIDAJTCD6O457QEXAMPLE"
11
+ },
12
+ "requestParameters": {
13
+ "sourceIPAddress": "52.33.148.151"
14
+ },
15
+ "responseElements": {
16
+ "x-amz-request-id": "C6044CD6DEXAMPLE",
17
+ "x-amz-id-2": "V1wa7UVBCDXAFHPwoS6+I02H4idFtO4A5omkL2AeCfTsbz8/JWMVYgGzdE5+uhe1kWYYAEXAMPLE"
18
+ },
19
+ "s3": {
20
+ "s3SchemaVersion": "1.0",
21
+ "configurationId": "s3-antivirus",
22
+ "bucket": {
23
+ "name": "fake-bucket",
24
+ "ownerIdentity": {
25
+ "principalId": "AYC6O1EXAMPLE"
26
+ },
27
+ "arn": "arn:aws:s3:::fake-bucket"
28
+ },
29
+ "object": {
30
+ "key": "a.txt",
31
+ "size": 2,
32
+ "eTag": "60b725f10c9c85c70d97880dfEXAMPLE",
33
+ "sequencer": "005DF568980EXAMPLE"
34
+ }
35
+ }
36
+ }
37
+ ]
38
+ }
@@ -0,0 +1,8 @@
1
+ describe S3Antivirus::CLI do
2
+ describe "s3-antivirus" do
3
+ it "scan" do
4
+ out = execute("exe/s3-antivirus scan --noop")
5
+ expect(out).to include("Polling SQS queue")
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,36 @@
1
+ require "aws-sdk-sqs"
2
+ require "recursive-open-struct"
3
+ require "ostruct"
4
+ require "json"
5
+ class Aws::SQS::QueuePoller
6
+ @@msg = nil
7
+ def self.msg=(val)
8
+ @@msg = val
9
+ end
10
+
11
+ def poll
12
+ yield(@@msg)
13
+ end
14
+ end
15
+
16
+ describe S3Antivirus::Scan do
17
+ before(:each) do
18
+ Aws::SQS::QueuePoller.msg = RecursiveOpenStruct.new(body: body)
19
+ end
20
+
21
+ # Pretty causal test. Mocked out most of the methods.
22
+ let(:null) { double(:null).as_null_object }
23
+ let(:scan) do
24
+ scan = S3Antivirus::Scan.new
25
+ allow(scan).to receive(:download_file).and_return(true)
26
+ allow(scan).to receive(:scan_file)
27
+ scan
28
+ end
29
+ let(:body) { IO.read("spec/fixtures/sqs-event.json") }
30
+
31
+ describe "Scan" do
32
+ it "run" do
33
+ scan.run
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ ENV["S3_ANTIVIRUS_TEST"] = "1"
2
+ # Ensures aws api never called. Fixture home folder does not contain ~/.aws/credentials
3
+ ENV['HOME'] = File.join(Dir.pwd,'spec/fixtures/home')
4
+ ENV['AWS_REGION'] = "us-west-2"
5
+
6
+ # CodeClimate test coverage: https://docs.codeclimate.com/docs/configuring-test-coverage
7
+ # require 'simplecov'
8
+ # SimpleCov.start
9
+
10
+ require "pp"
11
+ require "byebug"
12
+ root = File.expand_path("../", File.dirname(__FILE__))
13
+ require "#{root}/lib/s3-antivirus"
14
+
15
+ module Helper
16
+ def execute(cmd)
17
+ puts "Running: #{cmd}" if show_command?
18
+ out = `#{cmd}`
19
+ puts out if show_command?
20
+ out
21
+ end
22
+
23
+ # Added SHOW_COMMAND because DEBUG is also used by other libraries like
24
+ # bundler and it shows its internal debugging logging also.
25
+ def show_command?
26
+ ENV['DEBUG'] || ENV['SHOW_COMMAND']
27
+ end
28
+ end
29
+
30
+ RSpec.configure do |c|
31
+ c.include Helper
32
+ end
metadata ADDED
@@ -0,0 +1,284 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: s3-antivirus
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tung Nguyen
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-12-15 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: aws-sdk-s3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: aws-sdk-sns
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: aws-sdk-sqs
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: memoist
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rainbow
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: recursive-open-struct
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: thor
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: zeitwerk
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: bundler
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: byebug
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: cli_markdown
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rake
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
195
+ - !ruby/object:Gem::Dependency
196
+ name: rspec
197
+ requirement: !ruby/object:Gem::Requirement
198
+ requirements:
199
+ - - ">="
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ type: :development
203
+ prerelease: false
204
+ version_requirements: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ description:
210
+ email:
211
+ - tongueroo@gmail.com
212
+ executables:
213
+ - s3-antivirus
214
+ extensions: []
215
+ extra_rdoc_files: []
216
+ files:
217
+ - ".gitignore"
218
+ - ".rspec"
219
+ - CHANGELOG.md
220
+ - Gemfile
221
+ - Guardfile
222
+ - LICENSE.txt
223
+ - README.md
224
+ - Rakefile
225
+ - exe/s3-antivirus
226
+ - lib/s3-antivirus.rb
227
+ - lib/s3_antivirus.rb
228
+ - lib/s3_antivirus/autoloader.rb
229
+ - lib/s3_antivirus/aws_services.rb
230
+ - lib/s3_antivirus/cli.rb
231
+ - lib/s3_antivirus/command.rb
232
+ - lib/s3_antivirus/completer.rb
233
+ - lib/s3_antivirus/completer/script.rb
234
+ - lib/s3_antivirus/completer/script.sh
235
+ - lib/s3_antivirus/conf.rb
236
+ - lib/s3_antivirus/config.rb
237
+ - lib/s3_antivirus/help.rb
238
+ - lib/s3_antivirus/help/completion.md
239
+ - lib/s3_antivirus/help/completion_script.md
240
+ - lib/s3_antivirus/help/scan.md
241
+ - lib/s3_antivirus/logger.rb
242
+ - lib/s3_antivirus/notifier.rb
243
+ - lib/s3_antivirus/s3_record.rb
244
+ - lib/s3_antivirus/scan.rb
245
+ - lib/s3_antivirus/tagger.rb
246
+ - lib/s3_antivirus/tee.rb
247
+ - lib/s3_antivirus/version.rb
248
+ - s3-antivirus.gemspec
249
+ - spec/fixtures/home/.s3-antivirus.conf
250
+ - spec/fixtures/sqs-event.json
251
+ - spec/lib/cli_spec.rb
252
+ - spec/lib/scan_spec.rb
253
+ - spec/spec_helper.rb
254
+ homepage: https://github.com/tongueroo/s3-antivirus
255
+ licenses:
256
+ - MIT
257
+ metadata:
258
+ homepage_uri: https://github.com/tongueroo/s3-antivirus
259
+ post_install_message:
260
+ rdoc_options: []
261
+ require_paths:
262
+ - lib
263
+ required_ruby_version: !ruby/object:Gem::Requirement
264
+ requirements:
265
+ - - ">="
266
+ - !ruby/object:Gem::Version
267
+ version: '0'
268
+ required_rubygems_version: !ruby/object:Gem::Requirement
269
+ requirements:
270
+ - - ">="
271
+ - !ruby/object:Gem::Version
272
+ version: '0'
273
+ requirements: []
274
+ rubygems_version: 3.0.6
275
+ signing_key:
276
+ specification_version: 4
277
+ summary: Detects if files uploaded to s3 contain a virus with ClamAV and auto-deletes
278
+ or tags them
279
+ test_files:
280
+ - spec/fixtures/home/.s3-antivirus.conf
281
+ - spec/fixtures/sqs-event.json
282
+ - spec/lib/cli_spec.rb
283
+ - spec/lib/scan_spec.rb
284
+ - spec/spec_helper.rb