120 lines
4.6 KiB
Diff
120 lines
4.6 KiB
Diff
|
|
From 890b6b62634fa61224222aee31081c61b054ff01 Mon Sep 17 00:00:00 2001
|
||
|
|
From: David Lord <davidism@gmail.com>
|
||
|
|
Date: Fri, 3 May 2024 14:49:43 -0700
|
||
|
|
Subject: [PATCH] only require trusted host for evalex
|
||
|
|
|
||
|
|
---
|
||
|
|
src/werkzeug/debug/__init__.py | 25 ++++++++++++++++++++-----
|
||
|
|
src/werkzeug/sansio/utils.py | 2 +-
|
||
|
|
2 files changed, 21 insertions(+), 6 deletions(-)
|
||
|
|
|
||
|
|
diff --git a/src/werkzeug/debug/__init__.py b/src/werkzeug/debug/__init__.py
|
||
|
|
index e779fd9..8952342 100644
|
||
|
|
--- a/src/werkzeug/debug/__init__.py
|
||
|
|
+++ b/src/werkzeug/debug/__init__.py
|
||
|
|
@@ -18,7 +18,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
|
||
|
|
@@ -350,7 +352,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,
|
||
|
|
)
|
||
|
|
@@ -378,6 +380,9 @@ class DebuggedApplication:
|
||
|
|
frame: t.Union[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:
|
||
|
|
@@ -388,6 +393,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 = {}
|
||
|
|
@@ -440,12 +448,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)
|
||
|
|
@@ -495,8 +509,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:"
|
||
|
|
@@ -512,8 +529,6 @@ class DebuggedApplication:
|
||
|
|
# form data! Otherwise the application won't have access to that data
|
||
|
|
# any more!
|
||
|
|
request = Request(environ)
|
||
|
|
- request.trusted_hosts = self.trusted_hosts
|
||
|
|
- assert request.host # will raise 400 error if not trusted
|
||
|
|
response = self.debug_application
|
||
|
|
if request.args.get("__debugger__") == "yes":
|
||
|
|
cmd = request.args.get("cmd")
|
||
|
|
@@ -525,7 +540,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
|
||
|
|
diff --git a/src/werkzeug/sansio/utils.py b/src/werkzeug/sansio/utils.py
|
||
|
|
index e639dcb..468f926 100644
|
||
|
|
--- a/src/werkzeug/sansio/utils.py
|
||
|
|
+++ b/src/werkzeug/sansio/utils.py
|
||
|
|
@@ -6,7 +6,7 @@ from ..urls import uri_to_iri
|
||
|
|
from ..urls import url_quote
|
||
|
|
|
||
|
|
|
||
|
|
-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.
|
||
|
|
--
|
||
|
|
2.41.0
|
||
|
|
|