Skip to content

Commit

Permalink
[FIX] http: comply with rfc6265
Browse files Browse the repository at this point in the history
Werkzeug changed the behavior of url_quote in
pallets/werkzeug@babfc93

Which appeared in Werkzeug 2.2.2 used in Debian Bookworm.

This change broke at least the export feature in Odoo. In summary,
the character set specified by [RFC5987] is more restricted than
that of [RFC3986]. So url_quote now allows invalid characters.

For example, a filename like `Journal Entry (account.move).xlsx`
leads to a crash of the client with Werkzeug 2.2.2.

url_quote is not really made to conform to [RFC6266] but we did not
find any [RFC6266] escaping tool in the standard library. This
commit explicitly specify as unsafe this list of chars.

[RFC6266]: https://datatracker.ietf.org/doc/html/rfc6266/ [RFC5987]:
https://datatracker.ietf.org/doc/html/rfc5987#section-3.2 [RFC3986]:
https://datatracker.ietf.org/doc/html/rfc3986/ [RFC2616]:
https://datatracker.ietf.org/doc/html/rfc2616#section-2

closes odoo#139483

X-original-commit: d145cb5
Signed-off-by: Julien Castiaux (juc) <juc@odoo.com>
Signed-off-by: Christophe Monniez (moc) <moc@odoo.com>
  • Loading branch information
d-fence committed Oct 24, 2023
1 parent 308854e commit f194271
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 3 deletions.
30 changes: 28 additions & 2 deletions odoo/addons/test_http/tests/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@
from urllib.parse import urlparse

import odoo
from odoo.http import root
from odoo.http import root, content_disposition
from odoo.tests import tagged
from odoo.tests.common import HOST, new_test_user, get_db_name
from odoo.tests.common import HOST, new_test_user, get_db_name, BaseCase
from odoo.tools import config, file_path
from odoo.addons.test_http.controllers import CT_JSON

Expand Down Expand Up @@ -237,3 +237,29 @@ def test_ensure_db3_change_db(self):
res.raise_for_status()
self.assertEqual(res.status_code, 200)
self.assertEqual(res.text, 'db1')

class TestContentDisposition(BaseCase):

def test_content_disposition(self):
""" Test that content_disposition filename conforms to RFC 6266, RFC 5987 """
assertions = [
('foo bar.xls', 'foo%20bar.xls', 'Space character'),
('foo(bar).xls', 'foo%28bar%29.xls', 'Parenthesis'),
('foo<bar>.xls', 'foo%3Cbar%3E.xls', 'Angle brackets'),
('foo[bar].xls', 'foo%5Bbar%5D.xls', 'Brackets'),
('foo{bar}.xls', 'foo%7Bbar%7D.xls', 'Curly brackets'),
('foo@bar.xls', 'foo%40bar.xls', 'At sign'),
('foo,bar.xls', 'foo%2Cbar.xls', 'Comma sign'),
('foo;bar.xls', 'foo%3Bbar.xls', 'Semicolon sign'),
('foo:bar.xls', 'foo%3Abar.xls', 'Colon sign'),
('foo\\bar.xls', 'foo%5Cbar.xls', 'Backslash sign'),
('foo"bar.xls', 'foo%22bar.xls', 'Double quote sign'),
('foo/bar.xls', 'foo%2Fbar.xls', 'Slash sign'),
('foo?bar.xls', 'foo%3Fbar.xls', 'Question mark'),
('foo=bar.xls', 'foo%3Dbar.xls', 'Equal sign'),
('foo*bar.xls', 'foo%2Abar.xls', 'Star sign'),
("foo'bar.xls", 'foo%27bar.xls', 'Single-quote sign'),
('foo%bar.xls', 'foo%25bar.xls', 'Percent sign'),
]
for filename, pct_encoded, hint in assertions:
self.assertEqual(content_disposition(filename), f"attachment; filename*=UTF-8''{pct_encoded}", f'{hint} should be percent encoded')
2 changes: 1 addition & 1 deletion odoo/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ class SessionExpiredException(Exception):

def content_disposition(filename):
return "attachment; filename*=UTF-8''{}".format(
url_quote(filename, safe='')
url_quote(filename, safe='', unsafe='()<>@,;:"/[]?={}\\*\'%') # RFC6266
)

def db_list(force=False, host=None):
Expand Down

0 comments on commit f194271

Please sign in to comment.