File CVE-2024-34069.patch of Package python-Werkzeug.36256

Index: Werkzeug-2.3.6/docs/debug.rst
===================================================================
--- Werkzeug-2.3.6.orig/docs/debug.rst
+++ Werkzeug-2.3.6/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-2.3.6/src/werkzeug/debug/__init__.py
===================================================================
--- Werkzeug-2.3.6.orig/src/werkzeug/debug/__init__.py
+++ Werkzeug-2.3.6/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-2.3.6/src/werkzeug/debug/shared/debugger.js
===================================================================
--- Werkzeug-2.3.6.orig/src/werkzeug/debug/shared/debugger.js
+++ Werkzeug-2.3.6/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-2.3.6/src/werkzeug/sansio/utils.py
===================================================================
--- Werkzeug-2.3.6.orig/src/werkzeug/sansio/utils.py
+++ Werkzeug-2.3.6/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.
Index: Werkzeug-2.3.6/src/werkzeug/serving.py
===================================================================
--- Werkzeug-2.3.6.orig/src/werkzeug/serving.py
+++ Werkzeug-2.3.6/src/werkzeug/serving.py
@@ -1066,6 +1066,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
openSUSE Build Service is sponsored by