[FIX] core: set Content-Security-Policy on static
The Content-Security-Policy[^1] http header was only set on the response generated by controllers but it was missing from the `/<module>/static/` route. It is not strictly necessary to set that header on the responses comming from that routes as it is not possible to add new static files or edit existing ones via the interface (not even as admin). Only the developers and system administrator can access those files. It is also worth mentionning that using the Odoo internal web server to deliver static files is suboptimal. Outside of a dev environment, those files will typically be delivered via a web server[^2] and sysadmins should configure their web server to set the CSP header on static images. [^1]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP [^2]: https://www.odoo.com/documentation/master/administration/install/deploy.html#serving-static-files-and-attachments closes odoo/odoo#146591 X-original-commit: 55e09d504df9bd134afb6e0b38f03457b5c71e8e Related: odoo/documentation#6953 Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
This commit is contained in:
@@ -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')
|
||||
|
||||
@@ -16,6 +16,14 @@
|
||||
<field name="public">True</field>
|
||||
</record>
|
||||
|
||||
<record id="xss_svg" model="ir.attachment">
|
||||
<field name="name">xss.svg</field>
|
||||
<field name="type">binary</field>
|
||||
<field name="datas" type="base64" file="test_http/static/src/img/xss.svg"/>
|
||||
<field name="url">/test_http/static/src/img/xss.svg</field>
|
||||
<field name="public">True</field>
|
||||
</record>
|
||||
|
||||
<record id="rickroll" model="ir.attachment">
|
||||
<field name="name">rickroll</field>
|
||||
<field name="type">url</field>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
|
||||
<script type="text/javascript" href="/test_http/fail"></script>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 146 B |
@@ -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
|
||||
|
||||
@@ -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)
|
||||
+3
-1
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user