Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog/68853.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Patch tornado for BDSA-2025-60810
26 changes: 20 additions & 6 deletions salt/ext/tornado/web.py
Original file line number Diff line number Diff line change
Expand Up @@ -313,11 +313,21 @@ def set_status(self, status_code, reason=None):
:arg int status_code: Response status code. If ``reason`` is ``None``,
it must be present in `httplib.responses <http.client.responses>`.
:arg string reason: Human-readable reason phrase describing the status
code. If ``None``, it will be filled in from
`httplib.responses <http.client.responses>`.
code (for example, the "Not Found" in ``HTTP/1.1 404 Not Found``).
Normally determined automatically from `http.client.responses`; this
argument should only be used if you need to use a non-standard
status code.
"""
self._status_code = status_code
if reason is not None:
if "<" in reason or not RequestHandler._REASON_PHRASE_RE.fullmatch(reason):
# Logically this would be better as an exception, but this method
# is called on error-handling paths that would need some refactoring
# to tolerate internal errors cleanly.
#
# The check for "<" is a defense-in-depth against XSS attacks (we also
# escape the reason when rendering error pages).
reason = "Unknown"
self._reason = escape.native_str(reason)
else:
try:
Expand Down Expand Up @@ -358,6 +368,7 @@ def clear_header(self, name):
del self._headers[name]

_INVALID_HEADER_CHAR_RE = re.compile(r"[\x00-\x1f]")
_REASON_PHRASE_RE = re.compile(r"(?:[\t ]|[\x21-\x7E]|[\x80-\xFF])+")

def _convert_header_value(self, value):
# type: (_HeaderTypes) -> str
Expand Down Expand Up @@ -1035,7 +1046,8 @@ def send_error(self, status_code=500, **kwargs):
reason = exception.reason
self.set_status(status_code, reason=reason)
try:
self.write_error(status_code, **kwargs)
if status_code != 304:
self.write_error(status_code, **kwargs)
except Exception:
app_log.error("Uncaught exception in write_error", exc_info=True)
if not self._finished:
Expand Down Expand Up @@ -1063,7 +1075,7 @@ def write_error(self, status_code, **kwargs):
self.finish("<html><title>%(code)d: %(message)s</title>"
"<body>%(code)d: %(message)s</body></html>" % {
"code": status_code,
"message": self._reason,
"message": escape.xhtml_escape(self._reason),
})

@property
Expand Down Expand Up @@ -2153,9 +2165,11 @@ class HTTPError(Exception):
mode). May contain ``%s``-style placeholders, which will be filled
in with remaining positional parameters.
:arg string reason: Keyword-only argument. The HTTP "reason" phrase
to pass in the status line along with ``status_code``. Normally
to pass in the status line along with ``status_code`` (for example,
the "Not Found" in ``HTTP/1.1 404 Not Found``). Normally
determined automatically from ``status_code``, but can be used
to use a non-standard numeric code.
to use a non-standard numeric code. This is not a general-purpose
error message.
"""
def __init__(self, status_code=500, log_message=None, *args, **kwargs):
self.status_code = status_code
Expand Down
Loading