virustotal_api 0.4.1 → 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +26 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +12 -5
  5. data/CHANGELOG.md +27 -2
  6. data/README.md +94 -56
  7. data/lib/virustotal_api.rb +7 -7
  8. data/lib/virustotal_api/analysis.rb +16 -0
  9. data/lib/virustotal_api/base.rb +41 -10
  10. data/lib/virustotal_api/domain.rb +18 -0
  11. data/lib/virustotal_api/exceptions.rb +3 -0
  12. data/lib/virustotal_api/file.rb +48 -0
  13. data/lib/virustotal_api/group.rb +18 -0
  14. data/lib/virustotal_api/ip.rb +18 -0
  15. data/lib/virustotal_api/uri.rb +2 -1
  16. data/lib/virustotal_api/url.rb +38 -0
  17. data/lib/virustotal_api/user.rb +18 -0
  18. data/lib/virustotal_api/version.rb +2 -1
  19. data/test/analysis_test.rb +26 -0
  20. data/test/base_test.rb +18 -23
  21. data/test/domain_test.rb +27 -0
  22. data/test/exceptions_test.rb +22 -0
  23. data/test/file_test.rb +63 -0
  24. data/test/fixtures/analysis.yml +544 -0
  25. data/test/fixtures/domain.yml +830 -0
  26. data/test/fixtures/domain_bad_request.yml +52 -0
  27. data/test/fixtures/file_analyse.yml +52 -0
  28. data/test/fixtures/file_find.yml +853 -0
  29. data/test/fixtures/file_not_found.yml +52 -0
  30. data/test/fixtures/file_rate_limit.yml +52 -0
  31. data/test/fixtures/file_unauthorized.yml +51 -0
  32. data/test/fixtures/file_upload.yml +54 -0
  33. data/test/fixtures/group_find.yml +216 -0
  34. data/test/fixtures/ip.yml +716 -0
  35. data/test/fixtures/unscanned_url_find.yml +44 -0
  36. data/test/fixtures/url_analyse.yml +52 -0
  37. data/test/fixtures/url_find.yml +599 -0
  38. data/test/fixtures/user_find.yml +213 -0
  39. data/test/group_test.rb +27 -0
  40. data/test/{ip_report_test.rb → ip_test.rb} +6 -4
  41. data/test/uri_test.rb +1 -1
  42. data/test/url_test.rb +47 -0
  43. data/test/user_test.rb +26 -0
  44. data/test/version_test.rb +1 -1
  45. data/virustotal_api.gemspec +10 -8
  46. metadata +102 -71
  47. data/.circleci/config.yml +0 -23
  48. data/.github/workflows/gem_publish.yml +0 -38
  49. data/lib/virustotal_api/domain_report.rb +0 -36
  50. data/lib/virustotal_api/file_report.rb +0 -37
  51. data/lib/virustotal_api/file_rescan.rb +0 -36
  52. data/lib/virustotal_api/file_scan.rb +0 -38
  53. data/lib/virustotal_api/ip_report.rb +0 -36
  54. data/lib/virustotal_api/url_report.rb +0 -41
  55. data/lib/virustotal_api/url_scan.rb +0 -36
  56. data/test/domain_report_test.rb +0 -32
  57. data/test/file_report_test.rb +0 -36
  58. data/test/file_rescan_test.rb +0 -32
  59. data/test/file_scan_test.rb +0 -30
  60. data/test/fixtures/domain_report.yml +0 -311
  61. data/test/fixtures/ip_report.yml +0 -1323
  62. data/test/fixtures/queue_unscanned_url_report.yml +0 -46
  63. data/test/fixtures/report.yml +0 -110
  64. data/test/fixtures/report_not_found.yml +0 -42
  65. data/test/fixtures/request_forbidden.yml +0 -38
  66. data/test/fixtures/rescan.yml +0 -47
  67. data/test/fixtures/scan.yml +0 -49
  68. data/test/fixtures/unscanned_url_report.yml +0 -43
  69. data/test/fixtures/url_report.yml +0 -95
  70. data/test/fixtures/url_scan.yml +0 -48
  71. data/test/url_report_test.rb +0 -57
  72. data/test/url_scan_test.rb +0 -30
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module VirustotalAPI
6
+ # A class for '/domains' API
7
+ class Domain < Base
8
+ # Find a domain.
9
+ #
10
+ # @param [String] domain The domain to search
11
+ # @param [String] api_key for virustotal
12
+ # @return [VirustotalAPI::Domain] Report Search Result
13
+ def self.find(domain, api_key)
14
+ report = perform("/domains/#{domain}", api_key)
15
+ new(report)
16
+ end
17
+ end
18
+ end
@@ -3,4 +3,7 @@
3
3
  module VirustotalAPI
4
4
  class RateLimitError < RuntimeError
5
5
  end
6
+
7
+ class Unauthorized < RuntimeError
8
+ end
6
9
  end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module VirustotalAPI
6
+ # A class for '/files' API
7
+ class File < Base
8
+ # Find a hash.
9
+ #
10
+ # @param [String] resource file as a md5/sha1/sha256 hash
11
+ # @param [String] api_key The key for virustotal
12
+ # @return [VirustotalAPI::File] Report Search Result
13
+ def self.find(resource, api_key)
14
+ report = perform("/files/#{resource}", api_key)
15
+ new(report)
16
+ end
17
+
18
+ # Upload a new file.
19
+ #
20
+ # @param [String] file_path for file to be sent for scan
21
+ # @param [String] api_key The key for virustotal
22
+ # @param [Hash] opts hash for additional options
23
+ # @return [VirusotalAPI::File] Report
24
+ def self.upload(file_path, api_key, opts = {})
25
+ filename = opts.fetch('filename') { ::File.basename(file_path) }
26
+ report = perform('/files', api_key, :post, filename: filename, file: ::File.open(file_path, 'r'))
27
+ new(report)
28
+ end
29
+
30
+ # Analyse a hash again.
31
+ #
32
+ # @param [String] resource file as a md5/sha1/sha256 hash
33
+ # @param [String] api_key The key for virustotal
34
+ # @return [VirustotalAPI::File] Report
35
+ def self.analyse(resource, api_key)
36
+ report = perform("/files/#{resource}/analyse", api_key, :post)
37
+ new(report)
38
+ end
39
+
40
+ # Check if the submitted hash is detected by an AV engine.
41
+ #
42
+ # @param [String] engine The engine to check.
43
+ # @return [Boolean] true if detected
44
+ def detected_by(engine)
45
+ report&.dig('data', 'attributes', 'last_analysis_results', engine, 'category') == 'malicious'
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module VirustotalAPI
6
+ # A class for '/groups' API
7
+ class Group < Base
8
+ # Find a Group.
9
+ #
10
+ # @param [String] group_id to find
11
+ # @param [String] api_key The key for virustotal
12
+ # @return [VirustotalAPI::User] Report
13
+ def self.find(group_id, api_key)
14
+ report = perform("/groups/#{group_id}", api_key)
15
+ new(report)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module VirustotalAPI
6
+ # A class for '/ip_addresses' API
7
+ class IP < Base
8
+ # Find an IP.
9
+ #
10
+ # @param [String] ip address The IP to find.
11
+ # @param [String] api_key The key for virustotal
12
+ # @return [VirustotalAPI::IPReport] Report
13
+ def self.find(ip, api_key)
14
+ report = perform("/ip_addresses/#{ip}", api_key)
15
+ new(report)
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module VirustotalAPI
4
- URI = 'https://www.virustotal.com/vtapi/v2'
4
+ # The API base URI
5
+ URI = 'https://www.virustotal.com/api/v3'
5
6
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module VirustotalAPI
6
+ # A class for '/urls' API
7
+ class URL < Base
8
+ # Find a URL.
9
+ #
10
+ # @param [String] resource as an ip/domain/url
11
+ # @param [String] api_key The key for virustotal
12
+ # @return [VirustotalAPI::URL] Report
13
+ def self.find(resource, api_key)
14
+ report = perform("/urls/#{url_identifier(resource)}", api_key)
15
+ new(report)
16
+ end
17
+
18
+ # Analyse a URL again.
19
+ #
20
+ # @param [String] resource as an ip/domain/url
21
+ # @param [String] api_key The key for virustotal
22
+ # @return [VirustotalAPI::URL] Report
23
+ def self.analyse(resource, api_key)
24
+ report = perform("/urls/#{url_identifier(resource)}/analyse", api_key, :post)
25
+ new(report)
26
+ end
27
+
28
+ # Upload a URL for detection.
29
+ #
30
+ # @param [String] resource as an ip/domain/url
31
+ # @param [String] api_key The key for virustotal
32
+ # @return [VirustotalAPI::URL] Report
33
+ def self.upload(resource, api_key)
34
+ report = perform('/urls', api_key, :post, url: resource)
35
+ new(report)
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base'
4
+
5
+ module VirustotalAPI
6
+ # A class for '/users' API
7
+ class User < Base
8
+ # Find a User.
9
+ #
10
+ # @param [String] user_key with id or api_key
11
+ # @param [String] api_key The key for virustotal
12
+ # @return [VirustotalAPI::User] Report
13
+ def self.find(user_key, api_key)
14
+ report = perform("/users/#{user_key}", api_key)
15
+ new(report)
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module VirustotalAPI
4
- VERSION = '0.4.1'
4
+ # The GEM version
5
+ VERSION = '0.5.4'
5
6
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require './test/test_helper'
4
+
5
+ class VirustotalAPIAnalysisTest < Minitest::Test
6
+ def setup
7
+ @url = 'http://www.google.com'
8
+ @api_key = 'theapikey'
9
+ end
10
+
11
+ def test_todo
12
+ VCR.use_cassette('url_find') do
13
+ vtreport = VirustotalAPI::URL.find(@url, @api_key)
14
+
15
+ @id = vtreport.id
16
+ assert @id.is_a?(String)
17
+ end
18
+
19
+ VCR.use_cassette('analysis') do
20
+ analysis = VirustotalAPI::Analysis.find(@id, @api_key)
21
+
22
+ assert analysis.exists?
23
+ assert analysis.id.is_a?(String)
24
+ end
25
+ end
26
+ end
@@ -1,10 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # rubocop:disable LineLength
4
3
  require './test/test_helper'
5
4
 
6
5
  class VirustotalAPIBaseTest < Minitest::Test
7
6
  def setup
7
+ @domain = 'xpressco.za'
8
8
  @sha256 = '01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b'
9
9
  @api_key = 'testapikey'
10
10
  end
@@ -15,45 +15,40 @@ class VirustotalAPIBaseTest < Minitest::Test
15
15
 
16
16
  # Instance Method
17
17
  def test_api_uri_instance_method
18
- base_uri = 'https://www.virustotal.com/vtapi/v2'
19
- vt_base = VirustotalAPI::Base.new
18
+ base_uri = 'https://www.virustotal.com/api/v3'
19
+ vt_base = VirustotalAPI::Base.new(nil)
20
20
 
21
21
  assert vt_base.api_uri.is_a?(String)
22
- assert vt_base.api_uri, base_uri
22
+ assert_equal base_uri, vt_base.api_uri
23
23
  end
24
24
 
25
25
  # Class Method
26
26
  def test_api_uri_class_method
27
- base_uri = 'https://www.virustotal.com/vtapi/v2'
27
+ base_uri = 'https://www.virustotal.com/api/v3'
28
28
 
29
29
  assert VirustotalAPI::Base.api_uri.is_a?(String)
30
- assert VirustotalAPI::Base.api_uri, base_uri
30
+ assert_equal base_uri, VirustotalAPI::Base.api_uri
31
31
  end
32
32
 
33
- def test_parse_code_200
34
- mock_response200 = Minitest::Mock.new
35
- mock_response200.expect(:code, 200)
36
- mock_response200.expect(:body, '{}')
33
+ def test_exists?
34
+ VCR.use_cassette('file_find') do
35
+ virustotal_report = VirustotalAPI::File.find(@sha256, @api_key)
37
36
 
38
- assert VirustotalAPI::Base.parse(mock_response200), {}
37
+ assert virustotal_report.exists?
38
+ end
39
39
  end
40
40
 
41
- def test_parse_code_204
42
- mock_response204 = Minitest::Mock.new
43
- mock_response204.expect(:code, 204)
44
- mock_response204.expect(:body, '{}')
41
+ def test_not_exists?
42
+ VCR.use_cassette('file_not_found') do
43
+ virustotal_report = VirustotalAPI::File.find(@sha256, @api_key)
45
44
 
46
- assert_raises VirustotalAPI::RateLimitError do
47
- VirustotalAPI::Base.parse(mock_response204)
45
+ assert !virustotal_report.exists?
48
46
  end
49
- end
50
47
 
51
- # Test using FileReport
52
- def test_exists?
53
- VCR.use_cassette('report') do
54
- virustotal_report = VirustotalAPI::FileReport.find(@sha256, @api_key)
48
+ VCR.use_cassette('domain_bad_request') do
49
+ virustotal_report = VirustotalAPI::Domain.find(@domain, @api_key)
55
50
 
56
- assert virustotal_report.exists?, true
51
+ assert !virustotal_report.exists?
57
52
  end
58
53
  end
59
54
  end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require './test/test_helper'
4
+
5
+ class VirustotalAPIDomainTest < Minitest::Test
6
+ def setup
7
+ @domain = 'virustotal.com'
8
+ @api_key = 'testapikey'
9
+ end
10
+
11
+ def test_class_exists
12
+ assert VirustotalAPI::Domain
13
+ end
14
+
15
+ def test_report_response
16
+ VCR.use_cassette('domain') do
17
+ vtdomain_report = VirustotalAPI::Domain.find(@domain, @api_key)
18
+
19
+ # Make sure that the JSON was parsed
20
+ assert vtdomain_report.exists?
21
+ assert vtdomain_report.is_a?(VirustotalAPI::Domain)
22
+ assert vtdomain_report.report.is_a?(Hash)
23
+ assert vtdomain_report.id.is_a?(String)
24
+ assert vtdomain_report.report_url.is_a?(String)
25
+ end
26
+ end
27
+ end
@@ -3,7 +3,29 @@
3
3
  require './test/test_helper'
4
4
 
5
5
  class RateLimitErrorTest < Minitest::Test
6
+ def setup
7
+ @sha256 = '01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b'
8
+ @api_key = 'testapikey'
9
+ end
10
+
6
11
  def test_class_exists
7
12
  assert VirustotalAPI::RateLimitError
13
+ assert VirustotalAPI::Unauthorized
14
+ end
15
+
16
+ def test_unauthorized
17
+ VCR.use_cassette('file_unauthorized') do
18
+ assert_raises VirustotalAPI::Unauthorized do
19
+ VirustotalAPI::File.find(@sha256, @api_key)
20
+ end
21
+ end
22
+ end
23
+
24
+ def test_rate_limit
25
+ VCR.use_cassette('file_rate_limit') do
26
+ assert_raises VirustotalAPI::RateLimitError do
27
+ VirustotalAPI::File.analyse(@sha256, @api_key)
28
+ end
29
+ end
8
30
  end
9
31
  end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require './test/test_helper'
4
+
5
+ class VirustotalAPIFileTest < Minitest::Test
6
+ def setup
7
+ @sha256 = '01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b'
8
+ @file_path = File.expand_path('test/fixtures/null_file')
9
+ @api_key = 'testapikey'
10
+ end
11
+
12
+ def test_class_exists
13
+ assert VirustotalAPI::File
14
+ end
15
+
16
+ def test_report_response
17
+ VCR.use_cassette('file_find') do
18
+ vt_file_report = VirustotalAPI::File.find(@sha256, @api_key)
19
+
20
+ # Make sure that the JSON was parsed
21
+ assert vt_file_report.exists?
22
+ assert vt_file_report.is_a?(VirustotalAPI::File)
23
+ assert vt_file_report.report.is_a?(Hash)
24
+ assert vt_file_report.id.is_a?(String)
25
+ assert vt_file_report.report_url.is_a?(String)
26
+ end
27
+ end
28
+
29
+ def test_find
30
+ id = '01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b'
31
+ permalink = "https://www.virustotal.com/api/v3/files/#{id}"
32
+
33
+ VCR.use_cassette('file_find') do
34
+ vt_file_report = VirustotalAPI::File.find(@sha256, @api_key)
35
+
36
+ assert_equal permalink, vt_file_report.report_url
37
+ assert_equal id, vt_file_report.id
38
+ assert vt_file_report.detected_by('Avira')
39
+ assert !vt_file_report.detected_by('Acronis')
40
+ assert !vt_file_report.detected_by('Yeyeyeye') # not present in file
41
+ end
42
+ end
43
+
44
+ def test_upload
45
+ VCR.use_cassette('file_upload') do
46
+ vt_file_upload = VirustotalAPI::File.upload(@file_path, @api_key)
47
+
48
+ assert vt_file_upload.exists?
49
+ assert vt_file_upload.report.is_a?(Hash)
50
+ assert vt_file_upload.id.is_a?(String)
51
+ end
52
+ end
53
+
54
+ def test_analyse
55
+ VCR.use_cassette('file_analyse') do
56
+ vt_file_analyse = VirustotalAPI::File.analyse(@sha256, @api_key)
57
+
58
+ assert vt_file_analyse.exists?
59
+ assert vt_file_analyse.report.is_a?(Hash)
60
+ assert vt_file_analyse.id.is_a?(String)
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,544 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://www.virustotal.com/api/v3/analyses/u-a354494a73382ea0b4bc47f4c9e8d6c578027cd4598196dc88f05a22b5817293-1599122231
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - "*/*"
12
+ User-Agent:
13
+ - rest-client/2.1.0 (linux-gnu x86_64) ruby/2.5.1p57
14
+ X-Apikey:
15
+ - theapikey
16
+ Content-Length:
17
+ - '0'
18
+ Content-Type:
19
+ - application/x-www-form-urlencoded
20
+ Accept-Encoding:
21
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
22
+ Host:
23
+ - www.virustotal.com
24
+ response:
25
+ status:
26
+ code: 200
27
+ message: OK
28
+ headers:
29
+ Cache-Control:
30
+ - no-cache
31
+ Content-Type:
32
+ - application/json; charset=utf-8
33
+ X-Cloud-Trace-Context:
34
+ - ee845ce2e3e28aee8c7f2c8e2de417cd
35
+ Date:
36
+ - Thu, 03 Sep 2020 08:43:03 GMT
37
+ Server:
38
+ - Google Frontend
39
+ Content-Length:
40
+ - '18600'
41
+ body:
42
+ encoding: UTF-8
43
+ string: |-
44
+ {
45
+ "data": {
46
+ "attributes": {
47
+ "date": 1599122231,
48
+ "results": {
49
+ "ADMINUSLabs": {
50
+ "category": "harmless",
51
+ "engine_name": "ADMINUSLabs",
52
+ "method": "blacklist",
53
+ "result": "clean"
54
+ },
55
+ "AegisLab WebGuard": {
56
+ "category": "harmless",
57
+ "engine_name": "AegisLab WebGuard",
58
+ "method": "blacklist",
59
+ "result": "clean"
60
+ },
61
+ "AlienVault": {
62
+ "category": "harmless",
63
+ "engine_name": "AlienVault",
64
+ "method": "blacklist",
65
+ "result": "clean"
66
+ },
67
+ "Antiy-AVL": {
68
+ "category": "harmless",
69
+ "engine_name": "Antiy-AVL",
70
+ "method": "blacklist",
71
+ "result": "clean"
72
+ },
73
+ "Artists Against 419": {
74
+ "category": "harmless",
75
+ "engine_name": "Artists Against 419",
76
+ "method": "blacklist",
77
+ "result": "clean"
78
+ },
79
+ "AutoShun": {
80
+ "category": "undetected",
81
+ "engine_name": "AutoShun",
82
+ "method": "blacklist",
83
+ "result": "unrated"
84
+ },
85
+ "Avira": {
86
+ "category": "harmless",
87
+ "engine_name": "Avira",
88
+ "method": "blacklist",
89
+ "result": "clean"
90
+ },
91
+ "BADWARE.INFO": {
92
+ "category": "harmless",
93
+ "engine_name": "BADWARE.INFO",
94
+ "method": "blacklist",
95
+ "result": "clean"
96
+ },
97
+ "Baidu-International": {
98
+ "category": "harmless",
99
+ "engine_name": "Baidu-International",
100
+ "method": "blacklist",
101
+ "result": "clean"
102
+ },
103
+ "BitDefender": {
104
+ "category": "harmless",
105
+ "engine_name": "BitDefender",
106
+ "method": "blacklist",
107
+ "result": "clean"
108
+ },
109
+ "BlockList": {
110
+ "category": "harmless",
111
+ "engine_name": "BlockList",
112
+ "method": "blacklist",
113
+ "result": "clean"
114
+ },
115
+ "Blueliv": {
116
+ "category": "harmless",
117
+ "engine_name": "Blueliv",
118
+ "method": "blacklist",
119
+ "result": "clean"
120
+ },
121
+ "CINS Army": {
122
+ "category": "harmless",
123
+ "engine_name": "CINS Army",
124
+ "method": "blacklist",
125
+ "result": "clean"
126
+ },
127
+ "CLEAN MX": {
128
+ "category": "harmless",
129
+ "engine_name": "CLEAN MX",
130
+ "method": "blacklist",
131
+ "result": "clean"
132
+ },
133
+ "CRDF": {
134
+ "category": "harmless",
135
+ "engine_name": "CRDF",
136
+ "method": "blacklist",
137
+ "result": "clean"
138
+ },
139
+ "Certego": {
140
+ "category": "harmless",
141
+ "engine_name": "Certego",
142
+ "method": "blacklist",
143
+ "result": "clean"
144
+ },
145
+ "Comodo Valkyrie Verdict": {
146
+ "category": "harmless",
147
+ "engine_name": "Comodo Valkyrie Verdict",
148
+ "method": "blacklist",
149
+ "result": "clean"
150
+ },
151
+ "CyRadar": {
152
+ "category": "harmless",
153
+ "engine_name": "CyRadar",
154
+ "method": "blacklist",
155
+ "result": "clean"
156
+ },
157
+ "Cyan": {
158
+ "category": "undetected",
159
+ "engine_name": "Cyan",
160
+ "method": "blacklist",
161
+ "result": "unrated"
162
+ },
163
+ "CyberCrime": {
164
+ "category": "harmless",
165
+ "engine_name": "CyberCrime",
166
+ "method": "blacklist",
167
+ "result": "clean"
168
+ },
169
+ "Cyren": {
170
+ "category": "harmless",
171
+ "engine_name": "Cyren",
172
+ "method": "blacklist",
173
+ "result": "clean"
174
+ },
175
+ "DNS8": {
176
+ "category": "harmless",
177
+ "engine_name": "DNS8",
178
+ "method": "blacklist",
179
+ "result": "clean"
180
+ },
181
+ "Dr.Web": {
182
+ "category": "harmless",
183
+ "engine_name": "Dr.Web",
184
+ "method": "blacklist",
185
+ "result": "clean"
186
+ },
187
+ "ESET": {
188
+ "category": "harmless",
189
+ "engine_name": "ESET",
190
+ "method": "blacklist",
191
+ "result": "clean"
192
+ },
193
+ "EmergingThreats": {
194
+ "category": "harmless",
195
+ "engine_name": "EmergingThreats",
196
+ "method": "blacklist",
197
+ "result": "clean"
198
+ },
199
+ "Emsisoft": {
200
+ "category": "harmless",
201
+ "engine_name": "Emsisoft",
202
+ "method": "blacklist",
203
+ "result": "clean"
204
+ },
205
+ "EonScope": {
206
+ "category": "harmless",
207
+ "engine_name": "EonScope",
208
+ "method": "blacklist",
209
+ "result": "clean"
210
+ },
211
+ "Feodo Tracker": {
212
+ "category": "harmless",
213
+ "engine_name": "Feodo Tracker",
214
+ "method": "blacklist",
215
+ "result": "clean"
216
+ },
217
+ "Forcepoint ThreatSeeker": {
218
+ "category": "harmless",
219
+ "engine_name": "Forcepoint ThreatSeeker",
220
+ "method": "blacklist",
221
+ "result": "clean"
222
+ },
223
+ "Fortinet": {
224
+ "category": "harmless",
225
+ "engine_name": "Fortinet",
226
+ "method": "blacklist",
227
+ "result": "clean"
228
+ },
229
+ "FraudScore": {
230
+ "category": "harmless",
231
+ "engine_name": "FraudScore",
232
+ "method": "blacklist",
233
+ "result": "clean"
234
+ },
235
+ "G-Data": {
236
+ "category": "harmless",
237
+ "engine_name": "G-Data",
238
+ "method": "blacklist",
239
+ "result": "clean"
240
+ },
241
+ "Google Safebrowsing": {
242
+ "category": "harmless",
243
+ "engine_name": "Google Safebrowsing",
244
+ "method": "blacklist",
245
+ "result": "clean"
246
+ },
247
+ "GreenSnow": {
248
+ "category": "harmless",
249
+ "engine_name": "GreenSnow",
250
+ "method": "blacklist",
251
+ "result": "clean"
252
+ },
253
+ "Hoplite Industries": {
254
+ "category": "harmless",
255
+ "engine_name": "Hoplite Industries",
256
+ "method": "blacklist",
257
+ "result": "clean"
258
+ },
259
+ "IPsum": {
260
+ "category": "harmless",
261
+ "engine_name": "IPsum",
262
+ "method": "blacklist",
263
+ "result": "clean"
264
+ },
265
+ "K7AntiVirus": {
266
+ "category": "harmless",
267
+ "engine_name": "K7AntiVirus",
268
+ "method": "blacklist",
269
+ "result": "clean"
270
+ },
271
+ "Kaspersky": {
272
+ "category": "harmless",
273
+ "engine_name": "Kaspersky",
274
+ "method": "blacklist",
275
+ "result": "clean"
276
+ },
277
+ "Lumu": {
278
+ "category": "undetected",
279
+ "engine_name": "Lumu",
280
+ "method": "blacklist",
281
+ "result": "unrated"
282
+ },
283
+ "MalSilo": {
284
+ "category": "harmless",
285
+ "engine_name": "MalSilo",
286
+ "method": "blacklist",
287
+ "result": "clean"
288
+ },
289
+ "Malware Domain Blocklist": {
290
+ "category": "harmless",
291
+ "engine_name": "Malware Domain Blocklist",
292
+ "method": "blacklist",
293
+ "result": "clean"
294
+ },
295
+ "MalwareDomainList": {
296
+ "category": "harmless",
297
+ "engine_name": "MalwareDomainList",
298
+ "method": "blacklist",
299
+ "result": "clean"
300
+ },
301
+ "MalwarePatrol": {
302
+ "category": "harmless",
303
+ "engine_name": "MalwarePatrol",
304
+ "method": "blacklist",
305
+ "result": "clean"
306
+ },
307
+ "Malwared": {
308
+ "category": "harmless",
309
+ "engine_name": "Malwared",
310
+ "method": "blacklist",
311
+ "result": "clean"
312
+ },
313
+ "Netcraft": {
314
+ "category": "undetected",
315
+ "engine_name": "Netcraft",
316
+ "method": "blacklist",
317
+ "result": "unrated"
318
+ },
319
+ "NotMining": {
320
+ "category": "undetected",
321
+ "engine_name": "NotMining",
322
+ "method": "blacklist",
323
+ "result": "unrated"
324
+ },
325
+ "Nucleon": {
326
+ "category": "harmless",
327
+ "engine_name": "Nucleon",
328
+ "method": "blacklist",
329
+ "result": "clean"
330
+ },
331
+ "OpenPhish": {
332
+ "category": "harmless",
333
+ "engine_name": "OpenPhish",
334
+ "method": "blacklist",
335
+ "result": "clean"
336
+ },
337
+ "PREBYTES": {
338
+ "category": "harmless",
339
+ "engine_name": "PREBYTES",
340
+ "method": "blacklist",
341
+ "result": "clean"
342
+ },
343
+ "PhishLabs": {
344
+ "category": "undetected",
345
+ "engine_name": "PhishLabs",
346
+ "method": "blacklist",
347
+ "result": "unrated"
348
+ },
349
+ "Phishing Database": {
350
+ "category": "harmless",
351
+ "engine_name": "Phishing Database",
352
+ "method": "blacklist",
353
+ "result": "clean"
354
+ },
355
+ "Phishtank": {
356
+ "category": "harmless",
357
+ "engine_name": "Phishtank",
358
+ "method": "blacklist",
359
+ "result": "clean"
360
+ },
361
+ "Quick Heal": {
362
+ "category": "harmless",
363
+ "engine_name": "Quick Heal",
364
+ "method": "blacklist",
365
+ "result": "clean"
366
+ },
367
+ "Quttera": {
368
+ "category": "harmless",
369
+ "engine_name": "Quttera",
370
+ "method": "blacklist",
371
+ "result": "clean"
372
+ },
373
+ "Rising": {
374
+ "category": "harmless",
375
+ "engine_name": "Rising",
376
+ "method": "blacklist",
377
+ "result": "clean"
378
+ },
379
+ "SCUMWARE.org": {
380
+ "category": "harmless",
381
+ "engine_name": "SCUMWARE.org",
382
+ "method": "blacklist",
383
+ "result": "clean"
384
+ },
385
+ "Sangfor": {
386
+ "category": "harmless",
387
+ "engine_name": "Sangfor",
388
+ "method": "blacklist",
389
+ "result": "clean"
390
+ },
391
+ "SecureBrain": {
392
+ "category": "harmless",
393
+ "engine_name": "SecureBrain",
394
+ "method": "blacklist",
395
+ "result": "clean"
396
+ },
397
+ "Snort IP sample list": {
398
+ "category": "harmless",
399
+ "engine_name": "Snort IP sample list",
400
+ "method": "blacklist",
401
+ "result": "clean"
402
+ },
403
+ "Sophos": {
404
+ "category": "harmless",
405
+ "engine_name": "Sophos",
406
+ "method": "blacklist",
407
+ "result": "clean"
408
+ },
409
+ "Spam404": {
410
+ "category": "harmless",
411
+ "engine_name": "Spam404",
412
+ "method": "blacklist",
413
+ "result": "clean"
414
+ },
415
+ "Spamhaus": {
416
+ "category": "harmless",
417
+ "engine_name": "Spamhaus",
418
+ "method": "blacklist",
419
+ "result": "clean"
420
+ },
421
+ "StopBadware": {
422
+ "category": "undetected",
423
+ "engine_name": "StopBadware",
424
+ "method": "blacklist",
425
+ "result": "unrated"
426
+ },
427
+ "StopForumSpam": {
428
+ "category": "harmless",
429
+ "engine_name": "StopForumSpam",
430
+ "method": "blacklist",
431
+ "result": "clean"
432
+ },
433
+ "Sucuri SiteCheck": {
434
+ "category": "harmless",
435
+ "engine_name": "Sucuri SiteCheck",
436
+ "method": "blacklist",
437
+ "result": "clean"
438
+ },
439
+ "Tencent": {
440
+ "category": "harmless",
441
+ "engine_name": "Tencent",
442
+ "method": "blacklist",
443
+ "result": "clean"
444
+ },
445
+ "ThreatHive": {
446
+ "category": "harmless",
447
+ "engine_name": "ThreatHive",
448
+ "method": "blacklist",
449
+ "result": "clean"
450
+ },
451
+ "Threatsourcing": {
452
+ "category": "harmless",
453
+ "engine_name": "Threatsourcing",
454
+ "method": "blacklist",
455
+ "result": "clean"
456
+ },
457
+ "Trustwave": {
458
+ "category": "harmless",
459
+ "engine_name": "Trustwave",
460
+ "method": "blacklist",
461
+ "result": "clean"
462
+ },
463
+ "URLhaus": {
464
+ "category": "harmless",
465
+ "engine_name": "URLhaus",
466
+ "method": "blacklist",
467
+ "result": "clean"
468
+ },
469
+ "VX Vault": {
470
+ "category": "harmless",
471
+ "engine_name": "VX Vault",
472
+ "method": "blacklist",
473
+ "result": "clean"
474
+ },
475
+ "Virusdie External Site Scan": {
476
+ "category": "harmless",
477
+ "engine_name": "Virusdie External Site Scan",
478
+ "method": "blacklist",
479
+ "result": "clean"
480
+ },
481
+ "Web Security Guard": {
482
+ "category": "harmless",
483
+ "engine_name": "Web Security Guard",
484
+ "method": "blacklist",
485
+ "result": "clean"
486
+ },
487
+ "Yandex Safebrowsing": {
488
+ "category": "harmless",
489
+ "engine_name": "Yandex Safebrowsing",
490
+ "method": "blacklist",
491
+ "result": "clean"
492
+ },
493
+ "ZeroCERT": {
494
+ "category": "harmless",
495
+ "engine_name": "ZeroCERT",
496
+ "method": "blacklist",
497
+ "result": "clean"
498
+ },
499
+ "desenmascara.me": {
500
+ "category": "harmless",
501
+ "engine_name": "desenmascara.me",
502
+ "method": "blacklist",
503
+ "result": "clean"
504
+ },
505
+ "malwares.com URL checker": {
506
+ "category": "harmless",
507
+ "engine_name": "malwares.com URL checker",
508
+ "method": "blacklist",
509
+ "result": "clean"
510
+ },
511
+ "securolytics": {
512
+ "category": "harmless",
513
+ "engine_name": "securolytics",
514
+ "method": "blacklist",
515
+ "result": "clean"
516
+ },
517
+ "zvelo": {
518
+ "category": "harmless",
519
+ "engine_name": "zvelo",
520
+ "method": "blacklist",
521
+ "result": "clean"
522
+ }
523
+ },
524
+ "stats": {
525
+ "harmless": 72,
526
+ "malicious": 0,
527
+ "suspicious": 0,
528
+ "timeout": 0,
529
+ "undetected": 7
530
+ },
531
+ "status": "completed"
532
+ },
533
+ "id": "u-a354494a73382ea0b4bc47f4c9e8d6c578027cd4598196dc88f05a22b5817293-1599122231",
534
+ "type": "analysis"
535
+ },
536
+ "meta": {
537
+ "url_info": {
538
+ "id": "a354494a73382ea0b4bc47f4c9e8d6c578027cd4598196dc88f05a22b5817293",
539
+ "url": "http://virustotal.com/"
540
+ }
541
+ }
542
+ }
543
+ recorded_at: Thu, 03 Sep 2020 08:43:03 GMT
544
+ recorded_with: VCR 6.0.0