ddr-antivirus 2.1.1 → 2.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 +5 -5
- data/.docker/Dockerfile +29 -0
- data/.docker/clamd.conf +2 -0
- data/.docker/docker-compose.yml +15 -0
- data/.docker/docker-entrypoint.sh +2 -0
- data/.dockerignore +4 -0
- data/.gitlab-ci.yml +30 -0
- data/README.md +2 -15
- data/ddr-antivirus.gemspec +2 -1
- data/lib/ddr/antivirus.rb +1 -14
- data/lib/ddr/antivirus/adapters/clamd_scanner_adapter.rb +12 -2
- data/lib/ddr/antivirus/exceptions.rb +19 -0
- data/lib/ddr/antivirus/scanner_adapter.rb +1 -6
- data/lib/ddr/antivirus/version.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/clamd_scanner_adapter_spec.rb +86 -38
- metadata +28 -8
- data/.travis.yml +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 5004e827b8e72a4bad210f59e100daa8bc8ee87c7f02778c1507eac15ea97b8f
|
4
|
+
data.tar.gz: 302cc664882883e7830664df63f0c21caec065dcf98d116ad8f42c102f659525
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 341b67b0bbe4678f02beb2cf5d10bfe7186c3a46aeaa1fa2b1beac6fa8bdcf33a3c0a5010bd9c90121f0b58ac09040c49191e12b88ed709a9042b3b59866b563
|
7
|
+
data.tar.gz: ce3fc8405d17441a81856fcb80f946e76dffcaae7570edb59c55873f8639da924b8a9d350a53f08cf6c9c72d06049dc458ff2801d2aaf0a99df36efc2f03b40b
|
data/.docker/Dockerfile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
ARG RUBY_VERSION
|
2
|
+
FROM ruby:${RUBY_VERSION}
|
3
|
+
|
4
|
+
ARG APPUSER=appuser
|
5
|
+
ARG APPGROUP=root
|
6
|
+
ARG APPROOT=/usr/src/app
|
7
|
+
|
8
|
+
RUN apt-get -y update \
|
9
|
+
&& apt-get -y install \
|
10
|
+
clamdscan \
|
11
|
+
less \
|
12
|
+
vim \
|
13
|
+
wait-for-it
|
14
|
+
|
15
|
+
WORKDIR /usr/src/app
|
16
|
+
COPY . .
|
17
|
+
RUN gem install bundler -N \
|
18
|
+
&& bundle install
|
19
|
+
|
20
|
+
COPY .docker/clamd.conf /etc/clamav/
|
21
|
+
COPY .docker/docker-entrypoint.sh /usr/bin/
|
22
|
+
RUN chmod +x /usr/bin/docker-entrypoint.sh
|
23
|
+
|
24
|
+
RUN groupadd -r -f $APPGROUP \
|
25
|
+
&& useradd -m -r -g $APPGROUP $APPUSER
|
26
|
+
|
27
|
+
USER $APPUSER
|
28
|
+
ENTRYPOINT ["docker-entrypoint.sh"]
|
29
|
+
CMD ["bundle", "exec", "rake"]
|
data/.docker/clamd.conf
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
version: '3.7'
|
2
|
+
|
3
|
+
services:
|
4
|
+
app:
|
5
|
+
image: ddr-antivirus-ruby-${RUBY_VERSION}
|
6
|
+
build:
|
7
|
+
context: ..
|
8
|
+
dockerfile: .docker/Dockerfile
|
9
|
+
args:
|
10
|
+
- RUBY_VERSION
|
11
|
+
depends_on:
|
12
|
+
- clamav
|
13
|
+
|
14
|
+
clamav:
|
15
|
+
image: gitlab-registry.oit.duke.edu/dul-its/clamav-docker:latest
|
data/.dockerignore
ADDED
data/.gitlab-ci.yml
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
stages:
|
2
|
+
- build
|
3
|
+
|
4
|
+
variables:
|
5
|
+
COMPOSE_FILE: .docker/docker-compose.yml
|
6
|
+
|
7
|
+
.build_app_template: &build_app_def
|
8
|
+
stage: build
|
9
|
+
before_script:
|
10
|
+
- docker --version
|
11
|
+
- docker-compose --version
|
12
|
+
script:
|
13
|
+
- docker-compose up --build --exit-code-from app
|
14
|
+
after_script:
|
15
|
+
- docker-compose down
|
16
|
+
|
17
|
+
build_app_ruby24:
|
18
|
+
<<: *build_app_def
|
19
|
+
variables:
|
20
|
+
RUBY_VERSION: '2.4'
|
21
|
+
|
22
|
+
build_app_ruby25:
|
23
|
+
<<: *build_app_def
|
24
|
+
variables:
|
25
|
+
RUBY_VERSION: '2.5'
|
26
|
+
|
27
|
+
build_app_ruby26:
|
28
|
+
<<: *build_app_def
|
29
|
+
variables:
|
30
|
+
RUBY_VERSION: '2.6'
|
data/README.md
CHANGED
@@ -2,9 +2,6 @@
|
|
2
2
|
|
3
3
|
Pluggable antivirus service for Ruby applications.
|
4
4
|
|
5
|
-
[](http://badge.fury.io/rb/ddr-antivirus)
|
6
|
-
[](https://travis-ci.org/duke-libraries/ddr-antivirus)
|
7
|
-
|
8
5
|
## Installation
|
9
6
|
|
10
7
|
Add this line to your application's Gemfile:
|
@@ -31,22 +28,12 @@ Ddr::Antivirus.scanner do |scanner|
|
|
31
28
|
end
|
32
29
|
```
|
33
30
|
|
34
|
-
### Exceptions
|
35
|
-
|
36
|
-
All exceptions under the `Ddr::Antivirus` namespace.
|
37
|
-
|
38
|
-
`Error` - Parent exception class.
|
39
|
-
|
40
|
-
`VirusFoundError` - A virus was found. The message includes the original output from the scanner.
|
41
|
-
|
42
|
-
`ScannerError` - The scanner encountered an error (e.g., error exit status).
|
43
|
-
|
44
31
|
### Example
|
45
32
|
|
46
33
|
```
|
47
34
|
> require 'ddr/antivirus'
|
48
35
|
=> true
|
49
|
-
|
36
|
+
|
50
37
|
> Ddr::Antivirus.scanner_adapter = :clamd
|
51
38
|
=> :clamd
|
52
39
|
|
@@ -74,7 +61,7 @@ Time: 0.001 sec (0 m 0 s)
|
|
74
61
|
|
75
62
|
### Logging
|
76
63
|
|
77
|
-
In a Rails application, `Ddr::Antivirus` will log messages to the Rails logger by default. The fallback logger writes to STDERR
|
64
|
+
In a Rails application, `Ddr::Antivirus` will log messages to the Rails logger by default. The fallback logger writes to `STDERR`. You may also explicitly set `Ddr::Antivirus.logger` to any object that supports the Ruby logger API:
|
78
65
|
|
79
66
|
```ruby
|
80
67
|
require "logger"
|
data/ddr-antivirus.gemspec
CHANGED
@@ -18,7 +18,8 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
spec.add_development_dependency "bundler"
|
21
|
+
spec.add_development_dependency "bundler"
|
22
22
|
spec.add_development_dependency "rake"
|
23
23
|
spec.add_development_dependency "rspec", "~> 3.0"
|
24
|
+
spec.add_development_dependency "rspec-its"
|
24
25
|
end
|
data/lib/ddr/antivirus.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "logger"
|
2
2
|
|
3
3
|
require_relative "antivirus/version"
|
4
|
+
require_relative "antivirus/exceptions"
|
4
5
|
require_relative "antivirus/scanner"
|
5
6
|
require_relative "antivirus/scan_result"
|
6
7
|
require_relative "antivirus/scanner_adapter"
|
@@ -9,20 +10,6 @@ require_relative "antivirus/adapters/null_scanner_adapter"
|
|
9
10
|
module Ddr
|
10
11
|
module Antivirus
|
11
12
|
|
12
|
-
class Error < ::StandardError; end
|
13
|
-
|
14
|
-
class ResultError < Error
|
15
|
-
attr_reader :result
|
16
|
-
def initialize(result)
|
17
|
-
super(result.to_s)
|
18
|
-
@result = result
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
class VirusFoundError < ResultError; end
|
23
|
-
|
24
|
-
class ScannerError < ResultError; end
|
25
|
-
|
26
13
|
class << self
|
27
14
|
attr_accessor :logger, :scanner_adapter
|
28
15
|
|
@@ -26,6 +26,7 @@ module Ddr::Antivirus
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def clamdscan(path)
|
29
|
+
check_file_size(path) if max_file_size
|
29
30
|
output = make_readable(path) do
|
30
31
|
command "--fdpass", safe_path(path)
|
31
32
|
end
|
@@ -37,17 +38,26 @@ module Ddr::Antivirus
|
|
37
38
|
end
|
38
39
|
|
39
40
|
def config
|
40
|
-
|
41
|
+
# If client and server are on separate hosts
|
42
|
+
# attempt to read config may raise an exception.
|
43
|
+
@config ||= `#{CONFIG}` rescue nil
|
41
44
|
end
|
42
45
|
|
43
46
|
def max_file_size
|
44
47
|
if m = MAX_FILE_SIZE_RE.match(config)
|
45
|
-
m[1]
|
48
|
+
m[1].to_i
|
46
49
|
end
|
47
50
|
end
|
48
51
|
|
49
52
|
private
|
50
53
|
|
54
|
+
def check_file_size(path)
|
55
|
+
if (file_size = File.size(path)) > max_file_size
|
56
|
+
raise MaxFileSizeExceeded, "Unable to scan file \"#{path}\" because size (#{file_size})" \
|
57
|
+
" exceeds clamconf MaxFileSize (#{max_file_size})."
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
51
61
|
def command(*args)
|
52
62
|
cmd = args.dup.unshift(SCANNER).join(" ")
|
53
63
|
`#{cmd}`
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Ddr::Antivirus
|
2
|
+
|
3
|
+
class Error < ::StandardError; end
|
4
|
+
|
5
|
+
class MaxFileSizeExceeded < Error; end
|
6
|
+
|
7
|
+
class ResultError < Error
|
8
|
+
attr_reader :result
|
9
|
+
def initialize(result)
|
10
|
+
super(result.to_s)
|
11
|
+
@result = result
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class VirusFoundError < ResultError; end
|
16
|
+
|
17
|
+
class ScannerError < ResultError; end
|
18
|
+
|
19
|
+
end
|
@@ -9,16 +9,11 @@ module Ddr::Antivirus
|
|
9
9
|
# Scan a file path for viruses.
|
10
10
|
#
|
11
11
|
# @param path [String] file path to scan.
|
12
|
-
# @return [Ddr::Antivirus::
|
12
|
+
# @return [Ddr::Antivirus::ScanResult] the result of the scan.
|
13
13
|
def scan(path)
|
14
14
|
raise NotImplementedError, "Adapters must implement the `scan' method."
|
15
15
|
end
|
16
16
|
|
17
|
-
# Return the adapter configuration options
|
18
|
-
def config
|
19
|
-
Ddr::Antivirus.adapter_config
|
20
|
-
end
|
21
|
-
|
22
17
|
private
|
23
18
|
|
24
19
|
def logger
|
data/spec/spec_helper.rb
CHANGED
@@ -7,56 +7,104 @@ module Ddr::Antivirus
|
|
7
7
|
|
8
8
|
before do
|
9
9
|
allow(subject).to receive(:version) { "version" }
|
10
|
+
allow(subject).to receive(:config) do
|
11
|
+
<<-EOS
|
12
|
+
MaxScanSize = "104857600"
|
13
|
+
MaxFileSize = "26214400"
|
14
|
+
MaxRecursion = "16"
|
15
|
+
MaxFiles = "10000"
|
16
|
+
EOS
|
17
|
+
end
|
10
18
|
end
|
11
19
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
its(:max_file_size) { is_expected.to eq(26214400) }
|
21
|
+
|
22
|
+
describe "#scan" do
|
23
|
+
describe "file size" do
|
24
|
+
let(:path) { File.expand_path(File.join("..", "..", "fixtures", "blue-devil.png"), __FILE__) }
|
25
|
+
describe "when max file size is not set or unknown" do
|
26
|
+
before do
|
27
|
+
allow(subject).to receive(:max_file_size) { nil }
|
28
|
+
end
|
29
|
+
it "scans the file" do
|
30
|
+
expect { subject.scan(path) }.not_to raise_error
|
31
|
+
end
|
32
|
+
end
|
33
|
+
describe "when max file size is greater than the size of the file to be scanned" do
|
34
|
+
before do
|
35
|
+
allow(subject).to receive(:max_file_size) { File.size(path) + 1 }
|
36
|
+
end
|
37
|
+
it "scans the file" do
|
38
|
+
expect { subject.scan(path) }.not_to raise_error
|
39
|
+
end
|
40
|
+
end
|
41
|
+
describe "when max file size is equal to the size of the file to be scanned" do
|
42
|
+
before do
|
43
|
+
allow(subject).to receive(:max_file_size) { File.size(path) }
|
44
|
+
end
|
45
|
+
it "scans the file" do
|
46
|
+
expect { subject.scan(path) }.not_to raise_error
|
47
|
+
end
|
23
48
|
end
|
24
|
-
|
25
|
-
|
49
|
+
describe "when max file size is less than the size of the file to be scanned" do
|
50
|
+
before do
|
51
|
+
allow(subject).to receive(:max_file_size) { File.size(path) - 1 }
|
52
|
+
end
|
53
|
+
it "raises an exception" do
|
54
|
+
expect { subject.scan(path) }.to raise_error(MaxFileSizeExceeded)
|
55
|
+
end
|
26
56
|
end
|
27
57
|
end
|
28
|
-
end
|
29
58
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
let(:exitcode) { 1 }
|
37
|
-
it "should raise a VirusFoundError" do
|
38
|
-
expect { subject.scan(path) }.to raise_error(VirusFoundError)
|
59
|
+
describe "permissions" do
|
60
|
+
before do
|
61
|
+
@file = Tempfile.new("test")
|
62
|
+
@file.write("Scan me!")
|
63
|
+
@file.close
|
64
|
+
FileUtils.chmod(0000, @file.path)
|
39
65
|
end
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
66
|
+
after { @file.unlink }
|
67
|
+
describe "when the file is not readable" do
|
68
|
+
it "scans the file" do
|
69
|
+
expect { subject.scan(@file.path) }.not_to raise_error
|
70
|
+
end
|
71
|
+
it "resets the original permissions" do
|
72
|
+
expect { subject.scan(@file.path) }.not_to change { File.stat(@file.path).mode }
|
73
|
+
end
|
45
74
|
end
|
46
75
|
end
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
76
|
+
|
77
|
+
describe "result" do
|
78
|
+
let(:path) { File.expand_path(File.join("..", "..", "fixtures", "blue-devil.png"), __FILE__) }
|
79
|
+
before do
|
80
|
+
allow(subject).to receive(:clamdscan).with(path) { ["output", exitcode] }
|
51
81
|
end
|
52
|
-
|
53
|
-
|
82
|
+
describe "when a virus is found" do
|
83
|
+
let(:exitcode) { 1 }
|
84
|
+
it "should raise a VirusFoundError" do
|
85
|
+
expect { subject.scan(path) }.to raise_error(VirusFoundError)
|
86
|
+
end
|
54
87
|
end
|
55
|
-
|
56
|
-
|
88
|
+
describe "when there is an error" do
|
89
|
+
let(:exitcode) { 2 }
|
90
|
+
it "raises a ScannerError" do
|
91
|
+
expect { subject.scan(path) }.to raise_error(ScannerError)
|
92
|
+
end
|
57
93
|
end
|
58
|
-
|
59
|
-
|
94
|
+
describe "success" do
|
95
|
+
let(:exitcode) { 0 }
|
96
|
+
it "has output" do
|
97
|
+
expect(subject.scan(path).output).to eq("output")
|
98
|
+
end
|
99
|
+
it "has a scanned_at time" do
|
100
|
+
expect(subject.scan(path).scanned_at).to be_a(Time)
|
101
|
+
end
|
102
|
+
it "has a version" do
|
103
|
+
expect(subject.scan(path).version).to eq("version")
|
104
|
+
end
|
105
|
+
it "has the file_path" do
|
106
|
+
expect(subject.scan(path).file_path).to eq(path)
|
107
|
+
end
|
60
108
|
end
|
61
109
|
end
|
62
110
|
end
|
metadata
CHANGED
@@ -1,29 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ddr-antivirus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Chandek-Stark
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-08-01 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
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: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,6 +52,20 @@ dependencies:
|
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '3.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rspec-its
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
description: Pluggable antivirus scanning service.
|
56
70
|
email:
|
57
71
|
- dchandekstark@gmail.com
|
@@ -59,9 +73,14 @@ executables: []
|
|
59
73
|
extensions: []
|
60
74
|
extra_rdoc_files: []
|
61
75
|
files:
|
76
|
+
- ".docker/Dockerfile"
|
77
|
+
- ".docker/clamd.conf"
|
78
|
+
- ".docker/docker-compose.yml"
|
79
|
+
- ".docker/docker-entrypoint.sh"
|
80
|
+
- ".dockerignore"
|
62
81
|
- ".gitignore"
|
82
|
+
- ".gitlab-ci.yml"
|
63
83
|
- ".rspec"
|
64
|
-
- ".travis.yml"
|
65
84
|
- Gemfile
|
66
85
|
- LICENSE.txt
|
67
86
|
- README.md
|
@@ -71,6 +90,7 @@ files:
|
|
71
90
|
- lib/ddr/antivirus.rb
|
72
91
|
- lib/ddr/antivirus/adapters/clamd_scanner_adapter.rb
|
73
92
|
- lib/ddr/antivirus/adapters/null_scanner_adapter.rb
|
93
|
+
- lib/ddr/antivirus/exceptions.rb
|
74
94
|
- lib/ddr/antivirus/scan_result.rb
|
75
95
|
- lib/ddr/antivirus/scanner.rb
|
76
96
|
- lib/ddr/antivirus/scanner_adapter.rb
|
@@ -100,7 +120,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
120
|
version: '0'
|
101
121
|
requirements: []
|
102
122
|
rubyforge_project:
|
103
|
-
rubygems_version: 2.
|
123
|
+
rubygems_version: 2.7.9
|
104
124
|
signing_key:
|
105
125
|
specification_version: 4
|
106
126
|
summary: Pluggable antivirus scanning service.
|