1 // Copyright 2017 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 "services/network/content_security_policy/csp_source.h"
6 #include "services/network/content_security_policy/csp_context.h"
7 #include "testing/gtest/include/gtest/gtest.h"
8
9 namespace content {
10
11 namespace {
12
13 // Allow() is an abbreviation of CSPSource::Allow(). Useful for writing test
14 // expectations on one line.
Allow(const network::mojom::CSPSourcePtr & source,const GURL & url,CSPContext * context,bool is_redirect=false)15 bool Allow(const network::mojom::CSPSourcePtr& source,
16 const GURL& url,
17 CSPContext* context,
18 bool is_redirect = false) {
19 return CheckCSPSource(source, url, context, is_redirect);
20 }
21
22 } // namespace
23
TEST(CSPSourceTest,BasicMatching)24 TEST(CSPSourceTest, BasicMatching) {
25 CSPContext context;
26
27 auto source = network::mojom::CSPSource::New("http", "example.com", 8000,
28 "/foo/", false, false);
29
30 EXPECT_TRUE(Allow(source, GURL("http://example.com:8000/foo/"), &context));
31 EXPECT_TRUE(Allow(source, GURL("http://example.com:8000/foo/bar"), &context));
32 EXPECT_TRUE(Allow(source, GURL("HTTP://EXAMPLE.com:8000/foo/BAR"), &context));
33
34 EXPECT_FALSE(Allow(source, GURL("http://example.com:8000/bar/"), &context));
35 EXPECT_FALSE(Allow(source, GURL("https://example.com:8000/bar/"), &context));
36 EXPECT_FALSE(Allow(source, GURL("http://example.com:9000/bar/"), &context));
37 EXPECT_FALSE(
38 Allow(source, GURL("HTTP://example.com:8000/FOO/bar"), &context));
39 EXPECT_FALSE(
40 Allow(source, GURL("HTTP://example.com:8000/FOO/BAR"), &context));
41 }
42
TEST(CSPSourceTest,AllowScheme)43 TEST(CSPSourceTest, AllowScheme) {
44 CSPContext context;
45
46 // http -> {http, https}.
47 {
48 auto source = network::mojom::CSPSource::New(
49 "http", "", url::PORT_UNSPECIFIED, "", false, false);
50 EXPECT_TRUE(Allow(source, GURL("http://a.com"), &context));
51 EXPECT_TRUE(Allow(source, GURL("https://a.com"), &context));
52 // This passes because the source is "scheme only" so the upgrade is
53 // allowed.
54 EXPECT_TRUE(Allow(source, GURL("https://a.com:80"), &context));
55 EXPECT_FALSE(Allow(source, GURL("ftp://a.com"), &context));
56 EXPECT_FALSE(Allow(source, GURL("ws://a.com"), &context));
57 EXPECT_FALSE(Allow(source, GURL("wss://a.com"), &context));
58 }
59
60 // ws -> {ws, wss}.
61 {
62 auto source = network::mojom::CSPSource::New(
63 "ws", "", url::PORT_UNSPECIFIED, "", false, false);
64 EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
65 EXPECT_FALSE(Allow(source, GURL("https://a.com"), &context));
66 EXPECT_FALSE(Allow(source, GURL("ftp://a.com"), &context));
67 EXPECT_TRUE(Allow(source, GURL("ws://a.com"), &context));
68 EXPECT_TRUE(Allow(source, GURL("wss://a.com"), &context));
69 }
70
71 // Exact matches required (ftp)
72 {
73 auto source = network::mojom::CSPSource::New(
74 "ftp", "", url::PORT_UNSPECIFIED, "", false, false);
75 EXPECT_TRUE(Allow(source, GURL("ftp://a.com"), &context));
76 EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
77 }
78
79 // Exact matches required (https)
80 {
81 auto source = network::mojom::CSPSource::New(
82 "https", "", url::PORT_UNSPECIFIED, "", false, false);
83 EXPECT_TRUE(Allow(source, GURL("https://a.com"), &context));
84 EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
85 }
86
87 // Exact matches required (wss)
88 {
89 auto source = network::mojom::CSPSource::New(
90 "wss", "", url::PORT_UNSPECIFIED, "", false, false);
91 EXPECT_TRUE(Allow(source, GURL("wss://a.com"), &context));
92 EXPECT_FALSE(Allow(source, GURL("ws://a.com"), &context));
93 }
94
95 // Scheme is empty (ProtocolMatchesSelf).
96 {
97 auto source = network::mojom::CSPSource::New(
98 "", "a.com", url::PORT_UNSPECIFIED, "", false, false);
99 EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
100
101 // Self's scheme is http.
102 context.SetSelf(url::Origin::Create(GURL("http://a.com")));
103 EXPECT_TRUE(Allow(source, GURL("http://a.com"), &context));
104 EXPECT_TRUE(Allow(source, GURL("https://a.com"), &context));
105 EXPECT_FALSE(Allow(source, GURL("ftp://a.com"), &context));
106
107 // Self's is https.
108 context.SetSelf(url::Origin::Create(GURL("https://a.com")));
109 EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
110 EXPECT_TRUE(Allow(source, GURL("https://a.com"), &context));
111 EXPECT_FALSE(Allow(source, GURL("ftp://a.com"), &context));
112
113 // Self's scheme is not in the http familly.
114 context.SetSelf(url::Origin::Create(GURL("ftp://a.com/")));
115 EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
116 EXPECT_TRUE(Allow(source, GURL("ftp://a.com"), &context));
117
118 // Self's scheme is unique (non standard scheme).
119 context.SetSelf(url::Origin::Create(GURL("non-standard-scheme://a.com")));
120 EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
121 EXPECT_FALSE(Allow(source, GURL("non-standard-scheme://a.com"), &context));
122
123 // Self's scheme is unique (data-url).
124 context.SetSelf(
125 url::Origin::Create(GURL("data:text/html,<iframe src=[...]>")));
126 EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
127 EXPECT_FALSE(Allow(source, GURL("data:text/html,hello"), &context));
128 }
129 }
130
TEST(CSPSourceTest,AllowHost)131 TEST(CSPSourceTest, AllowHost) {
132 CSPContext context;
133 context.SetSelf(url::Origin::Create(GURL("http://example.com")));
134
135 // Host is * (source-expression = "http://*")
136 {
137 auto source = network::mojom::CSPSource::New(
138 "http", "", url::PORT_UNSPECIFIED, "", true, false);
139 EXPECT_TRUE(Allow(source, GURL("http://a.com"), &context));
140 EXPECT_TRUE(Allow(source, GURL("http://."), &context));
141 }
142
143 // Host is *.foo.bar
144 {
145 auto source = network::mojom::CSPSource::New(
146 "", "foo.bar", url::PORT_UNSPECIFIED, "", true, false);
147 EXPECT_FALSE(Allow(source, GURL("http://a.com"), &context));
148 EXPECT_FALSE(Allow(source, GURL("http://bar"), &context));
149 EXPECT_FALSE(Allow(source, GURL("http://foo.bar"), &context));
150 EXPECT_FALSE(Allow(source, GURL("http://o.bar"), &context));
151 EXPECT_TRUE(Allow(source, GURL("http://*.foo.bar"), &context));
152 EXPECT_TRUE(Allow(source, GURL("http://sub.foo.bar"), &context));
153 EXPECT_TRUE(Allow(source, GURL("http://sub.sub.foo.bar"), &context));
154 // Please see http://crbug.com/692505
155 EXPECT_TRUE(Allow(source, GURL("http://.foo.bar"), &context));
156 }
157
158 // Host is exact.
159 {
160 auto source = network::mojom::CSPSource::New(
161 "", "foo.bar", url::PORT_UNSPECIFIED, "", false, false);
162 EXPECT_TRUE(Allow(source, GURL("http://foo.bar"), &context));
163 EXPECT_FALSE(Allow(source, GURL("http://sub.foo.bar"), &context));
164 EXPECT_FALSE(Allow(source, GURL("http://bar"), &context));
165 // Please see http://crbug.com/692505
166 EXPECT_FALSE(Allow(source, GURL("http://.foo.bar"), &context));
167 }
168 }
169
TEST(CSPSourceTest,AllowPort)170 TEST(CSPSourceTest, AllowPort) {
171 CSPContext context;
172 context.SetSelf(url::Origin::Create(GURL("http://example.com")));
173
174 // Source's port unspecified.
175 {
176 auto source = network::mojom::CSPSource::New(
177 "", "a.com", url::PORT_UNSPECIFIED, "", false, false);
178 EXPECT_TRUE(Allow(source, GURL("http://a.com:80"), &context));
179 EXPECT_FALSE(Allow(source, GURL("http://a.com:8080"), &context));
180 EXPECT_FALSE(Allow(source, GURL("http://a.com:443"), &context));
181 EXPECT_FALSE(Allow(source, GURL("https://a.com:80"), &context));
182 EXPECT_FALSE(Allow(source, GURL("https://a.com:8080"), &context));
183 EXPECT_TRUE(Allow(source, GURL("https://a.com:443"), &context));
184 EXPECT_FALSE(Allow(source, GURL("unknown://a.com:80"), &context));
185 EXPECT_TRUE(Allow(source, GURL("http://a.com"), &context));
186 EXPECT_TRUE(Allow(source, GURL("http://a.com"), &context));
187 EXPECT_TRUE(Allow(source, GURL("https://a.com"), &context));
188 }
189
190 // Source's port is "*".
191 {
192 auto source = network::mojom::CSPSource::New(
193 "", "a.com", url::PORT_UNSPECIFIED, "", false, true);
194 EXPECT_TRUE(Allow(source, GURL("http://a.com"), &context));
195 EXPECT_TRUE(Allow(source, GURL("http://a.com:80"), &context));
196 EXPECT_TRUE(Allow(source, GURL("http://a.com:8080"), &context));
197 EXPECT_TRUE(Allow(source, GURL("https://a.com:8080"), &context));
198 EXPECT_TRUE(Allow(source, GURL("https://a.com:0"), &context));
199 EXPECT_TRUE(Allow(source, GURL("https://a.com"), &context));
200 }
201
202 // Source has a port.
203 {
204 auto source =
205 network::mojom::CSPSource::New("", "a.com", 80, "", false, false);
206 EXPECT_TRUE(Allow(source, GURL("http://a.com:80"), &context));
207 EXPECT_TRUE(Allow(source, GURL("http://a.com"), &context));
208 EXPECT_FALSE(Allow(source, GURL("http://a.com:8080"), &context));
209 EXPECT_TRUE(Allow(source, GURL("https://a.com"), &context));
210 }
211
212 // Allow upgrade from :80 to :443
213 {
214 auto source =
215 network::mojom::CSPSource::New("", "a.com", 80, "", false, false);
216 EXPECT_TRUE(Allow(source, GURL("https://a.com:443"), &context));
217 // Should not allow scheme upgrades unless both port and scheme are
218 // upgraded.
219 EXPECT_FALSE(Allow(source, GURL("http://a.com:443"), &context));
220 }
221
222 // Host is * but port is specified
223 {
224 auto source =
225 network::mojom::CSPSource::New("http", "", 111, "", true, false);
226 EXPECT_TRUE(Allow(source, GURL("http://a.com:111"), &context));
227 EXPECT_FALSE(Allow(source, GURL("http://a.com:222"), &context));
228 }
229 }
230
TEST(CSPSourceTest,AllowPath)231 TEST(CSPSourceTest, AllowPath) {
232 CSPContext context;
233 context.SetSelf(url::Origin::Create(GURL("http://example.com")));
234
235 // Path to a file
236 {
237 auto source = network::mojom::CSPSource::New(
238 "", "a.com", url::PORT_UNSPECIFIED, "/path/to/file", false, false);
239 EXPECT_TRUE(Allow(source, GURL("http://a.com/path/to/file"), &context));
240 EXPECT_FALSE(Allow(source, GURL("http://a.com/path/to/"), &context));
241 EXPECT_FALSE(
242 Allow(source, GURL("http://a.com/path/to/file/subpath"), &context));
243 EXPECT_FALSE(
244 Allow(source, GURL("http://a.com/path/to/something"), &context));
245 }
246
247 // Path to a directory
248 {
249 auto source = network::mojom::CSPSource::New(
250 "", "a.com", url::PORT_UNSPECIFIED, "/path/to/", false, false);
251 EXPECT_TRUE(Allow(source, GURL("http://a.com/path/to/file"), &context));
252 EXPECT_TRUE(Allow(source, GURL("http://a.com/path/to/"), &context));
253 EXPECT_FALSE(Allow(source, GURL("http://a.com/path/"), &context));
254 EXPECT_FALSE(Allow(source, GURL("http://a.com/path/to"), &context));
255 EXPECT_FALSE(Allow(source, GURL("http://a.com/path/to"), &context));
256 }
257
258 // Empty path
259 {
260 auto source = network::mojom::CSPSource::New(
261 "", "a.com", url::PORT_UNSPECIFIED, "", false, false);
262 EXPECT_TRUE(Allow(source, GURL("http://a.com/path/to/file"), &context));
263 EXPECT_TRUE(Allow(source, GURL("http://a.com/path/to/"), &context));
264 EXPECT_TRUE(Allow(source, GURL("http://a.com/"), &context));
265 EXPECT_TRUE(Allow(source, GURL("http://a.com"), &context));
266 }
267
268 // Almost empty path
269 {
270 auto source = network::mojom::CSPSource::New(
271 "", "a.com", url::PORT_UNSPECIFIED, "/", false, false);
272 EXPECT_TRUE(Allow(source, GURL("http://a.com/path/to/file"), &context));
273 EXPECT_TRUE(Allow(source, GURL("http://a.com/path/to/"), &context));
274 EXPECT_TRUE(Allow(source, GURL("http://a.com/"), &context));
275 EXPECT_TRUE(Allow(source, GURL("http://a.com"), &context));
276 }
277
278 // Path encoded.
279 {
280 auto source = network::mojom::CSPSource::New(
281 "http", "a.com", url::PORT_UNSPECIFIED, "/Hello Günter", false, false);
282 EXPECT_TRUE(
283 Allow(source, GURL("http://a.com/Hello%20G%C3%BCnter"), &context));
284 EXPECT_TRUE(Allow(source, GURL("http://a.com/Hello Günter"), &context));
285 }
286
287 // Host is * but path is specified.
288 {
289 auto source = network::mojom::CSPSource::New(
290 "http", "", url::PORT_UNSPECIFIED, "/allowed-path", true, false);
291 EXPECT_TRUE(Allow(source, GURL("http://a.com/allowed-path"), &context));
292 EXPECT_FALSE(Allow(source, GURL("http://a.com/disallowed-path"), &context));
293 }
294 }
295
TEST(CSPSourceTest,RedirectMatching)296 TEST(CSPSourceTest, RedirectMatching) {
297 CSPContext context;
298 auto source = network::mojom::CSPSource::New("http", "a.com", 8000, "/bar/",
299 false, false);
300 EXPECT_TRUE(Allow(source, GURL("http://a.com:8000/"), &context, true));
301 EXPECT_TRUE(Allow(source, GURL("http://a.com:8000/foo"), &context, true));
302 EXPECT_FALSE(Allow(source, GURL("https://a.com:8000/foo"), &context, true));
303 EXPECT_FALSE(
304 Allow(source, GURL("http://not-a.com:8000/foo"), &context, true));
305 EXPECT_FALSE(Allow(source, GURL("http://a.com:9000/foo/"), &context, false));
306 }
307
TEST(CSPSourceTest,ToString)308 TEST(CSPSourceTest, ToString) {
309 {
310 auto source = network::mojom::CSPSource::New(
311 "http", "", url::PORT_UNSPECIFIED, "", false, false);
312 EXPECT_EQ("http:", ToString(source));
313 }
314 {
315 auto source = network::mojom::CSPSource::New(
316 "http", "a.com", url::PORT_UNSPECIFIED, "", false, false);
317 EXPECT_EQ("http://a.com", ToString(source));
318 }
319 {
320 auto source = network::mojom::CSPSource::New(
321 "", "a.com", url::PORT_UNSPECIFIED, "", false, false);
322 EXPECT_EQ("a.com", ToString(source));
323 }
324 {
325 auto source = network::mojom::CSPSource::New(
326 "", "a.com", url::PORT_UNSPECIFIED, "", true, false);
327 EXPECT_EQ("*.a.com", ToString(source));
328 }
329 {
330 auto source = network::mojom::CSPSource::New("", "", url::PORT_UNSPECIFIED,
331 "", true, false);
332 EXPECT_EQ("*", ToString(source));
333 }
334 {
335 auto source =
336 network::mojom::CSPSource::New("", "a.com", 80, "", false, false);
337 EXPECT_EQ("a.com:80", ToString(source));
338 }
339 {
340 auto source = network::mojom::CSPSource::New(
341 "", "a.com", url::PORT_UNSPECIFIED, "", false, true);
342 EXPECT_EQ("a.com:*", ToString(source));
343 }
344 {
345 auto source = network::mojom::CSPSource::New(
346 "", "a.com", url::PORT_UNSPECIFIED, "/path", false, false);
347 EXPECT_EQ("a.com/path", ToString(source));
348 }
349 }
350
TEST(CSPSourceTest,UpgradeRequests)351 TEST(CSPSourceTest, UpgradeRequests) {
352 CSPContext context;
353 auto source =
354 network::mojom::CSPSource::New("http", "a.com", 80, "", false, false);
355 EXPECT_TRUE(Allow(source, GURL("http://a.com:80"), &context, true));
356 EXPECT_FALSE(Allow(source, GURL("https://a.com:80"), &context, true));
357 EXPECT_FALSE(Allow(source, GURL("http://a.com:443"), &context, true));
358 EXPECT_TRUE(Allow(source, GURL("https://a.com:443"), &context, true));
359 EXPECT_TRUE(Allow(source, GURL("https://a.com"), &context, true));
360 }
361
362 } // namespace content
363