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#139069

Signed-off-by: Julien Castiaux (juc) <[email protected]>
  • Loading branch information
d-fence committed Oct 20, 2023
1 parent 4cb18fa commit cf5a343
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 3 deletions.
30 changes: 28 additions & 2 deletions odoo/addons/base/tests/test_ir_http.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-

from odoo.http import content_disposition
from odoo.tests import common
import odoo

Expand Down Expand Up @@ -154,3 +153,30 @@ def test_ir_http_public_user_image(self):
public_user = self.env.ref('base.public_user')
code, *_ = self.env['ir.http']._binary_record_content(public_user.with_user(public_user), 'image_128')
self.assertEqual(code, 404)


class TestContentDisposition(common.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'),
('[email protected]', '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 @@ -1689,7 +1689,7 @@ def send_file(filepath_or_fp, mimetype=None, as_attachment=False, filename=None,

def content_disposition(filename):
filename = odoo.tools.ustr(filename)
escaped = urls.url_quote(filename, safe='')
escaped = urls.url_quote(filename, safe='', unsafe='()<>@,;:"/[]?={}\\*\'%') # RFC6266

return "attachment; filename*=UTF-8''%s" % escaped

Expand Down

0 comments on commit cf5a343

Please sign in to comment.