diff options
author | David Lord <davidism@gmail.com> | 2020-11-01 06:13:23 -0800 |
---|---|---|
committer | David Lord <davidism@gmail.com> | 2020-11-02 06:51:57 -0800 |
commit | 4c4130b0661da88d0e3df8946da638da295b7092 (patch) | |
tree | b97623110c0656554dac42f5dd8bbea061979ca1 | |
parent | fb971c7dc85dc941926314e0f8e970f2b3b83239 (diff) | |
download | werkzeug-4c4130b0661da88d0e3df8946da638da295b7092.tar.gz |
avoid double encoding in EnvironBuilder.from_environclient-path-encoding
-rw-r--r-- | CHANGES.rst | 2 | ||||
-rw-r--r-- | src/werkzeug/test.py | 24 | ||||
-rw-r--r-- | tests/test_test.py | 8 |
3 files changed, 22 insertions, 12 deletions
diff --git a/CHANGES.rst b/CHANGES.rst index 81606083..3bb30400 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -77,6 +77,8 @@ Unreleased add an ``Authorization`` header. It can be an ``Authorization`` object or a ``(username, password)`` tuple for ``Basic`` auth. :pr:`1809` +- ``EnvironBuilder.from_environ`` decodes values encoded for WSGI, to + avoid double encoding the new values. :pr:`1959` - The default stat reloader will watch Python files under non-system/virtualenv ``sys.path`` entries, which should contain most user code. It will also watch all Python files under diff --git a/src/werkzeug/test.py b/src/werkzeug/test.py index 36527789..93bc5052 100644 --- a/src/werkzeug/test.py +++ b/src/werkzeug/test.py @@ -29,6 +29,7 @@ from urllib.request import Request as _UrllibRequest from ._internal import _get_environ from ._internal import _make_encode_wrapper from ._internal import _to_bytes +from ._internal import _wsgi_decoding_dance from ._internal import _wsgi_encoding_dance from .datastructures import AnyHeaders from .datastructures import Authorization @@ -453,19 +454,25 @@ class EnvironBuilder: self.mimetype = mimetype @classmethod - def from_environ(cls, environ: WSGIEnvironment, **kwargs,) -> "EnvironBuilder": + def from_environ(cls, environ: WSGIEnvironment, **kwargs) -> "EnvironBuilder": """Turn an environ dict back into a builder. Any extra kwargs override the args extracted from the environ. + .. versionchanged:: 2.0 + Path and query values are passed through the WSGI decoding + dance to avoid double encoding. + .. versionadded:: 0.15 """ headers = Headers(EnvironHeaders(environ)) out = { - "path": environ["PATH_INFO"], + "path": _wsgi_decoding_dance(environ["PATH_INFO"]), "base_url": cls._make_base_url( - environ["wsgi.url_scheme"], headers.pop("Host"), environ["SCRIPT_NAME"], + environ["wsgi.url_scheme"], + headers.pop("Host"), + _wsgi_decoding_dance(environ["SCRIPT_NAME"]), ), - "query_string": environ["QUERY_STRING"], + "query_string": _wsgi_decoding_dance(environ["QUERY_STRING"]), "method": environ["REQUEST_METHOD"], "input_stream": environ["wsgi.input"], "content_type": headers.pop("Content-Type", None), @@ -753,18 +760,17 @@ class EnvironBuilder: def _path_encode(x): return _wsgi_encoding_dance(url_unquote(x, self.charset), self.charset) - qs = _wsgi_encoding_dance(self.query_string) - + raw_uri = _wsgi_encoding_dance(self.request_uri, self.charset) result.update( { "REQUEST_METHOD": self.method, "SCRIPT_NAME": _path_encode(self.script_root), "PATH_INFO": _path_encode(self.path), - "QUERY_STRING": qs, + "QUERY_STRING": _wsgi_encoding_dance(self.query_string, self.charset), # Non-standard, added by mod_wsgi, uWSGI - "REQUEST_URI": _wsgi_encoding_dance(self.request_uri), + "REQUEST_URI": raw_uri, # Non-standard, added by gunicorn - "RAW_URI": _wsgi_encoding_dance(self.request_uri), + "RAW_URI": raw_uri, "SERVER_NAME": self.server_name, "SERVER_PORT": str(self.server_port), "HTTP_HOST": self.host, diff --git a/tests/test_test.py b/tests/test_test.py index 51d41686..2cb080b1 100644 --- a/tests/test_test.py +++ b/tests/test_test.py @@ -367,17 +367,19 @@ def test_create_environ_query_string_error(): def test_builder_from_environ(): environ = create_environ( - "/foo", + "/ㄱ", base_url="https://example.com/base", query_string={"name": "Werkzeug"}, - data={"foo": "bar"}, - headers={"X-Foo": "bar"}, + data={"foo": "ㄴ"}, + headers={"X-Foo": "ㄷ"}, ) builder = EnvironBuilder.from_environ(environ) + try: new_environ = builder.get_environ() finally: builder.close() + assert new_environ == environ |