clambytoo 1.0.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 +7 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/Rakefile +6 -0
- data/clambytoo.gemspec +24 -0
- data/lib/clambytoo/command.rb +121 -0
- data/lib/clambytoo/error.rb +8 -0
- data/lib/clambytoo/version.rb +3 -0
- data/lib/clambytoo.rb +67 -0
- data/spec/.DS_Store +0 -0
- data/spec/clamby/command_spec.rb +165 -0
- data/spec/clamby_spec.rb +145 -0
- data/spec/fixtures/safe (special).txt +2 -0
- data/spec/fixtures/safe.txt +2 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/shared_context.rb +5 -0
- metadata +109 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 522d95e9b100b7fa407904af1d747054b3977f8eab68193a207fc3f0196fa663
|
|
4
|
+
data.tar.gz: b5b98d1e4342a98632afeae44beb5f0499a999e8b1ac430ec621ad43034b96a1
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 304daa2134c849ec2ee89aa4b0e0d9905d43d0ec877577d9e42f358c7c5690fef482dd66b1a5cdaf68d4a5d915bd656b3e10bdc60b74fc47c7d0ebb762cb6fb0
|
|
7
|
+
data.tar.gz: ebe2655c8dbd1e96d3d7d8374dfdf298bfdd43aae81e89ebffb6e63ffccf10adffd37c2b08764d0f83c6ae487e1a63068297669c3115cd5e9818f1d9457dde9d
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2014 kobaltz
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/clambytoo.gemspec
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'clambytoo/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "clambytoo"
|
|
8
|
+
spec.version = Clambytoo::VERSION
|
|
9
|
+
spec.authors = ["kobaltz"]
|
|
10
|
+
spec.email = ["dave@k-innovations.net"]
|
|
11
|
+
spec.summary = "Scan file uploads with ClamAV"
|
|
12
|
+
spec.description = "This is a temp fork for testing purposes only. Clamby allows users to scan files uploaded with Paperclip or Carrierwave. If a file has a virus, then you can delete this file and discard it without causing harm to other users."
|
|
13
|
+
spec.homepage = ""
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency "bundler"
|
|
22
|
+
spec.add_development_dependency "rake"
|
|
23
|
+
spec.add_development_dependency "rspec"
|
|
24
|
+
end
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
module Clambytoo
|
|
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 Clambytoo.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 = [Shellwords.escape(path), '--no-summary']
|
|
20
|
+
|
|
21
|
+
if Clambytoo.config[:daemonize]
|
|
22
|
+
args << '--fdpass' if Clambytoo.config[:fdpass]
|
|
23
|
+
args << '--stream' if Clambytoo.config[:stream]
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
args << "-d #{Clambytoo.config[:datadir]}" if Clambytoo.config[:datadir]
|
|
27
|
+
|
|
28
|
+
new.run scan_executable, *args
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# $CHILD_STATUS maybe nil if the execution itself (not the client process)
|
|
32
|
+
# fails
|
|
33
|
+
case $CHILD_STATUS && $CHILD_STATUS.exitstatus
|
|
34
|
+
when 0
|
|
35
|
+
return false
|
|
36
|
+
when nil, 2
|
|
37
|
+
# clamdscan returns 2 whenever error other than a detection happens
|
|
38
|
+
if Clambytoo.config[:error_clamscan_client_error] && Clambytoo.config[:daemonize]
|
|
39
|
+
raise Clambytoo::ClamscanClientError.new("Clamscan client error")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# returns true to maintain legacy behavior
|
|
43
|
+
return true
|
|
44
|
+
else
|
|
45
|
+
return true unless Clambytoo.config[:error_file_virus]
|
|
46
|
+
|
|
47
|
+
raise Clambytoo::VirusDetected.new("VIRUS DETECTED on #{Time.now}: #{path}")
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Update the virus definitions.
|
|
52
|
+
def self.freshclam
|
|
53
|
+
args = []
|
|
54
|
+
args << "--datadir=#{Clambytoo.config[:datadir]}" if Clambytoo.config[:datadir]
|
|
55
|
+
new.run 'freshclam', *args
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Show the ClamAV version. Also acts as a quick check if ClamAV functions.
|
|
59
|
+
def self.clamscan_version
|
|
60
|
+
new.run scan_executable, '--version'
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Run the given commands via a system call.
|
|
64
|
+
# The executable must be one of the permitted ClamAV executables.
|
|
65
|
+
# The arguments will be combined with default arguments if needed.
|
|
66
|
+
# The arguments are sorted alphabetically before being passed to the system.
|
|
67
|
+
#
|
|
68
|
+
# Examples:
|
|
69
|
+
# run('clamscan', file, '--verbose')
|
|
70
|
+
# run('clamscan', '-V')
|
|
71
|
+
def run(executable, *args)
|
|
72
|
+
executable_full = executable_path(executable)
|
|
73
|
+
self.command = args | default_args
|
|
74
|
+
self.command = command.sort.unshift(executable_full)
|
|
75
|
+
if caller_locations(1,1)[0].label == "clamscan_version"
|
|
76
|
+
Open3.popen3(self.command.join(' ')) do |stdin, stdout, stderr, wait_thr|
|
|
77
|
+
exit_status = wait_thr.value
|
|
78
|
+
return true if exit_status.success? && stderr.gets.nil?
|
|
79
|
+
return nil unless stderr.gets.nil? #if we have any kind of error executing a command we should return nil
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
system(self.command.join(' '), system_options)
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
private
|
|
87
|
+
|
|
88
|
+
def default_args
|
|
89
|
+
args = []
|
|
90
|
+
args << "--config-file=#{Clambytoo.config[:config_file]}" if Clambytoo.config[:daemonize] && Clambytoo.config[:config_file]
|
|
91
|
+
args << '--quiet' if Clambytoo.config[:output_level] == 'low'
|
|
92
|
+
args << '--verbose' if Clambytoo.config[:output_level] == 'high'
|
|
93
|
+
args
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# This applies to the `system` call itself; does not end up in the command.
|
|
97
|
+
def system_options
|
|
98
|
+
if Clambytoo.config[:output_level] == 'off'
|
|
99
|
+
{ out: File::NULL }
|
|
100
|
+
else
|
|
101
|
+
{}
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def executable_path(executable)
|
|
106
|
+
raise "`#{executable}` is not permitted" unless EXECUTABLES.include?(executable)
|
|
107
|
+
Clambytoo.config[:"executable_path_#{executable}"]
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.file_exists?(path)
|
|
111
|
+
return true if File.file?(path)
|
|
112
|
+
|
|
113
|
+
if Clambytoo.config[:error_file_missing]
|
|
114
|
+
raise Clambytoo::FileNotFound.new("File not found: #{path}")
|
|
115
|
+
else
|
|
116
|
+
puts "FILE NOT FOUND on #{Time.now}: #{path}"
|
|
117
|
+
return false
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
data/lib/clambytoo.rb
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
require "English"
|
|
2
|
+
require 'open3'
|
|
3
|
+
require "clambytoo/command"
|
|
4
|
+
require "clambytoo/error"
|
|
5
|
+
require "clambytoo/version"
|
|
6
|
+
|
|
7
|
+
module Clambytoo
|
|
8
|
+
DEFAULT_CONFIG = {
|
|
9
|
+
:check => true,
|
|
10
|
+
:daemonize => false,
|
|
11
|
+
:config_file => nil,
|
|
12
|
+
:error_clamscan_client_error => false,
|
|
13
|
+
:error_file_missing => true,
|
|
14
|
+
:error_file_virus => false,
|
|
15
|
+
:fdpass => false,
|
|
16
|
+
:stream => false,
|
|
17
|
+
:output_level => 'medium',
|
|
18
|
+
:datadir => nil,
|
|
19
|
+
:executable_path_clamscan => 'clamscan',
|
|
20
|
+
:executable_path_clamdscan => 'clamdscan',
|
|
21
|
+
:executable_path_freshclam => 'freshclam',
|
|
22
|
+
}.freeze
|
|
23
|
+
|
|
24
|
+
@config = DEFAULT_CONFIG.dup
|
|
25
|
+
|
|
26
|
+
@valid_config_keys = @config.keys
|
|
27
|
+
|
|
28
|
+
class << self
|
|
29
|
+
attr_reader :config
|
|
30
|
+
attr_reader :valid_config_keys
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.configure(opts = {})
|
|
34
|
+
if opts.delete(:silence_output)
|
|
35
|
+
warn ':silence_output config is deprecated. Use :output_level => "off" instead.'
|
|
36
|
+
opts[:output_level] = 'off'
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
opts.each {|k,v| config[k.to_sym] = v if valid_config_keys.include? k.to_sym}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.safe?(path)
|
|
43
|
+
value = virus?(path)
|
|
44
|
+
return nil if value.nil?
|
|
45
|
+
! value
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def self.virus?(path)
|
|
49
|
+
return nil unless scanner_exists?
|
|
50
|
+
Command.scan path
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def self.scanner_exists?
|
|
54
|
+
return true unless config[:check]
|
|
55
|
+
scanner = Command.clamscan_version
|
|
56
|
+
|
|
57
|
+
return scanner ? true : false
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def self.update
|
|
61
|
+
Command.freshclam
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def self.daemonize?
|
|
65
|
+
!! config[:daemonize]
|
|
66
|
+
end
|
|
67
|
+
end
|
data/spec/.DS_Store
ADDED
|
Binary file
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'support/shared_context'
|
|
3
|
+
|
|
4
|
+
describe Clambytoo::Command do
|
|
5
|
+
before { Clambytoo.configure(Clambytoo::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
|
+
Clambytoo.configure({:error_file_missing => true})
|
|
22
|
+
|
|
23
|
+
expect do
|
|
24
|
+
described_class.scan(bad_path)
|
|
25
|
+
end.to raise_exception(Clambytoo::FileNotFound)
|
|
26
|
+
end
|
|
27
|
+
it 'can be configured to return nil when file is missing' do
|
|
28
|
+
Clambytoo.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
|
+
Clambytoo.configure
|
|
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
|
+
Clambytoo.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
|
+
Clambytoo.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
|
+
Clambytoo.configure
|
|
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
|
+
Clambytoo.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
|
+
Clambytoo.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
|
+
Clambytoo.configure
|
|
90
|
+
|
|
91
|
+
expect(described_class.new.send(:default_args)).not_to include(a_string_matching(/--config-file/))
|
|
92
|
+
end
|
|
93
|
+
it 'does not include the parameter in the clamdscan command by default' do
|
|
94
|
+
Clambytoo.configure(daemonize: true)
|
|
95
|
+
|
|
96
|
+
expect(described_class.new.send(:default_args)).not_to include(a_string_matching(/--config-file/))
|
|
97
|
+
end
|
|
98
|
+
it 'omits the parameter when invoking clamscan if it is set' do
|
|
99
|
+
Clambytoo.configure(daemonize: false, config_file: 'clamd.conf')
|
|
100
|
+
|
|
101
|
+
expect(described_class.new.send(:default_args)).not_to include('--config-file=clamd.conf')
|
|
102
|
+
end
|
|
103
|
+
it 'passes the parameter when invoking clamdscan if it is set' do
|
|
104
|
+
Clambytoo.configure(daemonize: true, config_file: 'clamd.conf')
|
|
105
|
+
|
|
106
|
+
expect(described_class.new.send(:default_args)).to include('--config-file=clamd.conf')
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
describe 'specifying custom executable paths' do
|
|
111
|
+
let(:runner) { described_class.new }
|
|
112
|
+
let(:custom_path) { '/custom/path' }
|
|
113
|
+
|
|
114
|
+
before do
|
|
115
|
+
Clambytoo.configure(
|
|
116
|
+
executable_path_clamscan: "#{custom_path}/clamscan",
|
|
117
|
+
executable_path_clamdscan: "#{custom_path}/clamdscan",
|
|
118
|
+
executable_path_freshclam: "#{custom_path}/freshclam",
|
|
119
|
+
)
|
|
120
|
+
allow(described_class).to receive(:new).and_return(runner)
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it 'executes the freshclam executable from the custom path' do
|
|
124
|
+
expect(runner).to receive(:system).with(
|
|
125
|
+
"#{custom_path}/freshclam",
|
|
126
|
+
{}
|
|
127
|
+
) { system("exit 0", out: File::NULL) }
|
|
128
|
+
|
|
129
|
+
described_class.freshclam
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
context 'when not set with daemonize' do
|
|
133
|
+
before { Clambytoo.configure(daemonize: false) }
|
|
134
|
+
|
|
135
|
+
it 'executes the clamscan executable from the custom path' do
|
|
136
|
+
expect(runner).to receive(:system).with(
|
|
137
|
+
"#{custom_path}/clamscan --no-summary #{good_path}",
|
|
138
|
+
{}
|
|
139
|
+
) { system("exit 0", out: File::NULL) }
|
|
140
|
+
|
|
141
|
+
described_class.scan(good_path)
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
context 'when set with daemonize' do
|
|
146
|
+
before { Clambytoo.configure(daemonize: true) }
|
|
147
|
+
|
|
148
|
+
it 'executes the clamdscan executable from the custom path' do
|
|
149
|
+
expect(runner).to receive(:system).with(
|
|
150
|
+
"#{custom_path}/clamdscan --no-summary #{good_path}",
|
|
151
|
+
{}
|
|
152
|
+
) { system("exit 0", out: File::NULL) }
|
|
153
|
+
|
|
154
|
+
described_class.scan(good_path)
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
describe 'special filenames' do
|
|
160
|
+
it 'does not fail' do
|
|
161
|
+
expect(described_class.scan(special_path)).to be(false)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
data/spec/clamby_spec.rb
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
require 'support/shared_context'
|
|
3
|
+
|
|
4
|
+
describe Clambytoo do
|
|
5
|
+
include_context 'paths'
|
|
6
|
+
|
|
7
|
+
before { Clambytoo.configure(Clambytoo::DEFAULT_CONFIG.dup) }
|
|
8
|
+
|
|
9
|
+
it "should find clamscan" do
|
|
10
|
+
expect(Clambytoo.scanner_exists?).to be true
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it "should scan file as safe" do
|
|
14
|
+
expect(Clambytoo.safe?(good_path)).to be true
|
|
15
|
+
expect(Clambytoo.virus?(good_path)).to be false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
it "should scan file and return nil" do
|
|
19
|
+
Clambytoo.configure({:error_file_missing => false})
|
|
20
|
+
expect(Clambytoo.safe?(bad_path)).to be nil
|
|
21
|
+
expect(Clambytoo.virus?(bad_path)).to be nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it "should scan file as dangerous" do
|
|
25
|
+
begin
|
|
26
|
+
file = download('https://secure.eicar.org/eicar.com')
|
|
27
|
+
rescue SocketError => error
|
|
28
|
+
pending("Skipped because reasons: #{error}")
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
dangerous = file.path
|
|
32
|
+
Clambytoo.configure({:error_file_virus => true})
|
|
33
|
+
expect{Clambytoo.safe?(dangerous)}.to raise_exception(Clambytoo::VirusDetected)
|
|
34
|
+
expect{Clambytoo.virus?(dangerous)}.to raise_exception(Clambytoo::VirusDetected)
|
|
35
|
+
Clambytoo.configure({:error_file_virus => false})
|
|
36
|
+
expect(Clambytoo.safe?(dangerous)).to be false
|
|
37
|
+
expect(Clambytoo.virus?(dangerous)).to be true
|
|
38
|
+
File.delete(dangerous)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# From the clamscan man page:
|
|
42
|
+
# Pass the file descriptor permissions to clamd. This is useful if clamd is
|
|
43
|
+
# running as a different user as it is faster than streaming the file to
|
|
44
|
+
# clamd. Only available if connected to clamd via local(unix) socket.
|
|
45
|
+
context 'fdpass option' do
|
|
46
|
+
it 'is false by default' do
|
|
47
|
+
expect(Clambytoo.config[:fdpass]).to eq false
|
|
48
|
+
end
|
|
49
|
+
it 'accepts an fdpass option in the config' do
|
|
50
|
+
Clambytoo.configure(fdpass: true)
|
|
51
|
+
expect(Clambytoo.config[:fdpass]).to eq true
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# From the clamscan man page:
|
|
56
|
+
# Forces file streaming to clamd. This is generally not needed as clamdscan
|
|
57
|
+
# detects automatically if streaming is required. This option only exists for
|
|
58
|
+
# debugging and testing purposes, in all other cases --fdpass is preferred.
|
|
59
|
+
context 'stream option' do
|
|
60
|
+
it 'is false by default' do
|
|
61
|
+
expect(Clambytoo.config[:stream]).to eq false
|
|
62
|
+
end
|
|
63
|
+
it 'accepts an stream option in the config' do
|
|
64
|
+
Clambytoo.configure(stream: true)
|
|
65
|
+
expect(Clambytoo.config[:stream]).to eq true
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context 'error_clamscan_client_error option' do
|
|
70
|
+
it 'is false by default' do
|
|
71
|
+
expect(Clambytoo.config[:error_clamscan_client_error]).to eq false
|
|
72
|
+
end
|
|
73
|
+
it 'accepts an error_clamscan_client_error option in the config' do
|
|
74
|
+
Clambytoo.configure(error_clamscan_client_error: true)
|
|
75
|
+
expect(Clambytoo.config[:error_clamscan_client_error]).to eq true
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
before {
|
|
79
|
+
Clambytoo.configure(check: false)
|
|
80
|
+
allow_any_instance_of(Process::Status).to receive(:exitstatus).and_return(2)
|
|
81
|
+
allow(Clambytoo).to receive(:system)
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
context 'when false' do
|
|
85
|
+
before { Clambytoo.configure(error_clamscan_client_error: false) }
|
|
86
|
+
|
|
87
|
+
it 'virus? returns true when the daemonized client exits with status 2' do
|
|
88
|
+
Clambytoo.configure(daemonize: true)
|
|
89
|
+
expect(Clambytoo.virus?(good_path)).to eq true
|
|
90
|
+
end
|
|
91
|
+
it 'returns true when the client exits with status 2' do
|
|
92
|
+
Clambytoo.configure(daemonize: false)
|
|
93
|
+
expect(Clambytoo.virus?(good_path)).to eq true
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
context 'when true' do
|
|
98
|
+
before { Clambytoo.configure(error_clamscan_client_error: true) }
|
|
99
|
+
|
|
100
|
+
it 'virus? raises when the daemonized client exits with status 2' do
|
|
101
|
+
Clambytoo.configure(daemonize: true)
|
|
102
|
+
expect { Clambytoo.virus?(good_path) }.to raise_error(Clambytoo::ClamscanClientError)
|
|
103
|
+
end
|
|
104
|
+
it 'returns true when the client exits with status 2' do
|
|
105
|
+
Clambytoo.configure(daemonize: false)
|
|
106
|
+
expect(Clambytoo.virus?(good_path)).to eq true
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
context 'executable paths' do
|
|
112
|
+
context 'executable_path_clamscan option' do
|
|
113
|
+
it 'is clamscan by default' do
|
|
114
|
+
expect(Clambytoo.config[:executable_path_clamscan]).to eq 'clamscan'
|
|
115
|
+
end
|
|
116
|
+
it 'accepts an executable_path_clamscan option in the config' do
|
|
117
|
+
path = '/custom/path/clamscan'
|
|
118
|
+
Clambytoo.configure(executable_path_clamscan: path)
|
|
119
|
+
expect(Clambytoo.config[:executable_path_clamscan]).to eq path
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
context 'executable_path_clamdscan option' do
|
|
124
|
+
it 'is clamdscan by default' do
|
|
125
|
+
expect(Clambytoo.config[:executable_path_clamdscan]).to eq 'clamdscan'
|
|
126
|
+
end
|
|
127
|
+
it 'accepts an executable_path_clamdscan option in the config' do
|
|
128
|
+
path = '/custom/path/clamdscan'
|
|
129
|
+
Clambytoo.configure(executable_path_clamdscan: path)
|
|
130
|
+
expect(Clambytoo.config[:executable_path_clamdscan]).to eq path
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
context 'executable_path_freshclam option' do
|
|
135
|
+
it 'is freshclam by default' do
|
|
136
|
+
expect(Clambytoo.config[:executable_path_freshclam]).to eq 'freshclam'
|
|
137
|
+
end
|
|
138
|
+
it 'accepts an executable_path_freshclam option in the config' do
|
|
139
|
+
path = '/custom/path/freshclam'
|
|
140
|
+
Clambytoo.configure(executable_path_freshclam: path)
|
|
141
|
+
expect(Clambytoo.config[:executable_path_freshclam]).to eq path
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'bundler/setup'
|
|
2
|
+
Bundler.setup
|
|
3
|
+
|
|
4
|
+
require 'open-uri'
|
|
5
|
+
require 'tempfile'
|
|
6
|
+
|
|
7
|
+
require 'clamby' # and any other gems you need
|
|
8
|
+
|
|
9
|
+
RSpec.configure do |config|
|
|
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
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
RSpec.shared_context 'paths' do
|
|
2
|
+
let(:special_path) { File.expand_path('../../fixtures/safe (special).txt', __FILE__) }
|
|
3
|
+
let(:good_path) { File.expand_path('../../fixtures/safe.txt', __FILE__) }
|
|
4
|
+
let(:bad_path) { File.expand_path("not-here/#{rand 10e6}.txt", __FILE__) }
|
|
5
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: clambytoo
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- kobaltz
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2022-09-22 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :development
|
|
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: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
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: rspec
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
description: This is a temp fork for testing purposes only. Clamby allows users to
|
|
56
|
+
scan files uploaded with Paperclip or Carrierwave. If a file has a virus, then you
|
|
57
|
+
can delete this file and discard it without causing harm to other users.
|
|
58
|
+
email:
|
|
59
|
+
- dave@k-innovations.net
|
|
60
|
+
executables: []
|
|
61
|
+
extensions: []
|
|
62
|
+
extra_rdoc_files: []
|
|
63
|
+
files:
|
|
64
|
+
- Gemfile
|
|
65
|
+
- LICENSE.txt
|
|
66
|
+
- Rakefile
|
|
67
|
+
- clambytoo.gemspec
|
|
68
|
+
- lib/clambytoo.rb
|
|
69
|
+
- lib/clambytoo/command.rb
|
|
70
|
+
- lib/clambytoo/error.rb
|
|
71
|
+
- lib/clambytoo/version.rb
|
|
72
|
+
- spec/.DS_Store
|
|
73
|
+
- spec/clamby/command_spec.rb
|
|
74
|
+
- spec/clamby_spec.rb
|
|
75
|
+
- spec/fixtures/safe (special).txt
|
|
76
|
+
- spec/fixtures/safe.txt
|
|
77
|
+
- spec/spec_helper.rb
|
|
78
|
+
- spec/support/shared_context.rb
|
|
79
|
+
homepage: ''
|
|
80
|
+
licenses:
|
|
81
|
+
- MIT
|
|
82
|
+
metadata: {}
|
|
83
|
+
post_install_message:
|
|
84
|
+
rdoc_options: []
|
|
85
|
+
require_paths:
|
|
86
|
+
- lib
|
|
87
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
88
|
+
requirements:
|
|
89
|
+
- - ">="
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: '0'
|
|
92
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
requirements: []
|
|
98
|
+
rubygems_version: 3.0.9
|
|
99
|
+
signing_key:
|
|
100
|
+
specification_version: 4
|
|
101
|
+
summary: Scan file uploads with ClamAV
|
|
102
|
+
test_files:
|
|
103
|
+
- spec/.DS_Store
|
|
104
|
+
- spec/clamby/command_spec.rb
|
|
105
|
+
- spec/clamby_spec.rb
|
|
106
|
+
- spec/fixtures/safe (special).txt
|
|
107
|
+
- spec/fixtures/safe.txt
|
|
108
|
+
- spec/spec_helper.rb
|
|
109
|
+
- spec/support/shared_context.rb
|