metascan 0.0.1 → 0.1.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 +4 -4
- data/lib/batch.rb +59 -0
- data/lib/client.rb +60 -0
- data/lib/scan.rb +86 -0
- data/metascan.rb +18 -0
- metadata +22 -5
- data/lib/metascan.rb +0 -119
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b8b106f508db192aa4466c1a3d02026eff07f26f
|
4
|
+
data.tar.gz: 41e9c4da741cba423929530e55bd010af0f4cbf8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4abbce86ee4991ab4f081741eb897fa73830b70b0ff5c654e6bb71a3f7792eb18b58c459078a9472d3fcea4b18a80abb42a2da99f56168e54d9302162d06972b
|
7
|
+
data.tar.gz: abffaaa71c84dae9b6d547ec1c351f23353060a97008c05e65e0fd7063a967aa59c9ffe762bb32355bc5a26da1fc1d7dd8ee4931c68d06cbf0e944a96e830c16
|
data/lib/batch.rb
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
module Metascan
|
2
|
+
|
3
|
+
require_relative 'scan'
|
4
|
+
require_relative 'client'
|
5
|
+
|
6
|
+
# A batch of scanned files. Exposes similar methods to Scan, but
|
7
|
+
# for a group of files.
|
8
|
+
class Batch
|
9
|
+
|
10
|
+
# @scans is a hash like so:
|
11
|
+
# {
|
12
|
+
# filename (string) => <Metascan::Scan>,
|
13
|
+
# filename (string) => <Metascan::Scan>,
|
14
|
+
# ...
|
15
|
+
# }
|
16
|
+
attr_accessor :scans
|
17
|
+
|
18
|
+
def initialize(hydra)
|
19
|
+
@scans = {}
|
20
|
+
@hydra = hydra
|
21
|
+
end
|
22
|
+
|
23
|
+
# Add a scan to my scans, man.
|
24
|
+
def add(scan)
|
25
|
+
unless scan.kind_of? Metascan::Scan
|
26
|
+
raise TypeError, "Must pass a Scan object to Batch.add"
|
27
|
+
end
|
28
|
+
@scans = @scans.merge({ scan.filename => scan })
|
29
|
+
@hydra.queue scan.request
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return true iff all my scans are clean.
|
33
|
+
def clean?
|
34
|
+
@scans.map{ |id, s| s.clean? poll: true }.inject{ |s1, s2| s1 && s2 }
|
35
|
+
end
|
36
|
+
|
37
|
+
# Return a list of all the dirty scans in my batch.
|
38
|
+
def dirty
|
39
|
+
@scans.select{ |id, s| !(s.clean? poll: true) }.map{ |id, s| s }
|
40
|
+
end
|
41
|
+
|
42
|
+
def run
|
43
|
+
@hydra.run
|
44
|
+
end
|
45
|
+
|
46
|
+
# Retrieve results for all the Scans in my batch.
|
47
|
+
# Uses the hydra for parallel processing.
|
48
|
+
# Call this AFTER calling #run.
|
49
|
+
def retrieve_results
|
50
|
+
results_hydra = Typhoeus::Hydra.new
|
51
|
+
@scans.each do |id, s|
|
52
|
+
results_hydra.queue(s.retrieve_results)
|
53
|
+
end
|
54
|
+
results_hydra.run
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
data/lib/client.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Metascan
|
2
|
+
|
3
|
+
require 'typhoeus'
|
4
|
+
|
5
|
+
# The Client object, which stores an API key and has a (currently not used)
|
6
|
+
# Typhoeus::Hydra for when you have a lot of requests to make at once.
|
7
|
+
class Client
|
8
|
+
# An API key is required. Free at www.metascan-online.com
|
9
|
+
def initialize(api_key)
|
10
|
+
@api_key = api_key
|
11
|
+
@hydra = Typhoeus::Hydra.hydra
|
12
|
+
end
|
13
|
+
|
14
|
+
def api_key
|
15
|
+
@api_key
|
16
|
+
end
|
17
|
+
|
18
|
+
# A Typhoeus Hydra manages parallel HTTP requests.
|
19
|
+
def hydra
|
20
|
+
if !@hydra
|
21
|
+
@hydra = Typhoeus::Hydra.hydra
|
22
|
+
end
|
23
|
+
@hydra
|
24
|
+
end
|
25
|
+
|
26
|
+
# Returns a Scan object
|
27
|
+
# Sample usage:
|
28
|
+
#
|
29
|
+
# scanner = Metascan::Client.new(MY_API_KEY)
|
30
|
+
# filename = "/etc/unwise-backups/passwd.rar" # FULLY QUALIFIED
|
31
|
+
# scanner.scan_file(filename, archivepwd: "the eagle has left the nest")
|
32
|
+
# => <Metascan::Scan ... >
|
33
|
+
#
|
34
|
+
# https://www.metascan-online.com/en/public-api
|
35
|
+
def scan_file(filename, archivepwd: nil)
|
36
|
+
scan = Metascan::Scan.new(filename, self)
|
37
|
+
scan.run
|
38
|
+
scan
|
39
|
+
end
|
40
|
+
|
41
|
+
# Scan a batch of files by processing the requests in parallel with
|
42
|
+
# Typhoeus::Hydra. Returns a Metascan::Batch object, which can iterate
|
43
|
+
# over the set of finished scans.
|
44
|
+
# Sample usage:
|
45
|
+
#
|
46
|
+
# scanner = Metascan::Client.new(MY_API_KEY)
|
47
|
+
# filenames = ["/etc/passwd", "/dev/sda0", "/dev/random.tgz"] # don't try this
|
48
|
+
# scanner.scan_batch(filenames, archivepwds: { "/dev/random.tgz" => "hunter2" })
|
49
|
+
# => <Metascan::Batch ...>
|
50
|
+
def scan_batch(filenames, archivepwds: nil)
|
51
|
+
scans = Metascan::Batch.new(self.hydra)
|
52
|
+
filenames.each do |f|
|
53
|
+
scan = Metascan::Scan.new(f, self)
|
54
|
+
scans.add(scan)
|
55
|
+
end
|
56
|
+
scans.run
|
57
|
+
scans
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/scan.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
module Metascan
|
2
|
+
|
3
|
+
require 'typhoeus'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
# A single scan on the Metascan service.
|
7
|
+
# Initialized with the parameters to scan,
|
8
|
+
# exposes methods to inspect the scan results.
|
9
|
+
class Scan
|
10
|
+
attr_reader :data_id
|
11
|
+
attr_reader :filename
|
12
|
+
|
13
|
+
def initialize(filename, client, archivepwd: nil)
|
14
|
+
@filename = filename
|
15
|
+
@client = client
|
16
|
+
@archivepwd = archivepwd
|
17
|
+
end
|
18
|
+
|
19
|
+
# Initiate a scan of @filename
|
20
|
+
def run
|
21
|
+
self.request.run
|
22
|
+
end
|
23
|
+
|
24
|
+
# Construct and return the request I use, for the purpose of
|
25
|
+
# queueing in a Typhoeus::Hydra.
|
26
|
+
def request
|
27
|
+
request = Typhoeus::Request.new(
|
28
|
+
Metascan::PATHS[:scan_file],
|
29
|
+
headers: {
|
30
|
+
'filename' => @filename,
|
31
|
+
'archivepwd' => @archivepwd,
|
32
|
+
'apikey' => @client.api_key
|
33
|
+
}.select { |k, v| !v.nil? },
|
34
|
+
method: :post,
|
35
|
+
body: { file: File.open(@filename, "r") }
|
36
|
+
)
|
37
|
+
|
38
|
+
request.on_complete do |r|
|
39
|
+
@data_id = JSON.parse(r.body)["data_id"]
|
40
|
+
end
|
41
|
+
|
42
|
+
request
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns true iff the Metascan virus scan found no threats.
|
46
|
+
# If POLL is true (false by default) then retrieve_results first.
|
47
|
+
def clean?(poll: false)
|
48
|
+
self.results(poll: poll)["scan_results"]["scan_all_result_i"] == 0
|
49
|
+
end
|
50
|
+
|
51
|
+
# Only useful for testing.
|
52
|
+
def results=(results)
|
53
|
+
@results = results
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return the results of my scan.
|
57
|
+
# If the optional argument "poll" is set to true, then attempt
|
58
|
+
# to requery Metascan for the results before returning them.
|
59
|
+
def results(poll: false)
|
60
|
+
if poll and
|
61
|
+
(!@results or @results["scan_results"]["progress_percentage"] < 100) then
|
62
|
+
retrieve_results.run
|
63
|
+
end
|
64
|
+
@results
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns a HTTP request to retrieve the latest scan results for me.
|
68
|
+
# Fails if called before @data_id is set (when self.run is called, or
|
69
|
+
# my Batch runs me)
|
70
|
+
def retrieve_results
|
71
|
+
request = Typhoeus::Request.new(
|
72
|
+
Metascan::PATHS[:results_by_data_id] + @data_id,
|
73
|
+
headers: {
|
74
|
+
'apikey' => @client.api_key
|
75
|
+
},
|
76
|
+
method: :get
|
77
|
+
)
|
78
|
+
|
79
|
+
request.on_complete do |r|
|
80
|
+
@results = JSON.parse(r.body)
|
81
|
+
end
|
82
|
+
|
83
|
+
request
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
data/metascan.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
module Metascan
|
2
|
+
|
3
|
+
require 'typhoeus'
|
4
|
+
require 'json'
|
5
|
+
|
6
|
+
# Paths to use for api calls.
|
7
|
+
PATHS = {
|
8
|
+
:scan_file => "https://api.metascan-online.com/v1/file",
|
9
|
+
:results_by_data_id => "https://api.metascan-online.com/v1/file/",
|
10
|
+
:results_by_file_hash => "https://api.metascan-online.com/v1/hash/"
|
11
|
+
}
|
12
|
+
|
13
|
+
# a miserable pile of library classes!
|
14
|
+
require_relative 'lib/scan'
|
15
|
+
require_relative 'lib/client'
|
16
|
+
require_relative 'lib/batch'
|
17
|
+
|
18
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metascan
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Grayson Chao
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-04-
|
11
|
+
date: 2014-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -24,6 +24,20 @@ dependencies:
|
|
24
24
|
- - '>='
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: webmock
|
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'
|
27
41
|
- !ruby/object:Gem::Dependency
|
28
42
|
name: typhoeus
|
29
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -38,13 +52,16 @@ dependencies:
|
|
38
52
|
- - '>='
|
39
53
|
- !ruby/object:Gem::Version
|
40
54
|
version: 0.6.8
|
41
|
-
description: Allows scanning files using the Metascan public API. https://www.metascan-online.com/en/public-api
|
55
|
+
description: Allows virus scanning files using the Metascan public API. https://www.metascan-online.com/en/public-api
|
42
56
|
email: graysonchao@berkeley.edu
|
43
57
|
executables: []
|
44
58
|
extensions: []
|
45
59
|
extra_rdoc_files: []
|
46
60
|
files:
|
47
|
-
-
|
61
|
+
- metascan.rb
|
62
|
+
- lib/client.rb
|
63
|
+
- lib/batch.rb
|
64
|
+
- lib/scan.rb
|
48
65
|
homepage: http://rubygems.org/gems/metascan
|
49
66
|
licenses:
|
50
67
|
- MIT
|
@@ -68,5 +85,5 @@ rubyforge_project:
|
|
68
85
|
rubygems_version: 2.0.3
|
69
86
|
signing_key:
|
70
87
|
specification_version: 4
|
71
|
-
summary: Scan files using the Metascan public API.
|
88
|
+
summary: Scan files for viruses using the Metascan public API.
|
72
89
|
test_files: []
|
data/lib/metascan.rb
DELETED
@@ -1,119 +0,0 @@
|
|
1
|
-
module Metascan
|
2
|
-
|
3
|
-
require 'typhoeus'
|
4
|
-
require 'json'
|
5
|
-
|
6
|
-
# Constants like paths and stuff.
|
7
|
-
PATHS = {
|
8
|
-
:scan_file => "https://api.metascan-online.com/v1/file",
|
9
|
-
:results_by_data_id => "https://api.metascan-online.com/v1/file/",
|
10
|
-
:results_by_file_hash => "https://api.metascan-online.com/v1/hash/"
|
11
|
-
}
|
12
|
-
|
13
|
-
# A single scan on the Metascan service.
|
14
|
-
# Initialized with the parameters to scan,
|
15
|
-
# exposes methods to inspect the scan results.
|
16
|
-
class Scan
|
17
|
-
def initialize(filename, client, archivepwd: nil)
|
18
|
-
@filename = filename
|
19
|
-
@client = client
|
20
|
-
@archivepwd = archivepwd
|
21
|
-
end
|
22
|
-
|
23
|
-
# Initiate a scan of @filename
|
24
|
-
def run
|
25
|
-
request = Typhoeus::Request.new(
|
26
|
-
Metascan::PATHS[:scan_file],
|
27
|
-
headers: {
|
28
|
-
'filename' => @filename,
|
29
|
-
'archivepwd' => @archivepwd,
|
30
|
-
'apikey' => @client.api_key
|
31
|
-
}.select { |k, v| !v.nil? },
|
32
|
-
method: :post,
|
33
|
-
body: { file: File.open(@filename, "r") }
|
34
|
-
)
|
35
|
-
|
36
|
-
request.on_complete do |r|
|
37
|
-
@data_id = JSON.parse(r.body)["data_id"]
|
38
|
-
retrieve_results
|
39
|
-
end
|
40
|
-
|
41
|
-
request.run
|
42
|
-
end
|
43
|
-
|
44
|
-
# Is my file clean?
|
45
|
-
def clean?
|
46
|
-
self.results["scan_results"]["scan_all_result_i"] == 0
|
47
|
-
end
|
48
|
-
|
49
|
-
# Only useful for testing.
|
50
|
-
def results=(results)
|
51
|
-
@results = results
|
52
|
-
end
|
53
|
-
|
54
|
-
# Return the results of my scan.
|
55
|
-
# If the optional argument "poll" is set to true, then attempt
|
56
|
-
# to requery Metascan for the results before returning them.
|
57
|
-
def results(poll: true)
|
58
|
-
if !@results or
|
59
|
-
(poll and @results["scan_results"]["progress_percentage"] < 100) then
|
60
|
-
@results = retrieve_results
|
61
|
-
end
|
62
|
-
@results
|
63
|
-
end
|
64
|
-
|
65
|
-
def data_id
|
66
|
-
@data_id
|
67
|
-
end
|
68
|
-
|
69
|
-
def retrieve_results
|
70
|
-
request = Typhoeus::Request.new(
|
71
|
-
Metascan::PATHS[:results_by_data_id] + @data_id,
|
72
|
-
headers: {
|
73
|
-
'apikey' => @client.api_key
|
74
|
-
},
|
75
|
-
method: :get
|
76
|
-
)
|
77
|
-
|
78
|
-
response = request.run
|
79
|
-
JSON.parse(response.body)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# The Client object, which stores an API key and has a (currently not used)
|
84
|
-
# Typhoeus::Hydra for when you have a lot of requests to make at once.
|
85
|
-
class Client
|
86
|
-
# An API key is required. Free at www.metascan-online.com
|
87
|
-
def initialize(api_key)
|
88
|
-
@api_key = api_key
|
89
|
-
@hydra = Typhoeus::Hydra.hydra
|
90
|
-
end
|
91
|
-
|
92
|
-
def api_key
|
93
|
-
@api_key
|
94
|
-
end
|
95
|
-
|
96
|
-
# A Typhoeus Hydra manages parallel HTTP requests.
|
97
|
-
def hydra
|
98
|
-
if !@hydra
|
99
|
-
@hydra = Typhoeus::Hydra.hydra
|
100
|
-
end
|
101
|
-
@hydra
|
102
|
-
end
|
103
|
-
|
104
|
-
# Returns a Scan object
|
105
|
-
# Sample usage:
|
106
|
-
#
|
107
|
-
# scanner = Metascan::Client.new(MY_API_KEY)
|
108
|
-
# filename = "/etc/unwise-backups/passwd.rar" # FULLY QUALIFIED
|
109
|
-
# scanner.scan_file(filename, archivepwd: "the eagle has left the nest")
|
110
|
-
# => <Metascan::Scan ... >
|
111
|
-
#
|
112
|
-
# https://www.metascan-online.com/en/public-api
|
113
|
-
def scan_file(filename, archivepwd: nil)
|
114
|
-
scan = Metascan::Scan.new(filename, self)
|
115
|
-
scan.run
|
116
|
-
scan
|
117
|
-
end
|
118
|
-
end
|
119
|
-
end
|