From 145c26f5515c4f05609aa694e9696c265eb849f9 Mon Sep 17 00:00:00 2001 From: Simon Brulhart Date: Fri, 20 Aug 2021 15:01:16 +0200 Subject: [PATCH] Always escape special chars in URL query params --- pyodata/v2/service.py | 13 ++++--------- tests/test_service_v2.py | 19 +++++++++++++++++++ 2 files changed, 23 insertions(+), 9 deletions(-) diff --git a/pyodata/v2/service.py b/pyodata/v2/service.py index 05e1a86d..532bcb9b 100644 --- a/pyodata/v2/service.py +++ b/pyodata/v2/service.py @@ -13,13 +13,8 @@ from email.parser import Parser from http.client import HTTPResponse from io import BytesIO +from urllib.parse import urlencode -try: - # For Python 3.0 and later - from urllib.parse import quote -except ImportError: - # Fallback to urllib2 - from urllib2 import quote from pyodata.exceptions import HttpError, PyODataException, ExpressionError, ProgramError from . import model @@ -54,7 +49,7 @@ def encode_multipart(boundary, http_requests): # request line (method + path + query params) line = f'{req.get_method()} {req.get_path()}' - query_params = '&'.join(['{}={}'.format(key, val) for key, val in req.get_query_params().items()]) + query_params = urlencode(req.get_query_params()) if query_params: line += '?' + query_params line += ' HTTP/1.1' @@ -316,7 +311,7 @@ def execute(self): if body: self._logger.debug(' body: %s', body) - params = "&".join("%s=%s" % (k, v) for k, v in self.get_query_params().items()) + params = urlencode(self.get_query_params()) response = self._connection.request( self.get_method(), url, headers=headers, params=params, data=body) @@ -1216,7 +1211,7 @@ def _build_expression(self, field_name, operator, value): def __str__(self): expressions = self._process_expressions() result = self._combine_expressions(expressions) - return quote(result) + return result class GetEntitySetRequest(QueryRequest): diff --git a/tests/test_service_v2.py b/tests/test_service_v2.py index 3d857cb6..5b69866a 100644 --- a/tests/test_service_v2.py +++ b/tests/test_service_v2.py @@ -378,6 +378,25 @@ def test_function_import_primitive(service): assert result == 6 +@responses.activate +def test_function_import_escape_parameter(service): + """Simple function call with special URL characters in parameter value""" + + # pylint: disable=redefined-outer-name + + responses.add( + responses.GET, + f"{service.url}/retrieve?Param=%27%26|%2B|%3D|%2F|%3F|+|%40%27", + headers={'Content-type': 'application/json'}, + json={'d': True}, + status=200) + + chars = "|".join("&+=/? @") + result = service.functions.retrieve.parameter('Param', chars).execute() + assert result is True + + + @responses.activate @patch('logging.Logger.warning') def test_function_import_primitive_unexpected_status_code(mock_warning, service):