From 0b8b3f022522a6d5bb2ef30035db2ff1ed94e4fa Mon Sep 17 00:00:00 2001 From: Q_back Date: Wed, 10 Jun 2020 12:02:57 +0200 Subject: [PATCH 1/2] fix UnicodeDecodeError when parsing openapi spec --- .../parsers/doc/open_api/specification.py | 4 +- .../tests/data/swagger-special-chars.json | 73 +++++++++++++++++++ .../doc/open_api/tests/test_specification.py | 12 +++ 3 files changed, 87 insertions(+), 2 deletions(-) create mode 100644 w3af/core/data/parsers/doc/open_api/tests/data/swagger-special-chars.json diff --git a/w3af/core/data/parsers/doc/open_api/specification.py b/w3af/core/data/parsers/doc/open_api/specification.py index 4aaa93344e..dc338fcf72 100644 --- a/w3af/core/data/parsers/doc/open_api/specification.py +++ b/w3af/core/data/parsers/doc/open_api/specification.py @@ -21,7 +21,6 @@ """ import json -import yaml import logging from yaml import load @@ -232,7 +231,8 @@ def _load_spec_dict(self): :return: The dict with the open api data """ try: - spec_dict = json.loads(self.http_response.body) + decoded_response = self.http_response.body.decode('ascii', 'ignore') + spec_dict = json.loads(decoded_response) except ValueError: # Seems like the OpenAPI was specified using Yaml instead of # JSON. Let's parse the Yaml data! diff --git a/w3af/core/data/parsers/doc/open_api/tests/data/swagger-special-chars.json b/w3af/core/data/parsers/doc/open_api/tests/data/swagger-special-chars.json new file mode 100644 index 0000000000..a3eeff15d9 --- /dev/null +++ b/w3af/core/data/parsers/doc/open_api/tests/data/swagger-special-chars.json @@ -0,0 +1,73 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore, special chars: ąęćźżó^żć√≤Ķńå", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team" + }, + "license": { + "name": "MIT" + } + }, + "host": "petstore.swagger.io", + "basePath": "/api", + "schemes": [ + "http" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/pets": { + "get": { + "description": "Returns all pets from the system that the user has access to", + "produces": [ + "application/json" + ], + "responses": { + "200": { + "description": "A list of pets.", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/Pet" + } + } + } + } + } + }, + "/pets/{ąęćźżó^żć√≤Ķńå}": { + "get": { + "description": "Let's see if I'll return an error" + } + } + }, + "definitions": { + "Pet": { + "type": "object", + "required": [ + "id", + "name" + ], + "properties": { + "id": { + "type": "integer", + "format": "int64" + }, + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } +} diff --git a/w3af/core/data/parsers/doc/open_api/tests/test_specification.py b/w3af/core/data/parsers/doc/open_api/tests/test_specification.py index e6e96efb6a..3eb9636167 100644 --- a/w3af/core/data/parsers/doc/open_api/tests/test_specification.py +++ b/w3af/core/data/parsers/doc/open_api/tests/test_specification.py @@ -595,6 +595,18 @@ def test_parameter_handler_multiple_paths_and_headers(self): handler = SpecificationHandler(http_response) self.check_parameter_setting(handler) + def test_specification_handler_can_handle_spec_with_non_ascii_chars(self): + with open( + 'w3af/core/data/parsers/doc/open_api/tests/data/swagger-special-chars.json', + ) as file_: + spec_as_string = file_.read() + http_response = self.generate_response(spec_as_string) + spec_handler = SpecificationHandler(http_response) + result = spec_handler.get_api_information() + for _ in result: + pass + self.assertFalse(spec_handler._parsing_errors) + def check_parameter_setting(self, spec_handler): data = [d for d in spec_handler.get_api_information()] self.assertIsNotNone(data) From 2e88cc21c6aa483ccae772061376b6a26848af33 Mon Sep 17 00:00:00 2001 From: Q_back Date: Wed, 10 Jun 2020 16:04:21 +0200 Subject: [PATCH 2/2] fix request error when not required array param in open_api spec --- .../data/parsers/doc/open_api/requests.py | 2 + .../data/array_not_required_model_items.json | 71 +++++++++++++++++++ .../doc/open_api/tests/test_requests.py | 19 +++++ 3 files changed, 92 insertions(+) create mode 100644 w3af/core/data/parsers/doc/open_api/tests/data/array_not_required_model_items.json diff --git a/w3af/core/data/parsers/doc/open_api/requests.py b/w3af/core/data/parsers/doc/open_api/requests.py index 19366b0d7f..1a28daef88 100644 --- a/w3af/core/data/parsers/doc/open_api/requests.py +++ b/w3af/core/data/parsers/doc/open_api/requests.py @@ -184,6 +184,8 @@ def get_uri(self): continue if param_def.param_spec['type'] == 'array': + if not parameters[param_name] and not param_def.required: + continue parameters[param_name] = parameters[param_name][0] if parameters: diff --git a/w3af/core/data/parsers/doc/open_api/tests/data/array_not_required_model_items.json b/w3af/core/data/parsers/doc/open_api/tests/data/array_not_required_model_items.json new file mode 100644 index 0000000000..7a7877738f --- /dev/null +++ b/w3af/core/data/parsers/doc/open_api/tests/data/array_not_required_model_items.json @@ -0,0 +1,71 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "Swagger Petstore", + "description": "A sample API that uses a petstore as an example to demonstrate features in the swagger-2.0 specification", + "termsOfService": "http://swagger.io/terms/", + "contact": { + "name": "Swagger API Team" + }, + "license": { + "name": "MIT" + } + }, + "host": "petstore.swagger.io", + "basePath": "/api", + "schemes": [ + "http" + ], + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "paths": { + "/pets": { + "post": { + "description": "Add multiple pets", + "operationId": "addMultiplePets", + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "pets", + "in": "query", + "description": "array with pets to add", + "required": false, + "type": "array", + "items": {} + } + ], + "responses": { + "200": { + "description": "pet response", + "schema": { + "$ref": "#/definitions/Pet" + } + } + } + } + } + }, + "definitions": { + "Pet": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "tag": { + "type": "string" + } + } + } + } +} diff --git a/w3af/core/data/parsers/doc/open_api/tests/test_requests.py b/w3af/core/data/parsers/doc/open_api/tests/test_requests.py index 45027e6090..2a454e128a 100644 --- a/w3af/core/data/parsers/doc/open_api/tests/test_requests.py +++ b/w3af/core/data/parsers/doc/open_api/tests/test_requests.py @@ -20,6 +20,7 @@ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA """ +import json import unittest from w3af.core.data.parsers.doc.url import URL @@ -312,6 +313,24 @@ def test_array_with_model_items_param_in_json(self): self.assertEqual(fuzzable_request.get_headers(), e_headers) self.assertEqual(fuzzable_request.get_data(), e_data) + def test_array_param_not_required_in_json(self): + """ + Regression test when param type is array and param is not required. + Param must be in query, not in body. + """ + test_spec_filename = ( + 'w3af/core/data/parsers/doc/open_api/tests/data/array_not_required_model_items.json' + ) + with open(test_spec_filename, 'r') as file_: + specification_as_string = file_.read() + + http_response = self.generate_response(specification_as_string) + handler = SpecificationHandler(http_response) + data = [item for item in handler.get_api_information()] + for spec_obj in data: + factory = RequestFactory(*spec_obj) + req = factory.get_fuzzable_request() + def test_model_param_nested_allOf_in_json(self): specification_as_string = NestedModel().get_specification() http_response = self.generate_response(specification_as_string)