virustotal 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ Copyright (c) 2010-2011 Jacob Hammack, Hammackj LLC
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright
10
+ notice, this list of conditions and the following disclaimer in the
11
+ documentation and/or other materials provided with the distribution.
12
+ * Neither the name of the Jacob Hammack or Hammackj LLC nor the
13
+ names of its contributors may be used to endorse or promote products
14
+ derived from this software without specific prior written permission.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL JACOB HAMMACK or HAMMACKJ LLC BE LIABLE FOR ANY
20
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,30 @@
1
+ # News
2
+
3
+ # 2.0.0 (February 20, 2011)
4
+ - Converted the entire thing into a rubygem
5
+ - Cleaned up the code significantly
6
+ - Added a config file to store the api-key in
7
+ - fixed up the command line options some
8
+ - Optimized execution some
9
+
10
+ # 1.0.0 (September 27, 2010)
11
+ - Cleaned up some dead code
12
+ - Added URL for the report for easier web viewing.
13
+
14
+ # 0.0.5 (September 11, 2010)
15
+ - Added a url scan option -w, so that urls can be scanned using the new api
16
+ - Added file upload option -u, so that files can be uploaded and waits for results
17
+
18
+ # 0.0.4 (September 10, 2010)
19
+ - Modified to use the new virustotal.com API, the code has been simplified.
20
+
21
+ # 0.0.3 (June 10, 2010)
22
+ - Added a timer between hash lookups from files
23
+ - Added a check for the invalid hash error that seems to happen on some MD5 hashes
24
+ - Added debug output
25
+
26
+ # 0.0.2 (January 31, 2010)
27
+ - Updated a output bug on the usage statement. Thanks to smithj for finding it.
28
+
29
+ # 0.0.1 (July 11, 2009)
30
+ - Initial public release (BlackHat)
@@ -0,0 +1,42 @@
1
+ # ruby-virustotal
2
+
3
+ ruby-virustotal is [virustotal](http://www.virustotal.com) automation and convenience tool for hash, file and URL submission.
4
+
5
+ The current version is 2.0.
6
+
7
+ ## Requirements
8
+
9
+ * ruby
10
+ * rubygems
11
+ * json
12
+ * rest-client
13
+
14
+ * public api key from [virustotal.com](http://www.virustotal.com)
15
+
16
+ ## Installation
17
+
18
+ % gem install virustotal
19
+ % virustotal [options]
20
+
21
+ ## Usage
22
+
23
+ ### Searching a file of hashes
24
+
25
+ % ./virustotal.rb -f <file_with_hashes_one_per_line>
26
+
27
+ ### Searching a single hash
28
+
29
+ % ./virustotal.rb -h FD287794107630FA3116800E617466A9
30
+
31
+ ### Searching a file of hashes and outputting to XML
32
+ % ./virustotal.rb -f <file_with_hashes_one_per_line> -x
33
+
34
+ ### Upload a file to Virustotal and wait for analysis
35
+ % ./virustotal.rb -u </path/to/file>
36
+
37
+ ### Search for a single URL
38
+ % ./virustotal.rb -s "http://www.google.com"
39
+
40
+ ## Contact
41
+
42
+ You can reach me at Jacob[dot]Hammack[at]hammackj[dot]com or http://www.hammackj.com
@@ -0,0 +1,15 @@
1
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
2
+
3
+ require "virustotal"
4
+
5
+ task :build do
6
+ system "gem build #{VirusTotal::APP_NAME}.gemspec"
7
+ end
8
+
9
+ task :release => :build do
10
+ system "gem push #{VirusTotal::APP_NAME}-#{VirusTotal::VERSION}.gem"
11
+ end
12
+
13
+ task :clean do
14
+ system "rm *.gem"
15
+ end
@@ -0,0 +1,8 @@
1
+ # TODO
2
+
3
+ **Release dates are estimates, and features can be changed at any time.**
4
+
5
+ ## 2.0 (5/4/2011)
6
+ - Turn into a GEM
7
+ - clean up code
8
+ - add the ability to read from file for key
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+
4
+ # virustotal - a command line tool for working with virustotal.com
5
+ # Jacob Hammack <jacob.hammack@hammackj.com>
6
+ # http://www.hammackj.com
7
+ #
8
+ # hammackj - 01-05-2011 - Version 2.0.0
9
+ #
10
+
11
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '/../lib'))
12
+
13
+ require 'rubygems'
14
+ require 'virustotal'
15
+
16
+ app = VirusTotal::Application.new
17
+ app.run(ARGV)
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+
3
+ module VirusTotal
4
+ APP_NAME = "virustotal"
5
+ VERSION = "2.0.0"
6
+ CONFIG_FILE = "~/.virustotal"
7
+ end
8
+
9
+ require 'json'
10
+ require 'rest_client'
11
+ require 'optparse'
12
+ require 'yaml'
13
+
14
+ require 'virustotal/application'
15
+ require 'virustotal/virustotal'
16
+ require 'virustotal/virustotalresult'
@@ -0,0 +1,184 @@
1
+ # encoding: utf-8
2
+
3
+ module VirusTotal
4
+ class Application
5
+
6
+ # Creates a new instance of the [Application] class
7
+ #
8
+ def initialize
9
+ @options = {}
10
+ @config = {}
11
+ @hashes = Array.new
12
+ @files_of_hashes = Array.new
13
+ @sites = Array.new
14
+ @uploads = Array.new
15
+ end
16
+
17
+ # Parses the command the line options and returns the parsed options hash
18
+ #
19
+ # @return [Hash] of the parsed options
20
+ def parse_options(args)
21
+ begin
22
+ @options['output'] = :stdout
23
+ @options['debug'] = false
24
+
25
+ opt = OptionParser.new do |opt|
26
+ opt.banner = "#{APP_NAME} v#{VERSION}\nJacob Hammack\nhttp://www.hammackj.com\n\n"
27
+ opt.banner << "Usage: #{APP_NAME} <options>"
28
+ opt.separator('')
29
+ opt.separator("Search Options")
30
+
31
+ opt.on('-h HASH', '--search-hash HASH', 'Searches a single hash on virustotal.com') { |hash|
32
+ @hashes.push(hash)
33
+ }
34
+
35
+ opt.on('-f FILE', '--search-file FILE', 'Searches a each hash in a file of hashes on virustotal.com') { |file|
36
+ if File.exists?(file)
37
+ puts "[+] Adding file #{file}" if @options["debug"]
38
+ @files_of_hashes.push(file)
39
+ else
40
+ puts "[!] #{file} does not exist, please check your input!\n"
41
+ end
42
+ }
43
+
44
+ opt.on('-u FILE', '--upload-file FILE', 'Uploads a file to virustotal.com for analysis') do |file|
45
+ if File.exists?(file)
46
+ puts "[+] Adding file #{file}" if @options["debug"]
47
+ @uploads.push(file)
48
+ else
49
+ puts "[!] #{file} does not exist, please check your input!\n"
50
+ end
51
+ end
52
+
53
+ opt.on('-s SITE', '--search-site SITE', 'Searches for a single url on virustotal.com') { |site|
54
+ @sites.push(site)
55
+ }
56
+
57
+ opt.separator('')
58
+ opt.separator('Output Options')
59
+
60
+ opt.on('-x', '--xml-output', 'Print results as xml to stdout') {
61
+ @options["output"] = :xml
62
+ }
63
+
64
+ opt.on('-y', '--yaml-output', 'Print results as yaml to stdout') {
65
+ @options['output'] = :yaml
66
+ }
67
+
68
+ opt.on('--stdout-output', 'Print results as normal text line to stdout, this is default') {
69
+ @options['output'] = :stdout
70
+ }
71
+
72
+ opt.separator ''
73
+ opt.separator 'Advanced Options'
74
+
75
+ opt.on('-c', '--create-config', 'Creates a skeleton config file to use') do
76
+ if File.exists?(File.expand_path(CONFIG_FILE)) == false
77
+ File.open(File.expand_path(CONFIG_FILE), 'w+') do |f|
78
+ f.write("virustotal: \n api-key: \n timeout: 10\n\n")
79
+ end
80
+
81
+ puts "[*] An empty #{File.expand_path(CONFIG_FILE)} has been created. Please edit and fill in the correct values."
82
+ exit
83
+ else
84
+ puts "[!] #{File.expand_path(CONFIG_FILE)} already exists. Please delete it if you wish to re-create it."
85
+ exit
86
+ end
87
+ end
88
+
89
+ opt.on('-d', '--debug', 'Print verbose debug information') do |d|
90
+ @options["debug"] = d
91
+ end
92
+
93
+ opt.separator ''
94
+ opt.separator 'Other Options'
95
+
96
+ opt.on('-v', '--version', "Shows application version information") do
97
+ puts "#{APP_NAME} - #{VERSION}"
98
+ exit
99
+ end
100
+
101
+ opt.on_tail("-?", "--help", "Show this message") { |help|
102
+ puts opt.to_s + "\n"
103
+ exit
104
+ }
105
+ end
106
+
107
+ if ARGV.length != 0
108
+ opt.parse!
109
+ else
110
+ puts opt.to_s + "\n"
111
+ exit
112
+ end
113
+ rescue OptionParser::MissingArgument => m
114
+ puts opt.to_s + "\n"
115
+ exit
116
+ end
117
+ end
118
+
119
+ # Loads the .virustotal config file for the api key
120
+ #
121
+ def load_config
122
+ if File.exists?(File.expand_path(CONFIG_FILE))
123
+ @config = YAML.load_file File.expand_path(CONFIG_FILE)
124
+ else
125
+ puts "[!] #{CONFIG_FILE} does not exist. Please run virustotal --create-config, to create it."
126
+ exit
127
+ end
128
+ end
129
+
130
+ # Processes all of the command line arguments and displays the results
131
+ #
132
+ def run(args)
133
+ parse_options(args)
134
+ load_config
135
+
136
+ vt = VirusTotal.new(@config["virustotal"]["api-key"], @config["virustotal"]["timeout"], @options["debug"])
137
+
138
+ if @options['output'] == :stdout
139
+ output_method = :to_stdout
140
+ elsif @options['output'] == :yaml
141
+ output_method = :to_yaml
142
+ elsif @options['output'] == :xml
143
+ output_method = :to_xml
144
+ print "<results>\n"
145
+ end
146
+
147
+ if @files_of_hashes != nil
148
+ @files_of_hashes.each do |file|
149
+ f = File.open(file, 'r')
150
+
151
+ f.each do |hash|
152
+ hash.chomp!
153
+ @hashes.push hash
154
+ end
155
+ end
156
+ end
157
+
158
+ if @hashes != nil
159
+ @hashes.each do |hash|
160
+ result = vt.query_hash hash
161
+ print result.send output_method
162
+ end
163
+ end
164
+
165
+ if @sites != nil
166
+ @sites.each do |site|
167
+ result = vt.query_site site
168
+ print result.send output_method
169
+ end
170
+ end
171
+
172
+ if @uploads != nil
173
+ @uploads.each do |upload|
174
+ result = vt.query_upload upload
175
+ print result.send output_method
176
+ end
177
+ end
178
+
179
+ if @options['output'] == :xml
180
+ print "</results>\n"
181
+ end
182
+ end
183
+ end
184
+ end
@@ -0,0 +1,104 @@
1
+ # encoding: utf-8
2
+
3
+ module VirusTotal
4
+ class VirusTotal
5
+
6
+ # Creates a new instance of the [VirusTotal] class
7
+ #
8
+ # @return [VirusTotal]
9
+ def initialize(api_key, timeout = 7, debug = false)
10
+ @api_key = api_key
11
+ @timeout = timeout.to_i
12
+ @debug = debug
13
+ end
14
+
15
+ # Queries a single hash on virustotal.com
16
+ #
17
+ # @return [VirusTotalResult] of the results from the query
18
+ def query_hash hash
19
+ begin
20
+ puts "[*] Querying hash #{hash}" if @debug
21
+ hash.chomp!
22
+ if hash.include?('-')
23
+ hash = hash.split('-')[0]
24
+ end
25
+
26
+ response = RestClient.post 'https://www.virustotal.com/api/get_file_report.json', { :resource => hash, :key => @api_key }
27
+ results = VirusTotalResult.new hash, :hash, JSON.parse(response)
28
+
29
+ return results
30
+ rescue Exception => e
31
+ puts e.message
32
+ puts e.backtrace.join("\n")
33
+ STDERR.puts "[!] An error has occured. Retrying #{hash} in #{@timeout} seconds.\n"
34
+ sleep @timeout #So we do not DOS virustotal.com we wait at least 5 seconds between each query
35
+ retry
36
+ end
37
+ end
38
+
39
+ # Queries a single url on virustotal.com
40
+ #
41
+ # @return [VirusTotalResult] of the results from the query
42
+ def query_site url
43
+ begin
44
+ puts "[*] Querying url #{url}" if @debug
45
+
46
+ response = RestClient.post 'https://www.virustotal.com/api/get_url_report.json', { :resource => url, :key => @api_key }
47
+ results = VirusTotalResult.new url, :site, JSON.parse(response)
48
+
49
+ return results
50
+ rescue Exception => e
51
+ puts e.message
52
+ puts e.backtrace.join("\n")
53
+ STDERR.puts "[!] An error has occured. Retrying #{url} in #{@timeout} seconds\n"
54
+ sleep @timeout #So we do not DOS virustotal.com we wait at least 5 seconds between each query
55
+ retry
56
+ end
57
+ end
58
+
59
+ # Fetch results from virustotal using a specific hash
60
+ #
61
+ def query_upload file
62
+ results = Array.new
63
+ file = file.chomp
64
+
65
+ begin
66
+ puts "[*] Attempting to upload file #{file}" if @debug
67
+
68
+ response = RestClient.post 'https://www.virustotal.com/api/scan_file.json', { :key => @api_key, :file => File.new(file, 'rb') }
69
+ result = JSON.parse(response)
70
+
71
+ puts "[*] File #{file} uploaded, waiting for results this could take several minutes..." if @debug
72
+
73
+ if result['result'] == 1
74
+ results = query_hash result['scan_id']
75
+
76
+ while results.results[0]['result'] == "Hash Not Found"
77
+ puts "[*] File has not been analyized yet, waiting 60 seconds to try again" if @debug
78
+ sleep 60
79
+ results = query_hash result['scan_id']
80
+ end
81
+ elsif result['result'] == -2
82
+ puts "[!] Virustotal limits exceeded, ***do not edit the time out values.***"
83
+ else
84
+ fres = Hash.new
85
+ fres['hash'] = file
86
+ fres['scanner'] = '-'
87
+ fres['version'] = '-'
88
+ fres['date'] = '-'
89
+ fres['result'] = "File failed to upload"
90
+
91
+ results.push fres
92
+ end
93
+ rescue Exception => e
94
+ puts e.message
95
+ puts e.backtrace.join("\n")
96
+ STDERR.puts "[!] An error has occured. Retrying #{file} in #{@timeout} seconds\n"
97
+ sleep @timeout #So we do not DOS virustotal.com we wait at least 5 seconds between each query
98
+ retry
99
+ end
100
+
101
+ return results
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,110 @@
1
+ # encoding: utf-8
2
+
3
+ module VirusTotal
4
+
5
+ # A Wrapper class for the results from a virustotal.com
6
+ #
7
+ # @author Jacob Hammack <jacob.hammack@hammackj.com>
8
+ class VirusTotalResult
9
+ attr_accessor :results
10
+
11
+ # Creates a new instance of the [VirusTotalResult] class
12
+ #
13
+ def initialize hash, type, result
14
+ @type = type
15
+ @results = Array.new
16
+ fres = Hash.new
17
+
18
+ if result["result"] == 0
19
+
20
+ fres = Hash.new
21
+ fres['hash'] = hash
22
+ fres['scanner'] = '-'
23
+ fres['date'] = '-'
24
+ fres['permalink'] = '-'
25
+
26
+ if @type == :hash
27
+ fres['result'] = "Hash Not Found"
28
+ elsif @type == :site
29
+ fres['result'] = "Site Not Found"
30
+ end
31
+
32
+ @results.push fres
33
+ elsif result["result"] == -1
34
+ puts "[!] Invalid API KEY! Please correct this!"
35
+ exit
36
+ else
37
+ permalink = result["permalink"]
38
+ date = result["report"][0]
39
+ result["report"][1].each do |scanner, res|
40
+ if res != ''
41
+ fres = Hash.new
42
+ fres['hash'] = hash
43
+ fres['scanner'] = scanner
44
+ fres['date'] = date
45
+ fres['permalink'] = permalink unless permalink == nil
46
+ fres['result'] = res
47
+
48
+ @results.push fres
49
+ end
50
+ end
51
+ end
52
+
53
+ #if we didn't have any results let create a fake not found
54
+ if @results.size == 0
55
+ fres = Hash.new
56
+ fres['hash'] = hash
57
+ fres['scanner'] = '-'
58
+ fres['date'] = '-'
59
+ fres['permalink'] = '-'
60
+ if @type == :hash
61
+ fres['result'] = "No Results for Hash"
62
+ elsif @type == :site
63
+ fres['result'] = "No Results for Site"
64
+ end
65
+ @results.push fres
66
+ end
67
+ end
68
+
69
+ # Prints the [VirusTotalResult] object to screen
70
+ #
71
+ def to_stdout
72
+ result_string = String.new
73
+ @results.each do |result|
74
+ result_string << "#{result['hash']}: Scanner: #{result['scanner']} Result: #{result['result']}\n"
75
+ end
76
+ print result_string
77
+ end
78
+
79
+ # Prints the [VirusTotalResult] object as a xml string to the screen
80
+ #
81
+ def to_xml
82
+ result_string = String.new
83
+ @results.each do |result|
84
+ result_string << "\t<vtresult>\n"
85
+ result_string << "\t\t<hash>#{result['hash']}</hash>\n"
86
+ result_string << "\t\t<scanner>#{result['scanner']}</scanner>\n"
87
+ result_string << "\t\t<date>#{result['date']}</date>\n"
88
+ result_string << "\t\t<permalink>#{result['permalink']}</permalink>\n" unless result['permalink'] == nil
89
+ result_string << "\t\t<result>#{result['result']}</result>\n"
90
+ result_string << "\t</vtresult>\n"
91
+ end
92
+ print result_string
93
+ end
94
+
95
+ # Prints the [VirusTotalResult] object as a yaml string to the screen
96
+ #
97
+ def to_yaml
98
+ result_string = String.new
99
+ @results.each do |result|
100
+ result_string << "vt-result:\n"
101
+ result_string << " hash: #{result['hash']}\n"
102
+ result_string << " scanner: #{result['scanner']}\n"
103
+ result_string << " date: #{result['date']}\n"
104
+ result_string << " permalink: #{result['permalink']}\n" unless result['permalink'] == nil
105
+ result_string << " result: #{result['result']}\n\n"
106
+ end
107
+ print result_string
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
2
+
3
+ base = __FILE__
4
+ $:.unshift(File.join(File.dirname(base), 'lib'))
5
+
6
+ require 'virustotal'
7
+
8
+ Gem::Specification.new do |s|
9
+ s.name = 'virustotal'
10
+ s.version = VirusTotal::VERSION
11
+ s.homepage = "http://github.com/hammackj/ruby-virustotal/"
12
+ s.summary = "virustotal"
13
+ s.description = "virustotal is a script for automating virustotal.com queries"
14
+ s.license = "BSD"
15
+
16
+ s.author = "Jacob Hammack"
17
+ s.email = "jacob.hammack@hammackj.com"
18
+
19
+ s.files = Dir['[A-Z]*'] + Dir['lib/**/*'] + ['virustotal.gemspec']
20
+ s.default_executable = 'virustotal'
21
+ s.executables = ['virustotal']
22
+ s.require_paths = ["lib"]
23
+
24
+ s.required_rubygems_version = ">= 1.3.6"
25
+ s.rubyforge_project = "virustotal"
26
+
27
+ s.add_development_dependency("rspec", ">= 2.4.0")
28
+ s.add_development_dependency("yard", ">= 0.6.4")
29
+
30
+ s.has_rdoc = 'yard'
31
+ s.extra_rdoc_files = ["README.markdown", "LICENSE", "NEWS.markdown", "TODO.markdown"]
32
+
33
+ s.add_dependency('json', '>= 1.5.1')
34
+ s.add_dependency('rest-client', '>= 1.6.1')
35
+
36
+ end
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: virustotal
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 2.0.0
6
+ platform: ruby
7
+ authors:
8
+ - Jacob Hammack
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-02-18 00:00:00 -06:00
14
+ default_executable: virustotal
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: rspec
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: 2.4.0
25
+ type: :development
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: yard
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: 0.6.4
36
+ type: :development
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: json
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 1.5.1
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: rest-client
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: 1.6.1
58
+ type: :runtime
59
+ version_requirements: *id004
60
+ description: virustotal is a script for automating virustotal.com queries
61
+ email: jacob.hammack@hammackj.com
62
+ executables:
63
+ - virustotal
64
+ extensions: []
65
+
66
+ extra_rdoc_files:
67
+ - README.markdown
68
+ - LICENSE
69
+ - NEWS.markdown
70
+ - TODO.markdown
71
+ files:
72
+ - LICENSE
73
+ - NEWS.markdown
74
+ - Rakefile
75
+ - README.markdown
76
+ - TODO.markdown
77
+ - lib/virustotal/application.rb
78
+ - lib/virustotal/virustotal.rb
79
+ - lib/virustotal/virustotalresult.rb
80
+ - lib/virustotal.rb
81
+ - virustotal.gemspec
82
+ - bin/virustotal
83
+ has_rdoc: yard
84
+ homepage: http://github.com/hammackj/ruby-virustotal/
85
+ licenses:
86
+ - BSD
87
+ post_install_message:
88
+ rdoc_options: []
89
+
90
+ require_paths:
91
+ - lib
92
+ required_ruby_version: !ruby/object:Gem::Requirement
93
+ none: false
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: "0"
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ none: false
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 1.3.6
104
+ requirements: []
105
+
106
+ rubyforge_project: virustotal
107
+ rubygems_version: 1.5.2
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: virustotal
111
+ test_files: []
112
+