summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid Lord <davidism@gmail.com>2020-11-01 06:13:23 -0800
committerDavid Lord <davidism@gmail.com>2020-11-02 06:51:57 -0800
commit4c4130b0661da88d0e3df8946da638da295b7092 (patch)
treeb97623110c0656554dac42f5dd8bbea061979ca1
parentfb971c7dc85dc941926314e0f8e970f2b3b83239 (diff)
downloadwerkzeug-4c4130b0661da88d0e3df8946da638da295b7092.tar.gz
avoid double encoding in EnvironBuilder.from_environclient-path-encoding
-rw-r--r--CHANGES.rst2
-rw-r--r--src/werkzeug/test.py24
-rw-r--r--tests/test_test.py8
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