ddr-antivirus 1.2.1 → 1.3.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
  SHA1:
3
- metadata.gz: bbf8b0276005e8766ca6c985e7a4d4771c1ec2f1
4
- data.tar.gz: 39638ef73883d0ba112cf8d093ed84e9e021d39d
3
+ metadata.gz: 86a232e5b90342f2dd51279b2e589e51d7345a23
4
+ data.tar.gz: e48ae1a5fe0a895d18784a6ba120a0e7dfa892ba
5
5
  SHA512:
6
- metadata.gz: 96ea1c688237f4dfa5f9b6a8c7c5c5c87edd9eeb5a535ed55978680c9850f744bd2d7ba456d37811157d744bf8a2bdda1d2a9c0111d1a411c404ac2cd798f609
7
- data.tar.gz: 5e1c5433a733abdc129bf37893299ec2133b817e7a316613805e973927e13206a673f42f67c7c9671abb2816a68186e3096fecfff925eaa1971f6e71f09a1232
6
+ metadata.gz: 173c325eb8d7369d7823341593334e229ae5a48047938a680fda9afc21a6151d6e34722864a9acda826fd1f11113526b086bc7dc45acdaf7a410c8a70b4e0ee8
7
+ data.tar.gz: 51d17a1ea969fab1d69f86b4bd1f3c870fdcef45d7362f96bebb9387ab95105f2e61e55c79eee1be628a254d5ef15acffd0d894a6a6414bbc7a88e96a52bd3af
data/README.md CHANGED
@@ -24,7 +24,7 @@ Or install it yourself as:
24
24
  Ddr::Antivirus does *not* provide a virus scanning engine as a runtime dependency. Instead, it will select a scanner adapter class for the software it finds in your environment following this procedure:
25
25
 
26
26
  - If the [clamav](https://github.com/eagleas/clamav) gem is available, it will select the `ClamavScannerAdapter`.
27
- - If the ClamAV Daemon client `clamdscan` is on the user's path, it will select the `ClamdScannerAdapter`.
27
+ - If the ClamAV Daemon client `clamdscan` is on the user's path, it will select the `ClamdScannerAdapter`. Ddr::Antivirus *does not* manage clamd -- i.e., checking its status, starting or reloading the database. These tasks must be managed externally.
28
28
  - Otherwise, it will select the [`NullScannerAdapter`](#the-nullscanneradapter).
29
29
 
30
30
  The auto-selection process may be overridden by configuration:
@@ -57,7 +57,7 @@ The scanner raises a `Ddr::Antivirus::VirusFoundError` exception if a virus is f
57
57
 
58
58
  ### Results
59
59
 
60
- Class: `Ddr::Antivirus::ScanResult`
60
+ Class: `Ddr::Antivirus::Adapters::ScanResult`
61
61
 
62
62
  A scanner adapter may subclass the base class to parse the raw result properly.
63
63
 
@@ -66,7 +66,7 @@ A scanner adapter may subclass the base class to parse the raw result properly.
66
66
  => true
67
67
 
68
68
  >> result = Ddr::Antivirus::Scanner.scan("/path/to/blue-devil.png")
69
- => #<Ddr::Antivirus::Adapters::ClamavScannerAdapter::ClamavScanResult:0x007f98fb169cc0 ...
69
+ => #<Ddr::Antivirus::Adapters::ClamavScanResult:0x007f98fb169cc0 ...
70
70
 
71
71
  # Was there a virus?
72
72
  >> result.has_virus?
@@ -85,20 +85,29 @@ A scanner adapter may subclass the base class to parse the raw result properly.
85
85
  => 0 # ClamAV example
86
86
 
87
87
  # String representation (example)
88
- >> puts result.to_s
89
- => /path/to/blue-devil.png: OK (ClamAV 0.98.3/19559/Thu Oct 30 06:39:46 2014)
88
+ >> result.to_s
89
+ => "/path/to/blue-devil.png: OK (ClamAV 0.98.3/19595/Thu Nov 6 11:32:29 2014)"
90
+ ```
91
+
92
+ ### Logging
93
+
94
+ 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:
95
+
96
+ ```ruby
97
+ require "logger"
98
+ Ddr::Antivirus.logger = Logger.new("/path/to/custom.log")
90
99
  ```
91
100
 
92
101
  ### The NullScannerAdapter
93
102
 
94
- In order to avoid the overhead of ClamAV in test and/or development environments, the package provides a no-op adapter that logs a message and returns a scan result object (instance of Ddr::Antivirus::ScanResult).
103
+ In order to avoid the overhead of ClamAV in test and/or development environments, the package provides a no-op adapter:
95
104
 
96
105
  ```ruby
97
106
  >> Ddr::Antivirus.scanner_adapter = :null
98
107
  => :null
99
108
  >> Ddr::Antivirus::Scanner.scan("/path/to/blue-devil.png")
100
- W, [2014-10-30T16:21:24.349542 #76244] WARN -- : File not scanned -- using :null scanner adapter.
101
- I, [2014-10-30T16:21:24.350582 #76244] INFO -- : #<Ddr::Antivirus::ScanResult:0x007ff6c98d0500 @raw="File not scanned -- using :null scanner adapter.", @file_path="/path/to/blue-devil.png", @scanned_at=2014-10-30 20:21:24 UTC, @version="ddr-antivirus 1.0.0.rc1">
109
+ I, [2014-11-07T15:58:17.706866 #82651] INFO -- : /path/to/blue-devil.png: NOT SCANNED - using :null scanner adapter. (ddr-antivirus 1.2.0)
110
+ => #<Ddr::Antivirus::Adapters::NullScanResult:0x007f9e2ba1af38 @raw="/path/to/blue-devil.png: NOT SCANNED - using :null scanner adapter.", @file_path="/path/to/blue-devil.png", @scanned_at=2014-11-07 20:58:17 UTC, @version="ddr-antivirus 1.2.0">
102
111
  ```
103
112
 
104
113
  ## Contributing
@@ -1,7 +1,8 @@
1
- require "ddr/antivirus/version"
2
- require "ddr/antivirus/scanner"
3
- require "ddr/antivirus/scan_result"
4
- require "ddr/antivirus/adapters"
1
+ require "logger"
2
+
3
+ require_relative "antivirus/version"
4
+ require_relative "antivirus/scanner"
5
+ require_relative "antivirus/adapters"
5
6
 
6
7
  require "active_support/core_ext/module/attribute_accessors"
7
8
 
@@ -23,7 +24,6 @@ module Ddr
23
24
  if defined?(Rails) && Rails.logger
24
25
  Rails.logger
25
26
  else
26
- require "logger"
27
27
  Logger.new(STDERR)
28
28
  end
29
29
  end
@@ -1,23 +1,14 @@
1
- require_relative "./adapters/scanner_adapter"
2
-
3
1
  module Ddr
4
2
  module Antivirus
5
3
  module Adapters
6
4
 
7
- def self.get_adapter
8
- require_relative adapter_module
5
+ def self.get_adapter
6
+ require_relative "adapters/#{Ddr::Antivirus.scanner_adapter}_scanner_adapter"
7
+ adapter_name = "#{Ddr::Antivirus.scanner_adapter.to_s.capitalize}ScannerAdapter"
9
8
  klass = self.const_get(adapter_name.to_sym, false)
10
9
  klass.new
11
10
  end
12
11
 
13
- def self.adapter_name
14
- "#{Ddr::Antivirus.scanner_adapter.to_s.capitalize}ScannerAdapter"
15
- end
16
-
17
- def self.adapter_module
18
- "./adapters/#{Ddr::Antivirus.scanner_adapter}_scanner_adapter"
19
- end
20
-
21
12
  end
22
13
  end
23
14
  end
@@ -1,8 +1,12 @@
1
- require 'clamav'
1
+ require "clamav"
2
+
3
+ require_relative "scanner_adapter"
4
+ require_relative "scan_result"
2
5
 
3
6
  module Ddr
4
7
  module Antivirus
5
8
  module Adapters
9
+
6
10
  class ClamavScannerAdapter < ScannerAdapter
7
11
 
8
12
  def scan(path)
@@ -31,39 +35,43 @@ module Ddr
31
35
  ClamAV.instance
32
36
  end
33
37
 
34
- class ClamavScanResult < Ddr::Antivirus::ScanResult
38
+ end
35
39
 
36
- def virus_found
37
- raw if has_virus?
38
- end
40
+ #
41
+ # Result of a scan with the ClamavScannerAdapter.
42
+ #
43
+ class ClamavScanResult < ScanResult
39
44
 
40
- def has_virus?
41
- ![0, 1].include?(raw)
42
- end
45
+ def virus_found
46
+ raw if has_virus?
47
+ end
43
48
 
44
- def error?
45
- raw == 1
46
- end
49
+ def has_virus?
50
+ ![0, 1].include?(raw)
51
+ end
47
52
 
48
- def status
49
- return "FOUND #{virus_found}" if has_virus?
50
- return "ERROR" if error?
51
- "OK"
52
- end
53
+ def error?
54
+ raw == 1
55
+ end
53
56
 
54
- def to_s
55
- "#{file_path}: #{status} (#{version})"
56
- end
57
+ def status
58
+ return "FOUND #{virus_found}" if has_virus?
59
+ return "ERROR" if error?
60
+ "OK"
61
+ end
57
62
 
58
- def default_version
59
- # Engine and database versions
60
- # E.g., ClamAV 0.98.3/19010/Tue May 20 21:46:01 2014
61
- `sigtool --version`.strip
62
- end
63
+ def to_s
64
+ "#{file_path}: #{status} (#{version})"
65
+ end
63
66
 
67
+ def default_version
68
+ # Engine and database versions
69
+ # E.g., ClamAV 0.98.3/19010/Tue May 20 21:46:01 2014
70
+ `sigtool --version`.strip
64
71
  end
65
72
 
66
73
  end
74
+
67
75
  end
68
76
  end
69
77
  end
@@ -1,3 +1,6 @@
1
+ require_relative "scanner_adapter"
2
+ require_relative "scan_result"
3
+
1
4
  module Ddr
2
5
  module Antivirus
3
6
  module Adapters
@@ -17,37 +20,41 @@ module Ddr
17
20
  `clamdscan --no-summary #{path}`.strip
18
21
  end
19
22
 
20
- class ClamdScanResult < Ddr::Antivirus::ScanResult
21
-
22
- def virus_found
23
- if m = /: ([^\s]+) FOUND$/.match(raw)
24
- m[1]
25
- end
26
- end
23
+ end
27
24
 
28
- def has_virus?
29
- raw =~ / FOUND$/
25
+ #
26
+ # Result of a scan with the ClamdScannerAdapter
27
+ #
28
+ class ClamdScanResult < ScanResult
29
+
30
+ def virus_found
31
+ if m = /: ([^\s]+) FOUND$/.match(raw)
32
+ m[1]
30
33
  end
34
+ end
31
35
 
32
- def error?
33
- raw =~ / ERROR$/
34
- end
36
+ def has_virus?
37
+ raw =~ / FOUND$/
38
+ end
35
39
 
36
- def ok?
37
- raw =~ / OK$/
38
- end
40
+ def error?
41
+ raw =~ / ERROR$/
42
+ end
39
43
 
40
- def to_s
41
- "#{raw} (#{version})"
42
- end
44
+ def ok?
45
+ raw =~ / OK$/
46
+ end
43
47
 
44
- def default_version
45
- `sigtool --version`.strip
46
- end
48
+ def to_s
49
+ "#{raw} (#{version})"
50
+ end
47
51
 
52
+ def default_version
53
+ `sigtool --version`.strip
48
54
  end
49
55
 
50
56
  end
57
+
51
58
  end
52
59
  end
53
60
  end
@@ -1,3 +1,6 @@
1
+ require_relative "scanner_adapter"
2
+ require_relative "scan_result"
3
+
1
4
  module Ddr
2
5
  module Antivirus
3
6
  module Adapters
@@ -7,10 +10,16 @@ module Ddr
7
10
  class NullScannerAdapter < ScannerAdapter
8
11
 
9
12
  def scan(path)
10
- Ddr::Antivirus::ScanResult.new("#{path}: NOT SCANNED - using :null scanner adapter.", path)
13
+ NullScanResult.new("#{path}: NOT SCANNED - using :null scanner adapter.", path)
11
14
  end
12
15
 
13
16
  end
17
+
18
+ #
19
+ # The result of the scan with the NullScannerAdapter.
20
+ #
21
+ class NullScanResult < ScanResult; end
22
+
14
23
  end
15
24
  end
16
25
  end
@@ -0,0 +1,52 @@
1
+ module Ddr
2
+ module Antivirus
3
+ module Adapters
4
+ #
5
+ # Default scan result implementation
6
+ #
7
+ class ScanResult
8
+
9
+ attr_reader :raw, :file_path, :scanned_at, :version
10
+
11
+ def initialize(raw, file_path, opts={})
12
+ @raw = raw
13
+ @file_path = file_path
14
+ @scanned_at = opts.fetch(:scanned_at, default_time)
15
+ @version = opts.fetch(:version, default_version)
16
+ end
17
+
18
+ def default_time
19
+ Time.now.utc
20
+ end
21
+
22
+ def default_version
23
+ "ddr-antivirus #{Ddr::Antivirus::VERSION}"
24
+ end
25
+
26
+ # Subclasses may override to provide description of virus found.
27
+ def virus_found; end
28
+
29
+ # Subclasses should override
30
+ def has_virus?
31
+ !virus_found.nil?
32
+ end
33
+
34
+ # Subclasses may override to indicate an error condition (not necessarily an exception).
35
+ def error?
36
+ false
37
+ end
38
+
39
+ def ok?
40
+ !(has_virus? || error?)
41
+ end
42
+
43
+ # Subclasses may override
44
+ def to_s
45
+ "#{raw} (#{version})"
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+ end
52
+
@@ -6,10 +6,15 @@ module Ddr
6
6
  #
7
7
  class ScannerAdapter
8
8
 
9
+ # Scan a file path for viruses - subclasses must implement.
10
+ #
11
+ # @param [String] file path to scan
12
+ # @return [Ddr::Antivirus::ScanResult] the result
9
13
  def scan(path)
10
14
  raise NotImplementedError
11
15
  end
12
16
 
17
+ # Return the adapter configuration options
13
18
  def config
14
19
  Ddr::Antivirus.adapter_config
15
20
  end
@@ -1,5 +1,5 @@
1
1
  module Ddr
2
2
  module Antivirus
3
- VERSION = "1.2.1"
3
+ VERSION = "1.3.0"
4
4
  end
5
5
  end
@@ -35,9 +35,10 @@ module Ddr
35
35
  describe "result" do
36
36
  subject { adapter.scan(path) }
37
37
  let(:adapter) { described_class.new }
38
- it "should be a scan result" do
39
- expect(subject).to be_a(Ddr::Antivirus::ScanResult)
38
+ it "should be a ClamavScanResult" do
39
+ expect(subject).to be_a(ClamavScanResult)
40
40
  end
41
+ it_should_behave_like "a scan result"
41
42
  context "when a virus is found" do
42
43
  before { allow(adapter.engine).to receive(:scanfile).with(path) { "Bad boy 35" } }
43
44
  it "the raw result should be the virus description" do
@@ -12,8 +12,9 @@ module Ddr
12
12
  subject { adapter.scan(path) }
13
13
  let(:adapter) { described_class.new }
14
14
  it "should be a scan result" do
15
- expect(subject).to be_a(Ddr::Antivirus::ScanResult)
15
+ expect(subject).to be_a(ClamdScanResult)
16
16
  end
17
+ it_should_behave_like "a scan result"
17
18
  context "when a virus is found" do
18
19
  before { allow(adapter).to receive(:clamdscan).with(path) { "#{path}: Bad-boy-35 FOUND" } }
19
20
  it "should have a virus_found" do
@@ -0,0 +1,24 @@
1
+ require "shared_examples_for_scan_results"
2
+ require "ddr/antivirus/adapters/null_scanner_adapter"
3
+
4
+ module Ddr
5
+ module Antivirus
6
+ module Adapters
7
+ RSpec.describe NullScannerAdapter do
8
+
9
+ let(:path) { File.expand_path(File.join("..", "..", "fixtures", "blue-devil.png"), __FILE__) }
10
+
11
+ describe "result" do
12
+ subject { adapter.scan(path) }
13
+ let(:adapter) { described_class.new }
14
+ it "should be a NullScanResult" do
15
+ expect(subject).to be_a(NullScanResult)
16
+ end
17
+ it_should_behave_like "a scan result"
18
+ it_should_behave_like "a successful scan result"
19
+ end
20
+
21
+ end
22
+ end
23
+ end
24
+ end
@@ -2,23 +2,25 @@ require 'shared_examples_for_scan_results'
2
2
 
3
3
  module Ddr
4
4
  module Antivirus
5
- RSpec.describe ScanResult do
6
- subject { described_class.new("Raw result", "/tmp/foo") }
5
+ module Adapters
6
+ RSpec.describe ScanResult do
7
+ subject { described_class.new("Raw result", "/tmp/foo") }
7
8
 
8
- it_should_behave_like "a scan result"
9
+ it_should_behave_like "a scan result"
9
10
 
10
- describe "success" do
11
- it_should_behave_like "a successful scan result"
12
- end
11
+ describe "success" do
12
+ it_should_behave_like "a successful scan result"
13
+ end
13
14
 
14
- describe "error" do
15
- before { allow(subject).to receive(:error?) { true } }
16
- it_should_behave_like "an error scan result"
17
- end
15
+ describe "error" do
16
+ before { allow(subject).to receive(:error?) { true } }
17
+ it_should_behave_like "an error scan result"
18
+ end
18
19
 
19
- describe "virus found" do
20
- before { allow(subject).to receive(:virus_found) { "Bad boy 35" } }
21
- it_should_behave_like "a virus scan result"
20
+ describe "virus found" do
21
+ before { allow(subject).to receive(:virus_found) { "Bad boy 35" } }
22
+ it_should_behave_like "a virus scan result"
23
+ end
22
24
  end
23
25
  end
24
26
  end
@@ -1,31 +1,34 @@
1
+ require "ddr/antivirus/adapters/scan_result"
2
+
1
3
  module Ddr
2
4
  module Antivirus
3
5
  RSpec.describe Scanner do
4
6
 
5
7
  shared_examples "a scanner" do
6
- describe "when a virus is found" do
7
- before do
8
- allow_any_instance_of(ScanResult).to receive(:has_virus?) { true }
8
+ let(:path) { "/tmp/foo" }
9
+ describe "logging" do
10
+ it "should log the result" do
11
+ expect(Ddr::Antivirus.logger).to receive(:info)
12
+ subject.scan(path)
9
13
  end
14
+ end
15
+ describe "when a virus is found" do
16
+ before { allow_any_instance_of(Ddr::Antivirus::Adapters::ScanResult).to receive(:has_virus?) { true } }
10
17
  it "should raise an execption" do
11
- expect { subject.scan("/tmp/foo") }.to raise_error
18
+ expect { subject.scan(path) }.to raise_error
12
19
  end
13
20
  end
14
21
  describe "when a virus is not found" do
15
- before do
16
- allow_any_instance_of(ScanResult).to receive(:has_virus?) { false }
17
- end
22
+ before { allow_any_instance_of(Ddr::Antivirus::Adapters::ScanResult).to receive(:has_virus?) { false } }
18
23
  it "should return the scan result" do
19
- expect(subject.scan("/tmp/foo")).to be_a(ScanResult)
24
+ expect(subject.scan(path)).to be_a(Ddr::Antivirus::Adapters::ScanResult)
20
25
  end
21
26
  end
22
27
  describe "when an error occurs in the scanner" do
23
- before do
24
- allow_any_instance_of(ScanResult).to receive(:error?) { true }
25
- end
28
+ before { allow_any_instance_of(Ddr::Antivirus::Adapters::ScanResult).to receive(:error?) { true } }
26
29
  it "should log an error" do
27
30
  expect(Ddr::Antivirus.logger).to receive(:error)
28
- subject.scan("/tmp/foo")
31
+ subject.scan(path)
29
32
  end
30
33
  end
31
34
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ddr-antivirus
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - David Chandek-Stark
@@ -101,8 +101,8 @@ files:
101
101
  - lib/ddr/antivirus/adapters/clamav_scanner_adapter.rb
102
102
  - lib/ddr/antivirus/adapters/clamd_scanner_adapter.rb
103
103
  - lib/ddr/antivirus/adapters/null_scanner_adapter.rb
104
+ - lib/ddr/antivirus/adapters/scan_result.rb
104
105
  - lib/ddr/antivirus/adapters/scanner_adapter.rb
105
- - lib/ddr/antivirus/scan_result.rb
106
106
  - lib/ddr/antivirus/scanner.rb
107
107
  - lib/ddr/antivirus/version.rb
108
108
  - spec/fixtures/blue-devil.png
@@ -110,6 +110,7 @@ files:
110
110
  - spec/spec_helper.rb
111
111
  - spec/unit/clamav_scanner_adapter_spec.rb
112
112
  - spec/unit/clamd_scanner_adapter_spec.rb
113
+ - spec/unit/null_scanner_adapter_spec.rb
113
114
  - spec/unit/scan_result_spec.rb
114
115
  - spec/unit/scanner_spec.rb
115
116
  homepage: https://github.com/duke-libraries/ddr-antivirus
@@ -142,5 +143,6 @@ test_files:
142
143
  - spec/spec_helper.rb
143
144
  - spec/unit/clamav_scanner_adapter_spec.rb
144
145
  - spec/unit/clamd_scanner_adapter_spec.rb
146
+ - spec/unit/null_scanner_adapter_spec.rb
145
147
  - spec/unit/scan_result_spec.rb
146
148
  - spec/unit/scanner_spec.rb
@@ -1,50 +0,0 @@
1
- module Ddr
2
- module Antivirus
3
- #
4
- # Default scan result implementation
5
- #
6
- class ScanResult
7
-
8
- attr_reader :raw, :file_path, :scanned_at, :version
9
-
10
- def initialize(raw, file_path, opts={})
11
- @raw = raw
12
- @file_path = file_path
13
- @scanned_at = opts.fetch(:scanned_at, default_time)
14
- @version = opts.fetch(:version, default_version)
15
- end
16
-
17
- def default_time
18
- Time.now.utc
19
- end
20
-
21
- def default_version
22
- "ddr-antivirus #{Ddr::Antivirus::VERSION}"
23
- end
24
-
25
- # Subclasses may override to provide description of virus found.
26
- def virus_found; end
27
-
28
- # Subclasses should override
29
- def has_virus?
30
- !virus_found.nil?
31
- end
32
-
33
- # Subclasses may override to indicate an error condition (not necessarily an exception).
34
- def error?
35
- false
36
- end
37
-
38
- def ok?
39
- !(has_virus? || error?)
40
- end
41
-
42
- # Subclasses may override
43
- def to_s
44
- "#{raw} (#{version})"
45
- end
46
-
47
- end
48
- end
49
- end
50
-