vtapi 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # VtAPI
1
+ # VtAPI [![Gem Version](https://badge.fury.io/rb/vtapi.png)](http://badge.fury.io/rb/vtapi)
2
2
 
3
3
  Ruby gem for VirusTotal Public API v2.0
4
4
 
@@ -37,7 +37,24 @@ puts resp.positives # num of positives
37
37
  puts resp.scan_results # {"McAfee"=>nil, "Symantec"=>"Android.ZertSecurity", ... }
38
38
  ```
39
39
 
40
- ### File Upload
40
+ ### File Report (multiple resources)
41
+ ```ruby
42
+ # retrieve file report by file hash(SHA256, SHA1, MD5)
43
+ # up to 4 resources can assign
44
+ resources = ['00ce460c8b33711091206..', ..]
45
+ resps = api.file_report(resources)
46
+ resps.each do |r|
47
+ puts "#{r.sha256}: #{r.positives} / #{r.total}" if r.response_code == 1
48
+ end
49
+ ```
50
+ The following APIs are supported Posting multi resources(URLs).
51
+ * VtAPI#file_report
52
+ * VtAPI#file_rescan
53
+ * VtAPI#url_scan
54
+ * VtAPI#url_report
55
+
56
+
57
+ ### File Scan (File Upload)
41
58
  ```ruby
42
59
  # read file
43
60
  data = File.open(some_path, 'rb') {|f| f.read }
@@ -49,21 +66,43 @@ resp = api.file_scan(data)
49
66
  puts resp.response_code # 1: OK, 0: result doesn't exist, -2: still queued
50
67
  ```
51
68
 
69
+
70
+ ### URL Scan
71
+ ```ruby
72
+ # upload url
73
+ resp = api.url_scan(url)
74
+
75
+ # confirm response_code
76
+ puts resp.response_code # 1: OK, 0: result doesn't exist, -2: still queued
77
+ ```
78
+
79
+
80
+ ### URL Report
81
+ ```ruby
82
+ # upload url
83
+ resp = api.url_scan(url)
84
+
85
+ # confirm result
86
+ puts resp.scans
87
+ ```
88
+
89
+
52
90
  ## Features
53
- ### Supported API
91
+ ### Support API
54
92
  * file/scan
55
93
  * file/resan
56
94
  * file/report
57
-
58
- ### not implemented yet
59
95
  * url/scan
60
96
  * url/report
97
+
98
+ ### Not implemented yet
61
99
  * ip-address/report
62
100
  * domain/report
63
101
 
64
- ### unsupported API
102
+ ### Unsupported
65
103
  * comments/puts
66
104
 
105
+
67
106
  ## Contributing
68
107
 
69
108
  1. Fork it
data/lib/vtapi/api.rb CHANGED
@@ -24,13 +24,36 @@ class VtAPI
24
24
  end
25
25
 
26
26
  def file_rescan(resource)
27
+ if resource.is_a? Array
28
+ raise 'limit is up to 25 items' if resource.size > 25
29
+ resource = resource.join(', ')
30
+ end
27
31
  http_post('file/rescan', resource: resource)
28
32
  end
29
33
 
30
34
  def file_report(resource)
35
+ if resource.is_a? Array
36
+ raise 'limit is up to 4 items' if resource.size > 4
37
+ resource = resource.join(', ')
38
+ end
31
39
  http_post('file/report', resource: resource)
32
40
  end
33
41
 
42
+ def url_scan(url)
43
+ if url.is_a? Array
44
+ raise 'limit is up to 4 items' if url.size > 4
45
+ url = url.join("\n")
46
+ end
47
+ http_post('url/scan', url: url)
48
+ end
49
+
50
+ def url_report(url)
51
+ if url.is_a? Array
52
+ raise 'limit is up to 4 items' if url.size > 4
53
+ url = url.join(", ")
54
+ end
55
+ http_post('url/report', resource: url)
56
+ end
34
57
 
35
58
  def http_post(path, params = {})
36
59
  uri = BASE_URL + path
@@ -45,6 +68,6 @@ class VtAPI
45
68
  resp.return!(req, result, &block)
46
69
  end
47
70
  end
48
- Response.new(resp.body)
71
+ Response.parse(resp.body)
49
72
  end
50
73
  end
@@ -3,8 +3,17 @@ require 'time'
3
3
 
4
4
  class VtAPI
5
5
  class Response
6
- def initialize(response_body)
7
- @json = JSON.parse(response_body)
6
+ def self.parse(response_body)
7
+ json = JSON.parse(response_body)
8
+ if json.is_a? Array
9
+ return json.map{|e| Response.new(e) }
10
+ else
11
+ return Response.new(json)
12
+ end
13
+ end
14
+
15
+ def initialize(json)
16
+ @json = json
8
17
  @json.keys.each do |key|
9
18
  Response.class_eval {
10
19
  define_method key.to_s do |*args|
@@ -34,5 +43,9 @@ class VtAPI
34
43
  def [](key)
35
44
  @json.fetch(key.to_s) # raise KeyError when key doesn't exist.
36
45
  end
46
+
47
+ def to_s
48
+ @json.to_json
49
+ end
37
50
  end
38
51
  end
data/lib/vtapi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Vtapi
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -5,7 +5,9 @@ describe VtAPI::Response do
5
5
  let(:sample_response) {
6
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
7
  }
8
- let(:response) { VtAPI::Response.new(sample_response) }
8
+ let(:sample_json) { JSON.parse(sample_response) }
9
+ let(:response) { VtAPI::Response.new(sample_json) }
10
+
9
11
 
10
12
  describe '#md5' do
11
13
  it { expect(response.md5).to eq '1cf41bdc0fdd409774eb755031a6f49d' }
@@ -57,6 +59,14 @@ describe VtAPI::Response do
57
59
  subject { response.scan_results }
58
60
  it { expect(subject).to eq({"McAfee"=>nil, "Symantec"=>"Android.ZertSecurity", "Kaspersky"=>"HEUR:Trojan-Banker.AndroidOS.Zitmo.a", "TrendMicro"=>nil, "Microsoft"=>nil}) }
59
61
  end
62
+
63
+ describe '#to_s' do
64
+ subject { response.to_s }
65
+ it 'should be able to parse as json' do
66
+ expect{ JSON.parse(subject) }.to_not raise_error
67
+ end
68
+ it { expect(JSON.parse(subject).keys).to match_array ['scans', 'scan_id', 'sha1', 'resource', 'response_code', 'scan_date', 'permalink', 'verbose_msg', 'total', 'positives', 'sha256', 'md5'] }
69
+ end
60
70
  end
61
71
 
62
72
  context 'with no scan result' do
@@ -67,5 +77,28 @@ describe VtAPI::Response do
67
77
  describe '#positive_threats' do
68
78
  pending 'not implemented yet'
69
79
  end
80
+
81
+ end
82
+
83
+ describe '.parse' do
84
+ subject { VtAPI::Response.parse(response) }
85
+ context 'assigns single response' do
86
+ let(:response) { '{"md5":"1cf41bdc0fdd409774eb755031a6f49d"}' }
87
+ it { expect(subject).to be_a VtAPI::Response }
88
+ it { expect(subject.md5).to eq "1cf41bdc0fdd409774eb755031a6f49d" }
89
+ end
90
+
91
+ context 'assigns array response' do
92
+ subject {VtAPI::Response.parse(response) }
93
+ let(:response) { '[{"md5":"1cf41bdc0fdd409774eb755031a6f49d"}, {"md5":"1eef5bdc64241652f8a0df0c2dc92df6"}]' }
94
+ it { expect(subject).to be_a Array }
95
+ it { expect(subject).to have(2).items }
96
+
97
+ context 'about first object' do
98
+ subject {VtAPI::Response.parse(response).first }
99
+ it { expect(subject).to be_a VtAPI::Response }
100
+ end
101
+ end
70
102
  end
103
+
71
104
  end
data/spec/vtapi_spec.rb CHANGED
@@ -48,6 +48,22 @@ describe VtAPI do
48
48
  .to_return(:body => sample_response, :status => 200)
49
49
  subject
50
50
  end
51
+
52
+ context 'assigns 2 resources' do
53
+ let(:resource) { ['ff' * 32, '00' * 32] }
54
+ let(:sample_response) { '[{}]' }
55
+ it "should post resource parameter as comma separete value" do
56
+ stub_request(:post, api_url)
57
+ .with(:body => {'resource' => resource.join(', '), 'apikey' => apikey} )
58
+ .to_return(:body => sample_response, :status => 200)
59
+ subject
60
+ end
61
+ end
62
+
63
+ context 'when assign 25 resources(over the limitation)' do
64
+ let(:resource) { ['ff' * 32] * 25 }
65
+ it { expect{ subject }.to raise_error }
66
+ end
51
67
  end
52
68
 
53
69
  describe '#file_report' do
@@ -74,5 +90,85 @@ describe VtAPI do
74
90
  expect{ subject }.to raise_error(VtAPI::AuthError)
75
91
  end
76
92
  end
93
+
94
+ context 'when assign 2 resources' do
95
+ let(:resource) { ['ff' * 32, '00' * 32] }
96
+ it "should post resource parameter as comma separete value" do
97
+ stub_request(:post, api_url)
98
+ .with(:body => {'resource' => resource.join(', '), 'apikey' => apikey} )
99
+ .to_return(:body => sample_response, :status => 200)
100
+ subject
101
+ end
102
+ end
103
+
104
+ context 'when assign 5 resources(over the limitation)' do
105
+ let(:resource) { ['ff' * 32] * 5 }
106
+ it { expect{ subject }.to raise_error }
107
+ end
77
108
  end
109
+
110
+ describe '#url_scan' do
111
+ let(:sample_response) { '{}' }
112
+ let(:api_url) { 'https://www.virustotal.com/vtapi/v2/url/scan' }
113
+ let(:target_url) { 'http://www.foobar.com/' }
114
+ subject { api.url_scan(target_url) }
115
+
116
+ it "should connect to 'https://www.virustotal.com/vtapi/v2/url/scan'" do
117
+ stub_request(:post, api_url)
118
+ .with(:body => {'url' => target_url, 'apikey' => apikey} )
119
+ .to_return(:body => sample_response, :status => 200)
120
+ subject
121
+ end
122
+
123
+ context 'when assign 2 urls' do
124
+ let(:target_url) { ['http://foobar.com/', 'http://abc.com'] }
125
+
126
+ it "should post url parameter which is joined by '\\n'" do
127
+ stub_request(:post, api_url)
128
+ .with(:body => {'url' => target_url.join("\n"), 'apikey' => apikey} )
129
+ .to_return(:body => sample_response, :status => 200)
130
+ subject
131
+ end
132
+ end
133
+
134
+ context 'when assign 5 urls (over the limitation)' do
135
+ let(:target_url) { ['http://www.foobar.com/'] * 5 }
136
+
137
+ it { expect{ subject }.to raise_error }
138
+ end
139
+ end
140
+
141
+ describe '#url_report' do
142
+ let(:sample_response) { '{}' }
143
+ let(:api_url) { 'https://www.virustotal.com/vtapi/v2/url/report' }
144
+ let(:target_url) { 'http://www.foobar.com/' }
145
+ subject { api.url_report(target_url) }
146
+
147
+ it "should connect to 'https://www.virustotal.com/vtapi/v2/url/report'" do
148
+ stub_request(:post, api_url)
149
+ .with(:body => {'resource' => target_url, 'apikey' => apikey} )
150
+ .to_return(:body => sample_response, :status => 200)
151
+ subject
152
+ end
153
+
154
+ context 'when assign 2 urls' do
155
+ let(:target_url) { ['http://foobar.com/', 'http://abc.com'] }
156
+
157
+ it "should post url parameter which is joined by ', '" do
158
+ stub_request(:post, api_url)
159
+ .with(:body => {'resource' => target_url.join(", "), 'apikey' => apikey} )
160
+ .to_return(:body => sample_response, :status => 200)
161
+ subject
162
+ end
163
+ end
164
+
165
+ context 'when assign 5 urls (over the limitation)' do
166
+ let(:target_url) { ['http://www.foobar.com/'] * 5 }
167
+
168
+ it { expect{ subject }.to raise_error }
169
+ end
170
+ end
171
+
172
+
173
+
78
174
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vtapi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-05-20 00:00:00.000000000 Z
12
+ date: 2013-05-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
16
- requirement: &70305944392860 !ruby/object:Gem::Requirement
16
+ requirement: &70343748341080 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '1.3'
22
22
  type: :development
23
23
  prerelease: false
24
- version_requirements: *70305944392860
24
+ version_requirements: *70343748341080
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rake
27
- requirement: &70305944392140 !ruby/object:Gem::Requirement
27
+ requirement: &70343748340660 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70305944392140
35
+ version_requirements: *70343748340660
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70305944391080 !ruby/object:Gem::Requirement
38
+ requirement: &70343748340120 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 2.13.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70305944391080
46
+ version_requirements: *70343748340120
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: webmock
49
- requirement: &70305944390140 !ruby/object:Gem::Requirement
49
+ requirement: &70343748339620 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.11.0
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70305944390140
57
+ version_requirements: *70343748339620
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rest-client
60
- requirement: &70305944388880 !ruby/object:Gem::Requirement
60
+ requirement: &70343748339160 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ~>
@@ -65,7 +65,7 @@ dependencies:
65
65
  version: 1.6.7
66
66
  type: :runtime
67
67
  prerelease: false
68
- version_requirements: *70305944388880
68
+ version_requirements: *70343748339160
69
69
  description: gem for VirusTotal Public API version2.0.
70
70
  email:
71
71
  - masatanish@gmail.com
@@ -102,7 +102,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
102
102
  version: '0'
103
103
  segments:
104
104
  - 0
105
- hash: -530785710278489622
105
+ hash: -803017513526154134
106
106
  required_rubygems_version: !ruby/object:Gem::Requirement
107
107
  none: false
108
108
  requirements:
@@ -111,7 +111,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
111
  version: '0'
112
112
  segments:
113
113
  - 0
114
- hash: -530785710278489622
114
+ hash: -803017513526154134
115
115
  requirements: []
116
116
  rubyforge_project:
117
117
  rubygems_version: 1.8.16