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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ef3bca447dda61607d61ce407a66fe8a87cd1b0e
4
- data.tar.gz: f7c01b0c3cc710864aa86ecb7a02a89dbae82f0a
3
+ metadata.gz: b8b106f508db192aa4466c1a3d02026eff07f26f
4
+ data.tar.gz: 41e9c4da741cba423929530e55bd010af0f4cbf8
5
5
  SHA512:
6
- metadata.gz: 985594e081de4084d684ac4d6261a1995759bd6f3e9626a31489248c5f6c0f8c8f23ea83139dd1ca882aa50e1f1af6178661aa63bc7fdd0b0bcc82a05179459b
7
- data.tar.gz: 4cdf9e857fa9ae41531f8322e0a1a8dc1dab5db2d658cfa89fd4c1e6bcb82af371f681a403a6523c415e762c3a25fe916887d451aa3a94d71591f352f07f8bae
6
+ metadata.gz: 4abbce86ee4991ab4f081741eb897fa73830b70b0ff5c654e6bb71a3f7792eb18b58c459078a9472d3fcea4b18a80abb42a2da99f56168e54d9302162d06972b
7
+ data.tar.gz: abffaaa71c84dae9b6d547ec1c351f23353060a97008c05e65e0fd7063a967aa59c9ffe762bb32355bc5a26da1fc1d7dd8ee4931c68d06cbf0e944a96e830c16
@@ -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
@@ -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
@@ -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
@@ -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.1
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-18 00:00:00.000000000 Z
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
- - lib/metascan.rb
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: []
@@ -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