vt_api 0.1.2.1 → 0.1.3
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/.rubocop.yml +13 -0
- data/.scrutinizer.yml +16 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +44 -1
- data/README.md +1 -1
- data/Rakefile +25 -10
- data/lib/vt_api.rb +3 -21
- data/lib/vt_api/api.rb +2 -0
- data/lib/vt_api/api/config.rb +4 -1
- data/lib/vt_api/api/v2.rb +6 -8
- data/lib/vt_api/api/v2/comments.rb +9 -5
- data/lib/vt_api/api/v2/essentials.rb +13 -0
- data/lib/vt_api/api/v2/file.rb +14 -16
- data/lib/vt_api/api/v2/url.rb +16 -8
- data/lib/vt_api/errors.rb +23 -0
- data/lib/vt_api/internal.rb +3 -1
- data/lib/vt_api/internal/api_provider.rb +27 -18
- data/lib/vt_api/internal/api_version.rb +13 -12
- data/lib/vt_api/internal/endpoint.rb +5 -8
- data/lib/vt_api/internal/versions.rb +3 -1
- data/lib/vt_api/internal/versions/api_v2.rb +43 -58
- data/lib/vt_api/internal/versions/default.rb +3 -2
- data/lib/vt_api/version.rb +3 -1
- data/vt_api.gemspec +6 -4
- metadata +47 -4
- data/.yardopts +0 -1
- data/bin/console +0 -13
- data/bin/setup +0 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d38c571162ab68a4e9546da48dca0278826a563176aba99a4a6b08bb9fa07f5b
|
4
|
+
data.tar.gz: b96cc897788cbf83b84e37b5ad5822c2ada1dc5d3c6b3e820a4747345822c126
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 43e6919c0215b9d988889365d9812e86964613aacd21b056d89cb2351aa6d10881ec15cdebab89b2ab45a0dd8da861e923f4737418ae6b422f655a1fcda9b19a
|
7
|
+
data.tar.gz: 5ac6d587584b22e2a408986aecf9400950fd44112b7d3162d217718069c216fb5d6b27768c62b3fc4b76f8356e5850255b02ae628398978ece63f046d4591553
|
data/.rubocop.yml
ADDED
data/.scrutinizer.yml
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,28 +1,71 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
vt_api (0.1.
|
4
|
+
vt_api (0.1.3)
|
5
5
|
faraday (~> 0.15)
|
6
6
|
mimemagic (~> 0.3)
|
7
7
|
|
8
8
|
GEM
|
9
9
|
remote: https://rubygems.org/
|
10
10
|
specs:
|
11
|
+
addressable (2.6.0)
|
12
|
+
public_suffix (>= 2.0.2, < 4.0)
|
13
|
+
crack (0.4.3)
|
14
|
+
safe_yaml (~> 1.0.0)
|
15
|
+
diff-lcs (1.3)
|
11
16
|
faraday (0.15.4)
|
12
17
|
multipart-post (>= 1.2, < 3)
|
18
|
+
hashdiff (0.3.8)
|
13
19
|
mimemagic (0.3.3)
|
14
20
|
multipart-post (2.0.0)
|
21
|
+
mustermann (1.0.3)
|
22
|
+
public_suffix (3.0.3)
|
23
|
+
rack (2.0.6)
|
24
|
+
rack-protection (2.0.5)
|
25
|
+
rack
|
15
26
|
rake (10.5.0)
|
27
|
+
rspec (3.8.0)
|
28
|
+
rspec-core (~> 3.8.0)
|
29
|
+
rspec-expectations (~> 3.8.0)
|
30
|
+
rspec-mocks (~> 3.8.0)
|
31
|
+
rspec-core (3.8.0)
|
32
|
+
rspec-support (~> 3.8.0)
|
33
|
+
rspec-expectations (3.8.2)
|
34
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
35
|
+
rspec-support (~> 3.8.0)
|
36
|
+
rspec-mocks (3.8.0)
|
37
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
38
|
+
rspec-support (~> 3.8.0)
|
39
|
+
rspec-support (3.8.0)
|
40
|
+
safe_yaml (1.0.5)
|
41
|
+
sinatra (2.0.5)
|
42
|
+
mustermann (~> 1.0)
|
43
|
+
rack (~> 2.0)
|
44
|
+
rack-protection (= 2.0.5)
|
45
|
+
tilt (~> 2.0)
|
46
|
+
tilt (2.0.9)
|
47
|
+
webmock (3.5.1)
|
48
|
+
addressable (>= 2.3.6)
|
49
|
+
crack (>= 0.3.2)
|
50
|
+
hashdiff
|
16
51
|
yard (0.9.18)
|
17
52
|
|
18
53
|
PLATFORMS
|
54
|
+
ruby
|
55
|
+
unknown
|
19
56
|
x64-mingw32
|
20
57
|
|
21
58
|
DEPENDENCIES
|
22
59
|
bundler (~> 2.0)
|
23
60
|
rake (~> 10.0)
|
61
|
+
rspec (~> 3.8)
|
62
|
+
sinatra (~> 2.0)
|
24
63
|
vt_api!
|
64
|
+
webmock (~> 3.5)
|
25
65
|
yard (~> 0.9)
|
26
66
|
|
67
|
+
RUBY VERSION
|
68
|
+
ruby 2.5.1p57
|
69
|
+
|
27
70
|
BUNDLED WITH
|
28
71
|
2.0.1
|
data/README.md
CHANGED
@@ -42,7 +42,7 @@ scans = file.scans # => [#<VtApi::ApiV2::File::AvResult ...>, ...]
|
|
42
42
|
|
43
43
|
scan_id = VtApi::ApiV2::File.schedule_scan file: 'foobar.txt' # => "92605f5aa57e20475a893cd63d998ffdafca8b1c6142fca225ea5638a2437fe6-2139190"
|
44
44
|
|
45
|
-
url = VtApi::ApiV2::URL.report resource: 'https://bitbucket.org/
|
45
|
+
url = VtApi::ApiV2::URL.report resource: 'https://bitbucket.org/thedeadferryman/vt_api' # => #<VtApi::ApiV2::URL ...>
|
46
46
|
|
47
47
|
comments = VtApi::ApiV2::Comments.get resource: 'fca0b14b72c426a67f06a771c9e2d67e6883972e' # => [#<VtApi::ApiV2::Comments::Comment ...>, ...]
|
48
48
|
comments = VtApi::ApiV2::Comments.put resource: 'fca0b14b72c426a67f06a771c9e2d67e6883972e', text: 'Lorem ipsum dolor sit amet...' # => true
|
data/Rakefile
CHANGED
@@ -1,10 +1,25 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
|
5
|
+
YARD_SOURCES = Dir['lib/**/*.rb']
|
6
|
+
YARD_EXTRAS = Dir['*.md']
|
7
|
+
|
8
|
+
begin
|
9
|
+
require 'rspec/core/rake_task'
|
10
|
+
RSpec::Core::RakeTask.new
|
11
|
+
rescue LoadError
|
12
|
+
STDERR.puts 'RSpec not found, skipping testing tasks'
|
13
|
+
end
|
14
|
+
|
15
|
+
begin
|
16
|
+
require 'yard'
|
17
|
+
YARD::Rake::YardocTask.new do |opts|
|
18
|
+
opts.files = YARD_SOURCES + %w[-] + YARD_EXTRAS
|
19
|
+
opts.options = %w[--verbose --no-private]
|
20
|
+
end
|
21
|
+
rescue LoadError
|
22
|
+
STDERR.puts 'YARDoc not found, skipping doc tasks'
|
23
|
+
end
|
24
|
+
|
25
|
+
task default: %i[spec build]
|
data/lib/vt_api.rb
CHANGED
@@ -1,28 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'vt_api/version'
|
4
|
+
require_relative 'vt_api/errors'
|
2
5
|
require_relative 'vt_api/internal'
|
3
6
|
require_relative 'vt_api/api'
|
4
7
|
|
5
8
|
module VtApi
|
6
9
|
|
7
|
-
# Error is thrown when some of required request params are missing
|
8
|
-
#
|
9
|
-
# @see Endpoint#params_valid
|
10
|
-
# @see ApiProvider#request
|
11
|
-
class MissingParametersError < ArgumentError
|
12
|
-
end
|
13
|
-
|
14
|
-
# Error is thrown when requested method could not be found in interface.
|
15
|
-
#
|
16
|
-
# @see ApiVersion#endpoint?
|
17
|
-
# @see ApiProvider#request
|
18
|
-
class UndefinedMethodError < NoMethodError
|
19
|
-
end
|
20
|
-
|
21
|
-
# Error is thrown when API returns HTTP code marked by interface as error.
|
22
|
-
#
|
23
|
-
# @see ApiVersion#error?
|
24
|
-
# @see ApiVersion#error
|
25
|
-
# @see ApiProvider#request
|
26
|
-
class ApiError < RuntimeError
|
27
|
-
end
|
28
10
|
end
|
data/lib/vt_api/api.rb
CHANGED
data/lib/vt_api/api/config.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'ostruct'
|
2
4
|
|
5
|
+
# @author Karl F. Meinkopf
|
3
6
|
module VtApi
|
4
7
|
# Default options for VT API, common for all versions
|
5
8
|
DEFAULT_OPTIONS = OpenStruct.new(
|
@@ -30,4 +33,4 @@ module VtApi
|
|
30
33
|
def self.token=(token)
|
31
34
|
options[:token] = token
|
32
35
|
end
|
33
|
-
end
|
36
|
+
end
|
data/lib/vt_api/api/v2.rb
CHANGED
@@ -1,16 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'config'
|
4
|
+
|
5
|
+
require_relative 'v2/essentials'
|
2
6
|
require_relative 'v2/file'
|
3
7
|
require_relative 'v2/url'
|
4
8
|
require_relative 'v2/comments'
|
5
9
|
|
6
10
|
module VtApi
|
11
|
+
# VT Public 2.0 API bindings
|
7
12
|
module ApiV2
|
8
|
-
|
9
|
-
# Get ApiProvider singleton configured for VT Public API 2.0.
|
10
|
-
#
|
11
|
-
# @return [ApiProvider]
|
12
|
-
def self.provider
|
13
|
-
@provider ||= ApiProvider.new Versions::API_V2, VtApi.options.adapter
|
14
|
-
end
|
15
13
|
end
|
16
|
-
end
|
14
|
+
end
|
@@ -1,3 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
|
4
|
+
require_relative 'essentials'
|
5
|
+
|
1
6
|
module VtApi
|
2
7
|
module ApiV2
|
3
8
|
# VT API 2.0 for comments
|
@@ -45,15 +50,14 @@ module VtApi
|
|
45
50
|
resp.response_code == 1
|
46
51
|
end
|
47
52
|
|
48
|
-
private
|
49
|
-
|
50
53
|
def self.parse_comments(api_resp)
|
51
|
-
|
54
|
+
# noinspection RubyResolve
|
55
|
+
if api_resp.response_code.nil? || (api_resp.response_code != 1)
|
52
56
|
[]
|
53
57
|
else
|
54
|
-
api_resp.comments.map {|comment| Comment.new comment.date, comment.text}
|
58
|
+
api_resp.comments.map { |comment| Comment.new comment.date, comment.text }
|
55
59
|
end
|
56
60
|
end
|
57
61
|
end
|
58
62
|
end
|
59
|
-
end
|
63
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VtApi
|
4
|
+
# VT Public 2.0 API bindings
|
5
|
+
module ApiV2
|
6
|
+
# Get ApiProvider singleton configured for VT Public API 2.0.
|
7
|
+
#
|
8
|
+
# @return [ApiProvider]
|
9
|
+
def self.provider
|
10
|
+
@provider ||= ApiProvider.new Versions::API_V2, VtApi.options.adapter
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
data/lib/vt_api/api/v2/file.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
require 'pathname'
|
3
5
|
|
@@ -5,14 +7,12 @@ require 'faraday/upload_io'
|
|
5
7
|
require 'mimemagic'
|
6
8
|
|
7
9
|
require_relative '../config'
|
8
|
-
require_relative '
|
9
|
-
require_relative '../v2'
|
10
|
+
require_relative 'essentials'
|
10
11
|
|
11
12
|
module VtApi
|
12
13
|
module ApiV2
|
13
14
|
# Class that represents VT Public API 2.0 for files.
|
14
15
|
class File
|
15
|
-
|
16
16
|
# Representation of particular AV result on file.
|
17
17
|
class AvResult
|
18
18
|
attr_reader :av_name, :triggered, :version, :threat_name, :update
|
@@ -29,7 +29,7 @@ module VtApi
|
|
29
29
|
|
30
30
|
# Existing ID types
|
31
31
|
# @see #id
|
32
|
-
ID_TYPES = %w
|
32
|
+
ID_TYPES = %w[scan_id resource_id md5 sha1 sha256].freeze
|
33
33
|
|
34
34
|
class << self
|
35
35
|
# @see https://developers.virustotal.com/v2.0/reference#file-report
|
@@ -55,9 +55,7 @@ module VtApi
|
|
55
55
|
# @param [String|Pathname|UploadIO] file
|
56
56
|
# @return [String] Scan ID.
|
57
57
|
def schedule_scan(file:)
|
58
|
-
if file.
|
59
|
-
file = file_to_io file
|
60
|
-
end
|
58
|
+
file = file_to_io file if file.is_a?(String) || file.is_a?(Pathname)
|
61
59
|
|
62
60
|
resp = ApiV2.provider.request 'file.scan', apikey: VtApi.options.token, file: file
|
63
61
|
resp.scan_id
|
@@ -79,10 +77,11 @@ module VtApi
|
|
79
77
|
#
|
80
78
|
# @see #initialize
|
81
79
|
def self.from_response(api_resp)
|
82
|
-
|
80
|
+
# noinspection RubyResolve
|
81
|
+
if api_resp.response_code.nil? || (api_resp.response_code < 1)
|
83
82
|
nil
|
84
83
|
else
|
85
|
-
report =
|
84
|
+
report = new api_resp
|
86
85
|
|
87
86
|
report
|
88
87
|
end
|
@@ -91,7 +90,8 @@ module VtApi
|
|
91
90
|
attr_reader :scan_date, :permalink, :scans, :scan_count, :trigger_count
|
92
91
|
|
93
92
|
# Initializes new object from VT API response.
|
94
|
-
# @note Direct creation of object cas cause errors since it doesn't contain any validity checks.
|
93
|
+
# @note Direct creation of object cas cause errors since it doesn't contain any validity checks.
|
94
|
+
# Use predefined API method bindings instead.
|
95
95
|
#
|
96
96
|
# @see .report
|
97
97
|
# @see .schedule_scan
|
@@ -117,9 +117,7 @@ module VtApi
|
|
117
117
|
# @param [Symbol|String] type
|
118
118
|
# @return [String] ID string of specified type.
|
119
119
|
def id(type = :id)
|
120
|
-
unless ID_TYPES.include? type.to_sym
|
121
|
-
raise ArgumentError, "There is no such id type (#{type}) in VT API 2.0"
|
122
|
-
end
|
120
|
+
raise ArgumentError, "There is no such id type (#{type}) in VT API 2.0" unless ID_TYPES.include? type.to_sym
|
123
121
|
|
124
122
|
@ids[type.to_sym]
|
125
123
|
end
|
@@ -128,13 +126,13 @@ module VtApi
|
|
128
126
|
|
129
127
|
# noinspection RubyResolve
|
130
128
|
def load_ids!(api_resp)
|
131
|
-
@ids = OpenStruct.new
|
129
|
+
@ids = OpenStruct.new(
|
132
130
|
scan_id: api_resp.scan_id,
|
133
131
|
md5: api_resp.md5,
|
134
132
|
sha1: api_resp.sha1,
|
135
133
|
sha256: api_resp.sha256,
|
136
134
|
resource_id: api_resp.resource
|
137
|
-
|
135
|
+
)
|
138
136
|
end
|
139
137
|
|
140
138
|
def load_meta!(api_resp)
|
@@ -153,4 +151,4 @@ module VtApi
|
|
153
151
|
end
|
154
152
|
end
|
155
153
|
end
|
156
|
-
end
|
154
|
+
end
|
data/lib/vt_api/api/v2/url.rb
CHANGED
@@ -1,16 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../config'
|
4
|
+
require_relative 'essentials'
|
5
|
+
|
1
6
|
module VtApi
|
2
7
|
module ApiV2
|
3
8
|
# Class that represents URL scan report.
|
4
9
|
class URL
|
5
10
|
class << self
|
6
|
-
|
7
11
|
# @see https://developers.virustotal.com/v2.0/reference#url-report
|
8
12
|
#
|
9
13
|
# @param [String] resource
|
10
|
-
# @param [Boolean] scan
|
14
|
+
# @param [Boolean] scan Schedule URL scan if it is not present in system.
|
11
15
|
# @return [URL] URL report object is present, <code>nil</code> otherwise.
|
12
16
|
def report(resource:, scan: false)
|
13
|
-
resp = ApiV2.provider.request 'url.report',
|
17
|
+
resp = ApiV2.provider.request 'url.report',
|
18
|
+
apikey: VtApi.options.token,
|
19
|
+
resource: resource,
|
20
|
+
schedule_scan: (scan ? 1 : 0)
|
14
21
|
pp resp
|
15
22
|
from_response resp
|
16
23
|
end
|
@@ -23,17 +30,17 @@ module VtApi
|
|
23
30
|
resp = ApiV2.provider.request 'url.report', apikey: VtApi.options.token, url: url
|
24
31
|
resp.scan_id
|
25
32
|
end
|
26
|
-
|
27
33
|
end
|
28
34
|
|
29
35
|
# Shorthand for #initialize.
|
30
36
|
#
|
31
37
|
# @see #initialize
|
32
38
|
def self.from_response(api_resp)
|
33
|
-
|
39
|
+
# noinspection RubyResolve
|
40
|
+
if api_resp.response_code.nil? || (api_resp.response_code < 1)
|
34
41
|
nil
|
35
42
|
else
|
36
|
-
report =
|
43
|
+
report = new api_resp
|
37
44
|
report
|
38
45
|
end
|
39
46
|
end
|
@@ -41,7 +48,8 @@ module VtApi
|
|
41
48
|
attr_reader :id, :url, :scan_date, :permalink, :filescan_id
|
42
49
|
|
43
50
|
# Initializes new object from VT API response.
|
44
|
-
# @note Direct creation of object cas cause errors since it doesn't contain any validity checks.
|
51
|
+
# @note Direct creation of object cas cause errors since it doesn't contain any validity checks.
|
52
|
+
# Use predefined API method bindings instead.
|
45
53
|
#
|
46
54
|
# @see .report
|
47
55
|
# @see .schedule_scan
|
@@ -75,4 +83,4 @@ module VtApi
|
|
75
83
|
end
|
76
84
|
end
|
77
85
|
end
|
78
|
-
end
|
86
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module VtApi
|
2
|
+
# Error is thrown when some of required request params are missing
|
3
|
+
#
|
4
|
+
# @see Endpoint#params_valid
|
5
|
+
# @see ApiProvider#request
|
6
|
+
class MissingParametersError < ArgumentError
|
7
|
+
end
|
8
|
+
|
9
|
+
# Error is thrown when requested method could not be found in interface.
|
10
|
+
#
|
11
|
+
# @see ApiVersion#endpoint?
|
12
|
+
# @see ApiProvider#request
|
13
|
+
class UndefinedMethodError < NoMethodError
|
14
|
+
end
|
15
|
+
|
16
|
+
# Error is thrown when API returns HTTP code marked by interface as error.
|
17
|
+
#
|
18
|
+
# @see ApiVersion#error?
|
19
|
+
# @see ApiVersion#error
|
20
|
+
# @see ApiProvider#request
|
21
|
+
class ApiError < RuntimeError
|
22
|
+
end
|
23
|
+
end
|
data/lib/vt_api/internal.rb
CHANGED
@@ -1,25 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'faraday'
|
2
4
|
|
3
5
|
require 'json'
|
4
6
|
require 'ostruct'
|
5
7
|
|
6
8
|
require_relative 'versions/default'
|
7
|
-
require_relative '
|
9
|
+
require_relative '../errors'
|
8
10
|
|
9
11
|
module VtApi
|
10
|
-
# API
|
12
|
+
# API interaction class.
|
11
13
|
#
|
12
14
|
# Uses [Faraday] for HTTP interaction.
|
13
15
|
class ApiProvider
|
14
|
-
|
15
16
|
# Create new ApiProvider.
|
16
17
|
#
|
17
18
|
# @param [ApiVersion] api_version API interface to use.
|
18
19
|
# @param [Object] adapter Faraday HTTP adapter.
|
19
20
|
# @return [Object]
|
20
21
|
def initialize(api_version = Versions::DEFAULT, adapter = :net_http)
|
21
|
-
unless api_version.
|
22
|
-
raise ArgumentError,
|
22
|
+
unless api_version.is_a? ApiVersion
|
23
|
+
raise ArgumentError,
|
24
|
+
"Invalid API interface supplied! Must be subclass of 'ApiVersion', got #{api_version.class} instead"
|
23
25
|
end
|
24
26
|
|
25
27
|
@api = api_version
|
@@ -42,24 +44,31 @@ module VtApi
|
|
42
44
|
# @param [Hash] params
|
43
45
|
# @return [OpenStruct] Method result object.
|
44
46
|
def request(method_name, params = {})
|
45
|
-
|
46
|
-
raise UndefinedMethodError, "Endpoint '#{method_name}' not found in '#{@api.version}' API interface."
|
47
|
-
end
|
48
|
-
|
49
|
-
endpoint = @api.endpoint method_name
|
50
|
-
|
51
|
-
unless endpoint.params_valid? (params)
|
52
|
-
raise MissingParametersError, "Missed parameters #{endpoint.missing_params(params).to_s} required by endpoint '#{method_name}'."
|
53
|
-
end
|
47
|
+
endpoint = endpoint(method_name, params)
|
54
48
|
|
55
49
|
result = @connection.public_send(endpoint.method, endpoint.uri, params, {})
|
56
50
|
|
57
|
-
if @api.error? result.status
|
58
|
-
raise ApiError, "#{@api.version} error: #{@api.error(result.status)}"
|
59
|
-
end
|
51
|
+
raise ApiError, "#{@api.version} error: #{@api.error(result.status)}" if @api.error? result.status
|
60
52
|
|
61
53
|
# noinspection RubyResolve
|
62
54
|
JSON.parse result.body, object_class: OpenStruct
|
63
55
|
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def endpoint(method_name, params)
|
60
|
+
unless @api.endpoint? method_name
|
61
|
+
raise UndefinedMethodError,
|
62
|
+
"Endpoint '#{method_name}' not found in '#{@api.version}' API interface."
|
63
|
+
end
|
64
|
+
|
65
|
+
endpoint = @api.endpoint method_name
|
66
|
+
|
67
|
+
unless endpoint.params_valid? params
|
68
|
+
raise MissingParametersError,
|
69
|
+
"Missed parameters #{endpoint.missing_params(params)} required by endpoint '#{method_name}'."
|
70
|
+
end
|
71
|
+
endpoint
|
72
|
+
end
|
64
73
|
end
|
65
|
-
end
|
74
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
+
module VtApi
|
3
4
|
# @abstract Base class for API interfaces.
|
4
5
|
class ApiVersion
|
5
6
|
# Get API base URL.
|
@@ -10,42 +11,42 @@ module VtApi
|
|
10
11
|
end
|
11
12
|
|
12
13
|
# API interface name/version.
|
13
|
-
#
|
14
|
+
#
|
14
15
|
# @return [String]
|
15
16
|
def version
|
16
17
|
nil
|
17
18
|
end
|
18
19
|
|
19
20
|
# Get API HTTP-code description.
|
20
|
-
#
|
21
|
-
# @param [Integer]
|
21
|
+
#
|
22
|
+
# @param [Integer] _http_code
|
22
23
|
# @return [String] Error description.
|
23
|
-
def error(
|
24
|
+
def error(_http_code)
|
24
25
|
nil
|
25
26
|
end
|
26
27
|
|
27
28
|
# Check whether API HTTP-code means error.
|
28
29
|
#
|
29
|
-
# @param [Integer]
|
30
|
+
# @param [Integer] _http_code
|
30
31
|
# @return [Boolean] Error description.
|
31
|
-
def error?(
|
32
|
+
def error?(_http_code)
|
32
33
|
false
|
33
34
|
end
|
34
35
|
|
35
36
|
# Get API method endpoint interface.
|
36
37
|
#
|
37
|
-
# @param [String]
|
38
|
+
# @param [String] _method API method name to be called.
|
38
39
|
# @return [VtApi::Endpoint]
|
39
|
-
def endpoint(
|
40
|
+
def endpoint(_method)
|
40
41
|
nil
|
41
42
|
end
|
42
43
|
|
43
44
|
# Check whether given method is defined in interface.
|
44
45
|
#
|
45
|
-
# @param [String]
|
46
|
+
# @param [String] _method Method name.
|
46
47
|
# @return [Boolean]
|
47
|
-
def endpoint?(
|
48
|
+
def endpoint?(_method)
|
48
49
|
false
|
49
50
|
end
|
50
51
|
end
|
51
|
-
end
|
52
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'faraday/connection'
|
2
4
|
|
3
5
|
module VtApi
|
@@ -23,16 +25,13 @@ module VtApi
|
|
23
25
|
@params = params
|
24
26
|
end
|
25
27
|
|
26
|
-
|
27
28
|
# Check whether given parameters match endpoint interface.
|
28
29
|
#
|
29
30
|
# @param [Hash] passed_params
|
30
31
|
# @return [Boolean]
|
31
32
|
def params_valid?(passed_params)
|
32
33
|
@params.each do |param, required|
|
33
|
-
if required
|
34
|
-
return false
|
35
|
-
end
|
34
|
+
return false if required && passed_params[param].nil?
|
36
35
|
end
|
37
36
|
|
38
37
|
true
|
@@ -46,9 +45,7 @@ module VtApi
|
|
46
45
|
missing = []
|
47
46
|
|
48
47
|
@params.each do |param, required|
|
49
|
-
if required
|
50
|
-
missing << param
|
51
|
-
end
|
48
|
+
missing << param if required && passed_params[param].nil?
|
52
49
|
end
|
53
50
|
|
54
51
|
missing
|
@@ -66,4 +63,4 @@ module VtApi
|
|
66
63
|
uri.gsub %r{([^:])[\/]+}, '\1/'
|
67
64
|
end
|
68
65
|
end
|
69
|
-
end
|
66
|
+
end
|
@@ -1,73 +1,64 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative '../api_version'
|
2
4
|
require_relative '../endpoint'
|
3
5
|
|
4
6
|
module VtApi
|
5
7
|
module Versions
|
6
|
-
|
7
8
|
# VirusTotal Public API 2.0 singleton interface.
|
8
9
|
# Describes all available API methods (endpoints).
|
9
10
|
class ApiV2 < ApiVersion
|
10
|
-
|
11
11
|
# Root URI for all endpoints.
|
12
12
|
BASE_URI = 'https://virustotal.com/vtapi/v2/'
|
13
13
|
|
14
14
|
# VirusTotal Public API v2.0 interface description.
|
15
15
|
ENDPOINTS = {
|
16
|
-
'file.report': Endpoint.new('file/report', :get,
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
resource: true,
|
48
|
-
before: false
|
49
|
-
}),
|
50
|
-
'comments.put': Endpoint.new('comments/put', :post, {
|
51
|
-
apikey: true,
|
52
|
-
resource: true,
|
53
|
-
comment: true
|
54
|
-
}),
|
55
|
-
}
|
16
|
+
'file.report': Endpoint.new('file/report', :get,
|
17
|
+
apikey: true,
|
18
|
+
resource: true),
|
19
|
+
'file.scan': Endpoint.new('file/scan', :post,
|
20
|
+
apikey: true,
|
21
|
+
file: true),
|
22
|
+
'file.rescan': Endpoint.new('file/rescan', :post,
|
23
|
+
apikey: true,
|
24
|
+
resource: true),
|
25
|
+
'url.report': Endpoint.new('url/report', :get,
|
26
|
+
apikey: true,
|
27
|
+
resource: true,
|
28
|
+
schedule_scan: false),
|
29
|
+
'url.scan': Endpoint.new('url/scan', :post,
|
30
|
+
apikey: true,
|
31
|
+
url: true),
|
32
|
+
'domain.report': Endpoint.new('domain/report', :get,
|
33
|
+
apikey: true,
|
34
|
+
domain: true),
|
35
|
+
'ip-address.report': Endpoint.new('ip-address/report', :get,
|
36
|
+
apikey: true,
|
37
|
+
domain: true),
|
38
|
+
'comments.get': Endpoint.new('comments/get', :get,
|
39
|
+
apikey: true,
|
40
|
+
resource: true,
|
41
|
+
before: false),
|
42
|
+
'comments.put': Endpoint.new('comments/put', :post,
|
43
|
+
apikey: true,
|
44
|
+
resource: true,
|
45
|
+
comment: true)
|
46
|
+
}.freeze
|
56
47
|
|
57
48
|
# List of possible HTTP error codes with descriptions
|
58
49
|
ERRORS = {
|
59
|
-
204 =>
|
60
|
-
400 =>
|
61
|
-
403 =>
|
62
|
-
404 =>
|
63
|
-
}
|
50
|
+
204 => 'Request rate limit exceeded.',
|
51
|
+
400 => 'Invalid arguments.',
|
52
|
+
403 => 'Access denied.',
|
53
|
+
404 => 'Endpoint not found.'
|
54
|
+
}.freeze
|
64
55
|
|
65
56
|
class << self
|
66
57
|
# Get interface instance.
|
67
58
|
#
|
68
59
|
# @return [VtApi::Versions::ApiV2]
|
69
60
|
def instance
|
70
|
-
@instance ||=
|
61
|
+
@instance ||= new
|
71
62
|
end
|
72
63
|
end
|
73
64
|
|
@@ -84,7 +75,7 @@ module VtApi
|
|
84
75
|
# @param [String] method Method name.
|
85
76
|
# @return [Boolean]
|
86
77
|
def endpoint?(method)
|
87
|
-
|
78
|
+
!endpoint(method).nil?
|
88
79
|
end
|
89
80
|
|
90
81
|
# Get API HTTP-code description.
|
@@ -100,10 +91,10 @@ module VtApi
|
|
100
91
|
# @param [Integer] http_code
|
101
92
|
# @return [Boolean] Error description.
|
102
93
|
def error?(http_code)
|
103
|
-
|
94
|
+
!error(http_code).nil?
|
104
95
|
end
|
105
96
|
|
106
|
-
#
|
97
|
+
# API base URL.
|
107
98
|
#
|
108
99
|
# @return [String]
|
109
100
|
def base_url
|
@@ -116,15 +107,9 @@ module VtApi
|
|
116
107
|
def version
|
117
108
|
'VTAPI-2.0'
|
118
109
|
end
|
119
|
-
|
120
|
-
private
|
121
|
-
|
122
|
-
# Locked constructor.
|
123
|
-
def initialize
|
124
|
-
end
|
125
110
|
end
|
126
111
|
|
127
112
|
# Shorthand for <code>ApiV2.instance</code>
|
128
113
|
API_V2 = ApiV2.instance
|
129
114
|
end
|
130
|
-
end
|
115
|
+
end
|
data/lib/vt_api/version.rb
CHANGED
data/vt_api.gemspec
CHANGED
@@ -26,20 +26,22 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.files = []
|
27
27
|
|
28
28
|
Dir.chdir(File.expand_path('..', __FILE__)) do
|
29
|
-
spec.files += `git ls-files -z`.split(0.chr).reject {|f| f.match(%r{^(test|spec|features)/})}
|
29
|
+
spec.files += `git ls-files -z`.split(0.chr).reject { |f| f.match(%r{^(test|spec|features)/}) }
|
30
30
|
end
|
31
31
|
|
32
32
|
|
33
33
|
spec.bindir = 'exe'
|
34
|
-
spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f)}
|
34
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
35
35
|
spec.require_paths = ['lib']
|
36
36
|
|
37
37
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
38
38
|
spec.add_development_dependency 'rake', '~> 10.0'
|
39
|
+
spec.add_development_dependency 'rspec', '~> 3.8'
|
40
|
+
spec.add_development_dependency 'sinatra', '~> 2.0'
|
41
|
+
spec.add_development_dependency 'webmock', '~> 3.5'
|
39
42
|
spec.add_development_dependency 'yard', '~> 0.9'
|
40
|
-
# spec.add_development_dependency 'rspec', '~> 3.8'
|
41
43
|
# spec.add_development_dependency 'pry', '~> 0.12'
|
42
|
-
|
44
|
+
|
43
45
|
spec.add_dependency 'faraday', '~> 0.15'
|
44
46
|
spec.add_dependency 'mimemagic', '~> 0.3'
|
45
47
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vt_api
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Karl 'Charon' Meinkopf
|
@@ -38,6 +38,48 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.8'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.8'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: sinatra
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '2.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '2.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: webmock
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '3.5'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '3.5'
|
41
83
|
- !ruby/object:Gem::Dependency
|
42
84
|
name: yard
|
43
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -88,22 +130,23 @@ extensions: []
|
|
88
130
|
extra_rdoc_files: []
|
89
131
|
files:
|
90
132
|
- ".gitignore"
|
133
|
+
- ".rubocop.yml"
|
134
|
+
- ".scrutinizer.yml"
|
91
135
|
- ".travis.yml"
|
92
|
-
- ".yardopts"
|
93
136
|
- Gemfile
|
94
137
|
- Gemfile.lock
|
95
138
|
- LICENSE.md
|
96
139
|
- README.md
|
97
140
|
- Rakefile
|
98
|
-
- bin/console
|
99
|
-
- bin/setup
|
100
141
|
- lib/vt_api.rb
|
101
142
|
- lib/vt_api/api.rb
|
102
143
|
- lib/vt_api/api/config.rb
|
103
144
|
- lib/vt_api/api/v2.rb
|
104
145
|
- lib/vt_api/api/v2/comments.rb
|
146
|
+
- lib/vt_api/api/v2/essentials.rb
|
105
147
|
- lib/vt_api/api/v2/file.rb
|
106
148
|
- lib/vt_api/api/v2/url.rb
|
149
|
+
- lib/vt_api/errors.rb
|
107
150
|
- lib/vt_api/internal.rb
|
108
151
|
- lib/vt_api/internal/api_provider.rb
|
109
152
|
- lib/vt_api/internal/api_version.rb
|
data/.yardopts
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
--verbose --no-private lib/**/*.rb - README.md LICENSE.md
|
data/bin/console
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "bundler/setup"
|
4
|
-
require "vt_api"
|
5
|
-
|
6
|
-
VtApi.token = 'fe2b8030e62c7d2e3a2ce7450159612fd3bf0419e0ffbdeea3e06e80d51a01af'
|
7
|
-
|
8
|
-
include VtApi
|
9
|
-
|
10
|
-
ApiV2::File.report resource: '13166fc9de263ee2c676430ae88e65040e10761c90e79347a2684476bc60e622'
|
11
|
-
|
12
|
-
# require "pry"
|
13
|
-
# Pry.start
|