Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

match header option value with single quotes #1208

Merged
merged 1 commit into from Dec 5, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGES.rst
Expand Up @@ -31,6 +31,7 @@ unreleased
- Secure cookie contrib works with string secret key on Python 3. (`#1205`_)
- Shared data middleware accepts a list instead of a dict of static locations
to preserve lookup order. (`#1197`_)
- HTTP header values without encoding can contain single quotes. (`#1208`_)
- The built-in dev server supports receiving requests with chunked transfer
encoding. (`#1198`_)

Expand All @@ -47,6 +48,7 @@ unreleased
.. _#1197: https://github.com/pallets/werkzeug/pull/1197
.. _#1198: https://github.com/pallets/werkzeug/pull/1198
.. _#1205: https://github.com/pallets/werkzeug/pull/1205
.. _#1208: https://github.com/pallets/werkzeug/pull/1208


Version 0.12.2
Expand Down
8 changes: 8 additions & 0 deletions tests/test_http.py
Expand Up @@ -280,6 +280,14 @@ def test_parse_options_header(self):
('form-data', {'name': u'\u016an\u012dc\u014dde\u033d',
'filename': 'some_file.txt'})

def test_parse_options_header_value_with_quotes(self):
assert http.parse_options_header(
'form-data; name="file"; filename="t\'es\'t.txt"'
) == ('form-data', {'name': 'file', 'filename': "t'es't.txt"})
assert http.parse_options_header(
'form-data; name="file"; filename*=UTF-8\'\'"\'🐍\'.txt"'

This comment was marked as off-topic.

) == ('form-data', {'name': 'file', 'filename': u"'🐍'.txt"})

def test_parse_options_header_broken_values(self):
# Issue #995
assert http.parse_options_header(' ') == ('', {})
Expand Down
30 changes: 24 additions & 6 deletions werkzeug/http.py
Expand Up @@ -62,12 +62,30 @@
'^_`abcdefghijklmnopqrstuvwxyz|~')
_etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)')
_unsafe_header_chars = set('()<>@,;:\"/[]?={} \t')
_quoted_string_re = r'"[^"\\]*(?:\\.[^"\\]*)*"'
_option_header_piece_re = re.compile(
r';\s*(%s|[^\s;,=\*]+)\s*'
r'(?:\*?=\s*(?:([^\s]+?)\'([^\s]*?)\')?(%s|[^;,]+)?)?\s*' %
(_quoted_string_re, _quoted_string_re)
)
_option_header_piece_re = re.compile(r'''
;\s*
(?P<key>
"[^"\\]*(?:\\.[^"\\]*)*" # quoted string
|
[^\s;,=*]+ # token
)
\s*
(?: # optionally followed by =value
(?: # equals sign, possibly with encoding
\*\s*=\s* # * indicates extended notation
(?P<encoding>[^\s]+?)
'(?P<language>[^\s]*?)'
|
=\s* # basic notation
)
(?P<value>
"[^"\\]*(?:\\.[^"\\]*)*" # quoted string
|
[^;,]+ # token
)?
)?
\s*
''', flags=re.VERBOSE)
_option_header_start_mime_type = re.compile(r',\s*([^;,\s]+)([;,]\s*.+)?')

_entity_headers = frozenset([
Expand Down