1 // Copyright 2013 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 <stddef.h>
6 
7 #include "base/strings/string_split.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/stringprintf.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "extensions/common/csp_validator.h"
12 #include "extensions/common/error_utils.h"
13 #include "extensions/common/install_warning.h"
14 #include "extensions/common/manifest_constants.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 using extensions::csp_validator::ContentSecurityPolicyIsLegal;
18 using extensions::csp_validator::GetEffectiveSandoxedPageCSP;
19 using extensions::csp_validator::SanitizeContentSecurityPolicy;
20 using extensions::csp_validator::ContentSecurityPolicyIsSandboxed;
21 using extensions::csp_validator::OPTIONS_NONE;
22 using extensions::csp_validator::OPTIONS_ALLOW_UNSAFE_EVAL;
23 using extensions::csp_validator::OPTIONS_ALLOW_INSECURE_OBJECT_SRC;
24 using extensions::ErrorUtils;
25 using extensions::InstallWarning;
26 using extensions::Manifest;
27 
28 namespace {
29 
InsecureValueWarning(const std::string & directive,const std::string & value,const std::string & manifest_key=extensions::manifest_keys::kContentSecurityPolicy)30 std::string InsecureValueWarning(
31     const std::string& directive,
32     const std::string& value,
33     const std::string& manifest_key =
34         extensions::manifest_keys::kContentSecurityPolicy) {
35   return ErrorUtils::FormatErrorMessage(
36       extensions::manifest_errors::kInvalidCSPInsecureValueIgnored,
37       manifest_key, value, directive);
38 }
39 
MissingSecureSrcWarning(const std::string & manifest_key,const std::string & directive)40 std::string MissingSecureSrcWarning(const std::string& manifest_key,
41                                     const std::string& directive) {
42   return ErrorUtils::FormatErrorMessage(
43       extensions::manifest_errors::kInvalidCSPMissingSecureSrc, manifest_key,
44       directive);
45 }
46 
CSPEquals(const std::string & csp1,const std::string & csp2)47 bool CSPEquals(const std::string& csp1, const std::string& csp2) {
48   std::vector<std::string> csp1_parts = base::SplitString(
49       csp1, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
50   std::sort(csp1_parts.begin(), csp1_parts.end());
51   std::vector<std::string> csp2_parts = base::SplitString(
52       csp2, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
53   std::sort(csp2_parts.begin(), csp2_parts.end());
54   return csp1_parts == csp2_parts;
55 }
56 
57 struct SanitizedCSPResult {
58   std::string csp;
59   std::vector<InstallWarning> warnings;
60 };
61 
SanitizeCSP(const std::string & policy,int options)62 SanitizedCSPResult SanitizeCSP(const std::string& policy, int options) {
63   SanitizedCSPResult result;
64   result.csp = SanitizeContentSecurityPolicy(
65       policy, extensions::manifest_keys::kContentSecurityPolicy, options,
66       &result.warnings);
67   return result;
68 }
69 
SanitizeSandboxPageCSP(const std::string & policy)70 SanitizedCSPResult SanitizeSandboxPageCSP(const std::string& policy) {
71   SanitizedCSPResult result;
72   result.csp = GetEffectiveSandoxedPageCSP(
73       policy, extensions::manifest_keys::kSandboxedPagesCSP, &result.warnings);
74   return result;
75 }
76 
CheckCSP(const SanitizedCSPResult & actual,const std::string & expected_csp,const std::vector<std::string> & expected_warnings)77 testing::AssertionResult CheckCSP(
78     const SanitizedCSPResult& actual,
79     const std::string& expected_csp,
80     const std::vector<std::string>& expected_warnings) {
81   if (!CSPEquals(expected_csp, actual.csp)) {
82     return testing::AssertionFailure()
83            << "SanitizeContentSecurityPolicy returned an unexpected CSP.\n"
84            << "Expected CSP: " << expected_csp << "\n"
85            << "  Actual CSP: " << actual.csp;
86   }
87 
88   if (expected_warnings.size() != actual.warnings.size()) {
89     testing::Message msg;
90     msg << "Expected " << expected_warnings.size() << " warnings, but got "
91         << actual.warnings.size();
92     for (size_t i = 0; i < actual.warnings.size(); ++i)
93       msg << "\nWarning " << i << " " << actual.warnings[i].message;
94     return testing::AssertionFailure() << msg;
95   }
96 
97   for (size_t i = 0; i < expected_warnings.size(); ++i) {
98     if (expected_warnings[i] != actual.warnings[i].message)
99       return testing::AssertionFailure()
100              << "Unexpected warning from SanitizeContentSecurityPolicy.\n"
101              << "Expected warning[" << i << "]: " << expected_warnings[i]
102              << "  Actual warning[" << i << "]: " << actual.warnings[i].message;
103   }
104   return testing::AssertionSuccess();
105 }
106 
CheckCSP(const SanitizedCSPResult & actual)107 testing::AssertionResult CheckCSP(const SanitizedCSPResult& actual) {
108   return CheckCSP(actual, actual.csp, std::vector<std::string>());
109 }
110 
CheckCSP(const SanitizedCSPResult & actual,const std::string & expected_csp)111 testing::AssertionResult CheckCSP(const SanitizedCSPResult& actual,
112                                   const std::string& expected_csp) {
113   std::vector<std::string> expected_warnings;
114   return CheckCSP(actual, expected_csp, expected_warnings);
115 }
116 
CheckCSP(const SanitizedCSPResult & actual,const std::string & expected_csp,const std::string & warning1)117 testing::AssertionResult CheckCSP(const SanitizedCSPResult& actual,
118                                   const std::string& expected_csp,
119                                   const std::string& warning1) {
120   std::vector<std::string> expected_warnings(1, warning1);
121   return CheckCSP(actual, expected_csp, expected_warnings);
122 }
123 
CheckCSP(const SanitizedCSPResult & actual,const std::string & expected_csp,const std::string & warning1,const std::string & warning2)124 testing::AssertionResult CheckCSP(const SanitizedCSPResult& actual,
125                                   const std::string& expected_csp,
126                                   const std::string& warning1,
127                                   const std::string& warning2) {
128   std::vector<std::string> expected_warnings(1, warning1);
129   expected_warnings.push_back(warning2);
130   return CheckCSP(actual, expected_csp, expected_warnings);
131 }
132 
CheckCSP(const SanitizedCSPResult & actual,const std::string & expected_csp,const std::string & warning1,const std::string & warning2,const std::string & warning3)133 testing::AssertionResult CheckCSP(const SanitizedCSPResult& actual,
134                                   const std::string& expected_csp,
135                                   const std::string& warning1,
136                                   const std::string& warning2,
137                                   const std::string& warning3) {
138   std::vector<std::string> expected_warnings(1, warning1);
139   expected_warnings.push_back(warning2);
140   expected_warnings.push_back(warning3);
141   return CheckCSP(actual, expected_csp, expected_warnings);
142 }
143 
144 }  // namespace
145 
TEST(ExtensionCSPValidator,IsLegal)146 TEST(ExtensionCSPValidator, IsLegal) {
147   EXPECT_TRUE(ContentSecurityPolicyIsLegal("foo"));
148   EXPECT_TRUE(ContentSecurityPolicyIsLegal(
149       "default-src 'self'; script-src http://www.google.com"));
150   EXPECT_FALSE(ContentSecurityPolicyIsLegal(
151       "default-src 'self';\nscript-src http://www.google.com"));
152   EXPECT_FALSE(ContentSecurityPolicyIsLegal(
153       "default-src 'self';\rscript-src http://www.google.com"));
154   EXPECT_FALSE(ContentSecurityPolicyIsLegal(
155       "default-src 'self';,script-src http://www.google.com"));
156 }
157 
TEST(ExtensionCSPValidator,IsSecure)158 TEST(ExtensionCSPValidator, IsSecure) {
159   auto missing_secure_src_warning = [](const std::string& directive) {
160     return MissingSecureSrcWarning(
161         extensions::manifest_keys::kContentSecurityPolicy, directive);
162   };
163 
164   EXPECT_TRUE(CheckCSP(SanitizeCSP(std::string(), OPTIONS_ALLOW_UNSAFE_EVAL),
165                        "script-src 'self'; object-src 'self';",
166                        missing_secure_src_warning("script-src"),
167                        missing_secure_src_warning("object-src")));
168   EXPECT_TRUE(CheckCSP(
169       SanitizeCSP("img-src https://google.com", OPTIONS_ALLOW_UNSAFE_EVAL),
170       "img-src https://google.com; script-src 'self'; object-src 'self';",
171       missing_secure_src_warning("script-src"),
172       missing_secure_src_warning("object-src")));
173   EXPECT_TRUE(CheckCSP(SanitizeCSP("script-src a b", OPTIONS_ALLOW_UNSAFE_EVAL),
174                        "script-src; object-src 'self';",
175                        InsecureValueWarning("script-src", "a"),
176                        InsecureValueWarning("script-src", "b"),
177                        missing_secure_src_warning("object-src")));
178 
179   EXPECT_TRUE(CheckCSP(SanitizeCSP("default-src *", OPTIONS_ALLOW_UNSAFE_EVAL),
180                        "default-src;",
181                        InsecureValueWarning("default-src", "*")));
182   EXPECT_TRUE(CheckCSP(SanitizeCSP(
183       "default-src 'self';", OPTIONS_ALLOW_UNSAFE_EVAL)));
184   EXPECT_TRUE(CheckCSP(SanitizeCSP(
185       "default-src 'none';", OPTIONS_ALLOW_UNSAFE_EVAL)));
186   EXPECT_TRUE(
187       CheckCSP(SanitizeCSP("default-src 'self' ftp://google.com",
188                            OPTIONS_ALLOW_UNSAFE_EVAL),
189                "default-src 'self';",
190                InsecureValueWarning("default-src", "ftp://google.com")));
191   EXPECT_TRUE(CheckCSP(SanitizeCSP(
192       "default-src 'self' https://google.com;", OPTIONS_ALLOW_UNSAFE_EVAL)));
193 
194   EXPECT_TRUE(CheckCSP(SanitizeCSP("default-src *; default-src 'self'",
195                                    OPTIONS_ALLOW_UNSAFE_EVAL),
196                        "default-src; default-src 'self';",
197                        InsecureValueWarning("default-src", "*")));
198   EXPECT_TRUE(CheckCSP(SanitizeCSP(
199       "default-src 'self'; default-src *;", OPTIONS_ALLOW_UNSAFE_EVAL),
200       "default-src 'self'; default-src;"));
201   EXPECT_TRUE(CheckCSP(
202       SanitizeCSP(
203           "default-src 'self'; default-src *; script-src *; script-src 'self'",
204           OPTIONS_ALLOW_UNSAFE_EVAL),
205       "default-src 'self'; default-src; script-src; script-src 'self';",
206       InsecureValueWarning("script-src", "*")));
207   EXPECT_TRUE(CheckCSP(SanitizeCSP(
208       "default-src 'self'; default-src *; script-src 'self'; script-src *;",
209       OPTIONS_ALLOW_UNSAFE_EVAL),
210       "default-src 'self'; default-src; script-src 'self'; script-src;"));
211   EXPECT_TRUE(CheckCSP(SanitizeCSP("default-src *; script-src 'self'",
212                                    OPTIONS_ALLOW_UNSAFE_EVAL),
213                        "default-src; script-src 'self';",
214                        InsecureValueWarning("default-src", "*")));
215   EXPECT_TRUE(
216       CheckCSP(SanitizeCSP("default-src *; script-src 'self'; img-src 'self'",
217                            OPTIONS_ALLOW_UNSAFE_EVAL),
218                "default-src; script-src 'self'; img-src 'self';",
219                InsecureValueWarning("default-src", "*")));
220   EXPECT_TRUE(CheckCSP(SanitizeCSP(
221       "default-src *; script-src 'self'; object-src 'self';",
222       OPTIONS_ALLOW_UNSAFE_EVAL),
223       "default-src; script-src 'self'; object-src 'self';"));
224   EXPECT_TRUE(CheckCSP(SanitizeCSP(
225       "script-src 'self'; object-src 'self';", OPTIONS_ALLOW_UNSAFE_EVAL)));
226   EXPECT_TRUE(CheckCSP(SanitizeCSP(
227       "default-src 'unsafe-eval';", OPTIONS_ALLOW_UNSAFE_EVAL)));
228 
229   EXPECT_TRUE(CheckCSP(SanitizeCSP("default-src 'unsafe-eval'", OPTIONS_NONE),
230                        "default-src;",
231                        InsecureValueWarning("default-src", "'unsafe-eval'")));
232   EXPECT_TRUE(CheckCSP(
233       SanitizeCSP("default-src 'unsafe-inline'", OPTIONS_ALLOW_UNSAFE_EVAL),
234       "default-src;", InsecureValueWarning("default-src", "'unsafe-inline'")));
235   EXPECT_TRUE(CheckCSP(SanitizeCSP("default-src 'unsafe-inline' 'none'",
236                                    OPTIONS_ALLOW_UNSAFE_EVAL),
237                        "default-src 'none';",
238                        InsecureValueWarning("default-src", "'unsafe-inline'")));
239   EXPECT_TRUE(
240       CheckCSP(SanitizeCSP("default-src 'self' http://google.com",
241                            OPTIONS_ALLOW_UNSAFE_EVAL),
242                "default-src 'self';",
243                InsecureValueWarning("default-src", "http://google.com")));
244   EXPECT_TRUE(CheckCSP(SanitizeCSP(
245       "default-src 'self' https://google.com;", OPTIONS_ALLOW_UNSAFE_EVAL)));
246   EXPECT_TRUE(CheckCSP(SanitizeCSP(
247       "default-src 'self' chrome://resources;", OPTIONS_ALLOW_UNSAFE_EVAL)));
248   EXPECT_TRUE(CheckCSP(SanitizeCSP(
249       "default-src 'self' chrome-extension://aabbcc;",
250       OPTIONS_ALLOW_UNSAFE_EVAL)));
251   EXPECT_TRUE(
252       CheckCSP(SanitizeCSP("default-src 'self';", OPTIONS_ALLOW_UNSAFE_EVAL)));
253   EXPECT_TRUE(CheckCSP(
254       SanitizeCSP("default-src 'self' https:", OPTIONS_ALLOW_UNSAFE_EVAL),
255       "default-src 'self';", InsecureValueWarning("default-src", "https:")));
256   EXPECT_TRUE(CheckCSP(
257       SanitizeCSP("default-src 'self' http:", OPTIONS_ALLOW_UNSAFE_EVAL),
258       "default-src 'self';", InsecureValueWarning("default-src", "http:")));
259   EXPECT_TRUE(CheckCSP(
260       SanitizeCSP("default-src 'self' google.com", OPTIONS_ALLOW_UNSAFE_EVAL),
261       "default-src 'self';",
262       InsecureValueWarning("default-src", "google.com")));
263 
264   EXPECT_TRUE(CheckCSP(
265       SanitizeCSP("default-src 'self' *", OPTIONS_ALLOW_UNSAFE_EVAL),
266       "default-src 'self';", InsecureValueWarning("default-src", "*")));
267   EXPECT_TRUE(CheckCSP(
268       SanitizeCSP("default-src 'self' *:*", OPTIONS_ALLOW_UNSAFE_EVAL),
269       "default-src 'self';", InsecureValueWarning("default-src", "*:*")));
270   EXPECT_TRUE(CheckCSP(
271       SanitizeCSP("default-src 'self' *:*/", OPTIONS_ALLOW_UNSAFE_EVAL),
272       "default-src 'self';", InsecureValueWarning("default-src", "*:*/")));
273   EXPECT_TRUE(CheckCSP(
274       SanitizeCSP("default-src 'self' *:*/path", OPTIONS_ALLOW_UNSAFE_EVAL),
275       "default-src 'self';", InsecureValueWarning("default-src", "*:*/path")));
276   EXPECT_TRUE(CheckCSP(
277       SanitizeCSP("default-src 'self' https://", OPTIONS_ALLOW_UNSAFE_EVAL),
278       "default-src 'self';", InsecureValueWarning("default-src", "https://")));
279   EXPECT_TRUE(CheckCSP(
280       SanitizeCSP("default-src 'self' https://*:*", OPTIONS_ALLOW_UNSAFE_EVAL),
281       "default-src 'self';",
282       InsecureValueWarning("default-src", "https://*:*")));
283   EXPECT_TRUE(CheckCSP(
284       SanitizeCSP("default-src 'self' https://*:*/", OPTIONS_ALLOW_UNSAFE_EVAL),
285       "default-src 'self';",
286       InsecureValueWarning("default-src", "https://*:*/")));
287   EXPECT_TRUE(
288       CheckCSP(SanitizeCSP("default-src 'self' https://*:*/path",
289                            OPTIONS_ALLOW_UNSAFE_EVAL),
290                "default-src 'self';",
291                InsecureValueWarning("default-src", "https://*:*/path")));
292   EXPECT_TRUE(CheckCSP(SanitizeCSP("default-src 'self' https://*.com",
293                                    OPTIONS_ALLOW_UNSAFE_EVAL),
294                        "default-src 'self';",
295                        InsecureValueWarning("default-src", "https://*.com")));
296   EXPECT_TRUE(
297       CheckCSP(SanitizeCSP("default-src 'self' https://*.*.google.com/",
298                            OPTIONS_ALLOW_UNSAFE_EVAL),
299                "default-src 'self';",
300                InsecureValueWarning("default-src", "https://*.*.google.com/")));
301   EXPECT_TRUE(CheckCSP(
302       SanitizeCSP("default-src 'self' https://*.*.google.com:*/",
303                   OPTIONS_ALLOW_UNSAFE_EVAL),
304       "default-src 'self';",
305       InsecureValueWarning("default-src", "https://*.*.google.com:*/")));
306   EXPECT_TRUE(CheckCSP(
307       SanitizeCSP("default-src 'self' https://www.*.google.com/",
308                   OPTIONS_ALLOW_UNSAFE_EVAL),
309       "default-src 'self';",
310       InsecureValueWarning("default-src", "https://www.*.google.com/")));
311   EXPECT_TRUE(CheckCSP(
312       SanitizeCSP("default-src 'self' https://www.*.google.com:*/",
313                   OPTIONS_ALLOW_UNSAFE_EVAL),
314       "default-src 'self';",
315       InsecureValueWarning("default-src", "https://www.*.google.com:*/")));
316   EXPECT_TRUE(CheckCSP(
317       SanitizeCSP("default-src 'self' chrome://*", OPTIONS_ALLOW_UNSAFE_EVAL),
318       "default-src 'self';",
319       InsecureValueWarning("default-src", "chrome://*")));
320   EXPECT_TRUE(
321       CheckCSP(SanitizeCSP("default-src 'self' chrome-extension://*",
322                            OPTIONS_ALLOW_UNSAFE_EVAL),
323                "default-src 'self';",
324                InsecureValueWarning("default-src", "chrome-extension://*")));
325   EXPECT_TRUE(
326       CheckCSP(SanitizeCSP("default-src 'self' chrome-extension://",
327                            OPTIONS_ALLOW_UNSAFE_EVAL),
328                "default-src 'self';",
329                InsecureValueWarning("default-src", "chrome-extension://")));
330 
331   EXPECT_TRUE(CheckCSP(SanitizeCSP(
332       "default-src 'self' https://*.google.com;", OPTIONS_ALLOW_UNSAFE_EVAL)));
333   EXPECT_TRUE(CheckCSP(SanitizeCSP(
334       "default-src 'self' https://*.google.com:1;",
335       OPTIONS_ALLOW_UNSAFE_EVAL)));
336   EXPECT_TRUE(CheckCSP(SanitizeCSP(
337       "default-src 'self' https://*.google.com:*;",
338       OPTIONS_ALLOW_UNSAFE_EVAL)));
339   EXPECT_TRUE(CheckCSP(SanitizeCSP(
340       "default-src 'self' https://*.google.com:1/;",
341       OPTIONS_ALLOW_UNSAFE_EVAL)));
342   EXPECT_TRUE(CheckCSP(SanitizeCSP(
343       "default-src 'self' https://*.google.com:*/;",
344       OPTIONS_ALLOW_UNSAFE_EVAL)));
345 
346   EXPECT_TRUE(CheckCSP(SanitizeCSP(
347       "default-src 'self' http://127.0.0.1;", OPTIONS_ALLOW_UNSAFE_EVAL)));
348   EXPECT_TRUE(CheckCSP(SanitizeCSP(
349       "default-src 'self' http://localhost;", OPTIONS_ALLOW_UNSAFE_EVAL)));
350   EXPECT_TRUE(CheckCSP(SanitizeCSP("default-src 'self' http://lOcAlHoSt;",
351                                OPTIONS_ALLOW_UNSAFE_EVAL),
352                                "default-src 'self' http://lOcAlHoSt;"));
353   EXPECT_TRUE(CheckCSP(SanitizeCSP(
354       "default-src 'self' http://127.0.0.1:9999;", OPTIONS_ALLOW_UNSAFE_EVAL)));
355   EXPECT_TRUE(CheckCSP(SanitizeCSP(
356       "default-src 'self' http://localhost:8888;", OPTIONS_ALLOW_UNSAFE_EVAL)));
357   EXPECT_TRUE(CheckCSP(
358       SanitizeCSP("default-src 'self' http://127.0.0.1.example.com",
359                   OPTIONS_ALLOW_UNSAFE_EVAL),
360       "default-src 'self';",
361       InsecureValueWarning("default-src", "http://127.0.0.1.example.com")));
362   EXPECT_TRUE(CheckCSP(
363       SanitizeCSP("default-src 'self' http://localhost.example.com",
364                   OPTIONS_ALLOW_UNSAFE_EVAL),
365       "default-src 'self';",
366       InsecureValueWarning("default-src", "http://localhost.example.com")));
367 
368   EXPECT_TRUE(CheckCSP(SanitizeCSP(
369       "default-src 'self' blob:;", OPTIONS_ALLOW_UNSAFE_EVAL)));
370   EXPECT_TRUE(CheckCSP(
371       SanitizeCSP("default-src 'self' blob:http://example.com/XXX",
372                   OPTIONS_ALLOW_UNSAFE_EVAL),
373       "default-src 'self';",
374       InsecureValueWarning("default-src", "blob:http://example.com/XXX")));
375   EXPECT_TRUE(CheckCSP(SanitizeCSP(
376       "default-src 'self' filesystem:;", OPTIONS_ALLOW_UNSAFE_EVAL)));
377   EXPECT_TRUE(CheckCSP(
378       SanitizeCSP("default-src 'self' filesystem:http://example.com/XX",
379                   OPTIONS_ALLOW_UNSAFE_EVAL),
380       "default-src 'self';",
381       InsecureValueWarning("default-src", "filesystem:http://example.com/XX")));
382 
383   EXPECT_TRUE(CheckCSP(SanitizeCSP(
384       "default-src 'self' https://*.googleapis.com;",
385       OPTIONS_ALLOW_UNSAFE_EVAL)));
386   EXPECT_TRUE(CheckCSP(SanitizeCSP(
387       "default-src 'self' https://x.googleapis.com;",
388       OPTIONS_ALLOW_UNSAFE_EVAL)));
389 
390   EXPECT_TRUE(
391       CheckCSP(SanitizeCSP("script-src 'self'; object-src *", OPTIONS_NONE),
392                "script-src 'self'; object-src;",
393                InsecureValueWarning("object-src", "*")));
394   EXPECT_TRUE(CheckCSP(SanitizeCSP("script-src 'self'; object-src *",
395                                    OPTIONS_ALLOW_INSECURE_OBJECT_SRC),
396                        "script-src 'self'; object-src;",
397                        InsecureValueWarning("object-src", "*")));
398   EXPECT_TRUE(CheckCSP(SanitizeCSP(
399       "script-src 'self'; object-src *; plugin-types application/pdf;",
400       OPTIONS_ALLOW_INSECURE_OBJECT_SRC)));
401   EXPECT_TRUE(CheckCSP(SanitizeCSP("script-src 'self'; object-src *; "
402                                    "plugin-types application/x-shockwave-flash",
403                                    OPTIONS_ALLOW_INSECURE_OBJECT_SRC),
404                        "script-src 'self'; object-src; "
405                        "plugin-types application/x-shockwave-flash;",
406                        InsecureValueWarning("object-src", "*")));
407   EXPECT_TRUE(CheckCSP(
408       SanitizeCSP("script-src 'self'; object-src *; "
409                   "plugin-types application/x-shockwave-flash application/pdf;",
410                   OPTIONS_ALLOW_INSECURE_OBJECT_SRC),
411       "script-src 'self'; object-src; "
412       "plugin-types application/x-shockwave-flash application/pdf;",
413       InsecureValueWarning("object-src", "*")));
414   EXPECT_TRUE(CheckCSP(SanitizeCSP(
415       "script-src 'self'; object-src http://www.example.com; "
416       "plugin-types application/pdf;",
417       OPTIONS_ALLOW_INSECURE_OBJECT_SRC)));
418   EXPECT_TRUE(CheckCSP(SanitizeCSP(
419       "object-src http://www.example.com blob:; script-src 'self'; "
420       "plugin-types application/pdf;",
421       OPTIONS_ALLOW_INSECURE_OBJECT_SRC)));
422   EXPECT_TRUE(CheckCSP(SanitizeCSP(
423       "script-src 'self'; object-src http://*.example.com; "
424       "plugin-types application/pdf;",
425       OPTIONS_ALLOW_INSECURE_OBJECT_SRC)));
426   EXPECT_TRUE(CheckCSP(
427       SanitizeCSP("script-src *; object-src *; plugin-types application/pdf;",
428                   OPTIONS_ALLOW_INSECURE_OBJECT_SRC),
429       "script-src; object-src *; plugin-types application/pdf;",
430       InsecureValueWarning("script-src", "*")));
431 
432   EXPECT_TRUE(CheckCSP(SanitizeCSP(
433       "default-src; script-src"
434       " 'sha256-hndjYvzUzy2Ykuad81Cwsl1FOXX/qYs/aDVyUyNZwBw='"
435       " 'sha384-bSVm1i3sjPBRM4TwZtYTDjk9JxZMExYHWbFmP1SxDhJH4ue0Wu9OPOkY5hcqRcS"
436       "t'"
437       " 'sha512-440MmBLtj9Kp5Bqloogn9BqGDylY8vFsv5/zXL1zH2fJVssCoskRig4gyM+9Kqw"
438       "vCSapSz5CVoUGHQcxv43UQg==';",
439       OPTIONS_NONE)));
440 
441   // Reject non-standard algorithms, even if they are still supported by Blink.
442   EXPECT_TRUE(CheckCSP(
443       SanitizeCSP(
444           "default-src; script-src 'sha1-eYyYGmKWdhpUewohaXk9o8IaLSw=';",
445           OPTIONS_NONE),
446       "default-src; script-src;",
447       InsecureValueWarning("script-src",
448                            "'sha1-eYyYGmKWdhpUewohaXk9o8IaLSw='")));
449 
450   EXPECT_TRUE(CheckCSP(
451       SanitizeCSP("default-src; script-src "
452                   "'sha256-hndjYvzUzy2Ykuad81Cwsl1FOXX/qYs/aDVyUyNZ"
453                   "wBw= sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng=';",
454                   OPTIONS_NONE),
455       "default-src; script-src;",
456       InsecureValueWarning(
457           "script-src", "'sha256-hndjYvzUzy2Ykuad81Cwsl1FOXX/qYs/aDVyUyNZwBw="),
458       InsecureValueWarning(
459           "script-src",
460           "sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng='")));
461 }
462 
TEST(ExtensionCSPValidator,IsSandboxed)463 TEST(ExtensionCSPValidator, IsSandboxed) {
464   EXPECT_FALSE(ContentSecurityPolicyIsSandboxed(std::string(),
465                                                 Manifest::TYPE_EXTENSION));
466   EXPECT_FALSE(ContentSecurityPolicyIsSandboxed("img-src https://google.com",
467                                                 Manifest::TYPE_EXTENSION));
468 
469   // Sandbox directive is required.
470   EXPECT_TRUE(ContentSecurityPolicyIsSandboxed(
471       "sandbox", Manifest::TYPE_EXTENSION));
472 
473   // Additional sandbox tokens are OK.
474   EXPECT_TRUE(ContentSecurityPolicyIsSandboxed(
475       "sandbox allow-scripts", Manifest::TYPE_EXTENSION));
476   // Except for allow-same-origin.
477   EXPECT_FALSE(ContentSecurityPolicyIsSandboxed(
478       "sandbox allow-same-origin", Manifest::TYPE_EXTENSION));
479 
480   // Additional directives are OK.
481   EXPECT_TRUE(ContentSecurityPolicyIsSandboxed(
482       "sandbox; img-src https://google.com", Manifest::TYPE_EXTENSION));
483 
484   // Extensions allow navigation, platform apps don't.
485   EXPECT_TRUE(ContentSecurityPolicyIsSandboxed(
486       "sandbox allow-top-navigation", Manifest::TYPE_EXTENSION));
487   EXPECT_FALSE(ContentSecurityPolicyIsSandboxed(
488       "sandbox allow-top-navigation", Manifest::TYPE_PLATFORM_APP));
489 
490   // Popups are OK.
491   EXPECT_TRUE(ContentSecurityPolicyIsSandboxed(
492       "sandbox allow-popups", Manifest::TYPE_EXTENSION));
493   EXPECT_TRUE(ContentSecurityPolicyIsSandboxed(
494       "sandbox allow-popups", Manifest::TYPE_PLATFORM_APP));
495 }
496 
TEST(ExtensionCSPValidator,EffectiveSandboxedPageCSP)497 TEST(ExtensionCSPValidator, EffectiveSandboxedPageCSP) {
498   auto insecure_value_warning = [](const std::string& directive,
499                                    const std::string& value) {
500     return InsecureValueWarning(directive, value,
501                                 extensions::manifest_keys::kSandboxedPagesCSP);
502   };
503 
504   EXPECT_TRUE(CheckCSP(
505       SanitizeSandboxPageCSP(""),
506       "child-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval';"));
507   EXPECT_TRUE(CheckCSP(
508       SanitizeSandboxPageCSP("child-src http://www.google.com"),
509       "child-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval';",
510       insecure_value_warning("child-src", "http://www.google.com")));
511   EXPECT_TRUE(CheckCSP(
512       SanitizeSandboxPageCSP("child-src *"),
513       "child-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval';",
514       insecure_value_warning("child-src", "*")));
515   EXPECT_TRUE(CheckCSP(
516       SanitizeSandboxPageCSP("child-src 'none'"),
517       "child-src 'none'; script-src 'self' 'unsafe-inline' 'unsafe-eval';"));
518 
519   // Directive values of 'none' and 'self' are preserved.
520   EXPECT_TRUE(
521       CheckCSP(SanitizeSandboxPageCSP("script-src 'none'; frame-src 'self';"),
522                "frame-src 'self'; script-src 'none';"));
523   EXPECT_TRUE(CheckCSP(
524       SanitizeSandboxPageCSP(
525           "script-src 'none'; frame-src 'self' http://www.google.com;"),
526       "frame-src 'self'; script-src 'none';",
527       insecure_value_warning("frame-src", "http://www.google.com")));
528 
529   // script-src will add 'unsafe-inline' and 'unsafe-eval' only if script-src is
530   // not specified.
531   EXPECT_TRUE(CheckCSP(SanitizeSandboxPageCSP("script-src 'self'"),
532                        "script-src 'self'; child-src 'self'"));
533   EXPECT_TRUE(
534       CheckCSP(SanitizeSandboxPageCSP(
535                    "script-src 'self' 'unsafe-inline'; child-src 'self';"),
536                "child-src 'self'; script-src 'self' 'unsafe-inline';"));
537   EXPECT_TRUE(
538       CheckCSP(SanitizeSandboxPageCSP(
539                    "script-src 'self' 'unsafe-eval'; child-src 'self';"),
540                "child-src 'self'; script-src 'self' 'unsafe-eval';"));
541 
542   // child-src and frame-src are handled correctly.
543   EXPECT_TRUE(CheckCSP(
544       SanitizeSandboxPageCSP(
545           "script-src 'none'; frame-src 'self' http://www.google.com;"),
546       "frame-src 'self'; script-src 'none';",
547       insecure_value_warning("frame-src", "http://www.google.com")));
548   EXPECT_TRUE(CheckCSP(
549       SanitizeSandboxPageCSP(
550           "script-src 'none'; child-src 'self' http://www.google.com;"),
551       "child-src 'self'; script-src 'none';",
552       insecure_value_warning("child-src", "http://www.google.com")));
553 
554   // Multiple insecure values.
555   EXPECT_TRUE(CheckCSP(
556       SanitizeSandboxPageCSP(
557           "script-src 'none'; child-src http://bar.com 'self' http://foo.com;"),
558       "child-src 'self'; script-src 'none';",
559       insecure_value_warning("child-src", "http://bar.com"),
560       insecure_value_warning("child-src", "http://foo.com")));
561 }
562 
563 namespace extensions {
564 namespace csp_validator {
565 
PrintTo(const CSPParser::Directive & directive,::std::ostream * os)566 void PrintTo(const CSPParser::Directive& directive, ::std::ostream* os) {
567   *os << base::StringPrintf(
568       "[[%s] [%s] [%s]]", directive.directive_string.as_string().c_str(),
569       directive.directive_name.c_str(),
570       base::JoinString(directive.directive_values, ",").c_str());
571 }
572 
573 }  // namespace csp_validator
574 }  // namespace extensions
575 
TEST(ExtensionCSPValidator,ParseCSP)576 TEST(ExtensionCSPValidator, ParseCSP) {
577   using CSPParser = extensions::csp_validator::CSPParser;
578   using DirectiveList = CSPParser::DirectiveList;
579 
580   struct TestCase {
581     TestCase(const char* policy, DirectiveList expected_directives)
582         : policy(policy), expected_directives(std::move(expected_directives)) {}
583     const char* policy;
584     DirectiveList expected_directives;
585   };
586 
587   std::vector<TestCase> cases;
588 
589   cases.emplace_back("   \n \r \t ", DirectiveList());
590   cases.emplace_back("  ; \n ;\r \t ;;", DirectiveList());
591 
592   const char* policy = R"(  deFAULt-src   'self' ;
593   img-src * ; media-src media1.com MEDIA2.com;
594   img-src 'self';
595   )";
596   DirectiveList expected_directives;
597   expected_directives.emplace_back("deFAULt-src   'self'", "default-src",
598                                    std::vector<base::StringPiece>({"'self'"}));
599   expected_directives.emplace_back("img-src *", "img-src",
600                                    std::vector<base::StringPiece>({"*"}));
601   expected_directives.emplace_back(
602       "media-src media1.com MEDIA2.com", "media-src",
603       std::vector<base::StringPiece>({"media1.com", "MEDIA2.com"}));
604   expected_directives.emplace_back("img-src 'self'", "img-src",
605                                    std::vector<base::StringPiece>({"'self'"}));
606   cases.emplace_back(policy, std::move(expected_directives));
607 
608   for (const auto& test_case : cases) {
609     SCOPED_TRACE(test_case.policy);
610 
611     CSPParser parser(test_case.policy);
612 
613     // Cheat and compare serialized versions of the directives.
614     EXPECT_EQ(::testing::PrintToString(parser.directives()),
615               ::testing::PrintToString(test_case.expected_directives));
616   }
617 }
618 
TEST(ExtensionCSPValidator,DoesCSPDisallowRemoteCode)619 TEST(ExtensionCSPValidator, DoesCSPDisallowRemoteCode) {
620   const char* kManifestKey = "dummy_key";
621   auto insecure_value_error = [kManifestKey](const std::string& directive,
622                                              const std::string& value) {
623     return ErrorUtils::FormatErrorMessage(
624         extensions::manifest_errors::kInvalidCSPInsecureValueError,
625         kManifestKey, value, directive);
626   };
627 
628   auto missing_secure_src_error = [kManifestKey](const std::string& directive) {
629     return MissingSecureSrcWarning(kManifestKey, directive);
630   };
631 
632   struct {
633     const char* policy;
634     std::string expected_error;  // Empty if no error expected.
635   } test_cases[] = {
636       {"frame-src google.com; default-src yahoo.com; script-src 'self'; "
637        "worker-src; object-src http://localhost:80 'none'",
638        ""},
639       {"worker-src http://localhost google.com; script-src; object-src 'self'",
640        insecure_value_error("worker-src", "google.com")},
641       {"script-src; worker-src 'self';",
642        missing_secure_src_error("object-src")},
643       // Duplicate directives are ignored.
644       {"script-src; worker-src 'self'; default-src 'self'; script-src "
645        "google.com",
646        ""},
647       // "object-src" falls back to "default-src".
648       {"script-src; worker-src 'self'; default-src google.com",
649        insecure_value_error("object-src", "google.com")},
650       // "worker-src" falls back to "script-src".
651       {"script-src 'self'; object-src 'none'; default-src google.com", ""},
652       {"script-src 'unsafe-eval'; worker-src; default-src;",
653        insecure_value_error("script-src", "'unsafe-eval'")}};
654 
655   for (const auto& test_case : test_cases) {
656     SCOPED_TRACE(test_case.policy);
657     base::string16 error;
658     bool result = extensions::csp_validator::DoesCSPDisallowRemoteCode(
659         test_case.policy, kManifestKey, &error);
660     EXPECT_EQ(test_case.expected_error.empty(), result);
661     EXPECT_EQ(base::ASCIIToUTF16(test_case.expected_error), error);
662   }
663 }
664