vtapi 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in vtapi.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 masatanish
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # VtAPI
2
+
3
+ Ruby gem for VirusTotal Public API v2.0
4
+
5
+ https://www.virustotal.com/en/documentation/public-api/
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ gem 'vtapi'
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install vtapi
20
+
21
+ ## Usage
22
+ ### Prepare
23
+ ```ruby
24
+ # initialize
25
+ api = VtAPI.new('-- Your API KEY--')
26
+ ```
27
+
28
+ ### File Report
29
+ ```ruby
30
+ # retrieve file report by file hash(SHA256, SHA1, MD5)
31
+ resp = api.file_report('00ce460c8b337110912066f746731a916e85bf1d7f4b44f09ca3cc39f9b52a98')
32
+
33
+ puts resp.response_code # 1: OK, 0: result doesn't exist, -2: still queued
34
+
35
+ # resp is a instance of VtAPI::Response class
36
+ puts resp.positives # num of positives
37
+ puts resp.scan_results # {"McAfee"=>nil, "Symantec"=>"Android.ZertSecurity", ... }
38
+ ```
39
+
40
+ ### File Upload
41
+ ```ruby
42
+ # read file
43
+ data = File.open(some_path, 'rb') {|f| f.read }
44
+
45
+ # upload data
46
+ resp = api.file_scan(data)
47
+
48
+ # confirm response_code
49
+ puts resp.response_code # 1: OK, 0: result doesn't exist, -2: still queued
50
+ ```
51
+
52
+ ## Features
53
+ ### Supported API
54
+ * file/scan
55
+ * file/resan
56
+ * file/report
57
+
58
+ ### not implemented yet
59
+ * url/scan
60
+ * url/report
61
+ * ip-address/report
62
+ * domain/report
63
+
64
+ ### unsupported API
65
+ * comments/puts
66
+
67
+ ## Contributing
68
+
69
+ 1. Fork it
70
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
71
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
72
+ 4. Push to the branch (`git push origin my-new-feature`)
73
+ 5. Create new Pull Request
74
+
75
+
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+ task :default => :spec
data/lib/vtapi/api.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'rest-client'
2
+ require 'tempfile'
3
+ require 'json'
4
+
5
+ class VtAPI
6
+ # base URL of the VirusTotal Public API v2.0
7
+ BASE_URL = 'https://www.virustotal.com/vtapi/v2/'
8
+
9
+ attr_reader :apikey
10
+
11
+ def initialize(apikey)
12
+ @apikey = apikey
13
+ end
14
+
15
+ def file_scan(data)
16
+ # TODO: set filename or file path
17
+ tmp = Tempfile.open('tmp')
18
+ tmp.write data
19
+ def tmp.content_type
20
+ 'application/octet-stream'
21
+ end
22
+ tmp.pos=0
23
+ http_post('file/scan', file: tmp, multipart: true)
24
+ end
25
+
26
+ def file_rescan(resource)
27
+ http_post('file/rescan', resource: resource)
28
+ end
29
+
30
+ def file_report(resource)
31
+ http_post('file/report', resource: resource)
32
+ end
33
+
34
+
35
+ def http_post(path, params = {})
36
+ uri = BASE_URL + path
37
+ params['apikey'] = @apikey
38
+ resp = RestClient.post(uri, params) do |resp, req, result, &block|
39
+ case resp.code
40
+ when 204
41
+ raise ExceedAPILimit, "you exceed the public API request rate limit: key[#{@apikey}]"
42
+ when 403
43
+ raise AuthError, "you do not have the required priviledges: key[#{@apikey}]"
44
+ else
45
+ resp.return!(req, result, &block)
46
+ end
47
+ end
48
+ Response.new(resp.body)
49
+ end
50
+ end
@@ -0,0 +1,6 @@
1
+ class VtAPI
2
+ # 204 exceed the public API request rate limit
3
+ class ExceedAPILimit < StandardError; end
4
+ # 403 Forbidden
5
+ class AuthError < StandardError; end
6
+ end
@@ -0,0 +1,38 @@
1
+ require 'json'
2
+ require 'time'
3
+
4
+ class VtAPI
5
+ class Response
6
+ def initialize(response_body)
7
+ @json = JSON.parse(response_body)
8
+ @json.keys.each do |key|
9
+ Response.class_eval {
10
+ define_method key.to_s do |*args|
11
+ @json[key]
12
+ end
13
+ }
14
+ end
15
+ @json['scan_date'] = Time.parse(@json['scan_date'] + "UTC") if @json.has_key? 'scan_date'
16
+ end
17
+
18
+ def keys
19
+ @json.keys
20
+ end
21
+
22
+ def positive_threats
23
+ Hash[@json.fetch('scans', {}).select{|k,v| v['detected'] }.map{|k,v| [k, v['result']] }]
24
+ end
25
+
26
+ def positive_brands
27
+ @json.fetch('scans', {}).select{|k,v| v['detected'] }.keys
28
+ end
29
+
30
+ def scan_results
31
+ Hash[@json.fetch('scans', {}).map{|k,v| [k, v['result']] }]
32
+ end
33
+
34
+ def [](key)
35
+ @json.fetch(key.to_s) # raise KeyError when key doesn't exist.
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Vtapi
2
+ VERSION = "0.1.0"
3
+ end
data/lib/vtapi.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'vtapi/version'
2
+ require 'vtapi/exception'
3
+ require 'vtapi/response'
4
+ require 'vtapi/api'
5
+
@@ -0,0 +1,71 @@
1
+ require 'spec_helper'
2
+
3
+ describe VtAPI::Response do
4
+ context 'with sample file_report response' do
5
+ let(:sample_response) {
6
+ "{\"scans\":{\"McAfee\":{\"detected\":false,\"version\":\"5.400.0.1158\",\"result\":null,\"update\":\"20130512\"},\"Symantec\":{\"detected\":true,\"version\":\"20121.3.0.76\",\"result\":\"Android.ZertSecurity\",\"update\":\"20130512\"},\"Kaspersky\":{\"detected\":true,\"version\":\"9.0.0.837\",\"result\":\"HEUR:Trojan-Banker.AndroidOS.Zitmo.a\",\"update\":\"20130512\"},\"TrendMicro\":{\"detected\":false,\"version\":\"9.740.0.1012\",\"result\":null,\"update\":\"20130512\"},\"Microsoft\":{\"detected\":false,\"version\":\"1.9402\",\"result\":null,\"update\":\"20130512\"}},\"scan_id\":\"00ce460c8b337110912066f746731a916e85bf1d7f4b44f09ca3cc39f9b52a98-1368320515\",\"sha1\":\"e1b727b3e9336033606df79eeba03dd218b56c20\",\"resource\":\"00ce460c8b337110912066f746731a916e85bf1d7f4b44f09ca3cc39f9b52a98\",\"response_code\":1,\"scan_date\":\"2013-05-12 01:01:55\",\"permalink\":\"https://www.virustotal.com/file/00ce460c8b337110912066f746731a916e85bf1d7f4b44f09ca3cc39f9b52a98/analysis/1368320515/\",\"verbose_msg\":\"Scan finished, scan information embedded in this object\",\"total\":46,\"positives\":22,\"sha256\":\"00ce460c8b337110912066f746731a916e85bf1d7f4b44f09ca3cc39f9b52a98\",\"md5\":\"1cf41bdc0fdd409774eb755031a6f49d\"}"
7
+ }
8
+ let(:response) { VtAPI::Response.new(sample_response) }
9
+
10
+ describe '#md5' do
11
+ it { expect(response.md5).to eq '1cf41bdc0fdd409774eb755031a6f49d' }
12
+ end
13
+
14
+ describe '#sha1' do
15
+ it { expect(response.sha1).to eq 'e1b727b3e9336033606df79eeba03dd218b56c20' }
16
+ end
17
+
18
+ describe '#sha256' do
19
+ it { expect(response.sha256).to eq '00ce460c8b337110912066f746731a916e85bf1d7f4b44f09ca3cc39f9b52a98' }
20
+ end
21
+
22
+ describe '#scan_date' do
23
+ it { expect(response.scan_date).to be_a Time }
24
+ end
25
+
26
+ describe '#total' do
27
+ it { expect(response.total).to eq 46 }
28
+ end
29
+
30
+ describe '#positives' do
31
+ it { expect(response.positives).to eq 22 }
32
+ end
33
+
34
+ describe '#scan_id' do
35
+ it { expect(response.scan_id).to eq '00ce460c8b337110912066f746731a916e85bf1d7f4b44f09ca3cc39f9b52a98-1368320515' }
36
+ end
37
+
38
+ describe '#resource' do
39
+ it { expect(response.resource).to eq '00ce460c8b337110912066f746731a916e85bf1d7f4b44f09ca3cc39f9b52a98' }
40
+ end
41
+
42
+ describe '#keys' do
43
+ it { expect(response.keys).to match_array ['scans', 'scan_id', 'sha1', 'resource', 'response_code', 'scan_date', 'permalink', 'verbose_msg', 'total', 'positives', 'sha256', 'md5'] }
44
+ end
45
+
46
+ describe '#positive_brands' do
47
+ subject { response.positive_brands }
48
+ it { expect(subject).to match_array ["Kaspersky", "Symantec"] }
49
+ end
50
+
51
+ describe '#positive_threats' do
52
+ subject { response.positive_threats }
53
+ it { expect(subject).to eq({"Symantec"=>"Android.ZertSecurity", "Kaspersky"=>"HEUR:Trojan-Banker.AndroidOS.Zitmo.a"}) }
54
+ end
55
+
56
+ describe '#scan_results' do
57
+ subject { response.scan_results }
58
+ it { expect(subject).to eq({"McAfee"=>nil, "Symantec"=>"Android.ZertSecurity", "Kaspersky"=>"HEUR:Trojan-Banker.AndroidOS.Zitmo.a", "TrendMicro"=>nil, "Microsoft"=>nil}) }
59
+ end
60
+ end
61
+
62
+ context 'with no scan result' do
63
+ describe '#positive_brands' do
64
+ pending 'not implemented yet'
65
+ end
66
+
67
+ describe '#positive_threats' do
68
+ pending 'not implemented yet'
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,11 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+ require 'rspec'
5
+ require 'webmock/rspec'
6
+
7
+ require 'vtapi'
8
+
9
+ RSpec.configure do |config|
10
+
11
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe VtAPI do
4
+ before do
5
+ #RestClient.log = STDERR
6
+ end
7
+
8
+ let(:apikey) { 'test apikey' }
9
+ let(:api) { VtAPI.new(apikey) }
10
+ subject { api }
11
+
12
+ describe '#apikey' do
13
+ it { expect(subject.apikey).to eq apikey }
14
+ end
15
+
16
+ describe '#file_scan' do
17
+ let(:sample_response) { '{}' }
18
+ let(:api_url) { 'https://www.virustotal.com/vtapi/v2/file/scan' }
19
+ subject { api.file_scan('binary data') }
20
+ it "should connect to virustotal.com with 'multipart/form-data' Content-Type" do
21
+ stub_request(:post, api_url)
22
+ .with(:headers => { "Content-Type" => /^multipart\/form-data;.*/ })
23
+ .to_return(:body => sample_response)
24
+ subject
25
+ end
26
+ it 'should include data part in body' do
27
+ stub_request(:post, api_url)
28
+ .with(:body => /name="file"; filename="tmp.*\r\n/ )
29
+ .to_return(:body => sample_response)
30
+ subject
31
+ end
32
+ it 'should include posted binary data in body' do
33
+ stub_request(:post, api_url)
34
+ .with(:body => /binary data/ )
35
+ .to_return(:body => sample_response)
36
+ subject
37
+ end
38
+ end
39
+
40
+ describe '#file_rescan' do
41
+ let(:sample_response) { '{}' }
42
+ let(:api_url) { 'https://www.virustotal.com/vtapi/v2/file/rescan' }
43
+ let(:resource) { 'ff' * 32 }
44
+ subject { api.file_rescan(resource) }
45
+ it "should connect to 'https://www.virustotal.com/vtapi/v2/file/rescan'" do
46
+ stub_request(:post, api_url)
47
+ .with(:body => {'resource' => resource, 'apikey' => apikey} )
48
+ .to_return(:body => sample_response, :status => 200)
49
+ subject
50
+ end
51
+ end
52
+
53
+ describe '#file_report' do
54
+ let(:sample_response) { '{}' }
55
+ let(:api_url) { 'https://www.virustotal.com/vtapi/v2/file/report' }
56
+ let(:resource) { 'ff' * 32 }
57
+ subject { api.file_report(resource) }
58
+ it "should connect to 'https://www.virustotal.com/vtapi/v2/file/report'" do
59
+ stub_request(:post, api_url)
60
+ .with(:body => {'resource' => resource, 'apikey' => apikey} )
61
+ .to_return(:body => sample_response, :status => 200)
62
+ subject
63
+ end
64
+
65
+ context 'when server returns 204' do
66
+ it do
67
+ stub_request(:post, api_url).to_return(:status => 204)
68
+ expect{ subject }.to raise_error(VtAPI::ExceedAPILimit)
69
+ end
70
+ end
71
+ context 'when server returns 403' do
72
+ it do
73
+ stub_request(:post, api_url).to_return(:status => 403)
74
+ expect{ subject }.to raise_error(VtAPI::AuthError)
75
+ end
76
+ end
77
+ end
78
+ end
data/vtapi.gemspec ADDED
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'vtapi/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "vtapi"
8
+ spec.version = Vtapi::VERSION
9
+ spec.authors = ["masatanish"]
10
+ spec.email = ["masatanish@gmail.com"]
11
+ spec.description = %q{gem for VirusTotal Public API version2.0.}
12
+ spec.summary = %q{gem for VirusTotal Public API version2.0.}
13
+ spec.homepage = "https://github.com/masatanish/vtapi"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec", "~>2.13.0"
24
+ spec.add_development_dependency "webmock", "~>1.11.0"
25
+
26
+ spec.add_dependency "rest-client", "~>1.6.7"
27
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: vtapi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - masatanish
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-20 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: &70305944392860 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70305944392860
25
+ - !ruby/object:Gem::Dependency
26
+ name: rake
27
+ requirement: &70305944392140 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :development
34
+ prerelease: false
35
+ version_requirements: *70305944392140
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ requirement: &70305944391080 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: 2.13.0
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70305944391080
47
+ - !ruby/object:Gem::Dependency
48
+ name: webmock
49
+ requirement: &70305944390140 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.11.0
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70305944390140
58
+ - !ruby/object:Gem::Dependency
59
+ name: rest-client
60
+ requirement: &70305944388880 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ~>
64
+ - !ruby/object:Gem::Version
65
+ version: 1.6.7
66
+ type: :runtime
67
+ prerelease: false
68
+ version_requirements: *70305944388880
69
+ description: gem for VirusTotal Public API version2.0.
70
+ email:
71
+ - masatanish@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - .gitignore
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - lib/vtapi.rb
82
+ - lib/vtapi/api.rb
83
+ - lib/vtapi/exception.rb
84
+ - lib/vtapi/response.rb
85
+ - lib/vtapi/version.rb
86
+ - spec/response_spec.rb
87
+ - spec/spec_helper.rb
88
+ - spec/vtapi_spec.rb
89
+ - vtapi.gemspec
90
+ homepage: https://github.com/masatanish/vtapi
91
+ licenses:
92
+ - MIT
93
+ post_install_message:
94
+ rdoc_options: []
95
+ require_paths:
96
+ - lib
97
+ required_ruby_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ segments:
104
+ - 0
105
+ hash: -530785710278489622
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ segments:
113
+ - 0
114
+ hash: -530785710278489622
115
+ requirements: []
116
+ rubyforge_project:
117
+ rubygems_version: 1.8.16
118
+ signing_key:
119
+ specification_version: 3
120
+ summary: gem for VirusTotal Public API version2.0.
121
+ test_files:
122
+ - spec/response_spec.rb
123
+ - spec/spec_helper.rb
124
+ - spec/vtapi_spec.rb