class ResultValidatorTest(TestCase): SAMPLE_RESPONSES = {"405": {"description": "Invalid input"}} def setUp(self): self.validator = ResultValidator() self.response = MagicMock() self.response.status_code = 405 self.response.documented_reason = 'doc reason' self.response.text = 'Error text from server' def test_evaluate_returns_none_when_resp_status_code_in_expected_responses( self): result = self.validator.evaluate(self.response, self.SAMPLE_RESPONSES, True) self.assertEqual( result, { 'body': 'Error text from server', 'status_code': 405, 'documented_reason': 'Invalid input' }) def test_evaluate_returns_result_log_when_resp_status_code_not_expected_responses_and_logging_not_forced( self): self.response.status_code = 500 self.response.text = 'Internal Error' result = self.validator.evaluate(self.response, self.SAMPLE_RESPONSES, True) self.assertEqual( result, { "status_code": 500, "body": 'Internal Error', "documented_reason": None }) def test_evaluate_returns_log_when_resp_status_code_in_expected_responses_and_forced_for_all_codes( self): result = self.validator.evaluate(self.response, self.SAMPLE_RESPONSES, False) self.assertEqual( result, { "status_code": 405, "body": 'Error text from server', "documented_reason": 'Invalid input' }) def test_evaluate_returns_log_when_resp_status_code_not_in_expected_responses_and_forced_for_all_codes( self): self.response.status_code = 500 self.response.text = 'Internal Error' result = self.validator.evaluate(self.response, self.SAMPLE_RESPONSES, False) self.assertEqual( result, { "status_code": 500, "body": 'Internal Error', "documented_reason": None })
def start(self): print('Fetching open API from: ' + self.url) # Try to find the protocol, host and basePath from the Swagger spec. # host and schemes can be omitted, and the "standard" says you should use the spec's URL to derive them. # https://swagger.io/docs/specification/2-0/api-host-and-base-path/ schemes = [] host = None basePath = None try: spec = self.get_swagger_spec(self.url) specURL = urlparse(self.url) except json.JSONDecodeError: error_cant_connect() if 'openapi' not in spec: if 'swagger' not in spec: self.log_operation( None, self.url, { "status_code": "000", "documented_reason": "Specification: no version string found", "body": "Specification: no version string found in Swagger spec" }, '') raise SchemaException if not spec['swagger'].startswith('2'): self.log_operation( None, self.url, { "status_code": "000", "documented_reason": "Specification: wrong specification version", "body": "Specification: version in swagger spec not supported" }, '') raise SchemaException elif not spec['openapi'].startswith('3'): self.log_operation( None, self.url, { "status_code": "000", "documented_reason": "Specification: wrong specification version", "body": "Specification: version in openapi spec not supported" }, '') raise SchemaException baseUris = [] if 'servers' in spec and len(spec['servers']) > 0: for server in spec['servers']: baseUris.append(server['url']) elif 'schemes' in spec: schemes = spec['schemes'] else: # fake the array we'd find in the spec schemes.append(specURL.scheme) self.log_operation( None, self.url, { "status_code": "000", "documented_reason": "Specification: no schemes entry, fallback to spec URL scheme", "body": "Specification: host entry not present in Swagger spec" }, '') if len(baseUris) == 0: if self.host: host = self.host elif 'host' in spec: host = spec['host'] else: host = specURL.netloc self.log_operation( None, self.url, { "status_code": "000", "documented_reason": "Specification: no host entry, fallback to spec URL host", "body": "Specification: schemes entry not present in Swagger spec" }, '') # There is no nice way to derive the basePath from the spec's URL. They *have* to include it if 'basePath' not in spec: self.log_operation( None, self.url, { "status_code": "000", "documented_reason": "Specification: basePath entry missing from Swagger spec", "body": "Specification Error: basePath entry missing from Swagger spec" }, '') raise SchemaException basePath = self.basepath if self.basepath else spec['basePath'] host_basepath = host + basePath for protocol in schemes: baseUris.append(protocol + '://' + host_basepath) paths = spec['paths'] if 'definitions' in spec: type_definitions = spec['definitions'] elif 'components' in spec and 'schemas' in spec['components']: type_definitions = spec['components']['schemas'] else: self.log_operation( None, self.url, { "status_code": "000", "documented_reason": "Specification: type definitions missing from Swagger spec", "body": "Specification Error: type definitions missing from Swagger spec" }, '') raise SchemaException # the specifcation can list multiple schemes (http, https, ws, wss) - all should be tested. # Each scheme is a potentially different end point replicator = Replicator(type_definitions, self.use_string_pattern, True, self.max_string_length) for baseUri in baseUris: for path_key in paths.keys(): if path_key in self.ignored_paths: continue path = paths[path_key] for op_code in path.keys(): operation = HttpOperation(op_code, baseUri, path_key, replicator=replicator, op_infos=path[op_code], use_fuzzing=True, headers=self.headers, ignore_tls=self.ignore_tls) for _ in range(self.iterations): response = operation.execute() validator = ResultValidator() log = validator.evaluate( response, path[op_code]['responses'], self.log_unexpected_errors_only) curlcommand = CurlCommand(response.url, operation.op_code, operation.request_body, self.headers, self.ignore_tls) # log to screen for now self.log_operation(operation.op_code, response.url, log, curlcommand) return True
def start(self): print('Fetching open API from: ' + self.url) # Try to find the protocol, host and basePath from the Swagger spec. # host and schemes can be omitted, and the "standard" says you should use the spec's URL to derive them. # https://swagger.io/docs/specification/2-0/api-host-and-base-path/ schemes = [] host = None basePath = None client = SwaggerClient.from_url(self.url) spec = client.swagger_spec.spec_dict specURL = urlparse(self.url) if 'schemes' in spec: schemes = spec['schemes'] else: # fake the array we'd find in the spec schemes.append(specURL.scheme) self.log_operation( None, self.url, { "status_code": "000", "documented_reason": "Specification: no schemes entry, fallback to spec URL scheme", "body": "Specification: host entry not present in Swagger spec" }, '') if 'host' in spec: host = spec['host'] else: host = specURL.netloc self.log_operation( None, self.url, { "status_code": "000", "documented_reason": "Specification: no host entry, fallback to spec URL host", "body": "Specification: schemes entry not present in Swagger spec" }, '') # There is no nice way to derive the basePath from the spec's URL. They *have* to include it if 'basePath' not in spec: self.log_operation( None, self.url, { "status_code": "000", "body": "Specification Error: basePath entry missing from Swagger spec" }, '') host_basepath = host + spec['basePath'] paths = spec['paths'] type_definitions = spec['definitions'] # the specifcation can list multiple schemes (http, https, ws, wss) - all should be tested. # Each scheme is a potentially different end point for protocol in schemes: for path_key in paths.keys(): path = paths[path_key] for op_code in path.keys(): operation = HttpOperation(op_code, protocol + '://' + host_basepath, path_key, op_infos=path[op_code], use_fuzzing=True, headers=self.headers) for x in range(self.iterations): response = operation.execute(type_definitions) validator = ResultValidator() log = validator.evaluate( response, path[op_code]['responses'], self.log_unexpected_errors_only) curlcommand = CurlCommand(response.url, operation.op_code, operation.request_body, self.headers) # log to screen for now self.log_operation(operation.op_code, response.url, log, curlcommand)
def setUp(self): self.validator = ResultValidator() self.response = MagicMock() self.response.status_code = 405 self.response.documented_reason = 'doc reason' self.response.text = 'Error text from server'