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.
- checksums.yaml +4 -4
- data/.github/workflows/ruby.yml +26 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +12 -5
- data/CHANGELOG.md +27 -2
- data/README.md +94 -56
- data/lib/virustotal_api.rb +7 -7
- data/lib/virustotal_api/analysis.rb +16 -0
- data/lib/virustotal_api/base.rb +41 -10
- data/lib/virustotal_api/domain.rb +18 -0
- data/lib/virustotal_api/exceptions.rb +3 -0
- data/lib/virustotal_api/file.rb +48 -0
- data/lib/virustotal_api/group.rb +18 -0
- data/lib/virustotal_api/ip.rb +18 -0
- data/lib/virustotal_api/uri.rb +2 -1
- data/lib/virustotal_api/url.rb +38 -0
- data/lib/virustotal_api/user.rb +18 -0
- data/lib/virustotal_api/version.rb +2 -1
- data/test/analysis_test.rb +26 -0
- data/test/base_test.rb +18 -23
- data/test/domain_test.rb +27 -0
- data/test/exceptions_test.rb +22 -0
- data/test/file_test.rb +63 -0
- data/test/fixtures/analysis.yml +544 -0
- data/test/fixtures/domain.yml +830 -0
- data/test/fixtures/domain_bad_request.yml +52 -0
- data/test/fixtures/file_analyse.yml +52 -0
- data/test/fixtures/file_find.yml +853 -0
- data/test/fixtures/file_not_found.yml +52 -0
- data/test/fixtures/file_rate_limit.yml +52 -0
- data/test/fixtures/file_unauthorized.yml +51 -0
- data/test/fixtures/file_upload.yml +54 -0
- data/test/fixtures/group_find.yml +216 -0
- data/test/fixtures/ip.yml +716 -0
- data/test/fixtures/unscanned_url_find.yml +44 -0
- data/test/fixtures/url_analyse.yml +52 -0
- data/test/fixtures/url_find.yml +599 -0
- data/test/fixtures/user_find.yml +213 -0
- data/test/group_test.rb +27 -0
- data/test/{ip_report_test.rb → ip_test.rb} +6 -4
- data/test/uri_test.rb +1 -1
- data/test/url_test.rb +47 -0
- data/test/user_test.rb +26 -0
- data/test/version_test.rb +1 -1
- data/virustotal_api.gemspec +10 -8
- metadata +102 -71
- data/.circleci/config.yml +0 -23
- data/.github/workflows/gem_publish.yml +0 -38
- data/lib/virustotal_api/domain_report.rb +0 -36
- data/lib/virustotal_api/file_report.rb +0 -37
- data/lib/virustotal_api/file_rescan.rb +0 -36
- data/lib/virustotal_api/file_scan.rb +0 -38
- data/lib/virustotal_api/ip_report.rb +0 -36
- data/lib/virustotal_api/url_report.rb +0 -41
- data/lib/virustotal_api/url_scan.rb +0 -36
- data/test/domain_report_test.rb +0 -32
- data/test/file_report_test.rb +0 -36
- data/test/file_rescan_test.rb +0 -32
- data/test/file_scan_test.rb +0 -30
- data/test/fixtures/domain_report.yml +0 -311
- data/test/fixtures/ip_report.yml +0 -1323
- data/test/fixtures/queue_unscanned_url_report.yml +0 -46
- data/test/fixtures/report.yml +0 -110
- data/test/fixtures/report_not_found.yml +0 -42
- data/test/fixtures/request_forbidden.yml +0 -38
- data/test/fixtures/rescan.yml +0 -47
- data/test/fixtures/scan.yml +0 -49
- data/test/fixtures/unscanned_url_report.yml +0 -43
- data/test/fixtures/url_report.yml +0 -95
- data/test/fixtures/url_scan.yml +0 -48
- data/test/url_report_test.rb +0 -57
- data/test/url_scan_test.rb +0 -30
data/.circleci/config.yml
DELETED
@@ -1,23 +0,0 @@
|
|
1
|
-
version: 2
|
2
|
-
jobs:
|
3
|
-
test:
|
4
|
-
docker:
|
5
|
-
- image: circleci/ruby:2.5.3-stretch
|
6
|
-
steps:
|
7
|
-
- checkout
|
8
|
-
- run:
|
9
|
-
name: Setup
|
10
|
-
command: |
|
11
|
-
gem update bundler
|
12
|
-
bundle install
|
13
|
-
- run:
|
14
|
-
name: Rubocop
|
15
|
-
command: bundle exec rake rubocop
|
16
|
-
- run:
|
17
|
-
name: Run Tests
|
18
|
-
command: bundle exec rake test
|
19
|
-
workflows:
|
20
|
-
version: 2
|
21
|
-
test:
|
22
|
-
jobs:
|
23
|
-
- test
|
@@ -1,38 +0,0 @@
|
|
1
|
-
name: Ruby Gem
|
2
|
-
|
3
|
-
on: release
|
4
|
-
|
5
|
-
jobs:
|
6
|
-
build:
|
7
|
-
name: Build + Publish
|
8
|
-
runs-on: ubuntu-latest
|
9
|
-
|
10
|
-
steps:
|
11
|
-
- uses: actions/checkout@master
|
12
|
-
- name: Set up Ruby 2.6
|
13
|
-
uses: actions/setup-ruby@v1
|
14
|
-
with:
|
15
|
-
version: 2.6.x
|
16
|
-
|
17
|
-
- name: Publish to GPR
|
18
|
-
run: |
|
19
|
-
mkdir -p $HOME/.gem
|
20
|
-
touch $HOME/.gem/credentials
|
21
|
-
chmod 0600 $HOME/.gem/credentials
|
22
|
-
printf -- "---\n:github: Bearer ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
23
|
-
gem build *.gemspec
|
24
|
-
gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
|
25
|
-
env:
|
26
|
-
GEM_HOST_API_KEY: ${{secrets.GITHUB_TOKEN}}
|
27
|
-
OWNER: username
|
28
|
-
|
29
|
-
- name: Publish to RubyGems
|
30
|
-
run: |
|
31
|
-
mkdir -p $HOME/.gem
|
32
|
-
touch $HOME/.gem/credentials
|
33
|
-
chmod 0600 $HOME/.gem/credentials
|
34
|
-
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
35
|
-
gem build *.gemspec
|
36
|
-
gem push *.gem
|
37
|
-
env:
|
38
|
-
GEM_HOST_API_KEY: ${{secrets.RUBYGEMS_AUTH_TOKEN}}
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module VirustotalAPI
|
6
|
-
class DomainReport < Base
|
7
|
-
attr_reader :report
|
8
|
-
|
9
|
-
def initialize(report)
|
10
|
-
@report = report
|
11
|
-
end
|
12
|
-
|
13
|
-
# @param [String] domain
|
14
|
-
# @param [String] api_key for virustotal
|
15
|
-
# @return [VirustotalAPI::IPReport] Report Search Result
|
16
|
-
def self.find(domain, api_key)
|
17
|
-
response = RestClient.get(
|
18
|
-
api_uri + '/domain/report',
|
19
|
-
{ params: params(domain, api_key) }
|
20
|
-
)
|
21
|
-
report = parse(response)
|
22
|
-
|
23
|
-
new(report)
|
24
|
-
end
|
25
|
-
|
26
|
-
# @param [String] domain
|
27
|
-
# @param [String] api_key virustotal
|
28
|
-
# @return [Hash] params for GET Request
|
29
|
-
def self.params(domain, api_key)
|
30
|
-
{
|
31
|
-
domain: domain,
|
32
|
-
apikey: api_key
|
33
|
-
}
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module VirustotalAPI
|
6
|
-
class FileReport < Base
|
7
|
-
attr_reader :report, :report_url
|
8
|
-
|
9
|
-
def initialize(report)
|
10
|
-
@report = report
|
11
|
-
@report_url = report.fetch('permalink') { nil }
|
12
|
-
end
|
13
|
-
|
14
|
-
# @param [String] resource file as a md5/sha1/sha256 hash
|
15
|
-
# @param [String] api_key for virustotal
|
16
|
-
# @return [VirustotalAPI::FileReport] Report Search Result
|
17
|
-
def self.find(resource, api_key)
|
18
|
-
response = RestClient.post(
|
19
|
-
api_uri + '/file/report',
|
20
|
-
params(resource, api_key)
|
21
|
-
)
|
22
|
-
report = parse(response)
|
23
|
-
|
24
|
-
new(report)
|
25
|
-
end
|
26
|
-
|
27
|
-
# @param [String] resource file as a md5/sha1/sha256 hash
|
28
|
-
# @param [String] api_key for virustotal
|
29
|
-
# @return [Hash] params for POST Request
|
30
|
-
def self.params(resource, api_key)
|
31
|
-
{
|
32
|
-
resource: resource,
|
33
|
-
apikey: api_key
|
34
|
-
}
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module VirustotalAPI
|
6
|
-
class FileRescan < Base
|
7
|
-
attr_reader :report, :rescan_id
|
8
|
-
|
9
|
-
def initialize(report)
|
10
|
-
@report = report
|
11
|
-
@rescan_id = @report.fetch('scan_id') { nil }
|
12
|
-
end
|
13
|
-
|
14
|
-
# @param [String] resource file as a md5/sha1/sha256 hash
|
15
|
-
# @param [String] api_key for virustotal
|
16
|
-
# @return [VirustotalAPI::FileRescan] Report
|
17
|
-
def self.rescan(resource, api_key)
|
18
|
-
response = RestClient.post(
|
19
|
-
api_uri + '/file/rescan',
|
20
|
-
apikey: api_key,
|
21
|
-
resource: resource
|
22
|
-
)
|
23
|
-
report = parse(response)
|
24
|
-
|
25
|
-
new(report)
|
26
|
-
end
|
27
|
-
|
28
|
-
# @return [Boolean] if file was queued
|
29
|
-
# 0 => not_present, 1 => exists, -2 => queued_for_analysis
|
30
|
-
def queued_for_analysis?
|
31
|
-
response_code = @report.fetch('response_code') { nil }
|
32
|
-
|
33
|
-
response_code == -2
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module VirustotalAPI
|
6
|
-
class FileScan < Base
|
7
|
-
attr_reader :report, :scan_id
|
8
|
-
|
9
|
-
def initialize(report)
|
10
|
-
@report = report
|
11
|
-
@scan_id = @report.fetch('scan_id') { nil }
|
12
|
-
end
|
13
|
-
|
14
|
-
# @param [String] file_path for file to be sent for scan
|
15
|
-
# @param [String] api_key for virustotal
|
16
|
-
# @param [Hash] opts hash for additional options
|
17
|
-
# @return [VirusotalAPI::FileScan] Report
|
18
|
-
def self.scan(file_path, api_key, opts = {})
|
19
|
-
response = RestClient.post(
|
20
|
-
api_uri + '/file/scan',
|
21
|
-
apikey: api_key,
|
22
|
-
filename: opts.fetch('filename') { File.basename(file_path) },
|
23
|
-
file: File.open(file_path, 'r')
|
24
|
-
)
|
25
|
-
report = parse(response)
|
26
|
-
|
27
|
-
new(report)
|
28
|
-
end
|
29
|
-
|
30
|
-
# @return [Boolean] if file was queued
|
31
|
-
# 0 => not_present, 1 => exists, -2 => queued_for_analysis
|
32
|
-
def queued_for_analysis?
|
33
|
-
response_code = @report.fetch('response_code') { nil }
|
34
|
-
|
35
|
-
response_code == -2
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module VirustotalAPI
|
6
|
-
class IPReport < Base
|
7
|
-
attr_reader :report
|
8
|
-
|
9
|
-
def initialize(report)
|
10
|
-
@report = report
|
11
|
-
end
|
12
|
-
|
13
|
-
# @param [String] ip address IPv4 format
|
14
|
-
# @param [String] api_key for virustotal
|
15
|
-
# @return [VirustotalAPI::IPReport] Report Search Result
|
16
|
-
def self.find(ip, api_key)
|
17
|
-
response = RestClient.get(
|
18
|
-
api_uri + '/ip-address/report',
|
19
|
-
{ params: params(ip, api_key) }
|
20
|
-
)
|
21
|
-
report = parse(response)
|
22
|
-
|
23
|
-
new(report)
|
24
|
-
end
|
25
|
-
|
26
|
-
# @param [String] ip address IPv4 format
|
27
|
-
# @param [String] api_key for virustotal
|
28
|
-
# @return [Hash] params for GET Request
|
29
|
-
def self.params(ip, api_key)
|
30
|
-
{
|
31
|
-
ip: ip,
|
32
|
-
apikey: api_key
|
33
|
-
}
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module VirustotalAPI
|
6
|
-
class URLReport < Base
|
7
|
-
attr_reader :report, :report_url, :scan_id
|
8
|
-
|
9
|
-
def initialize(report)
|
10
|
-
@report = report
|
11
|
-
@report_url = report.fetch('permalink') { nil }
|
12
|
-
@scan_id = report.fetch('scan_id') { nil }
|
13
|
-
end
|
14
|
-
|
15
|
-
# @param [String] resource as an ip/domain/url
|
16
|
-
# @param [String] api_key for virustotal
|
17
|
-
# @param [Integer] optional param to start scan if not found. 1 for true
|
18
|
-
# @return [VirustotalAPI::URLReport] Report Search Result
|
19
|
-
def self.find(resource, api_key, scan = 0)
|
20
|
-
response = RestClient.post(
|
21
|
-
api_uri + '/url/report',
|
22
|
-
params(resource, api_key, scan)
|
23
|
-
)
|
24
|
-
report = parse(response)
|
25
|
-
|
26
|
-
new(report)
|
27
|
-
end
|
28
|
-
|
29
|
-
# @param [String] resource as an ip/domain/url
|
30
|
-
# @param [String] api_key for virustotal
|
31
|
-
# @param [Integer] optional param to start scan if not found. 1 for true
|
32
|
-
# @return [Hash] params for POST Request
|
33
|
-
def self.params(resource, api_key, scan = 0)
|
34
|
-
{
|
35
|
-
resource: resource,
|
36
|
-
apikey: api_key,
|
37
|
-
scan: scan.to_s
|
38
|
-
}
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'base'
|
4
|
-
|
5
|
-
module VirustotalAPI
|
6
|
-
class URLScan < Base
|
7
|
-
attr_reader :report, :scan_id
|
8
|
-
|
9
|
-
def initialize(report)
|
10
|
-
@report = report
|
11
|
-
@scan_id = @report.fetch('scan_id') { nil }
|
12
|
-
end
|
13
|
-
|
14
|
-
# @param [String] url
|
15
|
-
# @param [String] api_key for virustotal
|
16
|
-
# @return [VirustotalAPI::URLScan] Report
|
17
|
-
def self.scan(url, api_key)
|
18
|
-
response = RestClient.post(
|
19
|
-
api_uri + '/url/scan',
|
20
|
-
apikey: api_key,
|
21
|
-
url: url
|
22
|
-
)
|
23
|
-
report = parse(response)
|
24
|
-
|
25
|
-
new(report)
|
26
|
-
end
|
27
|
-
|
28
|
-
# @return [Boolean] if file was queued
|
29
|
-
# 0 => not_present, 1 => exists, -2 => queued_for_analysis
|
30
|
-
def queued_for_analysis?
|
31
|
-
response_code = @report.fetch('response_code') { nil }
|
32
|
-
|
33
|
-
response_code == -2
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
data/test/domain_report_test.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require './test/test_helper'
|
4
|
-
|
5
|
-
class VirustotalAPIDomainReportTest < Minitest::Test
|
6
|
-
def setup
|
7
|
-
@domain = 'virustotal.com'
|
8
|
-
@api_key = 'testapikey'
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_class_exists
|
12
|
-
assert VirustotalAPI::DomainReport
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_report_response
|
16
|
-
VCR.use_cassette('domain_report') do
|
17
|
-
vtdomain_report = VirustotalAPI::DomainReport.find(@domain, @api_key)
|
18
|
-
|
19
|
-
# Make sure that the JSON was parsed
|
20
|
-
assert vtdomain_report.is_a?(VirustotalAPI::DomainReport)
|
21
|
-
assert vtdomain_report.report.is_a?(Hash)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_exists?
|
26
|
-
VCR.use_cassette('domain_report') do
|
27
|
-
vtdomain_report = VirustotalAPI::DomainReport.find(@domain, @api_key)
|
28
|
-
|
29
|
-
assert vtdomain_report.exists?, true
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
data/test/file_report_test.rb
DELETED
@@ -1,36 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require './test/test_helper'
|
4
|
-
|
5
|
-
class VirustotalAPIFileReportTest < Minitest::Test
|
6
|
-
# rubocop:disable LineLength
|
7
|
-
def setup
|
8
|
-
@sha256 = '01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b'
|
9
|
-
@api_key = 'testapikey'
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_class_exists
|
13
|
-
assert VirustotalAPI::FileReport
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_report_response
|
17
|
-
VCR.use_cassette('report') do
|
18
|
-
virustotal_report = VirustotalAPI::FileReport.find(@sha256, @api_key)
|
19
|
-
|
20
|
-
# Make sure that the JSON was parsed
|
21
|
-
assert virustotal_report.is_a?(VirustotalAPI::FileReport)
|
22
|
-
assert virustotal_report.report.is_a?(Hash)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_report_url
|
27
|
-
permalink = 'https://www.virustotal.com/file/01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b/analysis/1418032127/'
|
28
|
-
VCR.use_cassette('report') do
|
29
|
-
virustotal_report = VirustotalAPI::FileReport.find(@sha256, @api_key)
|
30
|
-
|
31
|
-
assert virustotal_report.report_url.is_a?(String)
|
32
|
-
assert virustotal_report.report_url, permalink
|
33
|
-
end
|
34
|
-
end
|
35
|
-
# rubocop:enable LineLength
|
36
|
-
end
|
data/test/file_rescan_test.rb
DELETED
@@ -1,32 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require './test/test_helper'
|
4
|
-
|
5
|
-
class VirustotalAPIFileRescanTest < Minitest::Test
|
6
|
-
# rubocop:disable LineLength
|
7
|
-
def setup
|
8
|
-
@sha256 = '01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b'
|
9
|
-
@api_key = 'testapikey'
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_class_exists
|
13
|
-
assert VirustotalAPI::FileRescan
|
14
|
-
end
|
15
|
-
|
16
|
-
def test_rescan_response
|
17
|
-
VCR.use_cassette('rescan') do
|
18
|
-
virustotal_rescan = VirustotalAPI::FileRescan.rescan(@sha256, @api_key)
|
19
|
-
|
20
|
-
assert virustotal_rescan.report.is_a?(Hash)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_rescan_id
|
25
|
-
VCR.use_cassette('rescan') do
|
26
|
-
virustotal_rescan = VirustotalAPI::FileRescan.rescan(@sha256, @api_key)
|
27
|
-
|
28
|
-
assert virustotal_rescan.rescan_id.is_a?(String)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
# rubocop:enable LineLength
|
32
|
-
end
|
data/test/file_scan_test.rb
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require './test/test_helper'
|
4
|
-
|
5
|
-
class VirustotalAPIFileScanTest < Minitest::Test
|
6
|
-
def setup
|
7
|
-
@file_path = File.expand_path('test/fixtures/null_file')
|
8
|
-
@api_key = 'testapikey'
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_class_exists
|
12
|
-
assert VirustotalAPI::FileScan
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_response
|
16
|
-
VCR.use_cassette('scan') do
|
17
|
-
virustotal_scan = VirustotalAPI::FileScan.scan(@file_path, @api_key)
|
18
|
-
|
19
|
-
assert virustotal_scan.report.is_a?(Hash)
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def test_scan_id
|
24
|
-
VCR.use_cassette('scan') do
|
25
|
-
virustotal_scan = VirustotalAPI::FileScan.scan(@file_path, @api_key)
|
26
|
-
|
27
|
-
assert virustotal_scan.scan_id.is_a?(String)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|