littleneck_clamav 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --tty
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (C) 2012 Theo Cushion
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ Littleneck ClamAV [![Build status](https://secure.travis-ci.org/theozaurus/littleneck_clamav.png)](http://travis-ci.org/theozaurus/littleneck_clamav)
2
+ =================
3
+
4
+ [Littleneck ClamAV](http://github.com/theozaurus/littleneck_clamav) is a thin
5
+ wrapper to make it quick and easy to use [ClamAV](http://www.clamav.net) within
6
+ Ruby.
7
+
8
+ It will use `clamdscan` if available to save load time, or fallback to
9
+ `clamscan` if it is not. Compilation is avoided by using the command line
10
+ interface of ClamAV, and as such Littleneck expects a working and up to date
11
+ ClamAV installation.
12
+
13
+ Requirements
14
+ ============
15
+
16
+ It is currently fully tested against:
17
+
18
+ - Ruby 1.9.3
19
+
20
+ Usage
21
+ =====
22
+
23
+ To scan a file:
24
+
25
+ scanner = LittleneckClamAV.new
26
+ result = scanner.scan "jeepers.txt"
27
+ result.clean?
28
+ => false
29
+ result.infected?
30
+ => true
31
+ result.description
32
+ => "HLLP.Creeper.5127"
33
+ result.path
34
+ => "jeepers.txt"
35
+
36
+ To find out about the scanners database
37
+
38
+ scanner.database_version
39
+ => 15306
40
+ scanner.database_date
41
+ => 2012-08-28 20:18:12 +00:00
42
+ scanner.engine
43
+ => "0.97.5"
44
+
45
+ You can specify a specific scanner to use as well
46
+
47
+ scanner = LittleneckClamAV::Clam.new
48
+ # or
49
+ scanner = LittleneckClamAV::Clamd.new
50
+
@@ -0,0 +1,81 @@
1
+ require "cocaine"
2
+ require "time"
3
+
4
+ require_relative "result"
5
+ require_relative "error"
6
+
7
+ class LittleneckClamAV
8
+
9
+ class Clam
10
+
11
+ def engine
12
+ version[:engine] if available?
13
+ end
14
+
15
+ def database_version
16
+ version[:database_version].to_i if available?
17
+ end
18
+
19
+ def database_date
20
+ Time.parse(version[:database_date]) if available?
21
+ end
22
+
23
+ def available?
24
+ version[:success]
25
+ end
26
+
27
+ def scan(path)
28
+ check_scan! path
29
+ opts = { :swallow_stderr => true, :expected_outcodes => [0, 1] }
30
+ params = ["--no-summary", path].join(" ")
31
+ output = Cocaine::CommandLine.new( command, params, opts ).run
32
+ parse_result path, output, $?.exitstatus
33
+ end
34
+
35
+ def command
36
+ "clamscan"
37
+ end
38
+
39
+ private
40
+
41
+ def version
42
+ @version ||= begin
43
+ opts = { :swallow_stderr => true }
44
+ params = "--version"
45
+ output = Cocaine::CommandLine.new( command, params, opts ).run
46
+ output.strip!
47
+ engine, db_version, db_date = output.sub(/^ClamAV /,'').split("/",3)
48
+ success = !!(db_version && db_date)
49
+ {
50
+ :output => output,
51
+ :engine => engine,
52
+ :database_version => db_version,
53
+ :database_date => db_date,
54
+ :success => success
55
+ }
56
+ rescue Cocaine::ExitStatusError, Cocaine::CommandNotFoundError => e
57
+ {:error => e.message, :success => false }
58
+ end
59
+ end
60
+
61
+ def parse_result(path, output, code)
62
+ clean = $?.exitstatus == 0
63
+ description = output.split(":").last.strip
64
+ description.sub! " FOUND", ""
65
+ Result.new :path => path, :clean => clean, :description => description
66
+ end
67
+
68
+ def check_scan!(path)
69
+ exists = File.exists? path
70
+ if !exists
71
+ raise Error, "the path #{path} does not exist"
72
+ elsif !available?
73
+ message = "#{self.class} is not available"
74
+ message << "because #{version[:message]}" if version[:message]
75
+ raise Error, message
76
+ end
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,13 @@
1
+ require_relative "clam"
2
+
3
+ class LittleneckClamAV
4
+
5
+ class Clamd < Clam
6
+
7
+ def command
8
+ "clamdscan"
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,5 @@
1
+ class LittleneckClamAV
2
+
3
+ class Error < StandardError; end
4
+
5
+ end
@@ -0,0 +1,29 @@
1
+ class LittleneckClamAV
2
+
3
+ class Result
4
+
5
+ def initialize(opts)
6
+ @path = opts[:path]
7
+ @clean = opts[:clean]
8
+ @description = opts[:description]
9
+ end
10
+
11
+ def path
12
+ @path
13
+ end
14
+
15
+ def clean?
16
+ @clean
17
+ end
18
+
19
+ def infected?
20
+ !@clean
21
+ end
22
+
23
+ def description
24
+ @description
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,45 @@
1
+ require_relative "littleneck_clamav/clam"
2
+ require_relative "littleneck_clamav/clamd"
3
+ require_relative "littleneck_clamav/error"
4
+
5
+ class LittleneckClamAV
6
+
7
+ def engine
8
+ scanner.engine
9
+ end
10
+
11
+ def database_version
12
+ scanner.database_version
13
+ end
14
+
15
+ def database_date
16
+ scanner.database_date
17
+ end
18
+
19
+ def available?
20
+ !!scanner
21
+ end
22
+
23
+ def scan(*args)
24
+ scanner.scan(*args)
25
+ end
26
+
27
+ def scanner
28
+ @scanner ||= begin
29
+ scanner = scanners.find{|s| s.available? }
30
+ raise Error, "no scanner available, is ClamAV installed?" unless scanner
31
+ scanner
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def preference
38
+ [Clamd, Clam]
39
+ end
40
+
41
+ def scanners
42
+ @scanners ||= preference.map(&:new)
43
+ end
44
+
45
+ end
data/spec/clam_spec.rb ADDED
@@ -0,0 +1,19 @@
1
+ require "spec_helper"
2
+
3
+ describe LittleneckClamAV::Clam do
4
+
5
+ it_behaves_like "a scanner"
6
+
7
+ describe "instance method" do
8
+
9
+ describe "command" do
10
+
11
+ it "should return clamdscan" do
12
+ subject.command.should == "clamscan"
13
+ end
14
+
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,17 @@
1
+ describe LittleneckClamAV::Clamd do
2
+
3
+ it_behaves_like "a scanner"
4
+
5
+ describe "instance method" do
6
+
7
+ describe "command" do
8
+
9
+ it "should return clamdscan" do
10
+ subject.command.should == "clamdscan"
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+
17
+ end
@@ -0,0 +1,97 @@
1
+ require "spec_helper"
2
+
3
+ describe LittleneckClamAV do
4
+
5
+ describe "instance method" do
6
+
7
+ describe "engine" do
8
+
9
+ it "should call `engine` on the scanner" do
10
+ scanner = mock("scanner")
11
+ scanner.should_receive :engine
12
+ subject.stub( :scanner => scanner )
13
+
14
+ subject.engine
15
+ end
16
+
17
+ end
18
+
19
+ describe "database_version" do
20
+
21
+ it "should call `database_version` on the scanner" do
22
+ scanner = mock("scanner")
23
+ scanner.should_receive :database_version
24
+ subject.stub( :scanner => scanner )
25
+
26
+ subject.database_version
27
+ end
28
+
29
+ end
30
+
31
+ describe "database_date" do
32
+
33
+ it "should call `database_date` on the scanner" do
34
+ scanner = mock("scanner")
35
+ scanner.should_receive :database_date
36
+ subject.stub( :scanner => scanner )
37
+
38
+ subject.database_date
39
+ end
40
+
41
+ end
42
+
43
+ describe "available?" do
44
+
45
+ it "should return true if a scanner is available" do
46
+ subject.stub( :scanner => Object.new )
47
+
48
+ subject.available?.should == true
49
+ end
50
+
51
+ it "should return false if a scanner is not available" do
52
+ subject.stub( :scanner => nil )
53
+
54
+ subject.available?.should == false
55
+ end
56
+
57
+ end
58
+
59
+ describe "scan" do
60
+
61
+ it "should call `scan` on the scanner" do
62
+ scanner = mock("scanner")
63
+ scanner.should_receive :scan
64
+ subject.stub( :scanner => scanner )
65
+
66
+ subject.scan
67
+ end
68
+
69
+ end
70
+
71
+ describe "scanner" do
72
+
73
+ it "should return Clamd if available" do
74
+ LittleneckClamAV::Clamd.any_instance.stub(:available? => true)
75
+
76
+ subject.scanner.should be_a LittleneckClamAV::Clamd
77
+ end
78
+
79
+ it "should return Clam if Clamd is not available" do
80
+ LittleneckClamAV::Clamd.any_instance.stub(:available? => false)
81
+ LittleneckClamAV::Clam.any_instance.stub(:available? => true)
82
+
83
+ subject.scanner.should be_a LittleneckClamAV::Clam
84
+ end
85
+
86
+ it "should raise an error if neither are available" do
87
+ LittleneckClamAV::Clamd.any_instance.stub(:available? => false)
88
+ LittleneckClamAV::Clam.any_instance.stub(:available? => false)
89
+
90
+ lambda { subject.scanner }.should raise_error( LittleneckClamAV::Error )
91
+ end
92
+
93
+ end
94
+
95
+ end
96
+
97
+ end
@@ -0,0 +1,55 @@
1
+ require "spec_helper"
2
+
3
+ describe LittleneckClamAV::Result do
4
+
5
+ describe "instance method" do
6
+
7
+ describe "infected?" do
8
+
9
+ it "should return true when infected" do
10
+ result = result_factory :clean => false
11
+ result.infected?.should be_true
12
+ end
13
+
14
+ it "should return false when clean" do
15
+ result = result_factory :clean => true
16
+ result.infected?.should be_false
17
+ end
18
+
19
+ end
20
+
21
+ describe "clean?" do
22
+
23
+ it "should return true when clean" do
24
+ result = result_factory :clean => true
25
+ result.clean?.should be_true
26
+ end
27
+
28
+ it "should return false when infected" do
29
+ result = result_factory :clean => false
30
+ result.clean?.should be_false
31
+ end
32
+
33
+ end
34
+
35
+ describe "description" do
36
+
37
+ it "should return the description passed in" do
38
+ result = result_factory :description => "Hello"
39
+ result.description.should == "Hello"
40
+ end
41
+
42
+ end
43
+
44
+ describe "path" do
45
+
46
+ it "should return the path passed in" do
47
+ result = result_factory :path => "foo/bar"
48
+ result.path.should == "foo/bar"
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,11 @@
1
+ $:.unshift File.join(File.dirname(File.dirname(__FILE__)),'lib')
2
+
3
+ require "littleneck_clamav"
4
+ require "rspec/mocks/any_instance"
5
+
6
+ Dir[File.join(File.dirname(__FILE__),'support','*.rb')].each{|f| require f }
7
+
8
+ RSpec.configure do |config|
9
+ config.expect_with :rspec
10
+ config.mock_with :rspec
11
+ end
@@ -0,0 +1,9 @@
1
+ def result_factory(override = {})
2
+ options = {
3
+ :success => true,
4
+ :path => "foo.txt",
5
+ :description => "OK"
6
+ }.merge( override )
7
+
8
+ LittleneckClamAV::Result.new options
9
+ end
@@ -0,0 +1,131 @@
1
+ shared_examples_for "a scanner" do
2
+
3
+ def mock_cocaine(cocaine_options={})
4
+ options = { :output => "", :exitvalue => 0 }.merge( cocaine_options )
5
+ mock = Cocaine::CommandLine.should_receive( :new )
6
+ mock = mock.with(
7
+ options[:cmd], options[:opts], options[:params]
8
+ ) if options[:cmd] || options[:opts] || options[:params]
9
+
10
+ if cocaine_options[:raise]
11
+ mock.and_raise cocaine_options[:raise]
12
+ else
13
+ mock.and_return(
14
+ double "cocaine command", :run => options[:output]
15
+ )
16
+ end
17
+
18
+ `true` if options[:exitvalue] == 0
19
+ `false` if options[:exitvalue] == 1
20
+ end
21
+
22
+ describe "instance method" do
23
+
24
+ describe "engine" do
25
+
26
+ it "should call Cocaine with correct parameters" do
27
+ mock_cocaine :cmd => subject.command, :opts => "--version", :params => { :swallow_stderr => true }
28
+ subject.engine
29
+ end
30
+
31
+ it "should interpret the return value correctly" do
32
+ mock_cocaine :output => "ClamAV 0.97.5/15306/Tue Aug 28 20:18:12 2012\n"
33
+ subject.engine.should == "0.97.5"
34
+ end
35
+
36
+ end
37
+
38
+ describe "database_version" do
39
+
40
+ it "should call Cocaine with correct parameters" do
41
+ mock_cocaine :cmd => subject.command, :opts => "--version", :params => { :swallow_stderr => true }
42
+ subject.database_version
43
+ end
44
+
45
+ it "should interpret the return value correctly" do
46
+ mock_cocaine :output => "ClamAV 0.97.5/15306/Tue Aug 28 20:18:12 2012\n"
47
+ subject.database_version.should == 15306
48
+ end
49
+
50
+ end
51
+
52
+ describe "database_version" do
53
+
54
+ it "should call Cocaine with correct parameters" do
55
+ mock_cocaine :cmd => subject.command, :opts => "--version", :params => { :swallow_stderr => true }
56
+ subject.database_date
57
+ end
58
+
59
+ it "should interpret the return value correctly" do
60
+ mock_cocaine :output => "ClamAV 0.97.5/15306/Tue Aug 28 20:18:12 2012\n"
61
+ subject.database_date.should == Time.parse("Tue Aug 28 20:18:12 2012")
62
+ end
63
+
64
+ end
65
+
66
+ describe "available?" do
67
+
68
+ it "should return true when Cocaine returns okay" do
69
+ mock_cocaine :output => "ClamAV 0.97.5/15306/Tue Aug 28 20:18:12 2012\n"
70
+
71
+ subject.available?.should == true
72
+ end
73
+
74
+ it "should return false when command errors" do
75
+ mock_cocaine :raise => Cocaine::ExitStatusError.new( "oh noes" )
76
+
77
+ subject.available?.should == false
78
+ end
79
+
80
+ it "should return false when command is not found" do
81
+ mock_cocaine :raise => Cocaine::CommandNotFoundError
82
+
83
+ subject.available?.should == false
84
+ end
85
+
86
+ end
87
+
88
+ describe "scan" do
89
+
90
+ it "should call Cocaine" do
91
+ file = __FILE__
92
+
93
+ subject.stub(:available?).and_return(true)
94
+
95
+ mock_cocaine :cmd => subject.command,
96
+ :opts => "--no-summary #{file}",
97
+ :params => { :swallow_stderr => true, :expected_outcodes => [0, 1] },
98
+ :output => "#{file}: OK"
99
+
100
+ subject.scan file
101
+ end
102
+
103
+ it "should create a Result" do
104
+ file = __FILE__
105
+
106
+ subject.stub(:available?).and_return(true)
107
+
108
+ mock_cocaine :output => "#{file}: OK\n"
109
+
110
+ LittleneckClamAV::Result.should_receive(:new).with(:path => file, :clean => true, :description => "OK")
111
+
112
+ subject.scan file
113
+ end
114
+
115
+ it "should raise an error if the path does not exist" do
116
+ lambda { subject.scan "foo" }.should raise_error( LittleneckClamAV::Error )
117
+ end
118
+
119
+ it "should raise an error if it is not available" do
120
+ file = __FILE__
121
+
122
+ subject.stub(:available?).and_return(false)
123
+
124
+ lambda { subject.scan "foo" }.should raise_error( LittleneckClamAV::Error )
125
+ end
126
+
127
+ end
128
+
129
+ end
130
+
131
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: littleneck_clamav
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Theo Cushion
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cocaine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.11'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.11'
62
+ description:
63
+ email: theo.c@zepler.net
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files: []
67
+ files:
68
+ - lib/littleneck_clamav/clam.rb
69
+ - lib/littleneck_clamav/clamd.rb
70
+ - lib/littleneck_clamav/error.rb
71
+ - lib/littleneck_clamav/result.rb
72
+ - lib/littleneck_clamav.rb
73
+ - LICENSE
74
+ - README.md
75
+ - spec/clam_spec.rb
76
+ - spec/clamd_spec.rb
77
+ - spec/littleneck_clamav_spec.rb
78
+ - spec/result_spec.rb
79
+ - spec/spec_helper.rb
80
+ - spec/support/factories.rb
81
+ - spec/support/shared_examples.rb
82
+ - .rspec
83
+ homepage: http://github.com/theozaurus/littleneck_clamav
84
+ licenses:
85
+ - MIT
86
+ post_install_message:
87
+ rdoc_options: []
88
+ require_paths:
89
+ - lib
90
+ required_ruby_version: !ruby/object:Gem::Requirement
91
+ none: false
92
+ requirements:
93
+ - - ! '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ segments:
97
+ - 0
98
+ hash: -4199699967230361114
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: 1.3.6
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 1.8.24
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: A thin wrapper to make it quick and easy to use ClamAV (daemonised or not)
111
+ within Ruby
112
+ test_files:
113
+ - spec/clam_spec.rb
114
+ - spec/clamd_spec.rb
115
+ - spec/littleneck_clamav_spec.rb
116
+ - spec/result_spec.rb
117
+ - spec/spec_helper.rb
118
+ - spec/support/factories.rb
119
+ - spec/support/shared_examples.rb
120
+ - .rspec