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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b93b0c39cdd1dbf4efdeffbe4f9ed6c30892f75a3a73172b1e5eb2db11678334
4
- data.tar.gz: 38be1389296d3bd50edbf0bdb8906b83a2639a681b0b91b7a9fc1df8539a35b4
3
+ metadata.gz: f014d99e8a184fa94a9f2d696209672867c2d437c83bf094b49346078887b077
4
+ data.tar.gz: 0ff81e8a4a61b7283a8d54fe329127d602b07847f8a62ca70f7aa4112b6a6187
5
5
  SHA512:
6
- metadata.gz: 86a227bded77dba22902a4531f09683cfc618c850ae178762f6ba5cd1438dbd546313bcd67f27041af3b562dcb0d1e047b570f160ad5a6db91a773b9e8aa6edc
7
- data.tar.gz: 1b73b35965ee06d99b02fc5674d30ea234f5a64909a2a8a4dfb12cac6c38df8b648ada8c0aaf981338ca3e61accd267a971ddb3883a8eee0fb25f1774b387d4e
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
- * [VirusTotal API reference](https://developers.virustotal.com/v3.0/reference#overview)
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
- N/A.
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
- require "virustotal/clients/object"
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
 
@@ -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
- attr_reader :analysis
8
- attr_reader :domain
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
- @analysis = Client::Analysis.new(key: key)
17
- @domain = Client::Domain.new(key: key)
18
- @file = Client::File.new(key: key)
19
- @ip_address = Client::IPAddress.new(key: key)
20
- @url = Client::URL.new(key: key)
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
@@ -3,9 +3,7 @@
3
3
  module VirusTotal
4
4
  module Client
5
5
  class Analysis < Base
6
- def get(id)
7
- _get("/analyses/#{id}") { |json| json }
8
- end
6
+ include Action::Get
9
7
  end
10
8
  end
11
9
  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 < Object
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 < Object
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
- def votes(hash)
21
- _get("/files/#{hash}/votes") { |json| json }
22
- end
23
-
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 }
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 < Object
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 < Object
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
- def votes(url)
14
- id = to_id(url)
15
- _get("/urls/#{id}/votes") { |json| json }
16
- end
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module VirusTotal
4
- VERSION = "1.0.0"
4
+ VERSION = "1.1.0"
5
5
  end
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", "~> 12.3"
30
- spec.add_development_dependency "rspec", "~> 3.8"
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.0.0
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-09-26 00:00:00.000000000 Z
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: '12.3'
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: '12.3'
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.8'
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.8'
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.4
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