[FIX] core: restrict httprequest attributes

The version of werkzeug installed can vary from one deployment
to another, as we recommend to use the operating system package,
and the version can therefore change according to the operating
system version.

e.g. the werkzeug version installed using
`apt install python3-werkzeug` varies between
Ubuntu 18.04, 20.04, 22.04, 23.10, ...

We want to keep under control the attributes
developers use on werkzeug.wrappers.Request,
to avoid compatibility issues from one
version to another.

Therefore, this revision aims
to subclass werkzeug.wrappers.Request to limit
the attributes which can be used.

task-3734305

Part-of: odoo/odoo#78857

Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
This commit is contained in:
Denis Ledoux
2023-12-07 12:53:19 +00:00
parent 47ede112ed
commit b23cb16487
6 changed files with 77 additions and 11 deletions
+44 -5
View File
@@ -1165,6 +1165,49 @@ def borrow_request():
_request_stack.push(req)
def make_request_wrap_methods(attr):
def getter(self):
return getattr(self._HTTPRequest__wrapped, attr)
def setter(self, value):
return setattr(self._HTTPRequest__wrapped, attr, value)
return getter, setter
class HTTPRequest:
def __init__(self, environ):
httprequest = werkzeug.wrappers.Request(environ)
httprequest.user_agent_class = UserAgent # use vendored userAgent since it will be removed in 2.1
httprequest.parameter_storage_class = werkzeug.datastructures.ImmutableOrderedMultiDict
httprequest.max_content_length = DEFAULT_MAX_CONTENT_LENGTH
self.__wrapped = httprequest
self.__environ = self.__wrapped.environ
self.environ = {
key: value
for key, value in self.__environ.items()
if (not key.startswith(('werkzeug.', 'wsgi.', 'socket')) or key in ['wsgi.url_scheme'])
}
def __enter__(self):
return self
HTTPREQUEST_ATTRIBUTES = [
'__str__', '__repr__', '__exit__',
'accept_charsets', 'accept_languages', 'accept_mimetypes', 'access_route', 'args', 'authorization', 'base_url',
'charset', 'content_encoding', 'content_length', 'content_md5', 'content_type', 'cookies', 'data', 'date',
'encoding_errors', 'files', 'form', 'full_path', 'get_data', 'get_json', 'headers', 'host', 'host_url', 'if_match',
'if_modified_since', 'if_none_match', 'if_range', 'if_unmodified_since', 'is_json', 'is_secure', 'json',
'max_content_length', 'method', 'mimetype', 'mimetype_params', 'origin', 'path', 'pragma', 'query_string', 'range',
'referrer', 'remote_addr', 'remote_user', 'root_path', 'root_url', 'scheme', 'script_root', 'server', 'session',
'trusted_hosts', 'url', 'url_charset', 'url_root', 'user_agent', 'values',
]
for attr in HTTPREQUEST_ATTRIBUTES:
setattr(HTTPRequest, attr, property(*make_request_wrap_methods(attr)))
class Response(werkzeug.wrappers.Response):
"""
Outgoing HTTP response with body, status, headers and qweb support.
@@ -2132,11 +2175,7 @@ class Application:
return
ProxyFix(fake_app)(environ, fake_start_response)
with werkzeug.wrappers.Request(environ) as httprequest:
httprequest.user_agent_class = UserAgent # use vendored userAgent since it will be removed in 2.1
httprequest.parameter_storage_class = (
werkzeug.datastructures.ImmutableOrderedMultiDict)
httprequest.max_content_length = DEFAULT_MAX_CONTENT_LENGTH
with HTTPRequest(environ) as httprequest:
request = Request(httprequest)
_request_stack.push(request)
request._post_init()