Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "extensions/common/csp_validator.h" | 5 #include "extensions/common/csp_validator.h" |
| 6 | 6 |
| 7 #include <stddef.h> | 7 #include <stddef.h> |
| 8 | 8 |
| 9 #include <vector> | 9 #include <vector> |
| 10 | 10 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 21 | 21 |
| 22 namespace extensions { | 22 namespace extensions { |
| 23 | 23 |
| 24 namespace csp_validator { | 24 namespace csp_validator { |
| 25 | 25 |
| 26 namespace { | 26 namespace { |
| 27 | 27 |
| 28 const char kDefaultSrc[] = "default-src"; | 28 const char kDefaultSrc[] = "default-src"; |
| 29 const char kScriptSrc[] = "script-src"; | 29 const char kScriptSrc[] = "script-src"; |
| 30 const char kObjectSrc[] = "object-src"; | 30 const char kObjectSrc[] = "object-src"; |
| 31 const char kFrameSrc[] = "frame-src"; | |
| 32 const char kChildSrc[] = "child-src"; | |
| 33 | |
| 34 const char kDirectiveSeparator[] = ";"; | |
| 35 | |
| 31 const char kPluginTypes[] = "plugin-types"; | 36 const char kPluginTypes[] = "plugin-types"; |
| 32 | 37 |
| 33 const char kObjectSrcDefaultDirective[] = "object-src 'self';"; | 38 const char kObjectSrcDefaultDirective[] = "object-src 'self';"; |
| 34 const char kScriptSrcDefaultDirective[] = | 39 const char kScriptSrcDefaultDirective[] = |
| 35 "script-src 'self' chrome-extension-resource:;"; | 40 "script-src 'self' chrome-extension-resource:;"; |
| 36 | 41 |
| 42 const char kAppSandboxSubframeSrcDefaultDirective[] = "child-src 'self';"; | |
| 43 const char kAppSandboxScriptSrcDefaultDirective[] = "script-src 'self';"; | |
| 44 | |
| 37 const char kSandboxDirectiveName[] = "sandbox"; | 45 const char kSandboxDirectiveName[] = "sandbox"; |
| 38 const char kAllowSameOriginToken[] = "allow-same-origin"; | 46 const char kAllowSameOriginToken[] = "allow-same-origin"; |
| 39 const char kAllowTopNavigation[] = "allow-top-navigation"; | 47 const char kAllowTopNavigation[] = "allow-top-navigation"; |
| 40 | 48 |
| 41 // This is the list of plugin types which are fully sandboxed and are safe to | 49 // This is the list of plugin types which are fully sandboxed and are safe to |
| 42 // load up in an extension, regardless of the URL they are navigated to. | 50 // load up in an extension, regardless of the URL they are navigated to. |
| 43 const char* const kSandboxedPluginTypes[] = { | 51 const char* const kSandboxedPluginTypes[] = { |
| 44 "application/pdf", | 52 "application/pdf", |
| 45 "application/x-google-chrome-pdf", | 53 "application/x-google-chrome-pdf", |
| 46 "application/x-pnacl" | 54 "application/x-pnacl" |
| (...skipping 222 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 269 return policy.find_first_of(kBadChars, 0, arraysize(kBadChars)) == | 277 return policy.find_first_of(kBadChars, 0, arraysize(kBadChars)) == |
| 270 std::string::npos; | 278 std::string::npos; |
| 271 } | 279 } |
| 272 | 280 |
| 273 std::string SanitizeContentSecurityPolicy( | 281 std::string SanitizeContentSecurityPolicy( |
| 274 const std::string& policy, | 282 const std::string& policy, |
| 275 int options, | 283 int options, |
| 276 std::vector<InstallWarning>* warnings) { | 284 std::vector<InstallWarning>* warnings) { |
| 277 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. | 285 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. |
| 278 std::vector<std::string> directives = base::SplitString( | 286 std::vector<std::string> directives = base::SplitString( |
| 279 policy, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); | 287 policy, kDirectiveSeparator, base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL); |
| 280 | 288 |
| 281 DirectiveStatus default_src_status(kDefaultSrc); | 289 DirectiveStatus default_src_status(kDefaultSrc); |
| 282 DirectiveStatus script_src_status(kScriptSrc); | 290 DirectiveStatus script_src_status(kScriptSrc); |
| 283 DirectiveStatus object_src_status(kObjectSrc); | 291 DirectiveStatus object_src_status(kObjectSrc); |
| 284 | 292 |
| 285 bool allow_insecure_object_src = | 293 bool allow_insecure_object_src = |
| 286 AllowedToHaveInsecureObjectSrc(options, directives); | 294 AllowedToHaveInsecureObjectSrc(options, directives); |
| 287 | 295 |
| 288 std::vector<std::string> sane_csp_parts; | 296 std::vector<std::string> sane_csp_parts; |
| 289 std::vector<InstallWarning> default_src_csp_warnings; | 297 std::vector<InstallWarning> default_src_csp_warnings; |
| 290 for (size_t i = 0; i < directives.size(); ++i) { | 298 for (size_t i = 0; i < directives.size(); ++i) { |
| 291 std::string& input = directives[i]; | 299 std::string& input = directives[i]; |
| 292 base::StringTokenizer tokenizer(input, " \t\r\n"); | 300 base::StringTokenizer tokenizer(input, " \t\r\n"); |
| 293 if (!tokenizer.GetNext()) | 301 if (!tokenizer.GetNext()) |
| 294 continue; | 302 continue; |
| 295 | 303 |
| 296 std::string directive_name = base::ToLowerASCII(tokenizer.token_piece()); | 304 std::string directive_name = base::ToLowerASCII(tokenizer.token_piece()); |
| 297 if (UpdateStatus(directive_name, &tokenizer, &default_src_status, options, | 305 if (UpdateStatus(directive_name, &tokenizer, &default_src_status, options, |
| 298 &sane_csp_parts, &default_src_csp_warnings)) | 306 &sane_csp_parts, &default_src_csp_warnings)) |
| 299 continue; | 307 continue; |
| 300 if (UpdateStatus(directive_name, &tokenizer, &script_src_status, options, | 308 if (UpdateStatus(directive_name, &tokenizer, &script_src_status, options, |
| 301 &sane_csp_parts, warnings)) | 309 &sane_csp_parts, warnings)) |
| 302 continue; | 310 continue; |
| 303 if (!allow_insecure_object_src && | 311 if (!allow_insecure_object_src && |
| 304 UpdateStatus(directive_name, &tokenizer, &object_src_status, options, | 312 UpdateStatus(directive_name, &tokenizer, &object_src_status, options, |
| 305 &sane_csp_parts, warnings)) | 313 &sane_csp_parts, warnings)) |
| 306 continue; | 314 continue; |
| 307 | 315 |
| 308 // Pass the other CSP directives as-is without further validation. | 316 // Pass the other CSP directives as-is without further validation. |
| 309 sane_csp_parts.push_back(input + ";"); | 317 sane_csp_parts.push_back(input + kDirectiveSeparator); |
| 310 } | 318 } |
| 311 | 319 |
| 312 if (default_src_status.seen_in_policy) { | 320 if (default_src_status.seen_in_policy) { |
| 313 if (!script_src_status.seen_in_policy || | 321 if (!script_src_status.seen_in_policy || |
| 314 !object_src_status.seen_in_policy) { | 322 !object_src_status.seen_in_policy) { |
| 315 // Insecure values in default-src are only relevant if either script-src | 323 // Insecure values in default-src are only relevant if either script-src |
| 316 // or object-src is omitted. | 324 // or object-src is omitted. |
| 317 if (warnings) | 325 if (warnings) |
| 318 warnings->insert(warnings->end(), | 326 warnings->insert(warnings->end(), |
| 319 default_src_csp_warnings.begin(), | 327 default_src_csp_warnings.begin(), |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 330 sane_csp_parts.push_back(kObjectSrcDefaultDirective); | 338 sane_csp_parts.push_back(kObjectSrcDefaultDirective); |
| 331 if (warnings) | 339 if (warnings) |
| 332 warnings->push_back(CSPInstallWarning(ErrorUtils::FormatErrorMessage( | 340 warnings->push_back(CSPInstallWarning(ErrorUtils::FormatErrorMessage( |
| 333 manifest_errors::kInvalidCSPMissingSecureSrc, kObjectSrc))); | 341 manifest_errors::kInvalidCSPMissingSecureSrc, kObjectSrc))); |
| 334 } | 342 } |
| 335 } | 343 } |
| 336 | 344 |
| 337 return base::JoinString(sane_csp_parts, " "); | 345 return base::JoinString(sane_csp_parts, " "); |
| 338 } | 346 } |
| 339 | 347 |
| 348 std::string GetEffectiveSandoxedPageCSP(const std::string& policy) { | |
|
Devlin
2016/12/09 15:46:01
I kind of wonder if it makes sense to have a more
Charlie Reis
2016/12/09 19:55:07
I haven't seen one, but I agree that avoiding a bu
alexmos
2016/12/12 19:04:11
I haven't seen one either, and I agree it's a good
lazyboy
2016/12/14 00:49:05
I've introduced a CSPEnforcer class that can sanit
| |
| 349 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. | |
| 350 std::vector<std::string> effective_csp_parts; | |
| 351 bool seen_default_source = false; | |
| 352 bool seen_subframe_source = false; | |
| 353 bool seen_script_source = false; | |
| 354 for (const std::string& directive : base::SplitString( | |
| 355 policy, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { | |
| 356 base::StringTokenizer tokenizer(directive, " \t\r\n"); | |
| 357 if (!tokenizer.GetNext()) | |
| 358 continue; | |
| 359 | |
| 360 std::string directive_name = base::ToLowerASCII(tokenizer.token_piece()); | |
| 361 if (directive_name == kDefaultSrc) { | |
| 362 seen_default_source = true; | |
| 363 } else if (directive_name == kChildSrc || directive_name == kFrameSrc) { | |
| 364 seen_subframe_source = true; | |
| 365 } else if (directive_name == kScriptSrc) { | |
| 366 seen_script_source = true; | |
| 367 } else { | |
| 368 // Keep this directive as is. | |
| 369 effective_csp_parts.push_back(directive + kDirectiveSeparator); | |
| 370 continue; | |
| 371 } | |
| 372 | |
| 373 effective_csp_parts.push_back(directive_name); | |
| 374 bool seen_self_or_none = false; | |
| 375 while (tokenizer.GetNext()) { | |
| 376 std::string source_lower = base::ToLowerASCII(tokenizer.token()); | |
| 377 seen_self_or_none |= source_lower == "'none'" || source_lower == "'self'"; | |
| 378 | |
| 379 // Keyword directive sources are surrounded with quotes, e.g. 'self', | |
| 380 // 'sha256-...', 'unsafe-eval', 'nonce-...'. These do not specify a remote | |
| 381 // host or '*', so keep them and restrict the rest. | |
| 382 if (source_lower.size() > 1u && source_lower[0] == '\'' && | |
| 383 source_lower[source_lower.size() - 1] == '\'') { | |
|
Devlin
2016/12/09 15:46:01
When we see an insecure pattern, we could add an i
lazyboy
2016/12/14 00:49:05
Done.
| |
| 384 effective_csp_parts.push_back(source_lower); | |
| 385 } | |
| 386 } | |
| 387 | |
| 388 if (!seen_self_or_none) | |
| 389 effective_csp_parts.push_back("'self'"); | |
| 390 | |
| 391 // Add ";" at the end of the directive. | |
| 392 effective_csp_parts.back().append(kDirectiveSeparator); | |
| 393 } | |
| 394 | |
| 395 // If subframes, scripts were not restricted in |effective_csp_parts|, add the | |
| 396 // restriction. | |
| 397 if (!seen_subframe_source && !seen_default_source) | |
| 398 effective_csp_parts.push_back(kAppSandboxSubframeSrcDefaultDirective); | |
| 399 if (!seen_script_source && !seen_default_source) | |
| 400 effective_csp_parts.push_back(kAppSandboxScriptSrcDefaultDirective); | |
| 401 | |
| 402 return base::JoinString(effective_csp_parts, " "); | |
| 403 } | |
| 404 | |
| 340 bool ContentSecurityPolicyIsSandboxed( | 405 bool ContentSecurityPolicyIsSandboxed( |
| 341 const std::string& policy, Manifest::Type type) { | 406 const std::string& policy, Manifest::Type type) { |
| 342 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. | 407 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. |
| 343 bool seen_sandbox = false; | 408 bool seen_sandbox = false; |
| 344 for (const std::string& input : base::SplitString( | 409 for (const std::string& input : base::SplitString( |
| 345 policy, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { | 410 policy, ";", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) { |
| 346 base::StringTokenizer tokenizer(input, " \t\r\n"); | 411 base::StringTokenizer tokenizer(input, " \t\r\n"); |
| 347 if (!tokenizer.GetNext()) | 412 if (!tokenizer.GetNext()) |
| 348 continue; | 413 continue; |
| 349 | 414 |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 367 } | 432 } |
| 368 } | 433 } |
| 369 } | 434 } |
| 370 | 435 |
| 371 return seen_sandbox; | 436 return seen_sandbox; |
| 372 } | 437 } |
| 373 | 438 |
| 374 } // namespace csp_validator | 439 } // namespace csp_validator |
| 375 | 440 |
| 376 } // namespace extensions | 441 } // namespace extensions |
| OLD | NEW |