|
91 | 91 | # Unsafe bytes to be removed per WHATWG spec |
92 | 92 | _UNSAFE_URL_BYTES_TO_REMOVE = ['\t', '\r', '\n'] |
93 | 93 |
|
94 | | -# Allowed valid characters in parse_qsl |
95 | | -_VALID_QUERY_CHARS = "-._~!$&'()*+,;=:@/?%" |
| 94 | +# Allowed valid characters in parse_qsl as per RFC 3986. |
| 95 | +_VALID_RFC3986_QUERY_CHARS = "-._~!$&'()*+,;=:@/?%" |
96 | 96 |
|
97 | 97 | def clear_cache(): |
98 | 98 | """Clear internal performance caches. Undocumented; some tests want it.""" |
@@ -781,12 +781,12 @@ def parse_qs(qs, keep_blank_values=False, strict_parsing=False, |
781 | 781 | parsed_result[name] = [value] |
782 | 782 | return parsed_result |
783 | 783 |
|
784 | | -def _is_valid_query(to_check: str) -> bool: |
| 784 | +def _is_valid_rfc3986_query(chars): |
785 | 785 | """Return True if all characters are valid per RFC 3986.""" |
786 | | - for ch in to_check: |
| 786 | + for ch in chars: |
787 | 787 | if not ch.isascii(): |
788 | 788 | return False |
789 | | - if ch.isalnum() or ch in _VALID_QUERY_CHARS: |
| 789 | + if ch.isalnum() or ch in _VALID_RFC3986_QUERY_CHARS: |
790 | 790 | continue |
791 | 791 | return False |
792 | 792 | return True |
@@ -868,8 +868,10 @@ def _unquote(s): |
868 | 868 | raise ValueError("bad query field: %r" % (name_value,)) |
869 | 869 | if strict_parsing: |
870 | 870 | # Validate RFC3986 characters |
871 | | - to_check = (name_value.decode() if isinstance(name_value, bytes) else name_value) |
872 | | - if not _is_valid_query(to_check): |
| 871 | + to_check = _unquote(name_value) |
| 872 | + if isinstance(to_check, (bytes, bytearray)): |
| 873 | + to_check = to_check.decode(encoding, errors) |
| 874 | + if not _is_valid_rfc3986_query(to_check): |
873 | 875 | raise ValueError(f"Invalid characters in query string per RFC 3986: {name_value!r}") |
874 | 876 | if value or keep_blank_values: |
875 | 877 | name = _unquote(name) |
|
0 commit comments