1 // Copyright 2016 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/loader/cors/cors.h"
6
7 #include "testing/gtest/include/gtest/gtest.h"
8 #include "third_party/blink/public/mojom/fetch/fetch_api_request.mojom-blink.h"
9 #include "third_party/blink/renderer/platform/loader/fetch/resource_response.h"
10 #include "third_party/blink/renderer/platform/weborigin/security_origin.h"
11
12 namespace blink {
13
14 namespace {
15
16 class CorsExposedHeadersTest : public testing::Test {
17 public:
18 using CredentialsMode = network::mojom::CredentialsMode;
19
Parse(CredentialsMode credentials_mode,const AtomicString & header) const20 HTTPHeaderSet Parse(CredentialsMode credentials_mode,
21 const AtomicString& header) const {
22 ResourceResponse response;
23 response.AddHttpHeaderField("access-control-expose-headers", header);
24
25 return cors::ExtractCorsExposedHeaderNamesList(credentials_mode, response);
26 }
27 };
28
TEST_F(CorsExposedHeadersTest,ValidInput)29 TEST_F(CorsExposedHeadersTest, ValidInput) {
30 EXPECT_EQ(Parse(CredentialsMode::kOmit, "valid"), HTTPHeaderSet({"valid"}));
31
32 EXPECT_EQ(Parse(CredentialsMode::kOmit, "a,b"), HTTPHeaderSet({"a", "b"}));
33
34 EXPECT_EQ(Parse(CredentialsMode::kOmit, " a , b "),
35 HTTPHeaderSet({"a", "b"}));
36
37 EXPECT_EQ(Parse(CredentialsMode::kOmit, " \t \t\t a"),
38 HTTPHeaderSet({"a"}));
39
40 EXPECT_EQ(Parse(CredentialsMode::kOmit, "a , "), HTTPHeaderSet({"a", ""}));
41 }
42
TEST_F(CorsExposedHeadersTest,DuplicatedEntries)43 TEST_F(CorsExposedHeadersTest, DuplicatedEntries) {
44 EXPECT_EQ(Parse(CredentialsMode::kOmit, "a, a"), HTTPHeaderSet{"a"});
45
46 EXPECT_EQ(Parse(CredentialsMode::kOmit, "a, a, b"),
47 HTTPHeaderSet({"a", "b"}));
48 }
49
TEST_F(CorsExposedHeadersTest,InvalidInput)50 TEST_F(CorsExposedHeadersTest, InvalidInput) {
51 EXPECT_TRUE(Parse(CredentialsMode::kOmit, "not valid").empty());
52
53 EXPECT_TRUE(Parse(CredentialsMode::kOmit, "///").empty());
54
55 EXPECT_TRUE(Parse(CredentialsMode::kOmit, "/a/").empty());
56
57 EXPECT_TRUE(Parse(CredentialsMode::kOmit, ",").empty());
58
59 EXPECT_TRUE(Parse(CredentialsMode::kOmit, " , ").empty());
60
61 EXPECT_TRUE(Parse(CredentialsMode::kOmit, " , a").empty());
62
63 EXPECT_TRUE(Parse(CredentialsMode::kOmit, "").empty());
64
65 EXPECT_TRUE(Parse(CredentialsMode::kOmit, " ").empty());
66
67 // U+0141 which is 'A' (0x41) + 0x100.
68 EXPECT_TRUE(
69 Parse(CredentialsMode::kOmit, AtomicString(String::FromUTF8("\xC5\x81")))
70 .empty());
71 }
72
TEST_F(CorsExposedHeadersTest,Wildcard)73 TEST_F(CorsExposedHeadersTest, Wildcard) {
74 ResourceResponse response;
75 response.AddHttpHeaderField("access-control-expose-headers", "a, b, *");
76 response.AddHttpHeaderField("b", "-");
77 response.AddHttpHeaderField("c", "-");
78 response.AddHttpHeaderField("d", "-");
79 response.AddHttpHeaderField("*", "-");
80
81 EXPECT_EQ(
82 cors::ExtractCorsExposedHeaderNamesList(CredentialsMode::kOmit, response),
83 HTTPHeaderSet({"access-control-expose-headers", "b", "c", "d", "*"}));
84
85 EXPECT_EQ(
86 cors::ExtractCorsExposedHeaderNamesList(CredentialsMode::kSameOrigin,
87 response),
88 HTTPHeaderSet({"access-control-expose-headers", "b", "c", "d", "*"}));
89 }
90
TEST_F(CorsExposedHeadersTest,Asterisk)91 TEST_F(CorsExposedHeadersTest, Asterisk) {
92 ResourceResponse response;
93 response.AddHttpHeaderField("access-control-expose-headers", "a, b, *");
94 response.AddHttpHeaderField("b", "-");
95 response.AddHttpHeaderField("c", "-");
96 response.AddHttpHeaderField("d", "-");
97 response.AddHttpHeaderField("*", "-");
98
99 EXPECT_EQ(cors::ExtractCorsExposedHeaderNamesList(CredentialsMode::kInclude,
100 response),
101 HTTPHeaderSet({"a", "b", "*"}));
102 }
103
104 // Keep this in sync with the CalculateResponseTainting test in
105 // services/network/cors/cors_url_loader_unittest.cc.
TEST(CorsTest,CalculateResponseTainting)106 TEST(CorsTest, CalculateResponseTainting) {
107 using network::mojom::FetchResponseType;
108 using network::mojom::RequestMode;
109
110 const KURL same_origin_url("https://example.com/");
111 const KURL cross_origin_url("https://example2.com/");
112 scoped_refptr<SecurityOrigin> origin_refptr =
113 SecurityOrigin::Create(same_origin_url);
114 const SecurityOrigin* origin = origin_refptr.get();
115 const SecurityOrigin* no_origin = nullptr;
116
117 // CORS flag is false, same-origin request
118 EXPECT_EQ(
119 FetchResponseType::kBasic,
120 cors::CalculateResponseTainting(same_origin_url, RequestMode::kSameOrigin,
121 origin, nullptr, CorsFlag::Unset));
122 EXPECT_EQ(
123 FetchResponseType::kBasic,
124 cors::CalculateResponseTainting(same_origin_url, RequestMode::kNoCors,
125 origin, nullptr, CorsFlag::Unset));
126 EXPECT_EQ(FetchResponseType::kBasic,
127 cors::CalculateResponseTainting(same_origin_url, RequestMode::kCors,
128 origin, nullptr, CorsFlag::Unset));
129 EXPECT_EQ(FetchResponseType::kBasic,
130 cors::CalculateResponseTainting(
131 same_origin_url, RequestMode::kCorsWithForcedPreflight, origin,
132 nullptr, CorsFlag::Unset));
133 EXPECT_EQ(
134 FetchResponseType::kBasic,
135 cors::CalculateResponseTainting(same_origin_url, RequestMode::kNavigate,
136 origin, nullptr, CorsFlag::Unset));
137
138 // CORS flag is false, cross-origin request
139 EXPECT_EQ(
140 FetchResponseType::kOpaque,
141 cors::CalculateResponseTainting(cross_origin_url, RequestMode::kNoCors,
142 origin, nullptr, CorsFlag::Unset));
143 EXPECT_EQ(
144 FetchResponseType::kBasic,
145 cors::CalculateResponseTainting(cross_origin_url, RequestMode::kNavigate,
146 origin, nullptr, CorsFlag::Unset));
147
148 // CORS flag is true, same-origin request
149 EXPECT_EQ(FetchResponseType::kCors,
150 cors::CalculateResponseTainting(same_origin_url, RequestMode::kCors,
151 origin, nullptr, CorsFlag::Set));
152 EXPECT_EQ(FetchResponseType::kCors,
153 cors::CalculateResponseTainting(
154 same_origin_url, RequestMode::kCorsWithForcedPreflight, origin,
155 nullptr, CorsFlag::Set));
156
157 // CORS flag is true, cross-origin request
158 EXPECT_EQ(FetchResponseType::kCors, cors::CalculateResponseTainting(
159 cross_origin_url, RequestMode::kCors,
160 origin, nullptr, CorsFlag::Set));
161 EXPECT_EQ(FetchResponseType::kCors,
162 cors::CalculateResponseTainting(
163 cross_origin_url, RequestMode::kCorsWithForcedPreflight, origin,
164 nullptr, CorsFlag::Set));
165
166 // Origin is not provided.
167 EXPECT_EQ(
168 FetchResponseType::kBasic,
169 cors::CalculateResponseTainting(same_origin_url, RequestMode::kNoCors,
170 no_origin, nullptr, CorsFlag::Unset));
171 EXPECT_EQ(
172 FetchResponseType::kBasic,
173 cors::CalculateResponseTainting(same_origin_url, RequestMode::kNavigate,
174 no_origin, nullptr, CorsFlag::Unset));
175 EXPECT_EQ(
176 FetchResponseType::kBasic,
177 cors::CalculateResponseTainting(cross_origin_url, RequestMode::kNoCors,
178 no_origin, nullptr, CorsFlag::Unset));
179 EXPECT_EQ(
180 FetchResponseType::kBasic,
181 cors::CalculateResponseTainting(cross_origin_url, RequestMode::kNavigate,
182 no_origin, nullptr, CorsFlag::Unset));
183 }
184
185 } // namespace
186
187 } // namespace blink
188