Compare commits
10 Commits
147ad1a590
...
2e0194d9db
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e0194d9db | ||
|
|
f633b8388e | ||
|
|
9700a2e751 | ||
|
|
0048ebc37a | ||
|
|
a926306299 | ||
|
|
9020e9e70b | ||
|
|
04eacdd4b6 | ||
|
|
d5e619a3ac | ||
|
|
e0a8cc65b6 | ||
|
|
ea1ca248ad |
BIN
1.0.1.tar.gz
BIN
1.0.1.tar.gz
Binary file not shown.
119
CVE-2024-34069-only-require-trusted-host-for-evalex.patch
Normal file
119
CVE-2024-34069-only-require-trusted-host-for-evalex.patch
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
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
|
||||||
|
|
||||||
144
CVE-2024-34069-restrict-debugger-trusted-hosts.patch
Normal file
144
CVE-2024-34069-restrict-debugger-trusted-hosts.patch
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
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] 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.
|
||||||
|
---
|
||||||
|
docs/debug.rst | 35 +++++++++++++++++++++++----
|
||||||
|
src/werkzeug/debug/__init__.py | 10 ++++++++
|
||||||
|
src/werkzeug/debug/shared/debugger.js | 4 +--
|
||||||
|
src/werkzeug/serving.py | 3 +++
|
||||||
|
4 files changed, 45 insertions(+), 7 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/docs/debug.rst b/docs/debug.rst
|
||||||
|
index 25a9f0b..d842135 100644
|
||||||
|
--- a/docs/debug.rst
|
||||||
|
+++ b/docs/debug.rst
|
||||||
|
@@ -16,7 +16,8 @@ interactive debug console to execute code in any frame.
|
||||||
|
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 the attributes it has.
|
||||||
|
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
|
||||||
|
@@ -92,6 +92,31 @@ 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.**
|
||||||
|
+
|
||||||
|
+
|
||||||
|
Pasting Errors
|
||||||
|
--------------
|
||||||
|
|
||||||
|
diff --git a/src/werkzeug/debug/__init__.py b/src/werkzeug/debug/__init__.py
|
||||||
|
index 24d19bb..e779fd9 100644
|
||||||
|
--- a/src/werkzeug/debug/__init__.py
|
||||||
|
+++ b/src/werkzeug/debug/__init__.py
|
||||||
|
@@ -296,6 +296,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) -> t.Optional[str]:
|
||||||
|
if not hasattr(self, "_pin"):
|
||||||
|
@@ -504,6 +512,8 @@ 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")
|
||||||
|
diff --git a/src/werkzeug/debug/shared/debugger.js b/src/werkzeug/debug/shared/debugger.js
|
||||||
|
index 2354f03..bee079f 100644
|
||||||
|
--- a/src/werkzeug/debug/shared/debugger.js
|
||||||
|
+++ b/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);
|
||||||
|
diff --git a/src/werkzeug/serving.py b/src/werkzeug/serving.py
|
||||||
|
index 2a2e74d..19ed250 100644
|
||||||
|
--- a/src/werkzeug/serving.py
|
||||||
|
+++ b/src/werkzeug/serving.py
|
||||||
|
@@ -1028,6 +1028,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
|
||||||
|
--
|
||||||
|
2.41.0
|
||||||
|
|
||||||
@ -0,0 +1,83 @@
|
|||||||
|
From 8760275afb72bd10b57d92cb4d52abf759b2f3a7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: David Lord <davidism@gmail.com>
|
||||||
|
Date: Fri, 25 Oct 2024 06:46:50 -0700
|
||||||
|
Subject: [PATCH] apply max_form_memory_size another level up in the parser
|
||||||
|
|
||||||
|
---
|
||||||
|
src/werkzeug/formparser.py | 11 +++++++++++
|
||||||
|
src/werkzeug/sansio/multipart.py | 2 ++
|
||||||
|
tests/test_formparser.py | 12 ++++++++++++
|
||||||
|
3 files changed, 25 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/src/werkzeug/formparser.py b/src/werkzeug/formparser.py
|
||||||
|
index bebb2fc..b82af82 100644
|
||||||
|
--- a/src/werkzeug/formparser.py
|
||||||
|
+++ b/src/werkzeug/formparser.py
|
||||||
|
@@ -405,6 +405,7 @@ class MultiPartParser:
|
||||||
|
def parse(
|
||||||
|
self, stream: t.IO[bytes], boundary: bytes, content_length: t.Optional[int]
|
||||||
|
) -> t.Tuple[MultiDict, MultiDict]:
|
||||||
|
+ field_size: int | None = None
|
||||||
|
container: t.Union[t.IO[bytes], t.List[bytes]]
|
||||||
|
_write: t.Callable[[bytes], t.Any]
|
||||||
|
|
||||||
|
@@ -431,13 +432,23 @@ class MultiPartParser:
|
||||||
|
while not isinstance(event, (Epilogue, NeedData)):
|
||||||
|
if isinstance(event, Field):
|
||||||
|
current_part = event
|
||||||
|
+ field_size = 0
|
||||||
|
container = []
|
||||||
|
_write = container.append
|
||||||
|
elif isinstance(event, File):
|
||||||
|
current_part = event
|
||||||
|
+ field_size = None
|
||||||
|
container = self.start_file_streaming(event, content_length)
|
||||||
|
_write = container.write
|
||||||
|
elif isinstance(event, Data):
|
||||||
|
+ if self.max_form_memory_size is not None and field_size is not None:
|
||||||
|
+ # Ensure that accumulated data events do not exceed limit.
|
||||||
|
+ # Also checked within single event in MultipartDecoder.
|
||||||
|
+ field_size += len(event.data)
|
||||||
|
+
|
||||||
|
+ if field_size > self.max_form_memory_size:
|
||||||
|
+ raise RequestEntityTooLarge()
|
||||||
|
+
|
||||||
|
_write(event.data)
|
||||||
|
if not event.more_data:
|
||||||
|
if isinstance(current_part, Field):
|
||||||
|
diff --git a/src/werkzeug/sansio/multipart.py b/src/werkzeug/sansio/multipart.py
|
||||||
|
index 2684e5d..e2b0e79 100644
|
||||||
|
--- a/src/werkzeug/sansio/multipart.py
|
||||||
|
+++ b/src/werkzeug/sansio/multipart.py
|
||||||
|
@@ -142,6 +142,8 @@ class MultipartDecoder:
|
||||||
|
self.max_form_memory_size is not None
|
||||||
|
and len(self.buffer) + len(data) > self.max_form_memory_size
|
||||||
|
):
|
||||||
|
+ # Ensure that data within single event does not exceed limit.
|
||||||
|
+ # Also checked across accumulated events in MultiPartParser.
|
||||||
|
raise RequestEntityTooLarge()
|
||||||
|
else:
|
||||||
|
self.buffer.extend(data)
|
||||||
|
diff --git a/tests/test_formparser.py b/tests/test_formparser.py
|
||||||
|
index 4c518b1..05fa84e 100644
|
||||||
|
--- a/tests/test_formparser.py
|
||||||
|
+++ b/tests/test_formparser.py
|
||||||
|
@@ -455,3 +455,15 @@ class TestMultiPartParser:
|
||||||
|
) as request:
|
||||||
|
assert request.files["rfc2231"].filename == "a b c d e f.txt"
|
||||||
|
assert request.files["rfc2231"].read() == b"file contents"
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def test_multipart_max_form_memory_size() -> None:
|
||||||
|
+ """max_form_memory_size is tracked across multiple data events."""
|
||||||
|
+ data = b"--bound\r\nContent-Disposition: form-field; name=a\r\n\r\n"
|
||||||
|
+ data += b"a" * 15 + b"\r\n--bound--"
|
||||||
|
+ # The buffer size is less than the max size, so multiple data events will be
|
||||||
|
+ # returned. The field size is greater than the max.
|
||||||
|
+ parser = formparser.MultiPartParser(max_form_memory_size=10, buffer_size=5)
|
||||||
|
+
|
||||||
|
+ with pytest.raises(RequestEntityTooLarge):
|
||||||
|
+ parser.parse(io.BytesIO(data), b"bound", None)
|
||||||
|
--
|
||||||
|
2.33.0
|
||||||
|
|
||||||
BIN
Werkzeug-2.2.3.tar.gz
Normal file
BIN
Werkzeug-2.2.3.tar.gz
Normal file
Binary file not shown.
61
ephemeral_port_reserve.py
Executable file
61
ephemeral_port_reserve.py
Executable file
@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
from __future__ import absolute_import
|
||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import contextlib
|
||||||
|
import errno
|
||||||
|
from socket import error as SocketError
|
||||||
|
from socket import SO_REUSEADDR
|
||||||
|
from socket import socket
|
||||||
|
from socket import SOL_SOCKET
|
||||||
|
|
||||||
|
LOCALHOST = '127.0.0.1'
|
||||||
|
|
||||||
|
|
||||||
|
def reserve(ip=LOCALHOST, port=0):
|
||||||
|
"""Bind to an ephemeral port, force it into the TIME_WAIT state, and unbind it.
|
||||||
|
|
||||||
|
This means that further ephemeral port alloctions won't pick this "reserved" port,
|
||||||
|
but subprocesses can still bind to it explicitly, given that they use SO_REUSEADDR.
|
||||||
|
By default on linux you have a grace period of 60 seconds to reuse this port.
|
||||||
|
To check your own particular value:
|
||||||
|
$ cat /proc/sys/net/ipv4/tcp_fin_timeout
|
||||||
|
60
|
||||||
|
|
||||||
|
By default, the port will be reserved for localhost (aka 127.0.0.1).
|
||||||
|
To reserve a port for a different ip, provide the ip as the first argument.
|
||||||
|
Note that IP 0.0.0.0 is interpreted as localhost.
|
||||||
|
"""
|
||||||
|
port = int(port)
|
||||||
|
with contextlib.closing(socket()) as s:
|
||||||
|
s.setsockopt(SOL_SOCKET, SO_REUSEADDR, 1)
|
||||||
|
try:
|
||||||
|
s.bind((ip, port))
|
||||||
|
except SocketError as e:
|
||||||
|
# socket.error: EADDRINUSE Address already in use
|
||||||
|
if e.errno == errno.EADDRINUSE and port != 0:
|
||||||
|
s.bind((ip, 0))
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
# the connect below deadlocks on kernel >= 4.4.0 unless this arg is greater than zero
|
||||||
|
s.listen(1)
|
||||||
|
|
||||||
|
sockname = s.getsockname()
|
||||||
|
|
||||||
|
# these three are necessary just to get the port into a TIME_WAIT state
|
||||||
|
with contextlib.closing(socket()) as s2:
|
||||||
|
s2.connect(sockname)
|
||||||
|
sock, _ = s.accept()
|
||||||
|
with contextlib.closing(sock):
|
||||||
|
return sockname[1]
|
||||||
|
|
||||||
|
|
||||||
|
def main(): # pragma: no cover
|
||||||
|
from sys import argv
|
||||||
|
port = reserve(*argv[1:])
|
||||||
|
print(port)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
exit(main())
|
||||||
@ -1,16 +1,23 @@
|
|||||||
%global _empty_manifest_terminate_build 0
|
%global _empty_manifest_terminate_build 0
|
||||||
Name: python-werkzeug
|
Name: python-werkzeug
|
||||||
Version: 1.0.1
|
Version: 2.2.3
|
||||||
Release: 2
|
Release: 3
|
||||||
Summary: The comprehensive WSGI web application library.
|
Summary: The comprehensive WSGI web application library.
|
||||||
License: BSD-3-Clause
|
License: BSD-3-Clause
|
||||||
URL: https://palletsprojects.com/p/werkzeug/
|
URL: https://palletsprojects.com/p/werkzeug/
|
||||||
Source0: https://github.com/pallets/werkzeug/archive/1.0.1.tar.gz
|
Source0: https://files.pythonhosted.org/packages/source/W/Werkzeug/Werkzeug-2.2.3.tar.gz
|
||||||
BuildArch: noarch
|
# for test
|
||||||
|
Source1: https://github.com/Yelp/ephemeral-port-reserve/blob/master/ephemeral_port_reserve.py
|
||||||
|
|
||||||
BuildRequires: python3-werkzeug
|
Patch01: CVE-2024-34069-restrict-debugger-trusted-hosts.patch
|
||||||
|
Patch02: CVE-2024-34069-only-require-trusted-host-for-evalex.patch
|
||||||
|
Patch03: CVE-2024-49767--apply-max_form_memory_size-another-level-up.patch
|
||||||
|
|
||||||
|
BuildArch: noarch
|
||||||
|
BuildRequires: python3-werkzeug python3-markupsafe
|
||||||
|
|
||||||
Requires: python3-pytest
|
Requires: python3-pytest
|
||||||
|
Requires: python3-pytest-xprocess
|
||||||
Requires: python3-pytest-timeout
|
Requires: python3-pytest-timeout
|
||||||
Requires: python3-coverage
|
Requires: python3-coverage
|
||||||
Requires: python3-tox
|
Requires: python3-tox
|
||||||
@ -41,10 +48,10 @@ It includes:
|
|||||||
locally.
|
locally.
|
||||||
- A test client for simulating HTTP requests during testing without
|
- A test client for simulating HTTP requests during testing without
|
||||||
requiring running a server.
|
requiring running a server.
|
||||||
Werkzeug is Unicode aware and doesn't enforce any dependencies. It is up
|
Werkzeug doesn't enforce any dependencies. It is up to the developer to
|
||||||
to the developer to choose a template engine, database adapter, and even
|
choose a template engine, database adapter, and even how to handle
|
||||||
how to handle requests. It can be used to build all sorts of end user
|
requests. It can be used to build all sorts of end user applications
|
||||||
applications such as blogs, wikis, or bulletin boards.
|
such as blogs, wikis, or bulletin boards.
|
||||||
`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while
|
`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while
|
||||||
providing more structure and patterns for defining powerful
|
providing more structure and patterns for defining powerful
|
||||||
applications.
|
applications.
|
||||||
@ -52,13 +59,14 @@ applications.
|
|||||||
%package -n python3-werkzeug
|
%package -n python3-werkzeug
|
||||||
Summary: The comprehensive WSGI web application library.
|
Summary: The comprehensive WSGI web application library.
|
||||||
Provides: python-werkzeug
|
Provides: python-werkzeug
|
||||||
BuildRequires: python3-devel
|
|
||||||
BuildRequires: python3-pytest
|
BuildRequires: python3-pytest
|
||||||
|
BuildRequires: python3-pytest-xprocess
|
||||||
BuildRequires: python3-pytest-timeout
|
BuildRequires: python3-pytest-timeout
|
||||||
BuildRequires: python3-requests
|
BuildRequires: python3-requests
|
||||||
BuildRequires: python3-setuptools
|
BuildRequires: python3-devel
|
||||||
BuildRequires: python3-cryptography
|
BuildRequires: python3-cryptography
|
||||||
BuildRequires: python3-greenlet
|
BuildRequires: python3-greenlet
|
||||||
|
BuildRequires: python3-setuptools
|
||||||
%description -n python3-werkzeug
|
%description -n python3-werkzeug
|
||||||
*werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff")
|
*werkzeug* German noun: "tool". Etymology: *werk* ("work"), *zeug* ("stuff")
|
||||||
Werkzeug is a comprehensive `WSGI`_ web application library. It began as
|
Werkzeug is a comprehensive `WSGI`_ web application library. It began as
|
||||||
@ -81,10 +89,10 @@ It includes:
|
|||||||
locally.
|
locally.
|
||||||
- A test client for simulating HTTP requests during testing without
|
- A test client for simulating HTTP requests during testing without
|
||||||
requiring running a server.
|
requiring running a server.
|
||||||
Werkzeug is Unicode aware and doesn't enforce any dependencies. It is up
|
Werkzeug doesn't enforce any dependencies. It is up to the developer to
|
||||||
to the developer to choose a template engine, database adapter, and even
|
choose a template engine, database adapter, and even how to handle
|
||||||
how to handle requests. It can be used to build all sorts of end user
|
requests. It can be used to build all sorts of end user applications
|
||||||
applications such as blogs, wikis, or bulletin boards.
|
such as blogs, wikis, or bulletin boards.
|
||||||
`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while
|
`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while
|
||||||
providing more structure and patterns for defining powerful
|
providing more structure and patterns for defining powerful
|
||||||
applications.
|
applications.
|
||||||
@ -114,16 +122,17 @@ It includes:
|
|||||||
locally.
|
locally.
|
||||||
- A test client for simulating HTTP requests during testing without
|
- A test client for simulating HTTP requests during testing without
|
||||||
requiring running a server.
|
requiring running a server.
|
||||||
Werkzeug is Unicode aware and doesn't enforce any dependencies. It is up
|
Werkzeug doesn't enforce any dependencies. It is up to the developer to
|
||||||
to the developer to choose a template engine, database adapter, and even
|
choose a template engine, database adapter, and even how to handle
|
||||||
how to handle requests. It can be used to build all sorts of end user
|
requests. It can be used to build all sorts of end user applications
|
||||||
applications such as blogs, wikis, or bulletin boards.
|
such as blogs, wikis, or bulletin boards.
|
||||||
`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while
|
`Flask`_ wraps Werkzeug, using it to handle the details of WSGI while
|
||||||
providing more structure and patterns for defining powerful
|
providing more structure and patterns for defining powerful
|
||||||
applications.
|
applications.
|
||||||
|
|
||||||
%prep
|
%prep
|
||||||
%autosetup -n werkzeug-1.0.1
|
%autosetup -n Werkzeug-%{version} -p1
|
||||||
|
cp %{SOURCE1} %{_builddir}/Werkzeug-%{version}/tests/
|
||||||
|
|
||||||
%build
|
%build
|
||||||
%py3_build
|
%py3_build
|
||||||
@ -157,8 +166,7 @@ mv %{buildroot}/filelist.lst .
|
|||||||
mv %{buildroot}/doclist.lst .
|
mv %{buildroot}/doclist.lst .
|
||||||
|
|
||||||
%check
|
%check
|
||||||
PYTHONPATH=%{buildroot}%{python3_sitelib}
|
PYTHONPATH=%{buildroot}%{python3_sitelib} pytest -k 'not (test_serving)'
|
||||||
/usr/bin/pytest -p no:unraisableexception
|
|
||||||
|
|
||||||
%files -n python3-werkzeug -f filelist.lst
|
%files -n python3-werkzeug -f filelist.lst
|
||||||
%dir %{python3_sitelib}/*
|
%dir %{python3_sitelib}/*
|
||||||
@ -167,6 +175,21 @@ PYTHONPATH=%{buildroot}%{python3_sitelib}
|
|||||||
%{_docdir}/*
|
%{_docdir}/*
|
||||||
|
|
||||||
%changelog
|
%changelog
|
||||||
|
* Sat Oct 26 2024 liningjie <liningjie@xfusion.com> - 2.2.3-3
|
||||||
|
- Fix CVE-2024-49767
|
||||||
|
|
||||||
|
* Tue May 07 2024 yinyongkang <yinyongkang@kylinos.cn> - 2.2.3-2
|
||||||
|
- fix CVE-2024-34069
|
||||||
|
|
||||||
|
* Tue May 09 2023 wulei <wu_lei@hoperun.com> - 2.2.3-1
|
||||||
|
- Update to 2.2.3
|
||||||
|
|
||||||
|
* Sat Jan 7 2023 Bolehu <heyaohua@xfusion.com> - 2.0.3-2
|
||||||
|
- fix typo and grammar mistake
|
||||||
|
|
||||||
|
* Fri Jun 17 2022 jiangpengju <jiangpengju2@h-partners.com> - 2.0.3-1
|
||||||
|
- Upgrade python-werkzeug version to 2.0.3
|
||||||
|
|
||||||
* Mon Nov 15 2021 xu_ping <xuping33@huawei.com>-1.0.1-2
|
* Mon Nov 15 2021 xu_ping <xuping33@huawei.com>-1.0.1-2
|
||||||
- fix test failures due to unhandled exceptions being thrown without being propagated to caller.
|
- fix test failures due to unhandled exceptions being thrown without being propagated to caller.
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user