diff --git a/odoo/addons/test_http/controllers.py b/odoo/addons/test_http/controllers.py index dba5f5299e1..4da4da7ea73 100644 --- a/odoo/addons/test_http/controllers.py +++ b/odoo/addons/test_http/controllers.py @@ -170,6 +170,14 @@ class TestHttp(http.Controller): # ===================================================== # Errors # ===================================================== + @http.route('/test_http/fail', type='http', auth='none') + def fail(self): + _logger.error( + "The /test_http/fail route should never be called, referrer: %s", + http.request.httprequest.headers.get('referer') + ) + raise request.not_found() + @http.route('/test_http/json_value_error', type='json', auth='none') def json_value_error(self): raise ValueError('Unknown destination') diff --git a/odoo/addons/test_http/data.xml b/odoo/addons/test_http/data.xml index 023555bc903..3bb2d39197a 100644 --- a/odoo/addons/test_http/data.xml +++ b/odoo/addons/test_http/data.xml @@ -16,6 +16,14 @@ True + + xss.svg + binary + + /test_http/static/src/img/xss.svg + True + + rickroll url diff --git a/odoo/addons/test_http/static/src/img/xss.svg b/odoo/addons/test_http/static/src/img/xss.svg new file mode 100644 index 00000000000..37519ab0781 --- /dev/null +++ b/odoo/addons/test_http/static/src/img/xss.svg @@ -0,0 +1,3 @@ + + + diff --git a/odoo/addons/test_http/tests/__init__.py b/odoo/addons/test_http/tests/__init__.py index ad63b65fcac..cc771f36c03 100644 --- a/odoo/addons/test_http/tests/__init__.py +++ b/odoo/addons/test_http/tests/__init__.py @@ -7,3 +7,4 @@ from . import test_models from . import test_session from . import test_static from . import test_web_server +from . import test_xss diff --git a/odoo/addons/test_http/tests/test_xss.py b/odoo/addons/test_http/tests/test_xss.py new file mode 100644 index 00000000000..f8fc3a9a62b --- /dev/null +++ b/odoo/addons/test_http/tests/test_xss.py @@ -0,0 +1,19 @@ +from odoo.tests import tagged +from .test_common import TestHttpBase + + +@tagged('-at_install', 'post_install') +class TestXSS(TestHttpBase): + # In case a XSS isn't filtered, it'll load /test_http/fail which logs an error. + # browser_js only returns when it finds "test successful" in the logs. + fake_success = "console.log('test successfulness cannot be determined via JS')" + # ^^^^^^^^^^^^^^^ + + def test_xss_static(self): + self.browser_js('/test_http/static/src/img/xss.svg', self.fake_success) + + def test_xss_web_content(self): + self.browser_js('/web/content/test_http.xss_svg', self.fake_success) + + def test_xss_web_image(self): + self.browser_js('/web/image/test_http.xss_svg', self.fake_success) diff --git a/odoo/http.py b/odoo/http.py index 8e6fe8f1cb0..fe742ba5553 100644 --- a/odoo/http.py +++ b/odoo/http.py @@ -1681,9 +1681,11 @@ class Request: try: directory = root.statics[module] filepath = werkzeug.security.safe_join(directory, path) - return Stream.from_path(filepath).get_response( + res = Stream.from_path(filepath).get_response( max_age=0 if 'assets' in self.session.debug else STATIC_CACHE, ) + root.set_csp(res) + return res except KeyError: raise NotFound(f'Module "{module}" not found.\n') except OSError: # cover both missing file and invalid permissions