virustotalx 1.0.0 → 1.1.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 +21 -7
- data/lib/virustotal.rb +6 -1
- data/lib/virustotal/api.rb +62 -12
- data/lib/virustotal/clients/actions/comments.rb +42 -0
- data/lib/virustotal/clients/actions/get.rb +21 -0
- data/lib/virustotal/clients/actions/relationships.rb +28 -0
- data/lib/virustotal/clients/actions/votes.rb +42 -0
- data/lib/virustotal/clients/analysis.rb +1 -3
- data/lib/virustotal/clients/base.rb +35 -0
- data/lib/virustotal/clients/domain.rb +6 -2
- data/lib/virustotal/clients/file.rb +59 -18
- data/lib/virustotal/clients/graph.rb +195 -0
- data/lib/virustotal/clients/ip_address.rb +6 -2
- data/lib/virustotal/clients/url.rb +25 -20
- data/lib/virustotal/version.rb +1 -1
- data/virustotalx.gemspec +2 -2
- metadata +12 -8
- data/lib/virustotal/clients/object.rb +0 -72
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f014d99e8a184fa94a9f2d696209672867c2d437c83bf094b49346078887b077
|
4
|
+
data.tar.gz: 0ff81e8a4a61b7283a8d54fe329127d602b07847f8a62ca70f7aa4112b6a6187
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c957e719c7a2f9be34493128a95369f5a0bd0f0a534570f9466c70af1cec243daba9198b516cc21af576bd3f5d31b01a65dc017cd6a946663c6d9f3009906d04
|
7
|
+
data.tar.gz: a0497c176fc774a95193452590226f9887792aed9a42a1e144859b0db99684195c75d1e6ca8eb5de833a00a72b506b86f88bf347f621ea3f84b865c4960ef4e5
|
data/README.md
CHANGED
@@ -39,12 +39,12 @@ api.ip_address.get("1.1.1.1")
|
|
39
39
|
|
40
40
|
## Supported API endpoints
|
41
41
|
|
42
|
-
|
42
|
+
- [VirusTotal API reference](https://developers.virustotal.com/v3.0/reference#overview)
|
43
43
|
|
44
44
|
### Files
|
45
45
|
|
46
46
|
| HTTP Method | URL | API method |
|
47
|
-
|
47
|
+
| ----------- | ---------------------------------- | ---------------------------------------------------------- |
|
48
48
|
| POST | /files | api.file.upload(filepath) |
|
49
49
|
| GET | /files/upload_url | api.file.upload_url |
|
50
50
|
| GET | /files/{id} | api.file.get(id) |
|
@@ -62,7 +62,7 @@ api.ip_address.get("1.1.1.1")
|
|
62
62
|
### URLs
|
63
63
|
|
64
64
|
| HTTP Method | URL | API method |
|
65
|
-
|
65
|
+
| ----------- | --------------------------- | -------------------------------------------------------------- |
|
66
66
|
| POST | /urls | N/A |
|
67
67
|
| GET | /urls/{id} | api.url.get(id) |
|
68
68
|
| POST | /urls/{id}/analyse | api.url.analyse(id) |
|
@@ -78,7 +78,7 @@ Note: you can use a URL as an id.
|
|
78
78
|
### Domains
|
79
79
|
|
80
80
|
| HTTP Method | URL | API method |
|
81
|
-
|
81
|
+
| ----------- | -------------------------------- | ------------------------------------------------------------ |
|
82
82
|
| GET | /domains/{domain} | api.domain.get(domain) |
|
83
83
|
| GET | /domains/{domain}/comments | api.domain.comment(domain) |
|
84
84
|
| POST | /domains/{domain}/comments | api.domain.add_comment(domain, text) |
|
@@ -87,7 +87,7 @@ Note: you can use a URL as an id.
|
|
87
87
|
### IP addresses
|
88
88
|
|
89
89
|
| HTTP Method | URL | API method |
|
90
|
-
|
90
|
+
| ----------- | --------------------------------- | ------------------------------------------------------------------------------- |
|
91
91
|
| GET | /ip_addresses/{ip} | api.ip_address.get(ip) |
|
92
92
|
| GET | /ip_addresses/{ip}/comments | api.ip_address.comments(id) |
|
93
93
|
| POST | /ip_addresses/{ip}/comments | api.ip_address.add_comment(id, text) |
|
@@ -96,12 +96,26 @@ Note: you can use a URL as an id.
|
|
96
96
|
### Analyses
|
97
97
|
|
98
98
|
| HTTP Method | URL | API method |
|
99
|
-
|
99
|
+
| ----------- | -------------- | -------------------- |
|
100
100
|
| GET | /analyses/{id} | api.analysis.get(ip) |
|
101
101
|
|
102
102
|
## Graphs
|
103
103
|
|
104
|
-
|
104
|
+
| HTTP Method | URL | API method |
|
105
|
+
| ----------- | ---------------------------------------------------- | ----------------------------------------------------------------------------------- |
|
106
|
+
| GET | graphs | api.graph.search(filter: nil, limit: nil, cursor: nil, order: nil, attributes: nil) |
|
107
|
+
| POST | graphs | api.graph.create(\*\*params) |
|
108
|
+
| GET | graphs/{id} | api.graph.get(id) |
|
109
|
+
| PATCH | graphs/{id} | api.graph.update(id, \*\*params) |
|
110
|
+
| GET | graphs/{id}/relationships/viewers | api.graph.viewers(id, limit: nil, cursor: nil) |
|
111
|
+
| POST | graphs/{id}/relationships/viewers | api.graph.add_viewer(id, \*\*params) |
|
112
|
+
| GET | graphs/{id}/relationships/viewers/{user_or_group_id} | api.graph.check_viewer(id, user_or_group_id) |
|
113
|
+
| DELETE | graphs/{id}/relationships/viewers/{user_or_group_id} | api.graph.delete_viewer(id, user_or_group_id) |
|
114
|
+
| GET | graphs/{id}/relationships/editors | api.graph.editors(id, limit: nil, cursor: nil) |
|
115
|
+
| POST | graphs/{id}/relationships/editors | api.graph.add_editor(id, \*\*params) |
|
116
|
+
| GET | graphs/{id}/relationships/editors/{user_or_group_id} | api.graph.check_editor(id, user_or_group_id) |
|
117
|
+
| DELETE | graphs/{id}/relationships/editors/{user_or_group_id} | api.graph.delete_editor(id, user_or_group_id) |
|
118
|
+
| GET | graphs/{id}/relationships/{relationship} | api.graph.`relationship`(id) |
|
105
119
|
|
106
120
|
## License
|
107
121
|
|
data/lib/virustotal.rb
CHANGED
@@ -7,11 +7,16 @@ require "virustotal/errors"
|
|
7
7
|
require "virustotal/api"
|
8
8
|
|
9
9
|
require "virustotal/clients/base"
|
10
|
-
|
10
|
+
|
11
|
+
require "virustotal/clients/actions/comments"
|
12
|
+
require "virustotal/clients/actions/get"
|
13
|
+
require "virustotal/clients/actions/relationships"
|
14
|
+
require "virustotal/clients/actions/votes"
|
11
15
|
|
12
16
|
require "virustotal/clients/analysis"
|
13
17
|
require "virustotal/clients/domain"
|
14
18
|
require "virustotal/clients/file"
|
19
|
+
require "virustotal/clients/graph"
|
15
20
|
require "virustotal/clients/ip_address"
|
16
21
|
require "virustotal/clients/url"
|
17
22
|
|
data/lib/virustotal/api.rb
CHANGED
@@ -1,23 +1,73 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative "clients/base"
|
4
|
-
|
5
3
|
module VirusTotal
|
6
4
|
class API
|
7
|
-
|
8
|
-
attr_reader :
|
9
|
-
attr_reader :file
|
10
|
-
attr_reader :ip_address
|
11
|
-
attr_reader :url
|
5
|
+
# @return [String] VirusTotal API key
|
6
|
+
attr_reader :key
|
12
7
|
|
8
|
+
#
|
9
|
+
# @param [String] key VirusTotal API key
|
10
|
+
#
|
11
|
+
# @raise [ArgumentError] When given an empty key
|
12
|
+
#
|
13
13
|
def initialize(key: ENV["VIRUSTOTAL_API_KEY"])
|
14
|
+
@key = key
|
15
|
+
|
14
16
|
raise ArgumentError, "No API key has been found or provided! (setup your VIRUSTOTAL_API_KEY environment varialbe)" unless key
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Analyses API endpoint client
|
21
|
+
#
|
22
|
+
# @return [VirusTotal::Client::Analysis]
|
23
|
+
#
|
24
|
+
def analysis
|
25
|
+
@analysis ||= Client::Analysis.new(key: key)
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Domains API endpoint client
|
30
|
+
#
|
31
|
+
# @return [VirusTotal::Client::Domain]
|
32
|
+
#
|
33
|
+
def domain
|
34
|
+
@domain ||= Client::Domain.new(key: key)
|
35
|
+
end
|
36
|
+
|
37
|
+
#
|
38
|
+
# Files API endpoint client
|
39
|
+
#
|
40
|
+
# @return [VirusTotal::Client::File]
|
41
|
+
#
|
42
|
+
def file
|
43
|
+
@file ||= Client::File.new(key: key)
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# IP addresses API endpoint client
|
48
|
+
#
|
49
|
+
# @return [VirusTotal::Client::IPAddress]
|
50
|
+
#
|
51
|
+
def ip_address
|
52
|
+
@ip_address ||= Client::IPAddress.new(key: key)
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# URLs API endpoint client
|
57
|
+
#
|
58
|
+
# @return [VirusTotal::Client::URL]
|
59
|
+
#
|
60
|
+
def url
|
61
|
+
@url ||= Client::URL.new(key: key)
|
62
|
+
end
|
15
63
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
64
|
+
#
|
65
|
+
# Graphs API endpoint client
|
66
|
+
#
|
67
|
+
# @return [VirusTotal::Client::Graph]
|
68
|
+
#
|
69
|
+
def graph
|
70
|
+
@graph ||= Client::Graph.new(key: key)
|
21
71
|
end
|
22
72
|
end
|
23
73
|
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VirusTotal
|
4
|
+
module Client
|
5
|
+
module Action
|
6
|
+
module Comments
|
7
|
+
#
|
8
|
+
# Retrieve comments for an object
|
9
|
+
#
|
10
|
+
# @param [String] id Object identifier
|
11
|
+
#
|
12
|
+
# @return [Hash]
|
13
|
+
#
|
14
|
+
def comments(id)
|
15
|
+
id = to_id(id)
|
16
|
+
_get("/#{name}/#{id}/comments") { |json| json }
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Add a comment to an object
|
21
|
+
#
|
22
|
+
# @param [String] id Object identifier
|
23
|
+
# @param [String] text
|
24
|
+
#
|
25
|
+
# @return [Hash]
|
26
|
+
#
|
27
|
+
def add_comment(id, text)
|
28
|
+
id = to_id(id)
|
29
|
+
params = {
|
30
|
+
data: {
|
31
|
+
type: "comment",
|
32
|
+
attributes: {
|
33
|
+
text: text
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
_post("/#{name}/#{id}/comments", params) { |json| json }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VirusTotal
|
4
|
+
module Client
|
5
|
+
module Action
|
6
|
+
module Get
|
7
|
+
#
|
8
|
+
# Retrieve information about an object
|
9
|
+
#
|
10
|
+
# @param [String] id Object identifier
|
11
|
+
#
|
12
|
+
# @return [Hash]
|
13
|
+
#
|
14
|
+
def get(id)
|
15
|
+
id = to_id(id)
|
16
|
+
_get("/#{name}/#{id}") { |json| json }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VirusTotal
|
4
|
+
module Client
|
5
|
+
module Action
|
6
|
+
module Relationships
|
7
|
+
def relationships
|
8
|
+
[]
|
9
|
+
end
|
10
|
+
|
11
|
+
def method_missing(method, *args)
|
12
|
+
if relationships.include?(method)
|
13
|
+
id = to_id(args.first)
|
14
|
+
params = args.length == 2 ? args[1] : {}
|
15
|
+
|
16
|
+
_get("/#{name}/#{id}/#{method}", params) { |json| json }
|
17
|
+
else
|
18
|
+
super
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def respond_to_missing?(method_name, *)
|
23
|
+
relationships.include? method_name
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VirusTotal
|
4
|
+
module Client
|
5
|
+
module Action
|
6
|
+
module Votes
|
7
|
+
#
|
8
|
+
# Retrieve votes for an object
|
9
|
+
#
|
10
|
+
# @param [String] id Object identifier
|
11
|
+
#
|
12
|
+
# @return [Hash]
|
13
|
+
#
|
14
|
+
def votes(id)
|
15
|
+
id = to_id(id)
|
16
|
+
_get("/#{name}/#{id}/votes") { |json| json }
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Add a vote for an object
|
21
|
+
#
|
22
|
+
# @param [String] id Object identifier
|
23
|
+
# @param [String] verdict harmless or malicious
|
24
|
+
#
|
25
|
+
# @return [Hash]
|
26
|
+
#
|
27
|
+
def add_vote(id, verdict)
|
28
|
+
id = to_id(id)
|
29
|
+
params = {
|
30
|
+
data: {
|
31
|
+
type: "vote",
|
32
|
+
attributes: {
|
33
|
+
verdict: verdict
|
34
|
+
}
|
35
|
+
}
|
36
|
+
}
|
37
|
+
_post("/#{name}/#{id}/votes", params) { |json| json }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -11,6 +11,15 @@ module VirusTotal
|
|
11
11
|
VERSION = "v3"
|
12
12
|
BASE_URL = "https://#{HOST}/api/#{VERSION}"
|
13
13
|
|
14
|
+
CONVERT_TABLE = {
|
15
|
+
analysis: "analyses",
|
16
|
+
domain: "domains",
|
17
|
+
file: "files",
|
18
|
+
graph: "graphs",
|
19
|
+
ipaddress: "ip_addresses",
|
20
|
+
url: "urls",
|
21
|
+
}.freeze
|
22
|
+
|
14
23
|
attr_reader :key
|
15
24
|
|
16
25
|
def initialize(key: ENV["VIRUSTOTAL_API_KEY"])
|
@@ -111,6 +120,32 @@ module VirusTotal
|
|
111
120
|
|
112
121
|
request(post, &block)
|
113
122
|
end
|
123
|
+
|
124
|
+
def _patch(path, params = {}, &block)
|
125
|
+
patch = Net::HTTP::Patch.new(url_for(path))
|
126
|
+
patch.body = JSON.generate(params) if params
|
127
|
+
|
128
|
+
request(patch, &block)
|
129
|
+
end
|
130
|
+
|
131
|
+
def _delete(path, params = {}, &block)
|
132
|
+
delete = Net::HTTP::Delete.new(url_for(path))
|
133
|
+
delete.body = JSON.generate(params) if params
|
134
|
+
|
135
|
+
request(delete, &block)
|
136
|
+
end
|
137
|
+
|
138
|
+
def to_id(id)
|
139
|
+
id
|
140
|
+
end
|
141
|
+
|
142
|
+
def klass
|
143
|
+
self.class.to_s.split("::").last.to_s.downcase.to_sym
|
144
|
+
end
|
145
|
+
|
146
|
+
def name
|
147
|
+
CONVERT_TABLE.fetch klass
|
148
|
+
end
|
114
149
|
end
|
115
150
|
end
|
116
151
|
end
|
@@ -2,11 +2,15 @@
|
|
2
2
|
|
3
3
|
module VirusTotal
|
4
4
|
module Client
|
5
|
-
class Domain <
|
5
|
+
class Domain < Base
|
6
|
+
include Action::Comments
|
7
|
+
include Action::Get
|
8
|
+
include Action::Relationships
|
9
|
+
|
6
10
|
private
|
7
11
|
|
8
12
|
def relationships
|
9
|
-
%w(
|
13
|
+
@relationships ||= %w(
|
10
14
|
communicating_files
|
11
15
|
downloaded_files
|
12
16
|
graphs
|
@@ -2,45 +2,86 @@
|
|
2
2
|
|
3
3
|
module VirusTotal
|
4
4
|
module Client
|
5
|
-
class File <
|
5
|
+
class File < Base
|
6
|
+
include Action::Comments
|
7
|
+
include Action::Get
|
8
|
+
include Action::Relationships
|
9
|
+
include Action::Votes
|
10
|
+
|
11
|
+
#
|
12
|
+
# Upload and analyse a file
|
13
|
+
#
|
14
|
+
# @see https://developers.virustotal.com/v3.0/reference#files-scan
|
15
|
+
#
|
16
|
+
# @param [String] path File path to be scanned
|
17
|
+
#
|
18
|
+
# @return [Hash]
|
19
|
+
#
|
6
20
|
def upload(path)
|
7
21
|
name = ::File.basename(path)
|
8
22
|
data = ::File.read(path)
|
9
23
|
_post_with_file("/files", file: data, filename: name) { |json| json }
|
10
24
|
end
|
11
25
|
|
26
|
+
#
|
27
|
+
# Get a URL for uploading files larger than 32MB
|
28
|
+
#
|
29
|
+
# @see https://developers.virustotal.com/v3.0/reference#files-upload-url
|
30
|
+
#
|
31
|
+
# @return [Hash]
|
32
|
+
#
|
12
33
|
def upload_url
|
13
34
|
_get("/files/upload_url") { |json| json }
|
14
35
|
end
|
15
36
|
|
37
|
+
#
|
38
|
+
# Reanalyse a file already in VirusTotal
|
39
|
+
#
|
40
|
+
# @see https://developers.virustotal.com/v3.0/reference#files-analyse
|
41
|
+
#
|
42
|
+
# @param [String] hash SHA-256, SHA-1 or MD5 identifying the file
|
43
|
+
#
|
44
|
+
# @return [Hash]
|
45
|
+
#
|
16
46
|
def analyse(hash)
|
17
47
|
_post("/files/#{hash}/analyse") { |json| json }
|
18
48
|
end
|
19
49
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
verdict: verdict
|
30
|
-
}
|
31
|
-
}
|
32
|
-
}
|
33
|
-
_post("/files/#{hash}/votes", params) { |json| json }
|
34
|
-
end
|
35
|
-
|
50
|
+
#
|
51
|
+
# Get a download URL for a file
|
52
|
+
#
|
53
|
+
# @see https://developers.virustotal.com/v3.0/reference#files-download-url
|
54
|
+
#
|
55
|
+
# @param [String] hash SHA-256, SHA-1 or MD5 identifying the file
|
56
|
+
#
|
57
|
+
# @return [Hash]
|
58
|
+
#
|
36
59
|
def download_url(hash)
|
37
60
|
_get("/files/#{hash}/download_url") { |json| json }
|
38
61
|
end
|
39
62
|
|
63
|
+
#
|
64
|
+
# Download a file
|
65
|
+
#
|
66
|
+
# @see https://developers.virustotal.com/v3.0/reference#files-download
|
67
|
+
#
|
68
|
+
# @param [String] SHA-256, SHA-1 or MD5 identifying the file
|
69
|
+
#
|
70
|
+
# @return [<Type>] <description>
|
71
|
+
#
|
40
72
|
def download(hash)
|
41
73
|
_get("/files/#{hash}/download") { |location| location }
|
42
74
|
end
|
43
75
|
|
76
|
+
#
|
77
|
+
# Retrieve objects related to a file
|
78
|
+
#
|
79
|
+
# @see https://developers.virustotal.com/v3.0/reference#file_behaviours_pcap
|
80
|
+
#
|
81
|
+
# @param [String] id SHA-256, SHA-1 or MD5 identifying the file
|
82
|
+
#
|
83
|
+
# @return [Hash]
|
84
|
+
#
|
44
85
|
def pcap(id)
|
45
86
|
_get("/file_behaviours/#{id}/pcap") { |raw| raw }
|
46
87
|
end
|
@@ -48,7 +89,7 @@ module VirusTotal
|
|
48
89
|
private
|
49
90
|
|
50
91
|
def relationships
|
51
|
-
%w(
|
92
|
+
@relationships ||= %w(
|
52
93
|
analyses
|
53
94
|
behaviours
|
54
95
|
bundled_files
|
@@ -0,0 +1,195 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VirusTotal
|
4
|
+
module Client
|
5
|
+
class Graph < Base
|
6
|
+
include Action::Get
|
7
|
+
|
8
|
+
#
|
9
|
+
# Return graphs.
|
10
|
+
#
|
11
|
+
# @see https://developers.virustotal.com/v3.0/reference#graphs
|
12
|
+
#
|
13
|
+
# @param [String, nil] filter Return the graphs matching the given criteria only
|
14
|
+
# @param [Integer, nil] limit Maximum number graphs to retrieve
|
15
|
+
# @param [String, nil] cursor Continuation cursor
|
16
|
+
# @param [String, nil] order Sort order
|
17
|
+
# @param [String, nil] attributes Specific fields to retrieve
|
18
|
+
#
|
19
|
+
# @return [Hash]
|
20
|
+
#
|
21
|
+
def search(filter: nil, limit: nil, cursor: nil, order: nil, attributes: nil)
|
22
|
+
params = {
|
23
|
+
filter: filter,
|
24
|
+
limit: limit,
|
25
|
+
cursor: cursor,
|
26
|
+
order: order,
|
27
|
+
attributes: attributes
|
28
|
+
}.compact
|
29
|
+
_get("/graphs", params) { |json| json }
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# Create a graph with the given nodes and links.
|
34
|
+
#
|
35
|
+
# @see https://developers.virustotal.com/v3.0/reference#create-graphs
|
36
|
+
#
|
37
|
+
# @param [Hash] **params
|
38
|
+
#
|
39
|
+
# @return [Hash]
|
40
|
+
#
|
41
|
+
def create(**params)
|
42
|
+
_post("/graphs", params) { |json| json }
|
43
|
+
end
|
44
|
+
|
45
|
+
#
|
46
|
+
# Update a graph.
|
47
|
+
#
|
48
|
+
# @see https://developers.virustotal.com/v3.0/reference#graphs-update
|
49
|
+
#
|
50
|
+
# @param [String] id A 65 char length id which uniquely identify the graph.
|
51
|
+
# @param [Hash] **params
|
52
|
+
#
|
53
|
+
# @return [Hash]
|
54
|
+
#
|
55
|
+
def update(id, **params)
|
56
|
+
_patch("/graphs/#{id}", params) { |json| json }
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Retrieve graph viewers.
|
61
|
+
#
|
62
|
+
# @see https://developers.virustotal.com/v3.0/reference#graphs-viewers
|
63
|
+
#
|
64
|
+
# @param [String] id A 65 char length id which uniquely identify the graph.
|
65
|
+
# @param [String, nil] limit Maximum number of related objects to retrieve
|
66
|
+
# @param [Integer, nil] cursor Continuation cursor
|
67
|
+
#
|
68
|
+
# @return [Hash]
|
69
|
+
#
|
70
|
+
def viewers(id, limit: nil, cursor: nil)
|
71
|
+
params = {
|
72
|
+
limit: limit,
|
73
|
+
cursor: cursor
|
74
|
+
}.compact
|
75
|
+
_get("/graphs/#{id}/relationships/viewers", params) { |json| json }
|
76
|
+
end
|
77
|
+
|
78
|
+
#
|
79
|
+
# Add a user or group as a graph viewer.
|
80
|
+
#
|
81
|
+
# @see https://developers.virustotal.com/v3.0/reference#graphs-add-viewer
|
82
|
+
#
|
83
|
+
# @param [String] id A 65 char length id which uniquely identify the graph.
|
84
|
+
# @param [Hash] **params
|
85
|
+
#
|
86
|
+
# @return [Hash]
|
87
|
+
#
|
88
|
+
def add_viewer(id, **params)
|
89
|
+
_post("/graphs/#{id}/relationships/viewers", params) { |json| json }
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Check if a user or group is a graph viewer
|
94
|
+
#
|
95
|
+
# @see https://developers.virustotal.com/v3.0/reference#graphs-check-viewer
|
96
|
+
#
|
97
|
+
# @param [String] id A 65 char length id which uniquely identify the graph.
|
98
|
+
# @param [String] user_or_group_id user or group ID
|
99
|
+
#
|
100
|
+
# @return [<Type>] <description>
|
101
|
+
#
|
102
|
+
def check_viewer(id, user_or_group_id)
|
103
|
+
_get("/graphs/#{id}/relationships/viewers/#{user_or_group_id}") { |json| json }
|
104
|
+
end
|
105
|
+
|
106
|
+
#
|
107
|
+
# Remove a user or group as viewer of a graph.
|
108
|
+
#
|
109
|
+
# @see https://developers.virustotal.com/v3.0/reference#graphs-delete-viewer
|
110
|
+
#
|
111
|
+
# @param [String] id A 65 char length id which uniquely identify the graph.
|
112
|
+
# @param [String] user_or_group_id user or group ID
|
113
|
+
#
|
114
|
+
# @return [<Type>] <description>
|
115
|
+
#
|
116
|
+
def delete_viewer(id, user_or_group_id)
|
117
|
+
_delete("/graphs/#{id}/relationships/viewers/#{user_or_group_id}") { |json| json }
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
# Retrieve graph editors.
|
122
|
+
#
|
123
|
+
# @see https://developers.virustotal.com/v3.0/reference#graphs-editors
|
124
|
+
#
|
125
|
+
# @param [String] id A 65 char length id which uniquely identify the graph.
|
126
|
+
# @param [String, nil] limit Maximum number of related objects to retrieve
|
127
|
+
# @param [Integer, nil] cursor Continuation cursor
|
128
|
+
#
|
129
|
+
# @return [Hash]
|
130
|
+
#
|
131
|
+
def editors(id, limit: nil, cursor: nil)
|
132
|
+
params = {
|
133
|
+
limit: limit,
|
134
|
+
cursor: cursor
|
135
|
+
}.compact
|
136
|
+
_get("/graphs/#{id}/relationships/editors", params) { |json| json }
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
# Add a user or group as a graph editor.
|
141
|
+
#
|
142
|
+
# @see https://developers.virustotal.com/v3.0/reference#graphs-add-editor
|
143
|
+
#
|
144
|
+
# @param [String] id A 65 char length id which uniquely identify the graph.
|
145
|
+
# @param [Hash] **params
|
146
|
+
#
|
147
|
+
# @return [Hash]
|
148
|
+
#
|
149
|
+
def add_editor(id, **params)
|
150
|
+
_post("/graphs/#{id}/relationships/editors", params) { |json| json }
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# Check if a user or group is a graph editor.
|
155
|
+
#
|
156
|
+
# @see https://developers.virustotal.com/v3.0/reference#graphs-check-editor
|
157
|
+
#
|
158
|
+
# @param [String] id A 65 char length id which uniquely identify the graph.
|
159
|
+
# @param [String] user_or_group_id user or group ID
|
160
|
+
#
|
161
|
+
# @return [Hash]
|
162
|
+
#
|
163
|
+
def check_editor(id, user_or_group_id)
|
164
|
+
_get("/graphs/#{id}/relationships/editors/#{user_or_group_id}") { |json| json }
|
165
|
+
end
|
166
|
+
|
167
|
+
#
|
168
|
+
# Remove a user or group as editor of a graph.
|
169
|
+
#
|
170
|
+
# @see https://developers.virustotal.com/v3.0/reference#graphs-delete-editor
|
171
|
+
#
|
172
|
+
# @param [String] id A 65 char length id which uniquely identify the graph.
|
173
|
+
# @param [String] user_or_group_id user or group ID
|
174
|
+
#
|
175
|
+
# @return [Hash]
|
176
|
+
#
|
177
|
+
def delete_editor(id, user_or_group_id)
|
178
|
+
_delete("/graphs/#{id}/relationships/editors/#{user_or_group_id}") { |json| json }
|
179
|
+
end
|
180
|
+
|
181
|
+
private
|
182
|
+
|
183
|
+
def relationships
|
184
|
+
@relationships ||= %w(
|
185
|
+
comments
|
186
|
+
items
|
187
|
+
viewers
|
188
|
+
editors
|
189
|
+
owner
|
190
|
+
group
|
191
|
+
).map(&:to_sym)
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
end
|
@@ -2,11 +2,15 @@
|
|
2
2
|
|
3
3
|
module VirusTotal
|
4
4
|
module Client
|
5
|
-
class IPAddress <
|
5
|
+
class IPAddress < Base
|
6
|
+
include Action::Comments
|
7
|
+
include Action::Get
|
8
|
+
include Action::Relationships
|
9
|
+
|
6
10
|
private
|
7
11
|
|
8
12
|
def relationships
|
9
|
-
%w(
|
13
|
+
@relationships ||= %w(
|
10
14
|
communicating_files
|
11
15
|
downloaded_files
|
12
16
|
graphs
|
@@ -4,35 +4,40 @@ require "base64"
|
|
4
4
|
|
5
5
|
module VirusTotal
|
6
6
|
module Client
|
7
|
-
class URL <
|
7
|
+
class URL < Base
|
8
|
+
include Action::Comments
|
9
|
+
include Action::Get
|
10
|
+
include Action::Relationships
|
11
|
+
include Action::Votes
|
12
|
+
|
13
|
+
#
|
14
|
+
# Analyse an URL.
|
15
|
+
#
|
16
|
+
# @see https://developers.virustotal.com/v3.0/reference#urls-analyse
|
17
|
+
#
|
18
|
+
# @param [String] url URL identifier
|
19
|
+
#
|
20
|
+
# @return [Hash]
|
21
|
+
#
|
8
22
|
def analyse(url)
|
9
23
|
id = to_id(url)
|
10
24
|
_post("/urls/#{id}/analyse") { |json| json }
|
11
25
|
end
|
12
26
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
27
|
+
#
|
28
|
+
# Domain or IP address for a URL.
|
29
|
+
#
|
30
|
+
# @see https://developers.virustotal.com/v3.0/reference#urlsidnetwork_location
|
31
|
+
#
|
32
|
+
# @param [String] url URL identifier
|
33
|
+
#
|
34
|
+
# @return [Hash]
|
35
|
+
#
|
18
36
|
def network_location(url)
|
19
37
|
id = to_id(url)
|
20
38
|
_get("/urls/#{id}/network_location") { |json| json }
|
21
39
|
end
|
22
40
|
|
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
41
|
private
|
37
42
|
|
38
43
|
def to_id(url)
|
@@ -40,7 +45,7 @@ module VirusTotal
|
|
40
45
|
end
|
41
46
|
|
42
47
|
def relationships
|
43
|
-
%w(
|
48
|
+
@relationships ||= %w(
|
44
49
|
analyses
|
45
50
|
downloaded_files
|
46
51
|
graphs
|
data/lib/virustotal/version.rb
CHANGED
data/virustotalx.gemspec
CHANGED
@@ -26,8 +26,8 @@ Gem::Specification.new do |spec|
|
|
26
26
|
|
27
27
|
spec.add_development_dependency "bundler", "~> 2.0"
|
28
28
|
spec.add_development_dependency "coveralls", "~> 0.8"
|
29
|
-
spec.add_development_dependency "rake", "~>
|
30
|
-
spec.add_development_dependency "rspec", "~> 3.
|
29
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
30
|
+
spec.add_development_dependency "rspec", "~> 3.9"
|
31
31
|
spec.add_development_dependency "vcr", "~> 5.0"
|
32
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: 1.
|
4
|
+
version: 1.1.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-10-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,28 +44,28 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '13.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '13.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rspec
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '3.
|
61
|
+
version: '3.9'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '3.
|
68
|
+
version: '3.9'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: vcr
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
@@ -112,12 +112,16 @@ files:
|
|
112
112
|
- bin/setup
|
113
113
|
- lib/virustotal.rb
|
114
114
|
- lib/virustotal/api.rb
|
115
|
+
- lib/virustotal/clients/actions/comments.rb
|
116
|
+
- lib/virustotal/clients/actions/get.rb
|
117
|
+
- lib/virustotal/clients/actions/relationships.rb
|
118
|
+
- lib/virustotal/clients/actions/votes.rb
|
115
119
|
- lib/virustotal/clients/analysis.rb
|
116
120
|
- lib/virustotal/clients/base.rb
|
117
121
|
- lib/virustotal/clients/domain.rb
|
118
122
|
- lib/virustotal/clients/file.rb
|
123
|
+
- lib/virustotal/clients/graph.rb
|
119
124
|
- lib/virustotal/clients/ip_address.rb
|
120
|
-
- lib/virustotal/clients/object.rb
|
121
125
|
- lib/virustotal/clients/url.rb
|
122
126
|
- lib/virustotal/errors.rb
|
123
127
|
- lib/virustotal/version.rb
|
@@ -142,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
146
|
- !ruby/object:Gem::Version
|
143
147
|
version: '0'
|
144
148
|
requirements: []
|
145
|
-
rubygems_version: 3.0.
|
149
|
+
rubygems_version: 3.0.3
|
146
150
|
signing_key:
|
147
151
|
specification_version: 4
|
148
152
|
summary: Yet another VirusTotal API wrapper for Ruby
|
@@ -1,72 +0,0 @@
|
|
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
|