virustotalx 0.1.1 → 1.0.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/README.md +75 -45
- data/lib/virustotal.rb +2 -1
- data/lib/virustotal/api.rb +2 -0
- data/lib/virustotal/clients/analysis.rb +11 -0
- data/lib/virustotal/clients/base.rb +39 -26
- data/lib/virustotal/clients/domain.rb +14 -5
- data/lib/virustotal/clients/file.rb +54 -25
- data/lib/virustotal/clients/ip_address.rb +13 -5
- data/lib/virustotal/clients/object.rb +72 -0
- data/lib/virustotal/clients/url.rb +44 -8
- data/lib/virustotal/errors.rb +12 -0
- data/lib/virustotal/version.rb +1 -1
- data/virustotalx.gemspec +2 -2
- metadata +9 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b93b0c39cdd1dbf4efdeffbe4f9ed6c30892f75a3a73172b1e5eb2db11678334
|
4
|
+
data.tar.gz: 38be1389296d3bd50edbf0bdb8906b83a2639a681b0b91b7a9fc1df8539a35b4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86a227bded77dba22902a4531f09683cfc618c850ae178762f6ba5cd1438dbd546313bcd67f27041af3b562dcb0d1e047b570f160ad5a6db91a773b9e8aa6edc
|
7
|
+
data.tar.gz: 1b73b35965ee06d99b02fc5674d30ea234f5a64909a2a8a4dfb12cac6c38df8b648ada8c0aaf981338ca3e61accd267a971ddb3883a8eee0fb25f1774b387d4e
|
data/README.md
CHANGED
@@ -3,8 +3,9 @@
|
|
3
3
|
[](https://badge.fury.io/rb/virustotalx)
|
4
4
|
[](https://travis-ci.org/ninoseki/virustotalx)
|
5
5
|
[](https://coveralls.io/github/ninoseki/virustotalx?branch=master)
|
6
|
+
[](https://www.codefactor.io/repository/github/ninoseki/virustotalx)
|
6
7
|
|
7
|
-
Yet another VirusTotal API wrapper for Ruby
|
8
|
+
Yet another VirusTotal API (version 3) wrapper for Ruby.
|
8
9
|
|
9
10
|
## Installation
|
10
11
|
|
@@ -19,59 +20,88 @@ require "virustotalx"
|
|
19
20
|
# or
|
20
21
|
require "virustotal"
|
21
22
|
|
22
|
-
# when given nothing, it tries to load your API key from ENV["
|
23
|
+
# when given nothing, it tries to load your API key from ENV["VIRUSTOAL_API_KEY"]
|
23
24
|
api = VirusTotal::API.new
|
24
25
|
# or you can set it manually
|
25
26
|
api = VirusTotal::API.new(key: "YOUR_API_KEY")
|
26
27
|
|
27
28
|
hash = "726a2eedb9df3d63ec1b4a7d774a799901f1a2b9"
|
28
|
-
api.file.
|
29
|
-
api.file.
|
30
|
-
|
31
|
-
api.
|
32
|
-
api.
|
33
|
-
api.file.behaviour(hash)
|
34
|
-
api.file.network_traffic(hash)
|
35
|
-
api.file.clusters("DATETIME")
|
36
|
-
api.file.search("resource:#{hash}")
|
37
|
-
|
38
|
-
api.url.report("http://github.com")
|
39
|
-
api.url.scan("https://github.com/ninoseki/virustotalx")
|
40
|
-
|
41
|
-
api.domain.report("github.com")
|
42
|
-
|
43
|
-
api.ip_address.report("1.1.1.1")
|
44
|
-
|
45
|
-
# it returns nil when given a non-existing resource to #report methods
|
46
|
-
api.domain.report("a_domain_which_does_not_exist.com")
|
47
|
-
# => nil
|
48
|
-
```
|
29
|
+
api.file.get(hash)
|
30
|
+
api.file.upload("/tmp/test.txt")
|
31
|
+
|
32
|
+
api.url.get("http://github.com")
|
33
|
+
api.url.analyse("https://github.com/ninoseki/virustotalx")
|
49
34
|
|
50
|
-
|
35
|
+
api.domain.get("github.com")
|
36
|
+
|
37
|
+
api.ip_address.get("1.1.1.1")
|
38
|
+
```
|
51
39
|
|
52
40
|
## Supported API endpoints
|
53
41
|
|
54
|
-
* [VirusTotal API reference](https://developers.virustotal.com/reference)
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
|
59
|
-
|
60
|
-
|
|
61
|
-
|
|
62
|
-
| GET | /
|
63
|
-
|
|
64
|
-
|
|
65
|
-
| GET | /
|
66
|
-
|
|
67
|
-
| GET | /
|
68
|
-
|
|
69
|
-
|
|
70
|
-
| GET | /
|
71
|
-
| GET | /
|
72
|
-
| GET | /
|
73
|
-
|
74
|
-
|
42
|
+
* [VirusTotal API reference](https://developers.virustotal.com/v3.0/reference#overview)
|
43
|
+
|
44
|
+
### Files
|
45
|
+
|
46
|
+
| HTTP Method | URL | API method |
|
47
|
+
|-------------|------------------------------------|------------------------------------------------------------|
|
48
|
+
| POST | /files | api.file.upload(filepath) |
|
49
|
+
| GET | /files/upload_url | api.file.upload_url |
|
50
|
+
| GET | /files/{id} | api.file.get(id) |
|
51
|
+
| POST | /files | api.file.upload(path) |
|
52
|
+
| POST | /files/{id}/analyse | api.file.analyse(id) |
|
53
|
+
| GET | /files/{id}/comments | api.file.comments(id) |
|
54
|
+
| POST | /files/{id}/comments | api.file.add_comment(id, text) |
|
55
|
+
| GET | /files/{id}/votes | api.file.votes(id) |
|
56
|
+
| POST | /files/{id}/votes | api.file.add_vote(id, verdict) |
|
57
|
+
| GET | /files/{id}/download_url | api.file.downbload_url(id) |
|
58
|
+
| GET | /files/{id}/download | api.file.download(id) |
|
59
|
+
| GET | /files/{id}/{relationship} | api.file.`relationship`(id) (e.g. api.file.behaviours(id)) |
|
60
|
+
| GET | /file_behaviours/{sandbox_id}/pcap | api.file.pcap(sandbox_id) |
|
61
|
+
|
62
|
+
### URLs
|
63
|
+
|
64
|
+
| HTTP Method | URL | API method |
|
65
|
+
|-------------|-----------------------------|----------------------------------------------------------------|
|
66
|
+
| POST | /urls | N/A |
|
67
|
+
| GET | /urls/{id} | api.url.get(id) |
|
68
|
+
| POST | /urls/{id}/analyse | api.url.analyse(id) |
|
69
|
+
| GET | /urls/{id}/comments | api.url.comments(id) |
|
70
|
+
| POST | /urls/{id}/comments | api.url.add_comment(id) |
|
71
|
+
| GET | /urls/{id}/votes | api.url.votes(id) |
|
72
|
+
| POST | /urls/{id}/votes | api.url.add_vote(id, text) |
|
73
|
+
| GET | /urls/{id}/network_location | api.url.network_location(id) |
|
74
|
+
| GET | /urls/{id}/{relationship} | api.url.`relationship`(id) (e.g. api.url.downloaded_files(id)) |
|
75
|
+
|
76
|
+
Note: you can use a URL as an id.
|
77
|
+
|
78
|
+
### Domains
|
79
|
+
|
80
|
+
| HTTP Method | URL | API method |
|
81
|
+
|-------------|----------------------------------|--------------------------------------------------------------|
|
82
|
+
| GET | /domains/{domain} | api.domain.get(domain) |
|
83
|
+
| GET | /domains/{domain}/comments | api.domain.comment(domain) |
|
84
|
+
| POST | /domains/{domain}/comments | api.domain.add_comment(domain, text) |
|
85
|
+
| GET | /domains/{domain}/{relationship} | api.domain.`relationship`(domain) (e.g. api.domain.(domain)) |
|
86
|
+
|
87
|
+
### IP addresses
|
88
|
+
|
89
|
+
| HTTP Method | URL | API method |
|
90
|
+
|-------------|-----------------------------------|---------------------------------------------------------------------------------|
|
91
|
+
| GET | /ip_addresses/{ip} | api.ip_address.get(ip) |
|
92
|
+
| GET | /ip_addresses/{ip}/comments | api.ip_address.comments(id) |
|
93
|
+
| POST | /ip_addresses/{ip}/comments | api.ip_address.add_comment(id, text) |
|
94
|
+
| GET | /ip_addresses/{ip}/{relationship} | api.ip_address.`relationship`(id) (e.g. api.ip_address.communicating_files(ip)) |
|
95
|
+
|
96
|
+
### Analyses
|
97
|
+
|
98
|
+
| HTTP Method | URL | API method |
|
99
|
+
|-------------|----------------|----------------------|
|
100
|
+
| GET | /analyses/{id} | api.analysis.get(ip) |
|
101
|
+
|
102
|
+
## Graphs
|
103
|
+
|
104
|
+
N/A.
|
75
105
|
|
76
106
|
## License
|
77
107
|
|
data/lib/virustotal.rb
CHANGED
@@ -7,12 +7,13 @@ require "virustotal/errors"
|
|
7
7
|
require "virustotal/api"
|
8
8
|
|
9
9
|
require "virustotal/clients/base"
|
10
|
+
require "virustotal/clients/object"
|
10
11
|
|
12
|
+
require "virustotal/clients/analysis"
|
11
13
|
require "virustotal/clients/domain"
|
12
14
|
require "virustotal/clients/file"
|
13
15
|
require "virustotal/clients/ip_address"
|
14
16
|
require "virustotal/clients/url"
|
15
17
|
|
16
18
|
module VirusTotal
|
17
|
-
class Error < StandardError; end
|
18
19
|
end
|
data/lib/virustotal/api.rb
CHANGED
@@ -4,6 +4,7 @@ require_relative "clients/base"
|
|
4
4
|
|
5
5
|
module VirusTotal
|
6
6
|
class API
|
7
|
+
attr_reader :analysis
|
7
8
|
attr_reader :domain
|
8
9
|
attr_reader :file
|
9
10
|
attr_reader :ip_address
|
@@ -12,6 +13,7 @@ module VirusTotal
|
|
12
13
|
def initialize(key: ENV["VIRUSTOTAL_API_KEY"])
|
13
14
|
raise ArgumentError, "No API key has been found or provided! (setup your VIRUSTOTAL_API_KEY environment varialbe)" unless key
|
14
15
|
|
16
|
+
@analysis = Client::Analysis.new(key: key)
|
15
17
|
@domain = Client::Domain.new(key: key)
|
16
18
|
@file = Client::File.new(key: key)
|
17
19
|
@ip_address = Client::IPAddress.new(key: key)
|
@@ -8,8 +8,8 @@ module VirusTotal
|
|
8
8
|
module Client
|
9
9
|
class Base
|
10
10
|
HOST = "www.virustotal.com"
|
11
|
-
VERSION = "
|
12
|
-
BASE_URL = "https://#{HOST}/
|
11
|
+
VERSION = "v3"
|
12
|
+
BASE_URL = "https://#{HOST}/api/#{VERSION}"
|
13
13
|
|
14
14
|
attr_reader :key
|
15
15
|
|
@@ -24,10 +24,6 @@ module VirusTotal
|
|
24
24
|
!key.nil?
|
25
25
|
end
|
26
26
|
|
27
|
-
def default_params
|
28
|
-
{ apikey: key }
|
29
|
-
end
|
30
|
-
|
31
27
|
def url_for(path)
|
32
28
|
URI(BASE_URL + path)
|
33
29
|
end
|
@@ -46,58 +42,75 @@ module VirusTotal
|
|
46
42
|
end
|
47
43
|
end
|
48
44
|
|
45
|
+
def raise_error(code, message)
|
46
|
+
code = code.to_s.to_sym
|
47
|
+
|
48
|
+
table = {
|
49
|
+
"400": BadRequestError,
|
50
|
+
"401": AuthenticationRequiredError,
|
51
|
+
"403": ForbiddenError,
|
52
|
+
"404": NotFoundError,
|
53
|
+
"409": AlreadyExistsError,
|
54
|
+
"429": QuotaExceededError,
|
55
|
+
"503": TransientError,
|
56
|
+
}
|
57
|
+
raise Error, "Unsupported response code returned: #{code} - #{message}" unless table.key?(code)
|
58
|
+
|
59
|
+
klass = table[code]
|
60
|
+
raise klass, message
|
61
|
+
end
|
62
|
+
|
49
63
|
def request(req)
|
50
64
|
Net::HTTP.start(HOST, 443, https_options) do |http|
|
65
|
+
req["x-apikey"] = key
|
66
|
+
|
51
67
|
response = http.request(req)
|
52
68
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
69
|
+
code = response.code.to_i
|
70
|
+
body = response.body
|
71
|
+
json = JSON.parse(body) if response["Content-Type"].to_s.include?("application/json")
|
72
|
+
message = json ? json.dig("message") : body
|
73
|
+
|
74
|
+
case code
|
75
|
+
when 200
|
76
|
+
if json
|
77
|
+
yield json
|
57
78
|
else
|
58
|
-
yield
|
79
|
+
yield body
|
59
80
|
end
|
60
|
-
when
|
61
|
-
raise(RateLimitError, response.body)
|
62
|
-
when "302"
|
81
|
+
when 302
|
63
82
|
yield response["Location"]
|
64
83
|
else
|
65
|
-
|
84
|
+
raise_error code, message
|
66
85
|
end
|
67
86
|
end
|
68
87
|
end
|
69
88
|
|
70
|
-
def
|
89
|
+
def _get(path, params = {}, &block)
|
71
90
|
uri = url_for(path)
|
72
|
-
uri.query = URI.encode_www_form(params
|
91
|
+
uri.query = URI.encode_www_form(params)
|
73
92
|
get = Net::HTTP::Get.new(uri)
|
74
93
|
|
75
94
|
request(get, &block)
|
76
95
|
end
|
77
96
|
|
78
|
-
def
|
97
|
+
def _post(path, params = {}, &block)
|
79
98
|
post = Net::HTTP::Post.new(url_for(path))
|
80
|
-
post.
|
99
|
+
post.body = JSON.generate(params)
|
81
100
|
|
82
101
|
request(post, &block)
|
83
102
|
end
|
84
103
|
|
85
|
-
def
|
104
|
+
def _post_with_file(path, file:, filename:, &block)
|
86
105
|
post = Net::HTTP::Post.new(url_for(path))
|
87
106
|
|
88
107
|
data = [
|
89
108
|
["file", file, { "filename": filename }],
|
90
|
-
["apikey", key]
|
91
109
|
]
|
92
110
|
post.set_form(data, "multipart/form-data")
|
93
111
|
|
94
112
|
request(post, &block)
|
95
113
|
end
|
96
|
-
|
97
|
-
def handle_response_code(json)
|
98
|
-
response_code = json.dig("response_code").to_i
|
99
|
-
response_code.zero? ? nil : json
|
100
|
-
end
|
101
114
|
end
|
102
115
|
end
|
103
116
|
end
|
@@ -2,11 +2,20 @@
|
|
2
2
|
|
3
3
|
module VirusTotal
|
4
4
|
module Client
|
5
|
-
class Domain <
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
class Domain < Object
|
6
|
+
private
|
7
|
+
|
8
|
+
def relationships
|
9
|
+
%w(
|
10
|
+
communicating_files
|
11
|
+
downloaded_files
|
12
|
+
graphs
|
13
|
+
historical_whois
|
14
|
+
referrer_files
|
15
|
+
resolutions
|
16
|
+
siblings
|
17
|
+
urls
|
18
|
+
).map(&:to_sym)
|
10
19
|
end
|
11
20
|
end
|
12
21
|
end
|
@@ -2,47 +2,76 @@
|
|
2
2
|
|
3
3
|
module VirusTotal
|
4
4
|
module Client
|
5
|
-
class File <
|
6
|
-
def
|
7
|
-
params = { resource: resource, allinfo: allinfo }.compact
|
8
|
-
post("/file/report", params) do |json|
|
9
|
-
handle_response_code json
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
def scan(path)
|
5
|
+
class File < Object
|
6
|
+
def upload(path)
|
14
7
|
name = ::File.basename(path)
|
15
8
|
data = ::File.read(path)
|
16
|
-
|
9
|
+
_post_with_file("/files", file: data, filename: name) { |json| json }
|
17
10
|
end
|
18
11
|
|
19
|
-
def
|
20
|
-
|
12
|
+
def upload_url
|
13
|
+
_get("/files/upload_url") { |json| json }
|
21
14
|
end
|
22
15
|
|
23
|
-
def
|
24
|
-
|
16
|
+
def analyse(hash)
|
17
|
+
_post("/files/#{hash}/analyse") { |json| json }
|
25
18
|
end
|
26
19
|
|
27
|
-
def
|
28
|
-
|
20
|
+
def votes(hash)
|
21
|
+
_get("/files/#{hash}/votes") { |json| json }
|
29
22
|
end
|
30
23
|
|
31
|
-
def
|
32
|
-
|
24
|
+
def add_vote(hash, verdict)
|
25
|
+
params = {
|
26
|
+
data: {
|
27
|
+
type: "vote",
|
28
|
+
attributes: {
|
29
|
+
verdict: verdict
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
_post("/files/#{hash}/votes", params) { |json| json }
|
33
34
|
end
|
34
35
|
|
35
|
-
def
|
36
|
-
|
36
|
+
def download_url(hash)
|
37
|
+
_get("/files/#{hash}/download_url") { |json| json }
|
37
38
|
end
|
38
39
|
|
39
|
-
def
|
40
|
-
|
40
|
+
def download(hash)
|
41
|
+
_get("/files/#{hash}/download") { |location| location }
|
42
|
+
end
|
43
|
+
|
44
|
+
def pcap(id)
|
45
|
+
_get("/file_behaviours/#{id}/pcap") { |raw| raw }
|
41
46
|
end
|
42
47
|
|
43
|
-
|
44
|
-
|
45
|
-
|
48
|
+
private
|
49
|
+
|
50
|
+
def relationships
|
51
|
+
%w(
|
52
|
+
analyses
|
53
|
+
behaviours
|
54
|
+
bundled_files
|
55
|
+
carbonblack_children
|
56
|
+
carbonblack_parents
|
57
|
+
compressed_parents
|
58
|
+
contacted_domains
|
59
|
+
contacted_ips
|
60
|
+
contacted_urls
|
61
|
+
email_parents
|
62
|
+
embedded_domains
|
63
|
+
embedded_ips
|
64
|
+
execution_parents
|
65
|
+
graphs
|
66
|
+
itw_urls
|
67
|
+
overlay_parents
|
68
|
+
pcap_parents
|
69
|
+
pe_resource_parents
|
70
|
+
similar_files
|
71
|
+
submissions
|
72
|
+
screenshots
|
73
|
+
votes
|
74
|
+
).map(&:to_sym)
|
46
75
|
end
|
47
76
|
end
|
48
77
|
end
|
@@ -2,11 +2,19 @@
|
|
2
2
|
|
3
3
|
module VirusTotal
|
4
4
|
module Client
|
5
|
-
class IPAddress <
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
class IPAddress < Object
|
6
|
+
private
|
7
|
+
|
8
|
+
def relationships
|
9
|
+
%w(
|
10
|
+
communicating_files
|
11
|
+
downloaded_files
|
12
|
+
graphs
|
13
|
+
historical_whois
|
14
|
+
referrer_files
|
15
|
+
resolutions
|
16
|
+
urls
|
17
|
+
).map(&:to_sym)
|
10
18
|
end
|
11
19
|
end
|
12
20
|
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VirusTotal
|
4
|
+
module Client
|
5
|
+
class Object < Base
|
6
|
+
CONVERT_TABLE = {
|
7
|
+
ipaddress: "ip_addresses",
|
8
|
+
domain: "domains",
|
9
|
+
url: "urls",
|
10
|
+
file: "file"
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
def get(id)
|
14
|
+
id = to_id(id)
|
15
|
+
_get("/#{name}/#{id}") { |json| json }
|
16
|
+
end
|
17
|
+
|
18
|
+
def comments(id)
|
19
|
+
id = to_id(id)
|
20
|
+
_get("/#{name}/#{id}/comments") { |json| json }
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_comment(id, text)
|
24
|
+
id = to_id(id)
|
25
|
+
params = {
|
26
|
+
data: {
|
27
|
+
type: "comment",
|
28
|
+
attributes: {
|
29
|
+
text: text
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
_post("/#{name}/#{id}/comments", params) { |json| json }
|
34
|
+
end
|
35
|
+
|
36
|
+
def relationships
|
37
|
+
[]
|
38
|
+
end
|
39
|
+
|
40
|
+
def method_missing(method, *args)
|
41
|
+
if relationships.include?(method)
|
42
|
+
id = to_id(args.first)
|
43
|
+
params = args.length == 2 ? args[1] : {}
|
44
|
+
|
45
|
+
_get("/#{name}/#{id}/#{method}", params) { |json| json }
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def respond_to?(sym, *)
|
52
|
+
return true if relationships.include? sym
|
53
|
+
|
54
|
+
super
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def to_id(id)
|
60
|
+
id
|
61
|
+
end
|
62
|
+
|
63
|
+
def klass
|
64
|
+
self.class.to_s.split("::").last.to_s.downcase.to_sym
|
65
|
+
end
|
66
|
+
|
67
|
+
def name
|
68
|
+
CONVERT_TABLE.fetch klass
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
@@ -1,17 +1,53 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "base64"
|
4
|
+
|
3
5
|
module VirusTotal
|
4
6
|
module Client
|
5
|
-
class URL <
|
6
|
-
def
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
7
|
+
class URL < Object
|
8
|
+
def analyse(url)
|
9
|
+
id = to_id(url)
|
10
|
+
_post("/urls/#{id}/analyse") { |json| json }
|
11
|
+
end
|
12
|
+
|
13
|
+
def votes(url)
|
14
|
+
id = to_id(url)
|
15
|
+
_get("/urls/#{id}/votes") { |json| json }
|
16
|
+
end
|
17
|
+
|
18
|
+
def network_location(url)
|
19
|
+
id = to_id(url)
|
20
|
+
_get("/urls/#{id}/network_location") { |json| json }
|
21
|
+
end
|
22
|
+
|
23
|
+
def add_vote(url, verdict)
|
24
|
+
id = to_id(url)
|
25
|
+
params = {
|
26
|
+
data: {
|
27
|
+
type: "vote",
|
28
|
+
attributes: {
|
29
|
+
verdict: verdict
|
30
|
+
}
|
31
|
+
}
|
32
|
+
}
|
33
|
+
_post("/urls/#{id}/votes", params) { |json| json }
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def to_id(url)
|
39
|
+
Base64.urlsafe_encode64(url).split("=").first
|
11
40
|
end
|
12
41
|
|
13
|
-
def
|
14
|
-
|
42
|
+
def relationships
|
43
|
+
%w(
|
44
|
+
analyses
|
45
|
+
downloaded_files
|
46
|
+
graphs
|
47
|
+
last_serving_ip_address
|
48
|
+
redirecting_urls
|
49
|
+
submissions
|
50
|
+
).map(&:to_sym)
|
15
51
|
end
|
16
52
|
end
|
17
53
|
end
|
data/lib/virustotal/errors.rb
CHANGED
@@ -2,5 +2,17 @@
|
|
2
2
|
|
3
3
|
module VirusTotal
|
4
4
|
class Error < StandardError; end
|
5
|
+
|
6
|
+
class AlreadyExistsError < Error; end
|
7
|
+
class AuthenticationRequiredError < Error; end
|
8
|
+
class BadRequestError < Error; end
|
9
|
+
class ForbiddenError < Error; end
|
10
|
+
class InvalidArgumentError < Error; end
|
11
|
+
class NotFoundError < Error; end
|
12
|
+
class QuotaExceededError < Error; end
|
5
13
|
class RateLimitError < Error; end
|
14
|
+
class TooManyRequestsError < Error; end
|
15
|
+
class TransientError < Error; end
|
16
|
+
class UserNotActiveError < Error; end
|
17
|
+
class WrongCredentialsError < Error; end
|
6
18
|
end
|
data/lib/virustotal/version.rb
CHANGED
data/virustotalx.gemspec
CHANGED
@@ -28,6 +28,6 @@ Gem::Specification.new do |spec|
|
|
28
28
|
spec.add_development_dependency "coveralls", "~> 0.8"
|
29
29
|
spec.add_development_dependency "rake", "~> 12.3"
|
30
30
|
spec.add_development_dependency "rspec", "~> 3.8"
|
31
|
-
spec.add_development_dependency "vcr", "~>
|
32
|
-
spec.add_development_dependency "webmock", "~> 3.
|
31
|
+
spec.add_development_dependency "vcr", "~> 5.0"
|
32
|
+
spec.add_development_dependency "webmock", "~> 3.7"
|
33
33
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: virustotalx
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manabu Niseki
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-09-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -72,28 +72,28 @@ dependencies:
|
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '
|
75
|
+
version: '5.0'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '
|
82
|
+
version: '5.0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
84
|
name: webmock
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - "~>"
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '3.
|
89
|
+
version: '3.7'
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - "~>"
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '3.
|
96
|
+
version: '3.7'
|
97
97
|
description: Yet another VirusTotal API wrapper for Ruby
|
98
98
|
email:
|
99
99
|
- manabu.niseki@gmail.com
|
@@ -112,10 +112,12 @@ files:
|
|
112
112
|
- bin/setup
|
113
113
|
- lib/virustotal.rb
|
114
114
|
- lib/virustotal/api.rb
|
115
|
+
- lib/virustotal/clients/analysis.rb
|
115
116
|
- lib/virustotal/clients/base.rb
|
116
117
|
- lib/virustotal/clients/domain.rb
|
117
118
|
- lib/virustotal/clients/file.rb
|
118
119
|
- lib/virustotal/clients/ip_address.rb
|
120
|
+
- lib/virustotal/clients/object.rb
|
119
121
|
- lib/virustotal/clients/url.rb
|
120
122
|
- lib/virustotal/errors.rb
|
121
123
|
- lib/virustotal/version.rb
|
@@ -140,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
142
|
- !ruby/object:Gem::Version
|
141
143
|
version: '0'
|
142
144
|
requirements: []
|
143
|
-
rubygems_version: 3.0.
|
145
|
+
rubygems_version: 3.0.4
|
144
146
|
signing_key:
|
145
147
|
specification_version: 4
|
146
148
|
summary: Yet another VirusTotal API wrapper for Ruby
|