• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/350788890): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "url/url_canon.h"
11 #include "url/url_canon_internal.h"
12 
13 // Query canonicalization in IE
14 // ----------------------------
15 // IE is very permissive for query parameters specified in links on the page
16 // (in contrast to links that it constructs itself based on form data). It does
17 // not unescape any character. It does not reject any escape sequence (be they
18 // invalid like "%2y" or freaky like %00).
19 //
20 // IE only escapes spaces and nothing else. Embedded NULLs, tabs (0x09),
21 // LF (0x0a), and CR (0x0d) are removed (this probably happens at an earlier
22 // layer since they are removed from all portions of the URL). All other
23 // characters are passed unmodified. Invalid UTF-16 sequences are preserved as
24 // well, with each character in the input being converted to UTF-8. It is the
25 // server's job to make sense of this invalid query.
26 //
27 // Invalid multibyte sequences (for example, invalid UTF-8 on a UTF-8 page)
28 // are converted to the invalid character and sent as unescaped UTF-8 (0xef,
29 // 0xbf, 0xbd). This may not be canonicalization, the parser may generate these
30 // strings before the URL handler ever sees them.
31 //
32 // Our query canonicalization
33 // --------------------------
34 // We escape all non-ASCII characters and control characters, like Firefox.
35 // This is more conformant to the URL spec, and there do not seem to be many
36 // problems relating to Firefox's behavior.
37 //
38 // Like IE, we will never unescape (although the application may want to try
39 // unescaping to present the user with a more understandable URL). We will
40 // replace all invalid sequences (including invalid UTF-16 sequences, which IE
41 // doesn't) with the "invalid character," and we will escape it.
42 
43 namespace url {
44 
45 namespace {
46 
47 // Appends the given string to the output, escaping characters that do not
48 // match the given |type| in SharedCharTypes. This version will accept 8 or 16
49 // bit characters, but assumes that they have only 7-bit values. It also assumes
50 // that all UTF-8 values are correct, so doesn't bother checking
51 template<typename CHAR>
AppendRaw8BitQueryString(const CHAR * source,int length,CanonOutput * output)52 void AppendRaw8BitQueryString(const CHAR* source, int length,
53                               CanonOutput* output) {
54   for (int i = 0; i < length; i++) {
55     if (!IsQueryChar(static_cast<unsigned char>(source[i])))
56       AppendEscapedChar(static_cast<unsigned char>(source[i]), output);
57     else  // Doesn't need escaping.
58       output->push_back(static_cast<char>(source[i]));
59   }
60 }
61 
62 // Runs the converter on the given UTF-8 input. Since the converter expects
63 // UTF-16, we have to convert first. The converter must be non-NULL.
RunConverter(const char * spec,const Component & query,CharsetConverter * converter,CanonOutput * output)64 void RunConverter(const char* spec,
65                   const Component& query,
66                   CharsetConverter* converter,
67                   CanonOutput* output) {
68   DCHECK(query.is_valid());
69   // This function will replace any misencoded values with the invalid
70   // character. This is what we want so we don't have to check for error.
71   RawCanonOutputW<1024> utf16;
72   ConvertUTF8ToUTF16(&spec[query.begin], static_cast<size_t>(query.len),
73                      &utf16);
74   converter->ConvertFromUTF16(utf16.view(), output);
75 }
76 
77 // Runs the converter with the given UTF-16 input. We don't have to do
78 // anything, but this overridden function allows us to use the same code
79 // for both UTF-8 and UTF-16 input.
RunConverter(const char16_t * spec,const Component & query,CharsetConverter * converter,CanonOutput * output)80 void RunConverter(const char16_t* spec,
81                   const Component& query,
82                   CharsetConverter* converter,
83                   CanonOutput* output) {
84   DCHECK(query.is_valid());
85   converter->ConvertFromUTF16(query.as_string_view_on(spec), output);
86 }
87 
88 template <typename CHAR, typename UCHAR>
DoConvertToQueryEncoding(const CHAR * spec,const Component & query,CharsetConverter * converter,CanonOutput * output)89 void DoConvertToQueryEncoding(const CHAR* spec,
90                               const Component& query,
91                               CharsetConverter* converter,
92                               CanonOutput* output) {
93   if (converter) {
94     // Run the converter to get an 8-bit string, then append it, escaping
95     // necessary values.
96     RawCanonOutput<1024> eight_bit;
97     RunConverter(spec, query, converter, &eight_bit);
98     AppendRaw8BitQueryString(eight_bit.data(), eight_bit.length(), output);
99 
100   } else {
101     // No converter, do our own UTF-8 conversion.
102     AppendStringOfType(&spec[query.begin], static_cast<size_t>(query.len),
103                        CHAR_QUERY, output);
104   }
105 }
106 
107 template<typename CHAR, typename UCHAR>
DoCanonicalizeQuery(const CHAR * spec,const Component & query,CharsetConverter * converter,CanonOutput * output,Component * out_query)108 void DoCanonicalizeQuery(const CHAR* spec,
109                          const Component& query,
110                          CharsetConverter* converter,
111                          CanonOutput* output,
112                          Component* out_query) {
113   if (!query.is_valid()) {
114     *out_query = Component();
115     return;
116   }
117 
118   output->push_back('?');
119   out_query->begin = output->length();
120 
121   DoConvertToQueryEncoding<CHAR, UCHAR>(spec, query, converter, output);
122 
123   out_query->len = output->length() - out_query->begin;
124 }
125 
126 }  // namespace
127 
CanonicalizeQuery(const char * spec,const Component & query,CharsetConverter * converter,CanonOutput * output,Component * out_query)128 void CanonicalizeQuery(const char* spec,
129                        const Component& query,
130                        CharsetConverter* converter,
131                        CanonOutput* output,
132                        Component* out_query) {
133   DoCanonicalizeQuery<char, unsigned char>(spec, query, converter,
134                                            output, out_query);
135 }
136 
CanonicalizeQuery(const char16_t * spec,const Component & query,CharsetConverter * converter,CanonOutput * output,Component * out_query)137 void CanonicalizeQuery(const char16_t* spec,
138                        const Component& query,
139                        CharsetConverter* converter,
140                        CanonOutput* output,
141                        Component* out_query) {
142   DoCanonicalizeQuery<char16_t, char16_t>(spec, query, converter, output,
143                                           out_query);
144 }
145 
ConvertUTF16ToQueryEncoding(const char16_t * input,const Component & query,CharsetConverter * converter,CanonOutput * output)146 void ConvertUTF16ToQueryEncoding(const char16_t* input,
147                                  const Component& query,
148                                  CharsetConverter* converter,
149                                  CanonOutput* output) {
150   DoConvertToQueryEncoding<char16_t, char16_t>(input, query, converter, output);
151 }
152 
153 }  // namespace url
154