1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "third_party/blink/renderer/platform/network/http_parsers.h"
6
7 #include "base/stl_util.h"
8 #include "services/network/public/mojom/content_security_policy.mojom-blink.h"
9 #include "services/network/public/mojom/parsed_headers.mojom-blink.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "third_party/blink/renderer/platform/heap/handle.h"
12 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
13 #include "third_party/blink/renderer/platform/wtf/math_extras.h"
14 #include "third_party/blink/renderer/platform/wtf/text/atomic_string.h"
15
16 namespace blink {
17
TEST(HTTPParsersTest,ParseCacheControl)18 TEST(HTTPParsersTest, ParseCacheControl) {
19 CacheControlHeader header;
20
21 header = ParseCacheControlDirectives("no-cache", AtomicString());
22 EXPECT_TRUE(header.parsed);
23 EXPECT_TRUE(header.contains_no_cache);
24 EXPECT_FALSE(header.contains_no_store);
25 EXPECT_FALSE(header.contains_must_revalidate);
26 EXPECT_EQ(base::nullopt, header.max_age);
27 EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
28
29 header = ParseCacheControlDirectives("no-cache no-store", AtomicString());
30 EXPECT_TRUE(header.parsed);
31 EXPECT_TRUE(header.contains_no_cache);
32 EXPECT_FALSE(header.contains_no_store);
33 EXPECT_FALSE(header.contains_must_revalidate);
34 EXPECT_EQ(base::nullopt, header.max_age);
35 EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
36
37 header =
38 ParseCacheControlDirectives("no-store must-revalidate", AtomicString());
39 EXPECT_TRUE(header.parsed);
40 EXPECT_FALSE(header.contains_no_cache);
41 EXPECT_TRUE(header.contains_no_store);
42 EXPECT_FALSE(header.contains_must_revalidate);
43 EXPECT_EQ(base::nullopt, header.max_age);
44 EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
45
46 header = ParseCacheControlDirectives("max-age=0", AtomicString());
47 EXPECT_TRUE(header.parsed);
48 EXPECT_FALSE(header.contains_no_cache);
49 EXPECT_FALSE(header.contains_no_store);
50 EXPECT_FALSE(header.contains_must_revalidate);
51 EXPECT_EQ(base::TimeDelta(), header.max_age.value());
52 EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
53
54 header = ParseCacheControlDirectives("max-age", AtomicString());
55 EXPECT_TRUE(header.parsed);
56 EXPECT_FALSE(header.contains_no_cache);
57 EXPECT_FALSE(header.contains_no_store);
58 EXPECT_FALSE(header.contains_must_revalidate);
59 EXPECT_EQ(base::nullopt, header.max_age);
60 EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
61
62 header = ParseCacheControlDirectives("max-age=0 no-cache", AtomicString());
63 EXPECT_TRUE(header.parsed);
64 EXPECT_FALSE(header.contains_no_cache);
65 EXPECT_FALSE(header.contains_no_store);
66 EXPECT_FALSE(header.contains_must_revalidate);
67 EXPECT_EQ(base::TimeDelta(), header.max_age.value());
68 EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
69
70 header = ParseCacheControlDirectives("no-cache=foo", AtomicString());
71 EXPECT_TRUE(header.parsed);
72 EXPECT_FALSE(header.contains_no_cache);
73 EXPECT_FALSE(header.contains_no_store);
74 EXPECT_FALSE(header.contains_must_revalidate);
75 EXPECT_EQ(base::nullopt, header.max_age);
76 EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
77
78 header = ParseCacheControlDirectives("nonsense", AtomicString());
79 EXPECT_TRUE(header.parsed);
80 EXPECT_FALSE(header.contains_no_cache);
81 EXPECT_FALSE(header.contains_no_store);
82 EXPECT_FALSE(header.contains_must_revalidate);
83 EXPECT_EQ(base::nullopt, header.max_age);
84 EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
85
86 header = ParseCacheControlDirectives("\rno-cache\n\t\v\0\b", AtomicString());
87 EXPECT_TRUE(header.parsed);
88 EXPECT_TRUE(header.contains_no_cache);
89 EXPECT_FALSE(header.contains_no_store);
90 EXPECT_FALSE(header.contains_must_revalidate);
91 EXPECT_EQ(base::nullopt, header.max_age);
92 EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
93
94 header = ParseCacheControlDirectives(" no-cache ", AtomicString());
95 EXPECT_TRUE(header.parsed);
96 EXPECT_TRUE(header.contains_no_cache);
97 EXPECT_FALSE(header.contains_no_store);
98 EXPECT_FALSE(header.contains_must_revalidate);
99 EXPECT_EQ(base::nullopt, header.max_age);
100 EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
101
102 header = ParseCacheControlDirectives(AtomicString(), "no-cache");
103 EXPECT_TRUE(header.parsed);
104 EXPECT_TRUE(header.contains_no_cache);
105 EXPECT_FALSE(header.contains_no_store);
106 EXPECT_FALSE(header.contains_must_revalidate);
107 EXPECT_EQ(base::nullopt, header.max_age);
108 EXPECT_EQ(base::nullopt, header.stale_while_revalidate);
109
110 header = ParseCacheControlDirectives(
111 "stale-while-revalidate=2,stale-while-revalidate=3", AtomicString());
112 EXPECT_TRUE(header.parsed);
113 EXPECT_FALSE(header.contains_no_cache);
114 EXPECT_FALSE(header.contains_no_store);
115 EXPECT_FALSE(header.contains_must_revalidate);
116 EXPECT_EQ(base::nullopt, header.max_age);
117 EXPECT_EQ(2.0, header.stale_while_revalidate.value().InSecondsF());
118 }
119
TEST(HTTPParsersTest,CommaDelimitedHeaderSet)120 TEST(HTTPParsersTest, CommaDelimitedHeaderSet) {
121 CommaDelimitedHeaderSet set1;
122 CommaDelimitedHeaderSet set2;
123 ParseCommaDelimitedHeader("dpr, rw, whatever", set1);
124 EXPECT_TRUE(set1.Contains("dpr"));
125 EXPECT_TRUE(set1.Contains("rw"));
126 EXPECT_TRUE(set1.Contains("whatever"));
127 ParseCommaDelimitedHeader("dprw\t , fo\to", set2);
128 EXPECT_FALSE(set2.Contains("dpr"));
129 EXPECT_FALSE(set2.Contains("rw"));
130 EXPECT_FALSE(set2.Contains("whatever"));
131 EXPECT_TRUE(set2.Contains("dprw"));
132 EXPECT_FALSE(set2.Contains("foo"));
133 EXPECT_TRUE(set2.Contains("fo\to"));
134 }
135
TEST(HTTPParsersTest,HTTPToken)136 TEST(HTTPParsersTest, HTTPToken) {
137 const UChar kHiraganaA[2] = {0x3042, 0};
138 const UChar kLatinCapitalAWithMacron[2] = {0x100, 0};
139
140 EXPECT_TRUE(blink::IsValidHTTPToken("gzip"));
141 EXPECT_TRUE(blink::IsValidHTTPToken("no-cache"));
142 EXPECT_TRUE(blink::IsValidHTTPToken("86400"));
143 EXPECT_TRUE(blink::IsValidHTTPToken("~"));
144 EXPECT_FALSE(blink::IsValidHTTPToken(""));
145 EXPECT_FALSE(blink::IsValidHTTPToken(" "));
146 EXPECT_FALSE(blink::IsValidHTTPToken("\t"));
147 EXPECT_FALSE(blink::IsValidHTTPToken("\x7f"));
148 EXPECT_FALSE(blink::IsValidHTTPToken("\xff"));
149 EXPECT_FALSE(blink::IsValidHTTPToken(String(kLatinCapitalAWithMacron)));
150 EXPECT_FALSE(blink::IsValidHTTPToken("t a"));
151 EXPECT_FALSE(blink::IsValidHTTPToken("()"));
152 EXPECT_FALSE(blink::IsValidHTTPToken("(foobar)"));
153 EXPECT_FALSE(blink::IsValidHTTPToken(String("\0", 1u)));
154 EXPECT_FALSE(blink::IsValidHTTPToken(String(kHiraganaA)));
155 }
156
TEST(HTTPParsersTest,ExtractMIMETypeFromMediaType)157 TEST(HTTPParsersTest, ExtractMIMETypeFromMediaType) {
158 const AtomicString text_html("text/html");
159
160 EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(AtomicString("text/html")));
161 EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
162 AtomicString("text/html; charset=iso-8859-1")));
163
164 // Quoted charset parameter
165 EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
166 AtomicString("text/html; charset=\"quoted\"")));
167
168 // Multiple parameters
169 EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
170 AtomicString("text/html; charset=x; foo=bar")));
171
172 // OWSes are trimmed.
173 EXPECT_EQ(text_html,
174 ExtractMIMETypeFromMediaType(AtomicString(" text/html ")));
175 EXPECT_EQ(text_html,
176 ExtractMIMETypeFromMediaType(AtomicString("\ttext/html \t")));
177 EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
178 AtomicString("text/html ; charset=iso-8859-1")));
179
180 // Non-standard multiple type/subtype listing using a comma as a separator
181 // is accepted.
182 EXPECT_EQ(text_html,
183 ExtractMIMETypeFromMediaType(AtomicString("text/html,text/plain")));
184 EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
185 AtomicString("text/html , text/plain")));
186 EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(
187 AtomicString("text/html\t,\ttext/plain")));
188 EXPECT_EQ(text_html, ExtractMIMETypeFromMediaType(AtomicString(
189 "text/html,text/plain;charset=iso-8859-1")));
190
191 // Preserves case.
192 EXPECT_EQ("tExt/hTMl",
193 ExtractMIMETypeFromMediaType(AtomicString("tExt/hTMl")));
194
195 EXPECT_EQ(g_empty_string,
196 ExtractMIMETypeFromMediaType(AtomicString(", text/html")));
197 EXPECT_EQ(g_empty_string,
198 ExtractMIMETypeFromMediaType(AtomicString("; text/html")));
199
200 // If no normalization is required, the same AtomicString should be returned.
201 const AtomicString& passthrough = ExtractMIMETypeFromMediaType(text_html);
202 EXPECT_EQ(text_html.Impl(), passthrough.Impl());
203 }
204
TEST(HTTPParsersTest,ExtractMIMETypeFromMediaTypeInvalidInput)205 TEST(HTTPParsersTest, ExtractMIMETypeFromMediaTypeInvalidInput) {
206 // extractMIMETypeFromMediaType() returns the string before the first
207 // semicolon after trimming OWSes at the head and the tail even if the
208 // string doesn't conform to the media-type ABNF defined in the RFC 7231.
209
210 // These behaviors could be fixed later when ready.
211
212 // Non-OWS characters meaning space are not trimmed.
213 EXPECT_EQ(AtomicString("\r\ntext/html\r\n"),
214 ExtractMIMETypeFromMediaType(AtomicString("\r\ntext/html\r\n")));
215 // U+2003, EM SPACE (UTF-8: E2 80 83).
216 EXPECT_EQ(AtomicString::FromUTF8("\xE2\x80\x83text/html"),
217 ExtractMIMETypeFromMediaType(
218 AtomicString::FromUTF8("\xE2\x80\x83text/html")));
219
220 // Invalid type/subtype.
221 EXPECT_EQ(AtomicString("a"), ExtractMIMETypeFromMediaType(AtomicString("a")));
222
223 // Invalid parameters.
224 EXPECT_EQ(AtomicString("text/html"),
225 ExtractMIMETypeFromMediaType(AtomicString("text/html;wow")));
226 EXPECT_EQ(AtomicString("text/html"),
227 ExtractMIMETypeFromMediaType(AtomicString("text/html;;;;;;")));
228 EXPECT_EQ(AtomicString("text/html"),
229 ExtractMIMETypeFromMediaType(AtomicString("text/html; = = = ")));
230
231 // Only OWSes at either the beginning or the end of the type/subtype
232 // portion.
233 EXPECT_EQ(AtomicString("text / html"),
234 ExtractMIMETypeFromMediaType(AtomicString("text / html")));
235 EXPECT_EQ(AtomicString("t e x t / h t m l"),
236 ExtractMIMETypeFromMediaType(AtomicString("t e x t / h t m l")));
237
238 EXPECT_EQ(AtomicString("text\r\n/\nhtml"),
239 ExtractMIMETypeFromMediaType(AtomicString("text\r\n/\nhtml")));
240 EXPECT_EQ(AtomicString("text\n/\nhtml"),
241 ExtractMIMETypeFromMediaType(AtomicString("text\n/\nhtml")));
242 EXPECT_EQ(AtomicString::FromUTF8("text\xE2\x80\x83/html"),
243 ExtractMIMETypeFromMediaType(
244 AtomicString::FromUTF8("text\xE2\x80\x83/html")));
245 }
246
TEST(HTTPParsersTest,ParseHTTPRefresh)247 TEST(HTTPParsersTest, ParseHTTPRefresh) {
248 base::TimeDelta delay;
249 String url;
250 EXPECT_FALSE(ParseHTTPRefresh("", nullptr, delay, url));
251 EXPECT_FALSE(ParseHTTPRefresh(" ", nullptr, delay, url));
252 EXPECT_FALSE(ParseHTTPRefresh("1.3xyz url=foo", nullptr, delay, url));
253 EXPECT_FALSE(ParseHTTPRefresh("1.3.4xyz url=foo", nullptr, delay, url));
254 EXPECT_FALSE(ParseHTTPRefresh("1e1 url=foo", nullptr, delay, url));
255
256 EXPECT_TRUE(ParseHTTPRefresh("123 ", nullptr, delay, url));
257 EXPECT_EQ(base::TimeDelta::FromSeconds(123), delay);
258 EXPECT_TRUE(url.IsEmpty());
259
260 EXPECT_TRUE(ParseHTTPRefresh("1 ; url=dest", nullptr, delay, url));
261 EXPECT_EQ(base::TimeDelta::FromSeconds(1), delay);
262 EXPECT_EQ("dest", url);
263 EXPECT_TRUE(
264 ParseHTTPRefresh("1 ;\nurl=dest", IsASCIISpace<UChar>, delay, url));
265 EXPECT_EQ(base::TimeDelta::FromSeconds(1), delay);
266 EXPECT_EQ("dest", url);
267 EXPECT_TRUE(ParseHTTPRefresh("1 ;\nurl=dest", nullptr, delay, url));
268 EXPECT_EQ(base::TimeDelta::FromSeconds(1), delay);
269 EXPECT_EQ("url=dest", url);
270
271 EXPECT_TRUE(ParseHTTPRefresh("1 url=dest", nullptr, delay, url));
272 EXPECT_EQ(base::TimeDelta::FromSeconds(1), delay);
273 EXPECT_EQ("dest", url);
274
275 EXPECT_TRUE(
276 ParseHTTPRefresh("10\nurl=dest", IsASCIISpace<UChar>, delay, url));
277 EXPECT_EQ(base::TimeDelta::FromSeconds(10), delay);
278 EXPECT_EQ("dest", url);
279
280 EXPECT_TRUE(
281 ParseHTTPRefresh("1.5; url=dest", IsASCIISpace<UChar>, delay, url));
282 EXPECT_EQ(base::TimeDelta::FromSecondsD(1.5), delay);
283 EXPECT_EQ("dest", url);
284 EXPECT_TRUE(
285 ParseHTTPRefresh("1.5.9; url=dest", IsASCIISpace<UChar>, delay, url));
286 EXPECT_EQ(base::TimeDelta::FromSecondsD(1.5), delay);
287 EXPECT_EQ("dest", url);
288 EXPECT_TRUE(
289 ParseHTTPRefresh("7..; url=dest", IsASCIISpace<UChar>, delay, url));
290 EXPECT_EQ(base::TimeDelta::FromSeconds(7), delay);
291 EXPECT_EQ("dest", url);
292 }
293
TEST(HTTPParsersTest,ParseMultipartHeadersResult)294 TEST(HTTPParsersTest, ParseMultipartHeadersResult) {
295 struct {
296 const char* data;
297 const bool result;
298 const size_t end;
299 } tests[] = {
300 {"This is junk", false, 0},
301 {"Foo: bar\nBaz:\n\nAfter:\n", true, 15},
302 {"Foo: bar\nBaz:\n", false, 0},
303 {"Foo: bar\r\nBaz:\r\n\r\nAfter:\r\n", true, 18},
304 {"Foo: bar\r\nBaz:\r\n", false, 0},
305 {"Foo: bar\nBaz:\r\n\r\nAfter:\n\n", true, 17},
306 {"Foo: bar\r\nBaz:\n", false, 0},
307 {"\r\n", true, 2},
308 };
309 for (size_t i = 0; i < base::size(tests); ++i) {
310 ResourceResponse response;
311 wtf_size_t end = 0;
312 bool result = ParseMultipartHeadersFromBody(
313 tests[i].data, static_cast<wtf_size_t>(strlen(tests[i].data)),
314 &response, &end);
315 EXPECT_EQ(tests[i].result, result);
316 EXPECT_EQ(tests[i].end, end);
317 }
318 }
319
TEST(HTTPParsersTest,ParseMultipartHeaders)320 TEST(HTTPParsersTest, ParseMultipartHeaders) {
321 ResourceResponse response;
322 response.AddHttpHeaderField("foo", "bar");
323 response.AddHttpHeaderField("range", "piyo");
324 response.AddHttpHeaderField("content-length", "999");
325 response.AddHttpHeaderField("set-cookie", "a=1");
326
327 const char kData[] =
328 "content-type: image/png\n"
329 "content-length: 10\n"
330 "set-cookie: x=2\n"
331 "set-cookie: y=3\n"
332 "\n";
333 wtf_size_t end = 0;
334 bool result =
335 ParseMultipartHeadersFromBody(kData, strlen(kData), &response, &end);
336
337 EXPECT_TRUE(result);
338 EXPECT_EQ(strlen(kData), end);
339 EXPECT_EQ("image/png", response.HttpHeaderField("content-type"));
340 EXPECT_EQ("10", response.HttpHeaderField("content-length"));
341 EXPECT_EQ("bar", response.HttpHeaderField("foo"));
342 EXPECT_EQ(AtomicString(), response.HttpHeaderField("range"));
343 EXPECT_EQ("x=2, y=3", response.HttpHeaderField("set-cookie"));
344 }
345
TEST(HTTPParsersTest,ParseMultipartHeadersContentCharset)346 TEST(HTTPParsersTest, ParseMultipartHeadersContentCharset) {
347 ResourceResponse response;
348 const char kData[] = "content-type: text/html; charset=utf-8\n\n";
349 wtf_size_t end = 0;
350 bool result =
351 ParseMultipartHeadersFromBody(kData, strlen(kData), &response, &end);
352
353 EXPECT_TRUE(result);
354 EXPECT_EQ(strlen(kData), end);
355 EXPECT_EQ("text/html; charset=utf-8",
356 response.HttpHeaderField("content-type"));
357 EXPECT_EQ("utf-8", response.TextEncodingName());
358 }
359
testServerTimingHeader(const char * headerValue,Vector<Vector<String>> expectedResults)360 void testServerTimingHeader(const char* headerValue,
361 Vector<Vector<String>> expectedResults) {
362 std::unique_ptr<ServerTimingHeaderVector> results =
363 ParseServerTimingHeader(headerValue);
364 EXPECT_EQ((*results).size(), expectedResults.size());
365 unsigned i = 0;
366 for (const auto& header : *results) {
367 Vector<String> expectedResult = expectedResults[i++];
368 EXPECT_EQ(header->Name(), expectedResult[0]);
369 EXPECT_EQ(header->Duration(), expectedResult[1].ToDouble());
370 EXPECT_EQ(header->Description(), expectedResult[2]);
371 }
372 }
373
TEST(HTTPParsersTest,ParseServerTimingHeader)374 TEST(HTTPParsersTest, ParseServerTimingHeader) {
375 // empty string
376 testServerTimingHeader("", {});
377
378 // name only
379 testServerTimingHeader("metric", {{"metric", "0", ""}});
380
381 // name and duration
382 testServerTimingHeader("metric;dur=123.4", {{"metric", "123.4", ""}});
383 testServerTimingHeader("metric;dur=\"123.4\"", {{"metric", "123.4", ""}});
384
385 // name and description
386 testServerTimingHeader("metric;desc=description",
387 {{"metric", "0", "description"}});
388 testServerTimingHeader("metric;desc=\"description\"",
389 {{"metric", "0", "description"}});
390
391 // name, duration, and description
392 testServerTimingHeader("metric;dur=123.4;desc=description",
393 {{"metric", "123.4", "description"}});
394 testServerTimingHeader("metric;desc=description;dur=123.4",
395 {{"metric", "123.4", "description"}});
396
397 // special chars in name
398 testServerTimingHeader("aB3!#$%&'*+-.^_`|~",
399 {{"aB3!#$%&'*+-.^_`|~", "0", ""}});
400
401 // delimiter chars in quoted description
402 testServerTimingHeader("metric;desc=\"descr;,=iption\";dur=123.4",
403 {{"metric", "123.4", "descr;,=iption"}});
404
405 // spaces
406 testServerTimingHeader("metric ; ", {{"metric", "0", ""}});
407 testServerTimingHeader("metric , ", {{"metric", "0", ""}});
408 testServerTimingHeader("metric ; dur = 123.4 ; desc = description",
409 {{"metric", "123.4", "description"}});
410 testServerTimingHeader("metric ; desc = description ; dur = 123.4",
411 {{"metric", "123.4", "description"}});
412 testServerTimingHeader("metric;desc = \"description\"",
413 {{"metric", "0", "description"}});
414
415 // tabs
416 /* known failures:
417 https://bugs.chromium.org/p/chromium/issues/detail?id=798446
418 testServerTimingHeader("metric\t;\t", {{"metric", "0", ""}});
419 testServerTimingHeader("metric\t,\t", {{"metric", "0", ""}});
420 testServerTimingHeader("metric\t;\tdur\t=\t123.4\t;\tdesc\t=\tdescription",
421 {{"metric", "123.4", "description"}});
422 testServerTimingHeader("metric\t;\tdesc\t=\tdescription\t;\tdur\t=\t123.4",
423 {{"metric", "123.4", "description"}});
424 testServerTimingHeader("metric;desc\t=\t\"description\"", {{"metric", "0",
425 "description"}});
426 */
427
428 // multiple entries
429 testServerTimingHeader(
430 "metric1;dur=12.3;desc=description1,metric2;dur=45.6;desc=description2,"
431 "metric3;dur=78.9;desc=description3",
432 {{"metric1", "12.3", "description1"},
433 {"metric2", "45.6", "description2"},
434 {"metric3", "78.9", "description3"}});
435 testServerTimingHeader("metric1,metric2 ,metric3, metric4 , metric5",
436 {{"metric1", "0", ""},
437 {"metric2", "0", ""},
438 {"metric3", "0", ""},
439 {"metric4", "0", ""},
440 {"metric5", "0", ""}});
441
442 // quoted-strings - happy path
443 testServerTimingHeader("metric;desc=\"description\"",
444 {{"metric", "0", "description"}});
445 testServerTimingHeader("metric;desc=\"\t description \t\"",
446 {{"metric", "0", "\t description \t"}});
447 testServerTimingHeader("metric;desc=\"descr\\\"iption\"",
448 {{"metric", "0", "descr\"iption"}});
449
450 // quoted-strings - others
451 // metric;desc=\ --> ''
452 testServerTimingHeader("metric;desc=\\", {{"metric", "0", ""}});
453 // metric;desc=" --> ''
454 testServerTimingHeader("metric;desc=\"", {{"metric", "0", ""}});
455 // metric;desc=\\ --> ''
456 testServerTimingHeader("metric;desc=\\\\", {{"metric", "0", ""}});
457 // metric;desc=\" --> ''
458 testServerTimingHeader("metric;desc=\\\"", {{"metric", "0", ""}});
459 // metric;desc="\ --> ''
460 testServerTimingHeader("metric;desc=\"\\", {{"metric", "0", ""}});
461 // metric;desc="" --> ''
462 testServerTimingHeader("metric;desc=\"\"", {{"metric", "0", ""}});
463 // metric;desc=\\\ --> ''
464 testServerTimingHeader("metric;desc=\\\\\\", {{"metric", "0", ""}});
465 // metric;desc=\\" --> ''
466 testServerTimingHeader("metric;desc=\\\\\"", {{"metric", "0", ""}});
467 // metric;desc=\"\ --> ''
468 testServerTimingHeader("metric;desc=\\\"\\", {{"metric", "0", ""}});
469 // metric;desc=\"" --> ''
470 testServerTimingHeader("metric;desc=\\\"\"", {{"metric", "0", ""}});
471 // metric;desc="\\ --> ''
472 testServerTimingHeader("metric;desc=\"\\\\", {{"metric", "0", ""}});
473 // metric;desc="\" --> ''
474 testServerTimingHeader("metric;desc=\"\\\"", {{"metric", "0", ""}});
475 // metric;desc=""\ --> ''
476 testServerTimingHeader("metric;desc=\"\"\\", {{"metric", "0", ""}});
477 // metric;desc=""" --> ''
478 testServerTimingHeader("metric;desc=\"\"\"", {{"metric", "0", ""}});
479 // metric;desc=\\\\ --> ''
480 testServerTimingHeader("metric;desc=\\\\\\\\", {{"metric", "0", ""}});
481 // metric;desc=\\\" --> ''
482 testServerTimingHeader("metric;desc=\\\\\\\"", {{"metric", "0", ""}});
483 // metric;desc=\\"\ --> ''
484 testServerTimingHeader("metric;desc=\\\\\"\\", {{"metric", "0", ""}});
485 // metric;desc=\\"" --> ''
486 testServerTimingHeader("metric;desc=\\\\\"\"", {{"metric", "0", ""}});
487 // metric;desc=\"\\ --> ''
488 testServerTimingHeader("metric;desc=\\\"\\\\", {{"metric", "0", ""}});
489 // metric;desc=\"\" --> ''
490 testServerTimingHeader("metric;desc=\\\"\\\"", {{"metric", "0", ""}});
491 // metric;desc=\""\ --> ''
492 testServerTimingHeader("metric;desc=\\\"\"\\", {{"metric", "0", ""}});
493 // metric;desc=\""" --> ''
494 testServerTimingHeader("metric;desc=\\\"\"\"", {{"metric", "0", ""}});
495 // metric;desc="\\\ --> ''
496 testServerTimingHeader("metric;desc=\"\\\\\\", {{"metric", "0", ""}});
497 // metric;desc="\\" --> '\'
498 testServerTimingHeader("metric;desc=\"\\\\\"", {{"metric", "0", "\\"}});
499 // metric;desc="\"\ --> ''
500 testServerTimingHeader("metric;desc=\"\\\"\\", {{"metric", "0", ""}});
501 // metric;desc="\"" --> '"'
502 testServerTimingHeader("metric;desc=\"\\\"\"", {{"metric", "0", "\""}});
503 // metric;desc=""\\ --> ''
504 testServerTimingHeader("metric;desc=\"\"\\\\", {{"metric", "0", ""}});
505 // metric;desc=""\" --> ''
506 testServerTimingHeader("metric;desc=\"\"\\\"", {{"metric", "0", ""}});
507 // metric;desc="""\ --> ''
508 testServerTimingHeader("metric;desc=\"\"\"\\", {{"metric", "0", ""}});
509 // metric;desc="""" --> ''
510 testServerTimingHeader("metric;desc=\"\"\"\"", {{"metric", "0", ""}});
511
512 // duplicate entry names
513 testServerTimingHeader(
514 "metric;dur=12.3;desc=description1,metric;dur=45.6;desc=description2",
515 {{"metric", "12.3", "description1"}, {"metric", "45.6", "description2"}});
516
517 // param name case sensitivity
518 testServerTimingHeader("metric;DuR=123.4;DeSc=description",
519 {{"metric", "123.4", "description"}});
520
521 // non-numeric durations
522 testServerTimingHeader("metric;dur=foo", {{"metric", "0", ""}});
523 testServerTimingHeader("metric;dur=\"foo\"", {{"metric", "0", ""}});
524
525 // unrecognized param names
526 testServerTimingHeader(
527 "metric1;foo=bar;desc=description;foo=bar;dur=123.4;foo=bar,metric2",
528 {{"metric1", "123.4", "description"}, {"metric2", "0", ""}});
529
530 // duplicate param names
531 testServerTimingHeader("metric;dur=123.4;dur=567.8",
532 {{"metric", "123.4", ""}});
533 testServerTimingHeader("metric;dur=foo;dur=567.8", {{"metric", "0", ""}});
534 testServerTimingHeader("metric;desc=description1;desc=description2",
535 {{"metric", "0", "description1"}});
536
537 // incomplete params
538 testServerTimingHeader("metric;dur;dur=123.4;desc=description",
539 {{"metric", "0", "description"}});
540 testServerTimingHeader("metric;dur=;dur=123.4;desc=description",
541 {{"metric", "0", "description"}});
542 testServerTimingHeader("metric;desc;desc=description;dur=123.4",
543 {{"metric", "123.4", ""}});
544 testServerTimingHeader("metric;desc=;desc=description;dur=123.4",
545 {{"metric", "123.4", ""}});
546
547 // extraneous characters after param value as token
548 testServerTimingHeader("metric;desc=d1 d2;dur=123.4",
549 {{"metric", "123.4", "d1"}});
550 testServerTimingHeader("metric1;desc=d1 d2,metric2",
551 {{"metric1", "0", "d1"}, {"metric2", "0", ""}});
552
553 // extraneous characters after param value as quoted-string
554 testServerTimingHeader("metric;desc=\"d1\" d2;dur=123.4",
555 {{"metric", "123.4", "d1"}});
556 testServerTimingHeader("metric1;desc=\"d1\" d2,metric2",
557 {{"metric1", "0", "d1"}, {"metric2", "0", ""}});
558
559 // nonsense - extraneous characters after entry name token
560 testServerTimingHeader("metric== \"\"foo;dur=123.4", {{"metric", "0", ""}});
561 testServerTimingHeader("metric1== \"\"foo,metric2", {{"metric1", "0", ""}});
562
563 // nonsense - extraneous characters after param name token
564 testServerTimingHeader("metric;dur foo=12", {{"metric", "0", ""}});
565 testServerTimingHeader("metric;foo dur=12", {{"metric", "0", ""}});
566
567 // nonsense - return zero entries
568 testServerTimingHeader(" ", {});
569 testServerTimingHeader("=", {});
570 testServerTimingHeader("[", {});
571 testServerTimingHeader("]", {});
572 testServerTimingHeader(";", {});
573 testServerTimingHeader(",", {});
574 testServerTimingHeader("=;", {});
575 testServerTimingHeader(";=", {});
576 testServerTimingHeader("=,", {});
577 testServerTimingHeader(",=", {});
578 testServerTimingHeader(";,", {});
579 testServerTimingHeader(",;", {});
580 testServerTimingHeader("=;,", {});
581
582 // TODO(cvazac) the following tests should actually NOT pass
583 // According to the definition of token/tchar
584 // (https://tools.ietf.org/html/rfc7230#appendix-B),
585 // HeaderFieldTokenizer.IsTokenCharacter is being too permissive for the
586 // following chars (decimal):
587 // 123 '{', 125 '}', and 127 (not defined)
588 testServerTimingHeader("{", {{"{", "0", ""}});
589 testServerTimingHeader("}", {{"}", "0", ""}});
590 testServerTimingHeader("{}", {{"{}", "0", ""}});
591 testServerTimingHeader("{\"foo\":\"bar\"},metric", {{"{", "0", ""}});
592 }
593
TEST(HTTPParsersTest,ParseContentTypeOptionsTest)594 TEST(HTTPParsersTest, ParseContentTypeOptionsTest) {
595 struct {
596 const char* value;
597 ContentTypeOptionsDisposition result;
598 } cases[] = {{"nosniff", kContentTypeOptionsNosniff},
599 {"NOSNIFF", kContentTypeOptionsNosniff},
600 {"NOsniFF", kContentTypeOptionsNosniff},
601 {"nosniff, nosniff", kContentTypeOptionsNosniff},
602 {"nosniff, not-nosniff", kContentTypeOptionsNosniff},
603 {"nosniff, none", kContentTypeOptionsNosniff},
604 {" nosniff", kContentTypeOptionsNosniff},
605 {"NOSNIFF ", kContentTypeOptionsNosniff},
606 {" NOsniFF ", kContentTypeOptionsNosniff},
607 {" nosniff, nosniff", kContentTypeOptionsNosniff},
608 {"nosniff , not-nosniff", kContentTypeOptionsNosniff},
609 {" nosniff , none", kContentTypeOptionsNosniff},
610 {"", kContentTypeOptionsNone},
611 {",", kContentTypeOptionsNone},
612 {"none", kContentTypeOptionsNone},
613 {"none, nosniff", kContentTypeOptionsNone}};
614 for (const auto& test : cases) {
615 SCOPED_TRACE(test.value);
616 EXPECT_EQ(test.result, ParseContentTypeOptionsHeader(test.value));
617 }
618 }
619
620 // -----------------------------------------------------------------------------
621 // Blink's HTTP parser is reusing:
622 // services/network/public/cpp/content_security_policy/, which is already tested
623 // and fuzzed.
624 // What needs to be tested is the basic conversion from/to blink types.
625 // -----------------------------------------------------------------------------
626 namespace {
627 WTF::Vector<network::mojom::blink::ContentSecurityPolicyPtr>
ParseContentSecurityPolicy(String http_headers)628 ParseContentSecurityPolicy(String http_headers) {
629 return std::move(ParseHeaders("HTTP/1.1 200 OK\r\n" + http_headers,
630 KURL("http://example.com"))
631 ->content_security_policy);
632 }
633 } // namespace
634
TEST(HTTPParsersTest,ParseContentSecurityPolicyEmpty)635 TEST(HTTPParsersTest, ParseContentSecurityPolicyEmpty) {
636 auto csp = ParseContentSecurityPolicy("");
637 EXPECT_TRUE(csp.IsEmpty());
638 }
639
TEST(HTTPParsersTest,ParseContentSecurityPolicyMultiple)640 TEST(HTTPParsersTest, ParseContentSecurityPolicyMultiple) {
641 auto csp = ParseContentSecurityPolicy(
642 "Content-Security-Policy: frame-ancestors a.com\r\n"
643 "Content-Security-Policy: frame-ancestors b.com\r\n");
644 ASSERT_EQ(2u, csp.size());
645 EXPECT_EQ("frame-ancestors a.com", csp[0]->header->header_value);
646 EXPECT_EQ("frame-ancestors b.com", csp[1]->header->header_value);
647 }
648
TEST(HTTPParsersTest,ParseContentSecurityPolicyCoalesce)649 TEST(HTTPParsersTest, ParseContentSecurityPolicyCoalesce) {
650 auto csp = ParseContentSecurityPolicy(
651 "Content-Security-Policy:"
652 "frame-ancestors a.com, frame-ancestors b.com\r\n");
653 ASSERT_EQ(2u, csp.size());
654 EXPECT_EQ("frame-ancestors a.com", csp[0]->header->header_value);
655 EXPECT_EQ("frame-ancestors b.com", csp[1]->header->header_value);
656 }
657
TEST(HTTPParsersTest,ParseContentSecurityPolicyHeader)658 TEST(HTTPParsersTest, ParseContentSecurityPolicyHeader) {
659 auto csp = ParseContentSecurityPolicy(
660 "Content-Security-Policy: frame-ancestors a.com\r\n"
661 "Content-Security-Policy-Report-Only: frame-ancestors b.com");
662 ASSERT_EQ(2u, csp.size());
663
664 // Header source:
665 EXPECT_EQ(network::mojom::ContentSecurityPolicySource::kHTTP,
666 csp[0]->header->source);
667 EXPECT_EQ(network::mojom::ContentSecurityPolicySource::kHTTP,
668 csp[1]->header->source);
669
670 // Header type:
671 EXPECT_EQ(network::mojom::ContentSecurityPolicyType::kEnforce,
672 csp[0]->header->type);
673 EXPECT_EQ(network::mojom::ContentSecurityPolicyType::kReport,
674 csp[1]->header->type);
675
676 // Header value
677 EXPECT_EQ("frame-ancestors a.com", csp[0]->header->header_value);
678 EXPECT_EQ("frame-ancestors b.com", csp[1]->header->header_value);
679 }
680
TEST(HTTPParsersTest,ParseContentSecurityPolicyDirectiveName)681 TEST(HTTPParsersTest, ParseContentSecurityPolicyDirectiveName) {
682 auto policies = ParseContentSecurityPolicy(
683 "Content-Security-Policy: frame-ancestors 'none'\r\n"
684 "Content-Security-Policy: sandbox allow-script\r\n"
685 "Content-Security-Policy: form-action 'none'\r\n"
686 "Content-Security-Policy: navigate-to 'none'\r\n"
687 "Content-Security-Policy: frame-src 'none'\r\n"
688 "Content-Security-Policy: child-src 'none'\r\n"
689 "Content-Security-Policy: script-src 'none'\r\n"
690 "Content-Security-Policy: default-src 'none'\r\n"
691 "Content-Security-Policy: upgrade-insecure-requests\r\n");
692 EXPECT_EQ(9u, policies.size());
693 // frame-ancestors
694 EXPECT_EQ(1u, policies[0]->directives.size());
695 // sandbox. TODO(https://crbug.com/1041376) Implement this.
696 EXPECT_EQ(0u, policies[1]->directives.size());
697 // form-action.
698 EXPECT_EQ(1u, policies[2]->directives.size());
699 // navigate-to.
700 EXPECT_EQ(1u, policies[3]->directives.size());
701 // frame-src.
702 EXPECT_EQ(1u, policies[4]->directives.size());
703 // child-src.
704 EXPECT_EQ(1u, policies[5]->directives.size());
705 // script-src.
706 EXPECT_EQ(1u, policies[6]->directives.size());
707 // default-src.
708 EXPECT_EQ(1u, policies[7]->directives.size());
709 // upgrade-insecure-policies.
710 EXPECT_EQ(true, policies[8]->upgrade_insecure_requests);
711 }
712
TEST(HTTPParsersTest,ParseContentSecurityPolicyReportTo)713 TEST(HTTPParsersTest, ParseContentSecurityPolicyReportTo) {
714 auto policies =
715 ParseContentSecurityPolicy("Content-Security-Policy: report-to a b\r\n");
716 EXPECT_TRUE(policies[0]->use_reporting_api);
717 // The specification https://w3c.github.io/webappsec-csp/#directive-report-to
718 // only allows for one endpoints to be defined. The other ones are ignored.
719 ASSERT_EQ(1u, policies[0]->report_endpoints.size());
720 EXPECT_EQ("a", policies[0]->report_endpoints[0]);
721 }
722
TEST(HTTPParsersTest,ParseContentSecurityPolicyReportUri)723 TEST(HTTPParsersTest, ParseContentSecurityPolicyReportUri) {
724 auto policies = ParseContentSecurityPolicy(
725 "Content-Security-Policy: report-uri ./report.py\r\n");
726 EXPECT_FALSE(policies[0]->use_reporting_api);
727 ASSERT_EQ(1u, policies[0]->report_endpoints.size());
728 EXPECT_EQ("http://example.com/report.py", policies[0]->report_endpoints[0]);
729 }
730
TEST(HTTPParsersTest,ParseContentSecurityPolicySourceBasic)731 TEST(HTTPParsersTest, ParseContentSecurityPolicySourceBasic) {
732 auto frame_ancestors = network::mojom::CSPDirectiveName::FrameAncestors;
733 auto policies = ParseContentSecurityPolicy(
734 "Content-Security-Policy: frame-ancestors 'none'\r\n"
735 "Content-Security-Policy: frame-ancestors *\r\n"
736 "Content-Security-Policy: frame-ancestors 'self'\r\n"
737 "Content-Security-Policy: frame-ancestors http://a.com:22/path\r\n"
738 "Content-Security-Policy: frame-ancestors a.com:*\r\n"
739 "Content-Security-Policy: frame-ancestors */report.py\r\n");
740 // 'none'
741 {
742 auto source_list = policies[0]->directives.Take(frame_ancestors);
743 EXPECT_EQ(0u, source_list->sources.size());
744 EXPECT_FALSE(source_list->allow_self);
745 EXPECT_FALSE(source_list->allow_star);
746 EXPECT_FALSE(source_list->allow_response_redirects);
747 }
748
749 // *
750 {
751 auto source_list = policies[1]->directives.Take(frame_ancestors);
752 EXPECT_EQ(0u, source_list->sources.size());
753 EXPECT_FALSE(source_list->allow_self);
754 EXPECT_TRUE(source_list->allow_star);
755 EXPECT_FALSE(source_list->allow_response_redirects);
756 }
757
758 // 'self'
759 {
760 auto source_list = policies[2]->directives.Take(frame_ancestors);
761 EXPECT_EQ(0u, source_list->sources.size());
762 EXPECT_TRUE(source_list->allow_self);
763 EXPECT_FALSE(source_list->allow_star);
764 EXPECT_FALSE(source_list->allow_response_redirects);
765 }
766
767 // http://a.com:22/path
768 {
769 auto source_list = policies[3]->directives.Take(frame_ancestors);
770 EXPECT_FALSE(source_list->allow_self);
771 EXPECT_FALSE(source_list->allow_star);
772 EXPECT_FALSE(source_list->allow_response_redirects);
773 EXPECT_EQ(1u, source_list->sources.size());
774 auto& source = source_list->sources[0];
775 EXPECT_EQ("http", source->scheme);
776 EXPECT_EQ("a.com", source->host);
777 EXPECT_EQ("/path", source->path);
778 EXPECT_FALSE(source->is_host_wildcard);
779 EXPECT_FALSE(source->is_port_wildcard);
780 }
781
782 // a.com:*
783 {
784 auto source_list = policies[4]->directives.Take(frame_ancestors);
785 EXPECT_FALSE(source_list->allow_self);
786 EXPECT_FALSE(source_list->allow_star);
787 EXPECT_FALSE(source_list->allow_response_redirects);
788 EXPECT_EQ(1u, source_list->sources.size());
789 auto& source = source_list->sources[0];
790 EXPECT_EQ("", source->scheme);
791 EXPECT_EQ("a.com", source->host);
792 EXPECT_EQ("", source->path);
793 EXPECT_FALSE(source->is_host_wildcard);
794 EXPECT_TRUE(source->is_port_wildcard);
795 }
796
797 // frame-ancestors */report.py
798 {
799 auto source_list = policies[5]->directives.Take(frame_ancestors);
800 EXPECT_FALSE(source_list->allow_self);
801 EXPECT_FALSE(source_list->allow_star);
802 EXPECT_FALSE(source_list->allow_response_redirects);
803 EXPECT_EQ(1u, source_list->sources.size());
804 auto& source = source_list->sources[0];
805 EXPECT_EQ("", source->scheme);
806 EXPECT_EQ("", source->host);
807 EXPECT_EQ(-1, source->port);
808 EXPECT_EQ("/report.py", source->path);
809 EXPECT_TRUE(source->is_host_wildcard);
810 EXPECT_FALSE(source->is_port_wildcard);
811 }
812 }
813
814 } // namespace blink
815