metascan 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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