api-tester 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 52c7685df4c907ec1841e6b73414281b003670bb
4
- data.tar.gz: 4d277569d088a704d840ad7e6c187bee4eeb0629
3
+ metadata.gz: 9f452ab5e2f25081c381aa5dc6d7ccbdfdd6608a
4
+ data.tar.gz: 7163a87ef8dc52d647c38c07d7fe2c1e4229c655
5
5
  SHA512:
6
- metadata.gz: 9ea181c7075b97cad1fbfbdfa235a0ab2fcda488a7bdc99efbe981b4bee6cbf69ec98c3024d6cb5cf321004b4bb2e2b4558ca611f9f5fc5628306cdad5dbf541
7
- data.tar.gz: d9ad6635b7521e596478c47bea98ceb5c778a18b12a3bb33f5bb50941ec60cf70aaf7f8c90b2ba8e25581ddcf4f85cec93b5da9c0d963cebff67522b85577cc1
6
+ metadata.gz: 99de80ccecd3293437b1b994608288d57da394d096dc1ca828b47b4b416907dd2b7b32cf2441cf035a1142efc47de9fe55d921b63246c8624fdffcaafa9b2b68
7
+ data.tar.gz: 18def5e09e3080fc1b6806cf2d98f497bd1cd228f4b5225c39144c325ad7a726d64aa2373a7628b0e11e3da13686681ad5e1ad46bfc1cff1c15e96cde3b18c6c
data/README.md CHANGED
@@ -8,6 +8,9 @@ for prime time! To isolate your project from the changes, be sure to specify whi
8
8
  This gem is intended to enable easy creation of tests for
9
9
  RESTful API services when given a contract.
10
10
 
11
+ When using, be sure to follow the documentation for the version of the gem you use. The documentation below
12
+ relates to the unpublished gem version actively under development
13
+
11
14
  Check out [API Tester Example](https://github.com/araneforseti/example_api-tester) for an example in action
12
15
 
13
16
  # Feature Plan
@@ -73,9 +76,9 @@ stable release
73
76
 
74
77
  Define your contract and endpoints using
75
78
  ```ruby
76
- require 'api-tester/definition/api_contract'
79
+ require 'api-tester/definition/contract'
77
80
  require 'api-tester/definition/endpoint'
78
- contract = ApiTester::ApiContract.new "API Name"
81
+ contract = ApiTester::Contract.new "API Name"
79
82
  endpoint = ApiTester::Endpoint.new "Some name which is currently unused", "http://yourbase.com/api/endpoint"
80
83
  ```
81
84
 
@@ -108,7 +111,7 @@ request = ApiTester::Request.new.add_field(ApiTester::Field.new "fieldName")
108
111
  expected_response = ApiTester::Response.new(200).add_field(ApiTester::Field.new "fieldName")
109
112
  endpoint = ApiTester::Endpoint.new "Unused Name", "http://yourbase.com/api/endpoint"
110
113
  endpoint.add_method ApiTester::SupportedVerbs::GET, expected_response, request
111
- contract = ApiContract.new "API Name"
114
+ contract = Contract.new "API Name"
112
115
  contract.add_endpoint endpoint
113
116
  config = ApiTester::Config().with_module(Format.new)
114
117
  expect(ApiTester.go(contract, config)).to be true
@@ -169,9 +172,10 @@ Do you want to do something with the definition which this gem currently does no
169
172
  You can create your own test module and add it to the config instance class!
170
173
  Just make sure it adheres to the following interface:
171
174
  ```ruby
172
- module Name
173
- def self.go contract, reports
174
- # Your test code here - the reports object is where the reports are all stored
175
+ module CustomModule
176
+ def self.go contract
177
+ # Your test code here
178
+ # the contract object is the full definition created
175
179
  end
176
180
 
177
181
  def self.order
@@ -179,6 +183,8 @@ module Name
179
183
  # Otherwise this can just be any number
180
184
  end
181
185
  end
186
+
187
+ config.with_module(CustomModule)
182
188
  ```
183
189
 
184
190
  # Reporting
@@ -186,10 +192,10 @@ Right now the default reporting mechanism prints out to
186
192
  the console all the issues which were found. You can
187
193
  create your own reporting class (so long as it responds
188
194
  to the same methods) or just extend the current one and
189
- override the print method. Then set the tester's report
190
- tool:
195
+ override the print method. Then set the report
196
+ tool in the config:
191
197
  ```ruby
192
- tester.with_reporter(new_reporter)
198
+ config.with_reporter(new_reporter)
193
199
  ```
194
200
 
195
201
  # Development
@@ -34,6 +34,8 @@ Gem::Specification.new do |spec|
34
34
  spec.add_development_dependency "rake", "~> 10.0"
35
35
  spec.add_development_dependency "rspec", "~> 3.0"
36
36
  spec.add_development_dependency "webmock", "~> 3.4"
37
+ spec.add_development_dependency "pry", "~> 0.11"
37
38
 
38
39
  spec.add_runtime_dependency "rest-client", "~> 2.0"
40
+ spec.add_runtime_dependency "injection_vulnerability_library", "0.0.2"
39
41
  end
@@ -1,3 +1,10 @@
1
+ 0.3.1
2
+
3
+ - Adding ability to change headers for request objects
4
+ - Removing the reports from the sub modules and making them just return their reports
5
+ - Adjusted modules to use the full contract instead of endpoints
6
+ - Created module to check for server information being broadcast
7
+
1
8
  0.3.0
2
9
 
3
10
  - Making modules actually be modules
@@ -1,11 +1,9 @@
1
1
  module ApiTester
2
- def self.go definition, config
2
+ def self.go contract, config
3
3
  reporter = config.reporter
4
4
 
5
- definition.endpoints.each do |endpoint|
6
- config.modules.sort_by{ |mod| mod.order }.each do |mod|
7
- mod.go endpoint, reporter
8
- end
5
+ config.modules.sort_by{ |mod| mod.order }.each do |mod|
6
+ reporter.add_reports mod.go contract
9
7
  end
10
8
 
11
9
  reporter.print
@@ -1,5 +1,5 @@
1
1
  module ApiTester
2
- class ApiContract
2
+ class Contract
3
3
  attr_accessor :name
4
4
  attr_accessor :endpoints
5
5
 
@@ -1,7 +1,8 @@
1
1
  require 'api-tester/definition/response'
2
- require 'api-tester/definition/api_method'
2
+ require 'api-tester/definition/method'
3
3
  require 'api-tester/test_helper'
4
4
  require 'rest-client'
5
+ require 'json'
5
6
 
6
7
  module ApiTester
7
8
  class Endpoint
@@ -33,10 +34,23 @@ module ApiTester
33
34
  temp_url
34
35
  end
35
36
 
37
+ def default_call
38
+ self.test_helper.before
39
+ method_defaults = self.methods[0].default_request
40
+ method_defaults[:url] = self.url
41
+ begin
42
+ response = RestClient::Request.execute(method_defaults)
43
+ rescue RestClient::ExceptionWithResponse => e
44
+ response = e.response
45
+ end
46
+ self.test_helper.after
47
+ response
48
+ end
49
+
36
50
  def call method, payload={}, headers={}
37
51
  self.test_helper.before
38
52
  begin
39
- response = RestClient::Request.execute(method: method.verb, url: self.url, payload: payload, headers: headers)
53
+ response = RestClient::Request.execute(method: method.verb, url: self.url, payload: payload.to_json, headers: headers)
40
54
  rescue RestClient::ExceptionWithResponse => e
41
55
  response = e.response
42
56
  end
@@ -45,7 +59,7 @@ module ApiTester
45
59
  end
46
60
 
47
61
  def add_method verb, response, request=Request.new()
48
- self.methods << ApiTester::ApiMethod.new(verb, response, request)
62
+ self.methods << ApiTester::Method.new(verb, response, request)
49
63
  self
50
64
  end
51
65
 
@@ -1,5 +1,5 @@
1
1
  module ApiTester
2
- class ApiMethod
2
+ class Method
3
3
  attr_accessor :request
4
4
  attr_accessor :expected_response
5
5
  attr_accessor :verb
@@ -9,5 +9,9 @@ module ApiTester
9
9
  self.request = request
10
10
  self.expected_response = response
11
11
  end
12
+
13
+ def default_request
14
+ {:method => self.verb, :payload => request.default_payload, :headers => request.default_headers}
15
+ end
12
16
  end
13
17
  end
@@ -28,7 +28,7 @@ module ApiTester
28
28
  end
29
29
 
30
30
  def default_headers
31
- {content_type: :json, accept: :json}
31
+ self.headers || {content_type: :json, accept: :json}
32
32
  end
33
33
 
34
34
  def cases
@@ -2,18 +2,21 @@
2
2
 
3
3
  module ApiTester
4
4
  module ExtraVerbs
5
- def self.go(endpoint, report)
5
+ def self.go contract
6
6
  reports = []
7
- extras = ApiTester::SupportedVerbs.all - endpoint.verbs
8
- extras.each do |verb|
9
- verb_case = BoundaryCase.new("Verb check with #{verb} for #{endpoint.name}", {}, {})
10
- method = ApiTester::ApiMethod.new verb, ApiTester::Response.new, ApiTester::Request.new
11
- response = endpoint.call method, verb_case.payload, verb_case.headers
12
- test = VerbClass.new response, verb_case.payload, endpoint.not_allowed_response, endpoint.url, verb
13
- reports.concat test.check
7
+
8
+ contract.endpoints.each do |endpoint|
9
+ extras = ApiTester::SupportedVerbs.all - endpoint.verbs
10
+ extras.each do |verb|
11
+ verb_case = BoundaryCase.new("Verb check with #{verb} for #{endpoint.name}", {}, {})
12
+ method = ApiTester::Method.new verb, ApiTester::Response.new, ApiTester::Request.new
13
+ response = endpoint.call method, verb_case.payload, verb_case.headers
14
+ test = VerbClass.new response, verb_case.payload, endpoint.not_allowed_response, endpoint.url, verb
15
+ reports.concat test.check
16
+ end
14
17
  end
15
- report.reports.concat reports
16
- reports == []
18
+
19
+ reports
17
20
  end
18
21
 
19
22
  def self.order
@@ -3,19 +3,20 @@ require 'api-tester/method_case_test'
3
3
 
4
4
  module ApiTester
5
5
  class Format
6
- def self.go definition, report
6
+ def self.go contract
7
7
  reports = []
8
- definition.methods.each do |method|
9
- cases = method.request.cases
10
- cases.each do |format_case|
11
- response = definition.call method, format_case.payload, format_case.headers
12
- test = FormatTest.new response, format_case.payload, definition.bad_request_response, definition.url, method.verb
13
- reports.concat test.check
8
+ contract.endpoints.each do |endpoint|
9
+ endpoint.methods.each do |method|
10
+ cases = method.request.cases
11
+ cases.each do |format_case|
12
+ response = endpoint.call method, format_case.payload, format_case.headers
13
+ test = FormatTest.new response, format_case.payload, endpoint.bad_request_response, endpoint.url, method.verb
14
+ reports.concat test.check
15
+ end
14
16
  end
15
17
  end
16
18
 
17
- report.reports.concat reports
18
- reports == []
19
+ reports
19
20
  end
20
21
 
21
22
  def self.order
@@ -3,22 +3,23 @@ require 'api-tester/method_case_test'
3
3
 
4
4
  module ApiTester
5
5
  class GoodCase
6
- def self.go endpoint, report
7
- reports = []
8
- endpoint.methods.each do |method|
9
- default_case = BoundaryCase.new endpoint.url, method.request.default_payload, method.request.default_headers
10
- response = endpoint.call method, default_case.payload, default_case.headers
11
- test = GoodCaseTest.new response, endpoint.url, method
12
- reports.concat test.check
13
- end
6
+ def self.go contract
7
+ reports = []
14
8
 
15
- report.reports.concat reports
16
- reports == []
9
+ contract.endpoints.each do |endpoint|
10
+ endpoint.methods.each do |method|
11
+ default_case = BoundaryCase.new endpoint.url, method.request.default_payload, method.request.default_headers
12
+ response = endpoint.call method, default_case.payload, default_case.headers
13
+ test = GoodCaseTest.new response, endpoint.url, method
14
+ reports.concat test.check
15
+ end
17
16
  end
17
+ reports
18
+ end
18
19
 
19
- def self.order
20
- 1
21
- end
20
+ def self.order
21
+ 1
22
+ end
22
23
  end
23
24
 
24
25
 
@@ -0,0 +1,66 @@
1
+ require "injection_vulnerability_library"
2
+
3
+ module ApiTester
4
+ module InjectionModule
5
+ def self.go contract
6
+ reports = []
7
+ contract.endpoints.each do |endpoint|
8
+ endpoint.methods.each do |method|
9
+ reports.concat inject_payload endpoint, method
10
+ end
11
+ end
12
+ reports
13
+ end
14
+
15
+ def self.inject_payload endpoint, method
16
+ reports = []
17
+ sql_injections = InjectionVulnerabilityLibrary.sql_vulnerabilities
18
+
19
+ method.request.fields.each do |field|
20
+ sql_injections.each do |injection|
21
+ injection_value = "#{field.default_value}#{injection}"
22
+ payload = method.request.altered_payload(field.name, injection_value)
23
+ response = endpoint.call method, payload, method.request.default_headers
24
+ if(!check_response(response, endpoint)) then
25
+ reports << InjectionReport.new("sql", endpoint.url, payload, response)
26
+ end
27
+ end
28
+ end
29
+
30
+ reports
31
+ end
32
+
33
+ def self.check_response(response, endpoint)
34
+ response.code == 200 || check_error(response, endpoint)
35
+ end
36
+
37
+ def self.check_error response, endpoint
38
+ evaluator = ApiTester::ResponseEvaluator.new response.body, endpoint.bad_request_response
39
+ missing_fields = evaluator.missing_fields
40
+ extra_fields = evaluator.extra_fields
41
+ response.code == endpoint.bad_request_response.code && missing_fields.size == 0 && extra_fields.size == 0
42
+ end
43
+ end
44
+ end
45
+
46
+ class InjectionReport
47
+ attr_accessor :injection_type
48
+ attr_accessor :url
49
+ attr_accessor :payload
50
+ attr_accessor :response
51
+
52
+ def initialize injection_type, url, payload, response
53
+ self.injection_type = injection_type
54
+ self.url = url
55
+ self.payload = payload
56
+ self.response = response
57
+ end
58
+
59
+ def print
60
+ puts "Found potential #{self.injection_type}: "
61
+ puts " Requested #{self.url} with payload:"
62
+ puts " #{self.payload}"
63
+ puts ' Received: '
64
+ puts " #{self.response}"
65
+ end
66
+ end
@@ -0,0 +1,39 @@
1
+ require 'pry'
2
+
3
+ module ApiTester
4
+ module ServerInformation
5
+ def self.go contract
6
+ reports = []
7
+ endpoint = contract.endpoints[0]
8
+ response = endpoint.default_call
9
+
10
+ [:server, :x_powered_by, :x_aspnetmvc_version, :x_aspnet_version].each do |server_key|
11
+ if response.headers[server_key] then
12
+ reports << ServerBroadcastReport.new(response.headers[server_key], server_key)
13
+ end
14
+ end
15
+
16
+ reports
17
+ end
18
+
19
+ def self.order
20
+ 10
21
+ end
22
+ end
23
+ end
24
+
25
+ class ServerBroadcastReport
26
+ attr_accessor :server_info
27
+ attr_accessor :server_key
28
+
29
+ def initialize server_info, server_key
30
+ self.server_info = server_info
31
+ self.server_key = server_key
32
+ end
33
+
34
+ def print
35
+ puts "Found server information being broadcast in headers:"
36
+ puts " #{self.server_info}"
37
+ puts " as #{self.server_key}"
38
+ end
39
+ end
@@ -3,43 +3,45 @@ require 'api-tester/util/supported_verbs'
3
3
 
4
4
  module ApiTester
5
5
  class Typo
6
- def self.go(endpoint, report)
7
- reports = []
8
- allowances(endpoint).each do |verbs|
9
- reports.concat check_typo_url(endpoint)
10
- end
11
-
12
- report.reports.concat reports
13
- reports == []
6
+ def self.go contract
7
+ reports = []
8
+
9
+ contract.endpoints.each do |endpoint|
10
+ allowances(endpoint).each do |verbs|
11
+ reports.concat check_typo_url(endpoint)
12
+ end
14
13
  end
15
14
 
16
- def self.check_typo_url endpoint
17
- bad_url = "#{endpoint.url}gibberishadsfasdf"
18
- bad_endpoint = ApiTester::Endpoint.new "Bad URL", bad_url
19
- typo_case = BoundaryCase.new("Typo URL check", {}, {})
20
- method = ApiTester::ApiMethod.new ApiTester::SupportedVerbs::GET, ApiTester::Response.new(200), ApiTester::Request.new
21
- response = bad_endpoint.call method, typo_case.payload, typo_case.headers
15
+ reports
16
+ end
22
17
 
23
- test = TypoClass.new response, typo_case.payload, endpoint.not_found_response, bad_url, ApiTester::SupportedVerbs::GET
24
- test.check
25
- end
18
+ def self.check_typo_url endpoint
19
+ bad_url = "#{endpoint.url}gibberishadsfasdf"
20
+ bad_endpoint = ApiTester::Endpoint.new "Bad URL", bad_url
21
+ typo_case = BoundaryCase.new("Typo URL check", {}, {})
22
+ method = ApiTester::Method.new ApiTester::SupportedVerbs::GET, ApiTester::Response.new(200), ApiTester::Request.new
23
+ response = bad_endpoint.call method, typo_case.payload, typo_case.headers
26
24
 
27
- def self.allowances(endpoint)
28
- allowances = []
29
- endpoint.methods.each do |method|
30
- allowances << method.verb
31
- end
32
- allowances.uniq
33
- end
25
+ test = TypoClass.new response, typo_case.payload, endpoint.not_found_response, bad_url, ApiTester::SupportedVerbs::GET
26
+ test.check
27
+ end
34
28
 
35
- def self.order
36
- 4
29
+ def self.allowances(endpoint)
30
+ allowances = []
31
+ endpoint.methods.each do |method|
32
+ allowances << method.verb
37
33
  end
34
+ allowances.uniq
35
+ end
36
+
37
+ def self.order
38
+ 4
39
+ end
38
40
  end
39
41
 
40
42
  class TypoClass < MethodCaseTest
41
- def initialize response, payload, expected_response, url, verb
42
- super response, payload, expected_response, url, verb, "TypoModule"
43
- end
43
+ def initialize response, payload, expected_response, url, verb
44
+ super response, payload, expected_response, url, verb, "TypoModule"
45
+ end
44
46
  end
45
47
  end
@@ -2,19 +2,20 @@ require 'api-tester/reporter/missing_response_field_report'
2
2
 
3
3
  module ApiTester
4
4
  class UnusedFields
5
- def self.go definition, report
5
+ def self.go contract
6
6
  reports = []
7
7
 
8
- definition.methods.each do |method|
9
- method.expected_response.body.each do |field|
10
- if field.is_seen == 0
11
- reports << MissingResponseFieldReport.new(definition.url, method.verb, field.name, "UnusedFieldsModule")
8
+ contract.endpoints.each do |endpoint|
9
+ endpoint.methods.each do |method|
10
+ method.expected_response.body.each do |field|
11
+ if field.is_seen == 0
12
+ reports << MissingResponseFieldReport.new(endpoint.url, method.verb, field.name, "UnusedFieldsModule")
13
+ end
12
14
  end
13
15
  end
14
16
  end
15
17
 
16
- report.reports.concat reports
17
- reports == []
18
+ reports
18
19
  end
19
20
 
20
21
  def self.order
@@ -17,6 +17,10 @@ module ApiTester
17
17
  self.reports << report
18
18
  end
19
19
 
20
+ def add_reports reports
21
+ self.reports.concat reports
22
+ end
23
+
20
24
  def print
21
25
  if self.reports.size > 0
22
26
  puts "Issues discovered: #{self.reports.size}"
@@ -1,3 +1,3 @@
1
1
  module ApiTester
2
- VERSION = "0.3.0"
2
+ VERSION = "0.3.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api-tester
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - arane
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-05-23 00:00:00.000000000 Z
11
+ date: 2018-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.4'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.11'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.11'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: rest-client
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,20 @@ dependencies:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
96
  version: '2.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: injection_vulnerability_library
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - '='
102
+ - !ruby/object:Gem::Version
103
+ version: 0.0.2
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - '='
109
+ - !ruby/object:Gem::Version
110
+ version: 0.0.2
83
111
  description: Tool to test APIs which will eventually do boundary testing and other
84
112
  sorts of testing automatically given a contract
85
113
  email:
@@ -101,9 +129,8 @@ files:
101
129
  - changelog.txt
102
130
  - lib/api-tester.rb
103
131
  - lib/api-tester/config.rb
104
- - lib/api-tester/definition/api_contract.rb
105
- - lib/api-tester/definition/api_method.rb
106
132
  - lib/api-tester/definition/boundary_case.rb
133
+ - lib/api-tester/definition/contract.rb
107
134
  - lib/api-tester/definition/endpoint.rb
108
135
  - lib/api-tester/definition/fields/array_field.rb
109
136
  - lib/api-tester/definition/fields/boolean_field.rb
@@ -112,12 +139,15 @@ files:
112
139
  - lib/api-tester/definition/fields/field.rb
113
140
  - lib/api-tester/definition/fields/number_field.rb
114
141
  - lib/api-tester/definition/fields/object_field.rb
142
+ - lib/api-tester/definition/method.rb
115
143
  - lib/api-tester/definition/request.rb
116
144
  - lib/api-tester/definition/response.rb
117
145
  - lib/api-tester/method_case_test.rb
118
146
  - lib/api-tester/modules/extra_verbs.rb
119
147
  - lib/api-tester/modules/format.rb
120
148
  - lib/api-tester/modules/good_case.rb
149
+ - lib/api-tester/modules/injection_module.rb
150
+ - lib/api-tester/modules/server_information.rb
121
151
  - lib/api-tester/modules/typo.rb
122
152
  - lib/api-tester/modules/unused_fields.rb
123
153
  - lib/api-tester/reporter/api_report.rb