File CVE-2024-34069.patch of Package python-Werkzeug.33728
Index: Werkzeug-1.0.1/docs/debug.rst
===================================================================
--- Werkzeug-1.0.1.orig/docs/debug.rst
+++ Werkzeug-1.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-1.0.1/src/werkzeug/debug/__init__.py
===================================================================
--- Werkzeug-1.0.1.orig/src/werkzeug/debug/__init__.py
+++ Werkzeug-1.0.1/src/werkzeug/debug/__init__.py
@@ -24,7 +24,9 @@ from os.path import join
from .._compat import text_type
from .._internal import _log
+from ..exceptions import SecurityError
from ..http import parse_cookie
+from ..wsgi import host_is_trusted
from ..security import gen_salt
from ..wrappers import BaseRequest as Request
from ..wrappers import BaseResponse as Response
@@ -280,6 +282,14 @@ class DebuggedApplication(object):
else:
self.pin = None
+ self.trusted_hosts = [".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):
if not hasattr(self, "_pin"):
@@ -341,17 +351,24 @@ class DebuggedApplication(object):
else:
is_trusted = bool(self.check_pin_trust(environ))
yield traceback.render_full(
- evalex=self.evalex, evalex_trusted=is_trusted, secret=self.secret
+ evalex=self.evalex and self.check_host_trust(environ),
+ evalex_trusted=is_trusted, secret=self.secret
).encode("utf-8", "replace")
traceback.log(environ["wsgi.errors"])
def execute_command(self, request, command, frame):
"""Execute a command in a console."""
+
+ if not self.check_host_trust(request.environ):
+ return SecurityError() # type: ignore[return-value]
return Response(frame.console.eval(command), mimetype="text/html")
def display_console(self, request):
"""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 = {}
@@ -400,12 +417,18 @@ class DebuggedApplication(object):
return None
return (time.time() - PIN_TIME) < int(ts)
+ def check_host_trust(self, environ):
+ return host_is_trusted(environ.get("HTTP_HOST"), self.trusted_hosts)
+
def _fail_pin_auth(self):
time.sleep(5.0 if self._failed_pin_auth > 5 else 0.5)
self._failed_pin_auth += 1
def pin_auth(self, request):
"""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)
@@ -453,6 +476,9 @@ class DebuggedApplication(object):
def log_pin_request(self):
"""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:"
@@ -480,7 +506,7 @@ class DebuggedApplication(object):
elif cmd == "pinauth" and secret == self.secret:
response = self.pin_auth(request)
elif cmd == "printpin" and secret == self.secret:
- response = self.log_pin_request()
+ response = self.log_pin_request(request) # type: ignore
elif (
self.evalex
and cmd is not None
Index: Werkzeug-1.0.1/src/werkzeug/debug/shared/debugger.js
===================================================================
--- Werkzeug-1.0.1.orig/src/werkzeug/debug/shared/debugger.js
+++ Werkzeug-1.0.1/src/werkzeug/debug/shared/debugger.js
@@ -73,7 +73,7 @@ $(function() {
label.val('submitting...');
$.ajax({
dataType: 'json',
- url: document.location.pathname,
+ url: document.location,
data: {__debugger__: 'yes', tb: TRACEBACK, cmd: 'paste',
s: SECRET},
success: function(data) {
@@ -104,7 +104,7 @@ function initPinBox() {
btn.disabled = true;
$.ajax({
dataType: 'json',
- url: document.location.pathname,
+ url: document.location,
data: {__debugger__: 'yes', cmd: 'pinauth', pin: pin,
s: SECRET},
success: function(data) {
@@ -132,7 +132,7 @@ function initPinBox() {
function promptForPin() {
if (!EVALEX_TRUSTED) {
$.ajax({
- url: document.location.pathname,
+ url: document.location,
data: {__debugger__: 'yes', cmd: 'printpin', s: SECRET}
});
$('.pin-prompt').fadeIn(function() {
Index: Werkzeug-1.0.1/src/werkzeug/serving.py
===================================================================
--- Werkzeug-1.0.1.orig/src/werkzeug/serving.py
+++ Werkzeug-1.0.1/src/werkzeug/serving.py
@@ -965,6 +965,9 @@ def run_simple(
from .debug import DebuggedApplication
application = DebuggedApplication(application, use_evalex)
+ # Allow the specified hostname to use the debugger, in addition to
+ # localhost domains.
+ application.trusted_hosts.append(hostname)
if static_files:
from .middleware.shared_data import SharedDataMiddleware