virustotalx 0.1.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/virustotalx.svg)](https://badge.fury.io/rb/virustotalx)
|
4
4
|
[![Build Status](https://travis-ci.org/ninoseki/virustotalx.svg?branch=master)](https://travis-ci.org/ninoseki/virustotalx)
|
5
5
|
[![Coverage Status](https://coveralls.io/repos/github/ninoseki/virustotalx/badge.svg?branch=master)](https://coveralls.io/github/ninoseki/virustotalx?branch=master)
|
6
|
+
[![CodeFactor](https://www.codefactor.io/repository/github/ninoseki/virustotalx/badge)](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
|