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