diff options
author | rdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-26 22:59:28 +0000 |
---|---|---|
committer | rdevlin.cronin@chromium.org <rdevlin.cronin@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-26 22:59:28 +0000 |
commit | 70c39bbb4b24c7e3c1cfbd044f862ee561f3e1bd (patch) | |
tree | 4f21f5c4c4ef358f2708f1a53a5820ce1ad7eb58 /extensions/common/csp_validator.cc | |
parent | 1cfeb5674e5ff5df1ba2223515ab7a575d0c8285 (diff) | |
download | chromium_src-70c39bbb4b24c7e3c1cfbd044f862ee561f3e1bd.zip chromium_src-70c39bbb4b24c7e3c1cfbd044f862ee561f3e1bd.tar.gz chromium_src-70c39bbb4b24c7e3c1cfbd044f862ee561f3e1bd.tar.bz2 |
Move ManifestHandlers to extensions/
Move ManifestHandlers for:
- CSP
- Kiosk Mode
- Offline Enabled
- Requirements
- Sandboxed Page
to top-level extensions/common/manifest_handlers.
Rename all handlers to be foo_info.h/cc, if necessary.
TBR=davemoore@chromium.org (c/b/chromeos/app_mode/startup_app_launcher.cc)
TBR=finnur@chromium.org (c/b/ui/webui/extensions/extension_basic_info.cc)
BUG=159265
Review URL: https://codereview.chromium.org/83843002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@237426 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'extensions/common/csp_validator.cc')
-rw-r--r-- | extensions/common/csp_validator.cc | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/extensions/common/csp_validator.cc b/extensions/common/csp_validator.cc new file mode 100644 index 0000000..05076f7 --- /dev/null +++ b/extensions/common/csp_validator.cc @@ -0,0 +1,200 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "extensions/common/csp_validator.h" + +#include "base/strings/string_split.h" +#include "base/strings/string_tokenizer.h" +#include "base/strings/string_util.h" + +namespace extensions { + +namespace csp_validator { + +namespace { + +const char kDefaultSrc[] = "default-src"; +const char kScriptSrc[] = "script-src"; +const char kObjectSrc[] = "object-src"; + +const char kSandboxDirectiveName[] = "sandbox"; +const char kAllowSameOriginToken[] = "allow-same-origin"; +const char kAllowTopNavigation[] = "allow-top-navigation"; + +struct DirectiveStatus { + explicit DirectiveStatus(const char* name) + : directive_name(name) + , seen_in_policy(false) + , is_secure(false) { + } + + const char* directive_name; + bool seen_in_policy; + bool is_secure; +}; + +bool HasOnlySecureTokens(base::StringTokenizer& tokenizer, + Manifest::Type type) { + while (tokenizer.GetNext()) { + std::string source = tokenizer.token(); + StringToLowerASCII(&source); + + // Don't alow whitelisting of all hosts. This boils down to: + // 1. Maximum of 2 '*' characters. + // 2. Each '*' is either followed by a '.' or preceded by a ':' + int wildcards = 0; + size_t length = source.length(); + for (size_t i = 0; i < length; ++i) { + if (source[i] == L'*') { + wildcards++; + if (wildcards > 2) + return false; + + bool isWildcardPort = i > 0 && source[i - 1] == L':'; + bool isWildcardSubdomain = i + 1 < length && source[i + 1] == L'.'; + if (!isWildcardPort && !isWildcardSubdomain) + return false; + } + } + + // We might need to relax this whitelist over time. + if (source == "'self'" || + source == "'none'" || + source == "http://127.0.0.1" || + LowerCaseEqualsASCII(source, "blob:") || + LowerCaseEqualsASCII(source, "filesystem:") || + LowerCaseEqualsASCII(source, "http://localhost") || + StartsWithASCII(source, "http://127.0.0.1:", false) || + StartsWithASCII(source, "http://localhost:", false) || + StartsWithASCII(source, "https://", true) || + StartsWithASCII(source, "chrome://", true) || + StartsWithASCII(source, "chrome-extension://", true) || + StartsWithASCII(source, "chrome-extension-resource:", true)) { + continue; + } + + // crbug.com/146487 + if (type == Manifest::TYPE_EXTENSION || + type == Manifest::TYPE_LEGACY_PACKAGED_APP) { + if (source == "'unsafe-eval'") + continue; + } + + return false; + } + + return true; // Empty values default to 'none', which is secure. +} + +// Returns true if |directive_name| matches |status.directive_name|. +bool UpdateStatus(const std::string& directive_name, + base::StringTokenizer& tokenizer, + DirectiveStatus* status, + Manifest::Type type) { + if (status->seen_in_policy) + return false; + if (directive_name != status->directive_name) + return false; + status->seen_in_policy = true; + status->is_secure = HasOnlySecureTokens(tokenizer, type); + return true; +} + +} // namespace + +bool ContentSecurityPolicyIsLegal(const std::string& policy) { + // We block these characters to prevent HTTP header injection when + // representing the content security policy as an HTTP header. + const char kBadChars[] = {',', '\r', '\n', '\0'}; + + return policy.find_first_of(kBadChars, 0, arraysize(kBadChars)) == + std::string::npos; +} + +bool ContentSecurityPolicyIsSecure(const std::string& policy, + Manifest::Type type) { + // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. + std::vector<std::string> directives; + base::SplitString(policy, ';', &directives); + + DirectiveStatus default_src_status(kDefaultSrc); + DirectiveStatus script_src_status(kScriptSrc); + DirectiveStatus object_src_status(kObjectSrc); + + for (size_t i = 0; i < directives.size(); ++i) { + std::string& input = directives[i]; + base::StringTokenizer tokenizer(input, " \t\r\n"); + if (!tokenizer.GetNext()) + continue; + + std::string directive_name = tokenizer.token(); + StringToLowerASCII(&directive_name); + + if (UpdateStatus(directive_name, tokenizer, &default_src_status, type)) + continue; + if (UpdateStatus(directive_name, tokenizer, &script_src_status, type)) + continue; + if (UpdateStatus(directive_name, tokenizer, &object_src_status, type)) + continue; + } + + if (script_src_status.seen_in_policy && !script_src_status.is_secure) + return false; + + if (object_src_status.seen_in_policy && !object_src_status.is_secure) + return false; + + if (default_src_status.seen_in_policy && !default_src_status.is_secure) { + return script_src_status.seen_in_policy && + object_src_status.seen_in_policy; + } + + return default_src_status.seen_in_policy || + (script_src_status.seen_in_policy && object_src_status.seen_in_policy); +} + +bool ContentSecurityPolicyIsSandboxed( + const std::string& policy, Manifest::Type type) { + // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. + std::vector<std::string> directives; + base::SplitString(policy, ';', &directives); + + bool seen_sandbox = false; + + for (size_t i = 0; i < directives.size(); ++i) { + std::string& input = directives[i]; + base::StringTokenizer tokenizer(input, " \t\r\n"); + if (!tokenizer.GetNext()) + continue; + + std::string directive_name = tokenizer.token(); + StringToLowerASCII(&directive_name); + + if (directive_name != kSandboxDirectiveName) + continue; + + seen_sandbox = true; + + while (tokenizer.GetNext()) { + std::string token = tokenizer.token(); + StringToLowerASCII(&token); + + // The same origin token negates the sandboxing. + if (token == kAllowSameOriginToken) + return false; + + // Platform apps don't allow navigation. + if (type == Manifest::TYPE_PLATFORM_APP) { + if (token == kAllowTopNavigation) + return false; + } + } + } + + return seen_sandbox; +} + +} // csp_validator + +} // extensions |