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