@@ -359,8 +359,10 @@ def set_status(self, status_code: int, reason: Optional[str] = None) -> None:
359359
360360 :arg int status_code: Response status code.
361361 :arg str reason: Human-readable reason phrase describing the status
362- code. If ``None``, it will be filled in from
363- `http.client.responses` or "Unknown".
362+ code (for example, the "Not Found" in ``HTTP/1.1 404 Not Found``).
363+ Normally determined automatically from `http.client.responses`; this
364+ argument should only be used if you need to use a non-standard
365+ status code.
364366
365367 .. versionchanged:: 5.0
366368
@@ -369,6 +371,14 @@ def set_status(self, status_code: int, reason: Optional[str] = None) -> None:
369371 """
370372 self ._status_code = status_code
371373 if reason is not None :
374+ if "<" in reason or not httputil ._ABNF .reason_phrase .fullmatch (reason ):
375+ # Logically this would be better as an exception, but this method
376+ # is called on error-handling paths that would need some refactoring
377+ # to tolerate internal errors cleanly.
378+ #
379+ # The check for "<" is a defense-in-depth against XSS attacks (we also
380+ # escape the reason when rendering error pages).
381+ reason = "Unknown"
372382 self ._reason = escape .native_str (reason )
373383 else :
374384 self ._reason = httputil .responses .get (status_code , "Unknown" )
@@ -1345,7 +1355,8 @@ def send_error(self, status_code: int = 500, **kwargs: Any) -> None:
13451355 reason = exception .reason
13461356 self .set_status (status_code , reason = reason )
13471357 try :
1348- self .write_error (status_code , ** kwargs )
1358+ if status_code != 304 :
1359+ self .write_error (status_code , ** kwargs )
13491360 except Exception :
13501361 app_log .error ("Uncaught exception in write_error" , exc_info = True )
13511362 if not self ._finished :
@@ -1373,7 +1384,7 @@ def write_error(self, status_code: int, **kwargs: Any) -> None:
13731384 self .finish (
13741385 "<html><title>%(code)d: %(message)s</title>"
13751386 "<body>%(code)d: %(message)s</body></html>"
1376- % {"code" : status_code , "message" : self ._reason }
1387+ % {"code" : status_code , "message" : escape . xhtml_escape ( self ._reason ) }
13771388 )
13781389
13791390 @property
@@ -2520,9 +2531,11 @@ class HTTPError(Exception):
25202531 mode). May contain ``%s``-style placeholders, which will be filled
25212532 in with remaining positional parameters.
25222533 :arg str reason: Keyword-only argument. The HTTP "reason" phrase
2523- to pass in the status line along with ``status_code``. Normally
2534+ to pass in the status line along with ``status_code`` (for example,
2535+ the "Not Found" in ``HTTP/1.1 404 Not Found``). Normally
25242536 determined automatically from ``status_code``, but can be used
2525- to use a non-standard numeric code.
2537+ to use a non-standard numeric code. This is not a general-purpose
2538+ error message.
25262539 """
25272540
25282541 def __init__ (
0 commit comments