Merge pull request #3594 from benoitc/fix/rfc9112-reject-asterisk-form-non-options

fix: reject asterisk-form request-target outside OPTIONS (RFC 9112 §3.2.4)
This commit is contained in:
Benoit Chesneau 2026-04-19 11:08:44 +02:00 committed by GitHub
commit e3ba1e07fa
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 32 additions and 0 deletions

View File

@ -456,6 +456,10 @@ class PythonProtocol:
if not self._is_valid_method(self.method):
raise InvalidRequestMethod(self.method.decode('latin-1'))
# RFC 9112 section 3.2.4: asterisk-form is only valid with OPTIONS.
if self.path == b'*' and self.method != b'OPTIONS':
raise InvalidRequestLine("Invalid request line")
# Parse version
version = parts[2]
if version == b'HTTP/1.1':

View File

@ -807,6 +807,10 @@ class Request(Message):
if len(self.uri) == 0:
raise InvalidRequestLine(bytes_to_str(line_bytes))
# RFC 9112 section 3.2.4: asterisk-form is only valid with OPTIONS.
if self.uri == "*" and self.method != "OPTIONS":
raise InvalidRequestLine(bytes_to_str(line_bytes))
try:
parts = split_request_uri(self.uri)
except ValueError:

View File

@ -0,0 +1,3 @@
GET * HTTP/1.1\r\n
Host: example.com\r\n
\r\n

View File

@ -0,0 +1,11 @@
#
# This file is part of gunicorn released under the MIT license.
# See the NOTICE for more information.
# RFC 9112 section 3.2.4: asterisk-form ("*") only targets the server itself
# and is only valid with the OPTIONS method. Any other method must be
# rejected as an ill-formed request-line.
from gunicorn.http.errors import InvalidRequestLine
request = InvalidRequestLine
# The C parser (gunicorn_h1c) does not yet enforce this rule.
python_only = True

View File

@ -78,5 +78,10 @@ def test_asgi_parser(fname, http_parser):
):
pytest.skip(f"Callback parser does not raise {expect.__name__}")
# Fixture-level opt-out for validations not (yet) implemented by the
# fast (C) callback parser. The sidecar sets `python_only = True`.
if http_parser == 'fast' and env.get('python_only'):
pytest.skip("fixture marked python_only")
req = treq_asgi.badrequest(fname)
req.check(cfg, expect, http_parser=http_parser)

View File

@ -51,6 +51,11 @@ def test_http_parser(fname, http_parser):
):
pytest.skip(f"fast parser does not raise {expect.__name__}")
# Fixture-level opt-out for validations not (yet) implemented by
# the C parser. The sidecar sets `python_only = True`.
if env.get('python_only'):
pytest.skip("fixture marked python_only")
# Determine acceptable exceptions (fast parser may raise alternates)
if http_parser == 'fast' and expect in _FAST_PARSER_ALTERNATES:
acceptable = (expect,) + _FAST_PARSER_ALTERNATES[expect]