[IMP] core: set CSP header on some non-HTML resources

Core client remains incompatible with CSP, however it can't hurt to
CSP the sub-resources.

Current scheme is simplistic, however if useful of necessary it could
be made more flexible e.g. there could be a map of mimetypes to CSP
configuration, that sort of things.
This commit is contained in:
Xavier Morel
2021-12-13 14:46:14 +01:00
parent 14bbac3f35
commit 8d7a9162f9
2 changed files with 29 additions and 29 deletions
+1 -1
View File
@@ -426,7 +426,7 @@ class IrHttp(models.AbstractModel):
return status, content, filename, mimetype, filehash
def _binary_set_headers(self, status, content, filename, mimetype, unique, filehash=None, download=False):
headers = [('Content-Type', mimetype), ('X-Content-Type-Options', 'nosniff')]
headers = [('Content-Type', mimetype), ('X-Content-Type-Options', 'nosniff'), ('Content-Security-Policy', "default-src 'none'")]
# cache
etag = bool(request) and request.httprequest.headers.get('If-None-Match')
status = status or 200
+28 -28
View File
@@ -3,6 +3,7 @@
# OpenERP HTTP layer
#----------------------------------------------------------
import ast
import cgi
import collections
import contextlib
import copy
@@ -1393,6 +1394,7 @@ class Root(object):
response = Response(result, mimetype='text/html')
else:
response = result
self.set_csp(response)
save_session = (not request.endpoint) or request.endpoint.routing.get('save_session', True)
if not save_session:
@@ -1418,6 +1420,23 @@ class Root(object):
return response
def set_csp(self, response):
# ignore HTTP errors
if not isinstance(response, werkzeug.wrappers.BaseResponse):
return
headers = response.headers
if 'Content-Security-Policy' in headers:
return
mime, _params = cgi.parse_header(headers.get('Content-Type', ''))
if not mime.startswith('image/'):
return
headers['Content-Security-Policy'] = "default-src 'none'"
def dispatch(self, environ, start_response):
"""
Performs the actual WSGI dispatching for the application.
@@ -1675,35 +1694,16 @@ def set_safe_image_headers(headers, content):
`Content-Type` set appropriately depending on the given `content` only if it
is safe to do, as well as `X-Content-Type-Options: nosniff` so that if the
file is of an unsafe type, it is not interpreted as that type if the
`Content-type` header was already set to a different mimetype"""
content_type = guess_mimetype(content)
safe_types = ['image/jpeg', 'image/png', 'image/gif', 'image/x-icon']
if content_type in safe_types:
headers = set_header_field(headers, 'Content-Type', content_type)
headers = set_header_field(headers, 'X-Content-Type-Options', 'nosniff')
set_header_field(headers, 'Content-Length', len(content))
return headers
def set_header_field(headers, name, value):
""" Return new headers based on `headers` but with `value` set for the
header field `name`.
:param headers: the existing headers
:type headers: list of tuples (name, value)
:param name: the header field name
:type name: string
:param value: the value to set for the `name` header
:type value: string
:return: the updated headers
:rtype: list of tuples (name, value)
`Content-type` header was already set to a different mimetype
"""
dictheaders = dict(headers)
dictheaders[name] = value
return list(dictheaders.items())
headers = werkzeug.datastructures.Headers(headers)
safe_types = {'image/jpeg', 'image/png', 'image/gif', 'image/x-icon'}
content_type = guess_mimetype(content)
if content_type in safe_types:
headers['Content-Type'] = content_type
headers['X-Content-Type-Options'] = 'nosniff'
headers['Content-Length'] = len(content)
return list(headers)
# main wsgi handler