Sync from SUSE:ALP:Source:Standard:1.0 python-Werkzeug revision 6f7cb9895298be51b0c1b694aa92dde9
This commit is contained in:
parent
f6e78eeac2
commit
a7e6a5c98a
226
CVE-2024-34069.patch
Normal file
226
CVE-2024-34069.patch
Normal file
@ -0,0 +1,226 @@
|
||||
From 71b69dfb7df3d912e66bab87fbb1f21f83504967 Mon Sep 17 00:00:00 2001
|
||||
From: David Lord <davidism@gmail.com>
|
||||
Date: Thu, 2 May 2024 11:55:52 -0700
|
||||
Subject: [PATCH 1/2] restrict debugger trusted hosts
|
||||
|
||||
Add a list of `trusted_hosts` to the `DebuggedApplication` middleware. It defaults to only allowing `localhost`, `.localhost` subdomains, and `127.0.0.1`. `run_simple(use_debugger=True)` adds its `hostname` argument to the trusted list as well. The middleware can be used directly to further modify the trusted list in less common development scenarios.
|
||||
|
||||
The debugger UI uses the full `document.location` instead of only `document.location.pathname`.
|
||||
|
||||
Either of these fixes on their own mitigates the reported vulnerability.
|
||||
---
|
||||
CHANGES.rst | 5 ++++
|
||||
docs/debug.rst | 35 +++++++++++++++++++++++----
|
||||
src/werkzeug/debug/__init__.py | 10 ++++++++
|
||||
src/werkzeug/debug/shared/debugger.js | 4 +--
|
||||
src/werkzeug/serving.py | 3 +++
|
||||
5 files changed, 50 insertions(+), 7 deletions(-)
|
||||
|
||||
Index: werkzeug-3.0.1/docs/debug.rst
|
||||
===================================================================
|
||||
--- werkzeug-3.0.1.orig/docs/debug.rst
|
||||
+++ werkzeug-3.0.1/docs/debug.rst
|
||||
@@ -16,7 +16,8 @@ interactive debug console to execute cod
|
||||
The debugger allows the execution of arbitrary code which makes it a
|
||||
major security risk. **The debugger must never be used on production
|
||||
machines. We cannot stress this enough. Do not enable the debugger
|
||||
- in production.**
|
||||
+ in production.** Production means anything that is not development,
|
||||
+ and anything that is publicly accessible.
|
||||
|
||||
.. note::
|
||||
|
||||
@@ -72,10 +73,9 @@ argument to get a detailed list of all t
|
||||
Debugger PIN
|
||||
------------
|
||||
|
||||
-Starting with Werkzeug 0.11 the debug console is protected by a PIN.
|
||||
-This is a security helper to make it less likely for the debugger to be
|
||||
-exploited if you forget to disable it when deploying to production. The
|
||||
-PIN based authentication is enabled by default.
|
||||
+The debug console is protected by a PIN. This is a security helper to make it
|
||||
+less likely for the debugger to be exploited if you forget to disable it when
|
||||
+deploying to production. The PIN based authentication is enabled by default.
|
||||
|
||||
The first time a console is opened, a dialog will prompt for a PIN that
|
||||
is printed to the command line. The PIN is generated in a stable way
|
||||
@@ -89,6 +89,31 @@ restarted.
|
||||
|
||||
**This feature is not meant to entirely secure the debugger. It is
|
||||
intended to make it harder for an attacker to exploit the debugger.
|
||||
+Never enable the debugger in production.**
|
||||
+
|
||||
+
|
||||
+Allowed Hosts
|
||||
+-------------
|
||||
+
|
||||
+The debug console will only be served if the request comes from a trusted host.
|
||||
+If a request comes from a browser page that is not served on a trusted URL, a
|
||||
+400 error will be returned.
|
||||
+
|
||||
+By default, ``localhost``, any ``.localhost`` subdomain, and ``127.0.0.1`` are
|
||||
+trusted. ``run_simple`` will trust its ``hostname`` argument as well. To change
|
||||
+this further, use the debug middleware directly rather than through
|
||||
+``use_debugger=True``.
|
||||
+
|
||||
+.. code-block:: python
|
||||
+
|
||||
+ if os.environ.get("USE_DEBUGGER") in {"1", "true"}:
|
||||
+ app = DebuggedApplication(app, evalex=True)
|
||||
+ app.trusted_hosts = [...]
|
||||
+
|
||||
+ run_simple("localhost", 8080, app)
|
||||
+
|
||||
+**This feature is not meant to entirely secure the debugger. It is
|
||||
+intended to make it harder for an attacker to exploit the debugger.
|
||||
Never enable the debugger in production.**
|
||||
|
||||
|
||||
Index: werkzeug-3.0.1/src/werkzeug/debug/__init__.py
|
||||
===================================================================
|
||||
--- werkzeug-3.0.1.orig/src/werkzeug/debug/__init__.py
|
||||
+++ werkzeug-3.0.1/src/werkzeug/debug/__init__.py
|
||||
@@ -19,7 +19,9 @@ from zlib import adler32
|
||||
|
||||
from .._internal import _log
|
||||
from ..exceptions import NotFound
|
||||
+from ..exceptions import SecurityError
|
||||
from ..http import parse_cookie
|
||||
+from ..sansio.utils import host_is_trusted
|
||||
from ..security import gen_salt
|
||||
from ..utils import send_file
|
||||
from ..wrappers.request import Request
|
||||
@@ -297,6 +299,14 @@ class DebuggedApplication:
|
||||
else:
|
||||
self.pin = None
|
||||
|
||||
+ self.trusted_hosts: list[str] = [".localhost", "127.0.0.1"]
|
||||
+ """List of domains to allow requests to the debugger from. A leading dot
|
||||
+ allows all subdomains. This only allows ``".localhost"`` domains by
|
||||
+ default.
|
||||
+
|
||||
+ .. versionadded:: 3.0.3
|
||||
+ """
|
||||
+
|
||||
@property
|
||||
def pin(self) -> str | None:
|
||||
if not hasattr(self, "_pin"):
|
||||
@@ -343,7 +353,7 @@ class DebuggedApplication:
|
||||
|
||||
is_trusted = bool(self.check_pin_trust(environ))
|
||||
html = tb.render_debugger_html(
|
||||
- evalex=self.evalex,
|
||||
+ evalex=self.evalex and self.check_host_trust(environ),
|
||||
secret=self.secret,
|
||||
evalex_trusted=is_trusted,
|
||||
)
|
||||
@@ -371,6 +381,9 @@ class DebuggedApplication:
|
||||
frame: DebugFrameSummary | _ConsoleFrame,
|
||||
) -> Response:
|
||||
"""Execute a command in a console."""
|
||||
+ if not self.check_host_trust(request.environ):
|
||||
+ return SecurityError() # type: ignore[return-value]
|
||||
+
|
||||
contexts = self.frame_contexts.get(id(frame), [])
|
||||
|
||||
with ExitStack() as exit_stack:
|
||||
@@ -381,6 +394,9 @@ class DebuggedApplication:
|
||||
|
||||
def display_console(self, request: Request) -> Response:
|
||||
"""Display a standalone shell."""
|
||||
+ if not self.check_host_trust(request.environ):
|
||||
+ return SecurityError() # type: ignore[return-value]
|
||||
+
|
||||
if 0 not in self.frames:
|
||||
if self.console_init_func is None:
|
||||
ns = {}
|
||||
@@ -433,12 +449,18 @@ class DebuggedApplication:
|
||||
return None
|
||||
return (time.time() - PIN_TIME) < ts
|
||||
|
||||
+ def check_host_trust(self, environ: WSGIEnvironment) -> bool:
|
||||
+ return host_is_trusted(environ.get("HTTP_HOST"), self.trusted_hosts)
|
||||
+
|
||||
def _fail_pin_auth(self) -> None:
|
||||
time.sleep(5.0 if self._failed_pin_auth > 5 else 0.5)
|
||||
self._failed_pin_auth += 1
|
||||
|
||||
def pin_auth(self, request: Request) -> Response:
|
||||
"""Authenticates with the pin."""
|
||||
+ if not self.check_host_trust(request.environ):
|
||||
+ return SecurityError() # type: ignore[return-value]
|
||||
+
|
||||
exhausted = False
|
||||
auth = False
|
||||
trust = self.check_pin_trust(request.environ)
|
||||
@@ -488,8 +510,11 @@ class DebuggedApplication:
|
||||
rv.delete_cookie(self.pin_cookie_name)
|
||||
return rv
|
||||
|
||||
- def log_pin_request(self) -> Response:
|
||||
+ def log_pin_request(self, request: Request) -> Response:
|
||||
"""Log the pin if needed."""
|
||||
+ if not self.check_host_trust(request.environ):
|
||||
+ return SecurityError() # type: ignore[return-value]
|
||||
+
|
||||
if self.pin_logging and self.pin is not None:
|
||||
_log(
|
||||
"info", " * To enable the debugger you need to enter the security pin:"
|
||||
@@ -516,7 +541,7 @@ class DebuggedApplication:
|
||||
elif cmd == "pinauth" and secret == self.secret:
|
||||
response = self.pin_auth(request) # type: ignore
|
||||
elif cmd == "printpin" and secret == self.secret:
|
||||
- response = self.log_pin_request() # type: ignore
|
||||
+ response = self.log_pin_request(request) # type: ignore
|
||||
elif (
|
||||
self.evalex
|
||||
and cmd is not None
|
||||
Index: werkzeug-3.0.1/src/werkzeug/debug/shared/debugger.js
|
||||
===================================================================
|
||||
--- werkzeug-3.0.1.orig/src/werkzeug/debug/shared/debugger.js
|
||||
+++ werkzeug-3.0.1/src/werkzeug/debug/shared/debugger.js
|
||||
@@ -48,7 +48,7 @@ function initPinBox() {
|
||||
btn.disabled = true;
|
||||
|
||||
fetch(
|
||||
- `${document.location.pathname}?__debugger__=yes&cmd=pinauth&pin=${pin}&s=${encodedSecret}`
|
||||
+ `${document.location}?__debugger__=yes&cmd=pinauth&pin=${pin}&s=${encodedSecret}`
|
||||
)
|
||||
.then((res) => res.json())
|
||||
.then(({auth, exhausted}) => {
|
||||
@@ -79,7 +79,7 @@ function promptForPin() {
|
||||
if (!EVALEX_TRUSTED) {
|
||||
const encodedSecret = encodeURIComponent(SECRET);
|
||||
fetch(
|
||||
- `${document.location.pathname}?__debugger__=yes&cmd=printpin&s=${encodedSecret}`
|
||||
+ `${document.location}?__debugger__=yes&cmd=printpin&s=${encodedSecret}`
|
||||
);
|
||||
const pinPrompt = document.getElementsByClassName("pin-prompt")[0];
|
||||
fadeIn(pinPrompt);
|
||||
Index: werkzeug-3.0.1/src/werkzeug/serving.py
|
||||
===================================================================
|
||||
--- werkzeug-3.0.1.orig/src/werkzeug/serving.py
|
||||
+++ werkzeug-3.0.1/src/werkzeug/serving.py
|
||||
@@ -1068,6 +1068,9 @@ def run_simple(
|
||||
from .debug import DebuggedApplication
|
||||
|
||||
application = DebuggedApplication(application, evalex=use_evalex)
|
||||
+ # Allow the specified hostname to use the debugger, in addition to
|
||||
+ # localhost domains.
|
||||
+ application.trusted_hosts.append(hostname)
|
||||
|
||||
if not is_running_from_reloader():
|
||||
fd = None
|
||||
Index: werkzeug-3.0.1/src/werkzeug/sansio/utils.py
|
||||
===================================================================
|
||||
--- werkzeug-3.0.1.orig/src/werkzeug/sansio/utils.py
|
||||
+++ werkzeug-3.0.1/src/werkzeug/sansio/utils.py
|
||||
@@ -8,7 +8,7 @@ from ..exceptions import SecurityError
|
||||
from ..urls import uri_to_iri
|
||||
|
||||
|
||||
-def host_is_trusted(hostname: str, trusted_list: t.Iterable[str]) -> bool:
|
||||
+def host_is_trusted(hostname: str | None, trusted_list: t.Iterable[str]) -> bool:
|
||||
"""Check if a host matches a list of trusted names.
|
||||
|
||||
:param hostname: The name to check.
|
@ -1,3 +1,9 @@
|
||||
-------------------------------------------------------------------
|
||||
Mon Jun 3 06:54:48 UTC 2024 - Daniel Garcia <daniel.garcia@suse.com>
|
||||
|
||||
- Add CVE-2024-34069.patch, restrict debugger trusted hosts.
|
||||
(bsc#1223979, CVE-2024-34069, gh#pallets/werkzeug@3386395b24c7)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
Fri Oct 27 03:06:50 UTC 2023 - Steve Kowalik <steven.kowalik@suse.com>
|
||||
|
||||
|
@ -33,6 +33,8 @@ Summary: The Swiss Army knife of Python web development
|
||||
License: BSD-3-Clause
|
||||
URL: https://werkzeug.palletsprojects.com
|
||||
Source: https://files.pythonhosted.org/packages/source/w/werkzeug/werkzeug-%{version}.tar.gz
|
||||
# PATCH-FIX-UPSTREAM CVE-2024-34069.patch gh#pallets/werkzeug@3386395b24c7
|
||||
Patch0: CVE-2024-34069.patch
|
||||
BuildRequires: %{python_module base >= 3.8}
|
||||
BuildRequires: %{python_module flit-core}
|
||||
BuildRequires: %{python_module pip}
|
||||
|
Loading…
Reference in New Issue
Block a user