clamby 1.4.0 → 1.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +34 -15
- data/lib/clamby.rb +24 -64
- data/lib/clamby/command.rb +102 -0
- data/lib/clamby/{exception.rb → error.rb} +2 -1
- data/lib/clamby/version.rb +1 -1
- data/spec/clamby/command_spec.rb +118 -0
- data/spec/clamby_spec.rb +15 -47
- data/spec/fixtures/safe.txt +2 -0
- data/spec/spec_helper.rb +24 -2
- data/spec/support/shared_context.rb +4 -0
- metadata +11 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14e5489bd2c961059e2642dfcd7e472a838c449d114ba7ce7849eb4a7d8a2ae9
|
4
|
+
data.tar.gz: a0e7743a75f54442325af1773d923eb3ead7342efb6fd147733020c2ad85551b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ec80ea427e309f396664d599e28e742ddbf1913f1f4c5a58e3be43557bd9ad0f85276827e58511d2132ce4c89e6ab577082d0e7dcc471dfea92cf3c08b448e44
|
7
|
+
data.tar.gz: '079f33a665afff1f99e6df4e8c6c7be877b5bade43d8d2fc4270976b77de7b84fc2cdeec8f1791c19b8c79b8ac921cd90e6974a448af0db6c96a2bac28601654'
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
# v1.5.0
|
2
|
+
- Exceptions are now raised under Clamby module - could be breaking change
|
3
|
+
- [szajbus](https://github.com/kobaltz/clamby/commits?author=szajbus) fixed specs! and updated README
|
4
|
+
- [broder](https://github.com/kobaltz/clamby/commits?author=broder) added path for config file to address strange clamscan situations
|
5
|
+
- [tilsammans](https://github.com/kobaltz/clamby/commits?author=tilsammans) added queit, verbose and Command class
|
6
|
+
|
1
7
|
# v1.4.0
|
2
8
|
- [emilong](https://github.com/kobaltz/clamby/commits/master?author=emilong) added `:error_clamscan_client_error => false` option to prevent error from missing running daemon or clamscan.
|
3
9
|
|
data/README.md
CHANGED
@@ -66,32 +66,51 @@ It's good to note that Clamby will not by default delete files which had a virus
|
|
66
66
|
|
67
67
|
# Configuration
|
68
68
|
|
69
|
-
Configuration is rather limited right now. You can exclude the check if `clamscan` exists which will save a bunch of time for scanning your files. However, for development purposes, your machine may not have `clamscan` installed and you may wonder why it's not working properly. This is just to give you a reminder to install `clamscan` on your development machine and production machine. You can add the following to
|
69
|
+
Configuration is rather limited right now. You can exclude the check if `clamscan` exists which will save a bunch of time for scanning your files. However, for development purposes, your machine may not have `clamscan` installed and you may wonder why it's not working properly. This is just to give you a reminder to install `clamscan` on your development machine and production machine. You can add the following to an initializer, for example `config/initializers/clamby.rb`:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
Clamby.configure({
|
73
|
+
:check => false,
|
74
|
+
:daemonize => false,
|
75
|
+
:config_file => nil,
|
76
|
+
:error_clamscan_missing => true,
|
77
|
+
:error_clamscan_client_error => false,
|
78
|
+
:error_file_missing => true,
|
79
|
+
:error_file_virus => false,
|
80
|
+
:fdpass => false,
|
81
|
+
:stream => false,
|
82
|
+
:output => 'medium' # one of 'off', 'low', 'medium', 'high'
|
83
|
+
})
|
84
|
+
```
|
85
|
+
|
86
|
+
#### Daemonize
|
87
|
+
|
88
|
+
I highly recommend using the `daemonize` set to `true`. This will allow for clamscan to remain in memory and will not have to load for each virus scan. It will save several seconds per request.
|
89
|
+
|
90
|
+
To specify a config file for clamdscan to use, you can set `config_file` with the relevant path. See [this page](https://linux.die.net/man/5/clamd.conf) for more information about the config file.
|
91
|
+
|
92
|
+
#### Error suppression
|
70
93
|
|
71
94
|
There has been added additional functionality where you can override exceptions. If you set the exceptions below to false, then there will not be a hard exception generated. Instead, it will post to your log that an error had occured. By default each one of these configuration options are set to true. You may want to set these to false in your production environment.
|
72
95
|
|
96
|
+
#### Pass file descriptor permissions to clamd
|
97
|
+
|
73
98
|
Setting the `fdpass` configuration option to `true` will pass the `--fdpass` option to clamscan. This might be useful if you are encountering permissions problems between clamscan and files being created by your application. From the clamscan man page:
|
74
99
|
|
75
100
|
`--fdpass : Pass the file descriptor permissions to clamd. This is useful if clamd is running as a different user as it is faster than streaming the file to clamd. Only available if connected to clamd via local(unix) socket.`
|
76
101
|
|
102
|
+
#### Force streaming files to clamd
|
103
|
+
|
77
104
|
Setting the `stream` configuration option will stream the file to the daemon. This may be useful for forcing streaming as a test for local development. Only works when also specifying `daemonize`. From the clamdscan man page:
|
78
105
|
|
79
106
|
`--stream : Forces file streaming to clamd. This is generally not needed as clamdscan detects automatically if streaming is required. This option only exists for debugging and testing purposes, in all other cases --fdpass is preferred.`
|
80
107
|
|
81
|
-
|
82
|
-
Clamby.configure({
|
83
|
-
:check => false,
|
84
|
-
:daemonize => false,
|
85
|
-
:error_clamscan_missing => false,
|
86
|
-
:error_file_missing => false,
|
87
|
-
:error_file_virus => false,
|
88
|
-
:fdpass => false,
|
89
|
-
:silence_output => false,
|
90
|
-
:stream => false
|
91
|
-
})
|
92
|
-
```
|
108
|
+
#### Output levels
|
93
109
|
|
94
|
-
|
110
|
+
- *off*: suppress all output
|
111
|
+
- *low*: show errors, but nothing else
|
112
|
+
- *medium*: show errors and briefly what happened _(default)_
|
113
|
+
- *high*: as verbose as possible
|
95
114
|
|
96
115
|
# Dependencies
|
97
116
|
|
@@ -102,7 +121,7 @@ I highly recommend using the `daemonize` set to true. This will allow for clamsc
|
|
102
121
|
Note, `clamav-daemon` is optional but recommended. It's needed if you wish to
|
103
122
|
run ClamAV in daemon mode.
|
104
123
|
|
105
|
-
***
|
124
|
+
***macOS***
|
106
125
|
|
107
126
|
`brew install clamav`
|
108
127
|
|
data/lib/clamby.rb
CHANGED
@@ -1,25 +1,38 @@
|
|
1
1
|
require "English"
|
2
|
+
require "clamby/command"
|
3
|
+
require "clamby/error"
|
2
4
|
require "clamby/version"
|
3
|
-
|
5
|
+
|
4
6
|
module Clamby
|
5
7
|
DEFAULT_CONFIG = {
|
6
8
|
:check => true,
|
7
9
|
:daemonize => false,
|
10
|
+
:config_file => nil,
|
8
11
|
:error_clamscan_missing => true,
|
9
12
|
:error_clamscan_client_error => false,
|
10
13
|
:error_file_missing => true,
|
11
14
|
:error_file_virus => false,
|
12
15
|
:fdpass => false,
|
13
|
-
:
|
14
|
-
:
|
16
|
+
:stream => false,
|
17
|
+
:output_level => 'medium'
|
15
18
|
}.freeze
|
16
19
|
|
17
20
|
@config = DEFAULT_CONFIG.dup
|
18
21
|
|
19
22
|
@valid_config_keys = @config.keys
|
20
23
|
|
24
|
+
class << self
|
25
|
+
attr_reader :config
|
26
|
+
attr_reader :valid_config_keys
|
27
|
+
end
|
28
|
+
|
21
29
|
def self.configure(opts = {})
|
22
|
-
|
30
|
+
if opts.delete(:silence_output)
|
31
|
+
warn ':silence_output config is deprecated. Use :output_level => "off" instead.'
|
32
|
+
opts[:output_level] = 'off'
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.each {|k,v| config[k.to_sym] = v if valid_config_keys.include? k.to_sym}
|
23
36
|
end
|
24
37
|
|
25
38
|
def self.safe?(path)
|
@@ -28,79 +41,26 @@ module Clamby
|
|
28
41
|
! value
|
29
42
|
end
|
30
43
|
|
31
|
-
# Assemble the system command to be called, including optional flags
|
32
|
-
# @param [String] path path to the file being scanned
|
33
|
-
# @return [String] command to be executed
|
34
|
-
def self.system_command(path)
|
35
|
-
command = [].tap do |cmd|
|
36
|
-
cmd << clamd_executable_name
|
37
|
-
cmd << '--fdpass' if @config[:fdpass]
|
38
|
-
cmd << '--stream' if @config[:stream] && @config[:daemonize]
|
39
|
-
cmd << path
|
40
|
-
cmd << '--no-summary'
|
41
|
-
cmd << { out: File::NULL } if @config[:silence_output]
|
42
|
-
end
|
43
|
-
command
|
44
|
-
end
|
45
|
-
|
46
44
|
def self.virus?(path)
|
47
45
|
return nil unless scanner_exists?
|
48
|
-
|
49
|
-
system(*system_command(path))
|
50
|
-
|
51
|
-
case $CHILD_STATUS.exitstatus
|
52
|
-
when 0
|
53
|
-
return false
|
54
|
-
when 2
|
55
|
-
# clamdscan returns 2 whenever error other than a detection happens
|
56
|
-
if @config[:error_clamscan_client_error] && @config[:daemonize]
|
57
|
-
raise Exceptions::ClamscanClientError.new("Clamscan client error")
|
58
|
-
end
|
59
|
-
|
60
|
-
# returns true to maintain legacy behavior
|
61
|
-
return true
|
62
|
-
else
|
63
|
-
return true unless @config[:error_file_virus]
|
64
|
-
|
65
|
-
raise Exceptions::VirusDetected.new("VIRUS DETECTED on #{Time.now}: #{path}")
|
66
|
-
end
|
46
|
+
Command.scan path
|
67
47
|
end
|
68
48
|
|
69
49
|
def self.scanner_exists?
|
70
|
-
return true unless
|
71
|
-
scanner =
|
50
|
+
return true unless config[:check]
|
51
|
+
scanner = Command.clamscan_version
|
72
52
|
|
73
53
|
return true if scanner
|
74
|
-
return false unless
|
75
|
-
|
76
|
-
raise Exceptions::ClamscanMissing.new("#{clamd_executable_name} application not found. Check your installation and path.")
|
77
|
-
end
|
78
|
-
|
79
|
-
def self.file_exists?(path)
|
80
|
-
return false if path.nil?
|
81
|
-
return true if File.file?(path)
|
54
|
+
return false unless config[:error_clamscan_missing]
|
82
55
|
|
83
|
-
|
84
|
-
raise Exceptions::FileNotFound.new("File not found: #{path}")
|
85
|
-
else
|
86
|
-
puts "FILE NOT FOUND on #{Time.now}: #{path}"
|
87
|
-
return false
|
88
|
-
end
|
56
|
+
raise Clamby::ClamscanMissing.new("#{Command.scan_executable} not found. Check your installation and path.")
|
89
57
|
end
|
90
58
|
|
91
59
|
def self.update
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
|
-
def self.config
|
96
|
-
@config
|
97
|
-
end
|
98
|
-
|
99
|
-
def self.clamd_executable_name(daemonize: false)
|
100
|
-
daemonize? ? "clamdscan" : "clamscan"
|
60
|
+
Command.freshclam
|
101
61
|
end
|
102
62
|
|
103
63
|
def self.daemonize?
|
104
|
-
!!
|
64
|
+
!! config[:daemonize]
|
105
65
|
end
|
106
66
|
end
|
@@ -0,0 +1,102 @@
|
|
1
|
+
module Clamby
|
2
|
+
# Interface with the system. Builds and runs the command.
|
3
|
+
class Command
|
4
|
+
EXECUTABLES = %w(clamscan clamdscan freshclam)
|
5
|
+
|
6
|
+
# Array containing the complete command line.
|
7
|
+
attr_accessor :command
|
8
|
+
|
9
|
+
# Returns the appropriate scan executable, based on clamd being used.
|
10
|
+
def self.scan_executable
|
11
|
+
return 'clamdscan' if Clamby.config[:daemonize]
|
12
|
+
return 'clamscan'
|
13
|
+
end
|
14
|
+
|
15
|
+
# Perform a ClamAV scan on the given path.
|
16
|
+
def self.scan(path)
|
17
|
+
return nil unless file_exists?(path)
|
18
|
+
|
19
|
+
args = [path, '--no-summary']
|
20
|
+
|
21
|
+
if Clamby.config[:daemonize]
|
22
|
+
args << '--fdpass' if Clamby.config[:fdpass]
|
23
|
+
args << '--stream' if Clamby.config[:stream]
|
24
|
+
args << "--config-file=#{Clamby.config[:config_file]}" if Clamby.config[:config_file] && Clamby.config[:daemonize]
|
25
|
+
end
|
26
|
+
|
27
|
+
new.run scan_executable, *args
|
28
|
+
|
29
|
+
case $CHILD_STATUS.exitstatus
|
30
|
+
when 0
|
31
|
+
return false
|
32
|
+
when 2
|
33
|
+
# clamdscan returns 2 whenever error other than a detection happens
|
34
|
+
if Clamby.config[:error_clamscan_client_error] && Clamby.config[:daemonize]
|
35
|
+
raise Clamby::ClamscanClientError.new("Clamscan client error")
|
36
|
+
end
|
37
|
+
|
38
|
+
# returns true to maintain legacy behavior
|
39
|
+
return true
|
40
|
+
else
|
41
|
+
return true unless Clamby.config[:error_file_virus]
|
42
|
+
|
43
|
+
raise Clamby::VirusDetected.new("VIRUS DETECTED on #{Time.now}: #{path}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Update the virus definitions.
|
48
|
+
def self.freshclam
|
49
|
+
new.run 'freshclam'
|
50
|
+
end
|
51
|
+
|
52
|
+
# Show the ClamAV version. Also acts as a quick check if ClamAV functions.
|
53
|
+
def self.clamscan_version
|
54
|
+
new.run 'clamscan', '--version'
|
55
|
+
end
|
56
|
+
|
57
|
+
# Run the given commands via a system call.
|
58
|
+
# The executable must be one of the permitted ClamAV executables.
|
59
|
+
# The arguments will be combined with default arguments if needed.
|
60
|
+
# The arguments are sorted alphabetically before being passed to the system.
|
61
|
+
#
|
62
|
+
# Examples:
|
63
|
+
# run('clamscan', file, '--verbose')
|
64
|
+
# run('clamscan', '-V')
|
65
|
+
def run(executable, *args)
|
66
|
+
raise "`#{executable}` is not permitted" unless EXECUTABLES.include?(executable)
|
67
|
+
self.command = args | default_args
|
68
|
+
self.command = command.sort.unshift(executable)
|
69
|
+
|
70
|
+
system(*self.command, system_options)
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
def default_args
|
76
|
+
args = []
|
77
|
+
args << '--quiet' if Clamby.config[:output_level] == 'low'
|
78
|
+
args << '--verbose' if Clamby.config[:output_level] == 'high'
|
79
|
+
args
|
80
|
+
end
|
81
|
+
|
82
|
+
# This applies to the `system` call itself; does not end up in the command.
|
83
|
+
def system_options
|
84
|
+
if Clamby.config[:output_level] == 'off'
|
85
|
+
{ out: File::NULL }
|
86
|
+
else
|
87
|
+
{}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.file_exists?(path)
|
92
|
+
return true if File.file?(path)
|
93
|
+
|
94
|
+
if Clamby.config[:error_file_missing]
|
95
|
+
raise Clamby::FileNotFound.new("File not found: #{path}")
|
96
|
+
else
|
97
|
+
puts "FILE NOT FOUND on #{Time.now}: #{path}"
|
98
|
+
return false
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
data/lib/clamby/version.rb
CHANGED
@@ -0,0 +1,118 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'support/shared_context'
|
3
|
+
|
4
|
+
describe Clamby::Command do
|
5
|
+
before { Clamby.configure(Clamby::DEFAULT_CONFIG.dup) }
|
6
|
+
|
7
|
+
describe 'ClamAV version' do
|
8
|
+
it 'returns true' do
|
9
|
+
command = described_class.clamscan_version
|
10
|
+
expect(command).to be true
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
describe 'scan' do
|
15
|
+
include_context 'paths'
|
16
|
+
|
17
|
+
let(:runner){ instance_double(described_class) }
|
18
|
+
|
19
|
+
describe 'exceptions' do
|
20
|
+
it "can be configured to raise exception when file is missing" do
|
21
|
+
Clamby.configure({:error_file_missing => true})
|
22
|
+
|
23
|
+
expect do
|
24
|
+
described_class.scan(bad_path)
|
25
|
+
end.to raise_exception(Clamby::FileNotFound)
|
26
|
+
end
|
27
|
+
it 'can be configured to return nil when file is missing' do
|
28
|
+
Clamby.configure({:error_file_missing => false})
|
29
|
+
command = described_class.scan(bad_path)
|
30
|
+
|
31
|
+
expect(command).to be(nil)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'passing file descriptor' do
|
36
|
+
it 'does not include fdpass in the command by default' do
|
37
|
+
Clamby.configure(fdpass: false)
|
38
|
+
expect(runner).to receive(:run).with('clamscan', good_path, '--no-summary')
|
39
|
+
allow(described_class).to receive(:new).and_return(runner)
|
40
|
+
|
41
|
+
described_class.scan(good_path)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'omits the fdpass option when invoking clamscan if it is set, but daemonize isn\'t' do
|
45
|
+
Clamby.configure(fdpass: true)
|
46
|
+
expect(runner).to receive(:run).with('clamscan', good_path, '--no-summary')
|
47
|
+
allow(described_class).to receive(:new).and_return(runner)
|
48
|
+
|
49
|
+
described_class.scan(good_path)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'passes the fdpass option when invoking clamscan if it is set with daemonize' do
|
53
|
+
Clamby.configure(fdpass: true, daemonize: true)
|
54
|
+
expect(runner).to receive(:run).with('clamdscan', good_path, '--no-summary', '--fdpass')
|
55
|
+
allow(described_class).to receive(:new).and_return(runner)
|
56
|
+
|
57
|
+
described_class.scan(good_path)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
describe 'streaming files to clamd' do
|
62
|
+
it 'does not include stream in the command by default' do
|
63
|
+
Clamby.configure(stream: false)
|
64
|
+
expect(runner).to receive(:run).with('clamscan', good_path, '--no-summary')
|
65
|
+
allow(described_class).to receive(:new).and_return(runner)
|
66
|
+
|
67
|
+
described_class.scan(good_path)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'omits the stream option when invoking clamscan if it is set, but daemonize isn\'t' do
|
71
|
+
Clamby.configure(stream: true)
|
72
|
+
expect(runner).to receive(:run).with('clamscan', good_path, '--no-summary')
|
73
|
+
allow(described_class).to receive(:new).and_return(runner)
|
74
|
+
|
75
|
+
described_class.scan(good_path)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'passes the stream option when invoking clamscan if it is set with daemonize' do
|
79
|
+
Clamby.configure(stream: true, daemonize: true)
|
80
|
+
expect(runner).to receive(:run).with('clamdscan', good_path, '--no-summary', '--stream')
|
81
|
+
allow(described_class).to receive(:new).and_return(runner)
|
82
|
+
|
83
|
+
described_class.scan(good_path)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'specifying config-file' do
|
88
|
+
it 'does not include the parameter in the clamscan command by default' do
|
89
|
+
Clamby.configure(daemonize: false, stream: false, fdpass: false)
|
90
|
+
expect(runner).to receive(:run).with('clamscan', good_path, '--no-summary')
|
91
|
+
allow(described_class).to receive(:new).and_return(runner)
|
92
|
+
|
93
|
+
described_class.scan(good_path)
|
94
|
+
end
|
95
|
+
it 'does not include the parameter in the clamdscan command by default' do
|
96
|
+
Clamby.configure(daemonize: true, stream: false, fdpass: false)
|
97
|
+
expect(runner).to receive(:run).with('clamdscan', good_path, '--no-summary')
|
98
|
+
allow(described_class).to receive(:new).and_return(runner)
|
99
|
+
|
100
|
+
described_class.scan(good_path)
|
101
|
+
end
|
102
|
+
it 'omits the parameter when invoking clamscan if it is set' do
|
103
|
+
Clamby.configure(daemonize: false, stream: false, fdpass: false, config_file: 'clamd.conf')
|
104
|
+
expect(runner).to receive(:run).with('clamscan', good_path, '--no-summary')
|
105
|
+
allow(described_class).to receive(:new).and_return(runner)
|
106
|
+
|
107
|
+
described_class.scan(good_path)
|
108
|
+
end
|
109
|
+
it 'passes the parameter when invoking clamdscan if it is set' do
|
110
|
+
Clamby.configure(daemonize: true, stream: false, fdpass: false, config_file: 'clamd.conf')
|
111
|
+
expect(runner).to receive(:run).with('clamdscan', good_path, '--no-summary', '--config-file=clamd.conf')
|
112
|
+
allow(described_class).to receive(:new).and_return(runner)
|
113
|
+
|
114
|
+
described_class.scan(good_path)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
data/spec/clamby_spec.rb
CHANGED
@@ -1,26 +1,15 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
-
|
3
|
-
good_path = 'README.md'
|
4
|
-
bad_path = 'BAD_FILE.md'
|
2
|
+
require 'support/shared_context'
|
5
3
|
|
6
4
|
describe Clamby do
|
7
|
-
|
5
|
+
include_context 'paths'
|
8
6
|
|
9
|
-
|
10
|
-
expect(Clamby.file_exists?(good_path)).to be true
|
11
|
-
end
|
7
|
+
before { Clamby.configure(Clamby::DEFAULT_CONFIG.dup) }
|
12
8
|
|
13
9
|
it "should find clamscan" do
|
14
10
|
expect(Clamby.scanner_exists?).to be true
|
15
11
|
end
|
16
12
|
|
17
|
-
it "should not find files." do
|
18
|
-
Clamby.configure({:error_file_missing => true})
|
19
|
-
expect{Clamby.file_exists?(bad_path)}.to raise_exception(Exceptions::FileNotFound)
|
20
|
-
Clamby.configure({:error_file_missing => false})
|
21
|
-
expect(Clamby.file_exists?(bad_path)).to be false
|
22
|
-
end
|
23
|
-
|
24
13
|
it "should scan file as safe" do
|
25
14
|
expect(Clamby.safe?(good_path)).to be true
|
26
15
|
expect(Clamby.virus?(good_path)).to be false
|
@@ -33,21 +22,20 @@ describe Clamby do
|
|
33
22
|
end
|
34
23
|
|
35
24
|
it "should scan file as dangerous" do
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
else
|
41
|
-
`curl http://www.eicar.org/download/eicar.com > eicar.com`
|
25
|
+
begin
|
26
|
+
file = download('https://secure.eicar.org/eicar.com')
|
27
|
+
rescue SocketError => error
|
28
|
+
pending("Skipped because reasons: #{error}")
|
42
29
|
end
|
43
|
-
|
30
|
+
|
31
|
+
dangerous = file.path
|
44
32
|
Clamby.configure({:error_file_virus => true})
|
45
|
-
expect{Clamby.safe?(
|
46
|
-
expect{Clamby.virus?(
|
33
|
+
expect{Clamby.safe?(dangerous)}.to raise_exception(Clamby::VirusDetected)
|
34
|
+
expect{Clamby.virus?(dangerous)}.to raise_exception(Clamby::VirusDetected)
|
47
35
|
Clamby.configure({:error_file_virus => false})
|
48
|
-
expect(Clamby.safe?(
|
49
|
-
expect(Clamby.virus?(
|
50
|
-
File.delete(
|
36
|
+
expect(Clamby.safe?(dangerous)).to be false
|
37
|
+
expect(Clamby.virus?(dangerous)).to be true
|
38
|
+
File.delete(dangerous)
|
51
39
|
end
|
52
40
|
|
53
41
|
# From the clamscan man page:
|
@@ -62,14 +50,6 @@ describe Clamby do
|
|
62
50
|
Clamby.configure(fdpass: true)
|
63
51
|
expect(Clamby.config[:fdpass]).to eq true
|
64
52
|
end
|
65
|
-
it 'does not include fdpass in the command by default' do
|
66
|
-
Clamby.configure(fdpass: false)
|
67
|
-
expect(Clamby.system_command(good_path)).to eq ["clamscan", good_path, "--no-summary"]
|
68
|
-
end
|
69
|
-
it 'passes the fdpass option when invoking clamscan if it is set' do
|
70
|
-
Clamby.configure(fdpass: true)
|
71
|
-
expect(Clamby.system_command(good_path)).to eq ["clamscan", "--fdpass", good_path, "--no-summary"]
|
72
|
-
end
|
73
53
|
end
|
74
54
|
|
75
55
|
# From the clamscan man page:
|
@@ -84,18 +64,6 @@ describe Clamby do
|
|
84
64
|
Clamby.configure(stream: true)
|
85
65
|
expect(Clamby.config[:stream]).to eq true
|
86
66
|
end
|
87
|
-
it 'does not include stream in the command by default' do
|
88
|
-
Clamby.configure(stream: false)
|
89
|
-
expect(Clamby.system_command(good_path)).to eq ["clamscan", good_path, "--no-summary"]
|
90
|
-
end
|
91
|
-
it 'omits the stream option when invoking clamscan if it is set, but daemonize isn\'t' do
|
92
|
-
Clamby.configure(stream: true)
|
93
|
-
expect(Clamby.system_command(good_path)).to eq ["clamscan", good_path, "--no-summary"]
|
94
|
-
end
|
95
|
-
it 'passes the stream option when invoking clamscan if it is set with daemonize' do
|
96
|
-
Clamby.configure(stream: true, daemonize: true)
|
97
|
-
expect(Clamby.system_command(good_path)).to eq ["clamdscan", "--stream", good_path, "--no-summary"]
|
98
|
-
end
|
99
67
|
end
|
100
68
|
|
101
69
|
context 'error_clamscan_client_error option' do
|
@@ -131,7 +99,7 @@ describe Clamby do
|
|
131
99
|
|
132
100
|
it 'virus? raises when the daemonized client exits with status 2' do
|
133
101
|
Clamby.configure(daemonize: true)
|
134
|
-
expect { Clamby.virus?(good_path) }.to raise_error(
|
102
|
+
expect { Clamby.virus?(good_path) }.to raise_error(Clamby::ClamscanClientError)
|
135
103
|
end
|
136
104
|
it 'returns true when the client exits with status 2' do
|
137
105
|
Clamby.configure(daemonize: false)
|
data/spec/spec_helper.rb
CHANGED
@@ -1,8 +1,30 @@
|
|
1
1
|
require 'bundler/setup'
|
2
2
|
Bundler.setup
|
3
3
|
|
4
|
+
require 'open-uri'
|
5
|
+
require 'tempfile'
|
6
|
+
|
4
7
|
require 'clamby' # and any other gems you need
|
5
8
|
|
6
9
|
RSpec.configure do |config|
|
7
|
-
|
8
|
-
|
10
|
+
config.mock_with :rspec do |mocks|
|
11
|
+
# so that Command can keep doing what it always does.
|
12
|
+
mocks.verify_partial_doubles = true
|
13
|
+
end
|
14
|
+
|
15
|
+
def download(url)
|
16
|
+
file = open(url)
|
17
|
+
file.is_a?(StringIO) ? to_tempfile(file) : file
|
18
|
+
end
|
19
|
+
|
20
|
+
# OpenURI returns either Tempfile or StringIO depending of the size of
|
21
|
+
# the response. We want to unify this and always return Tempfile.
|
22
|
+
def to_tempfile(io)
|
23
|
+
tempfile = Tempfile.new('tmp')
|
24
|
+
tempfile.binmode
|
25
|
+
::OpenURI::Meta.init(tempfile, io)
|
26
|
+
tempfile << io.string
|
27
|
+
tempfile.rewind
|
28
|
+
tempfile
|
29
|
+
end
|
30
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: clamby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- kobaltz
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -73,11 +73,15 @@ files:
|
|
73
73
|
- clamby.gemspec
|
74
74
|
- clamby_logo.png
|
75
75
|
- lib/clamby.rb
|
76
|
-
- lib/clamby/
|
76
|
+
- lib/clamby/command.rb
|
77
|
+
- lib/clamby/error.rb
|
77
78
|
- lib/clamby/version.rb
|
78
79
|
- spec/.DS_Store
|
80
|
+
- spec/clamby/command_spec.rb
|
79
81
|
- spec/clamby_spec.rb
|
82
|
+
- spec/fixtures/safe.txt
|
80
83
|
- spec/spec_helper.rb
|
84
|
+
- spec/support/shared_context.rb
|
81
85
|
homepage: ''
|
82
86
|
licenses:
|
83
87
|
- MIT
|
@@ -98,11 +102,14 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
98
102
|
version: '0'
|
99
103
|
requirements: []
|
100
104
|
rubyforge_project:
|
101
|
-
rubygems_version: 2.7.
|
105
|
+
rubygems_version: 2.7.7
|
102
106
|
signing_key:
|
103
107
|
specification_version: 4
|
104
108
|
summary: Scan file uploads with ClamAV
|
105
109
|
test_files:
|
106
110
|
- spec/.DS_Store
|
111
|
+
- spec/clamby/command_spec.rb
|
107
112
|
- spec/clamby_spec.rb
|
113
|
+
- spec/fixtures/safe.txt
|
108
114
|
- spec/spec_helper.rb
|
115
|
+
- spec/support/shared_context.rb
|