Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(705)

Unified Diff: extensions/common/csp_validator.cc

Issue 747403002: Ignore insecure parts of CSP in extensions and allow extension to load (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « extensions/common/csp_validator.h ('k') | extensions/common/csp_validator_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: extensions/common/csp_validator.cc
diff --git a/extensions/common/csp_validator.cc b/extensions/common/csp_validator.cc
index 23af91c75787c06964b62ea5ea1ee393e6deb19d..9a9549c8054b59b327669e023cd9bb763a48d724 100644
--- a/extensions/common/csp_validator.cc
+++ b/extensions/common/csp_validator.cc
@@ -11,6 +11,9 @@
#include "base/strings/string_util.h"
#include "content/public/common/url_constants.h"
#include "extensions/common/constants.h"
+#include "extensions/common/error_utils.h"
+#include "extensions/common/install_warning.h"
+#include "extensions/common/manifest_constants.h"
#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
namespace extensions {
@@ -22,6 +25,9 @@ namespace {
const char kDefaultSrc[] = "default-src";
const char kScriptSrc[] = "script-src";
const char kObjectSrc[] = "object-src";
+const char kObjectSrcDefaultDirective[] = "object-src 'self';";
+const char kScriptSrcDefaultDirective[] =
+ "script-src 'self' chrome-extension-resource:;";
const char kSandboxDirectiveName[] = "sandbox";
const char kAllowSameOriginToken[] = "allow-same-origin";
@@ -29,14 +35,10 @@ const char kAllowTopNavigation[] = "allow-top-navigation";
struct DirectiveStatus {
explicit DirectiveStatus(const char* name)
- : directive_name(name)
- , seen_in_policy(false)
- , is_secure(false) {
- }
+ : directive_name(name), seen_in_policy(false) {}
const char* directive_name;
bool seen_in_policy;
- bool is_secure;
};
// Returns whether |url| starts with |scheme_and_separator| and does not have a
@@ -54,12 +56,6 @@ bool isNonWildcardTLD(const std::string& url,
if (end_of_host == std::string::npos)
end_of_host = url.size();
- // A missing host such as "chrome-extension://" is invalid, but for backwards-
- // compatibility, accept such CSP parts. They will be ignored by Blink anyway.
- // TODO(robwu): Remove this special case once crbug.com/434773 is fixed.
- if (start_of_host == end_of_host)
- return true;
-
// Note: It is sufficient to only compare the first character against '*'
// because the CSP only allows wildcards at the start of a directive, see
// host-source and host-part at http://www.w3.org/TR/CSP2/#source-list-syntax
@@ -106,8 +102,12 @@ bool isNonWildcardTLD(const std::string& url,
return registry_length != 0;
}
-bool HasOnlySecureTokens(base::StringTokenizer& tokenizer,
- Manifest::Type type) {
+void GetSecureDirectiveValues(const std::string& directive_name,
+ base::StringTokenizer& tokenizer,
+ Manifest::Type type,
+ std::vector<std::string>& csp_parts,
+ std::vector<std::string>* csp_warnings) {
+ csp_parts.push_back(directive_name);
while (tokenizer.GetNext()) {
std::string source = tokenizer.token();
base::StringToLowerASCII(&source);
@@ -128,33 +128,48 @@ bool HasOnlySecureTokens(base::StringTokenizer& tokenizer,
url::kStandardSchemeSeparator,
false) ||
StartsWithASCII(source, "chrome-extension-resource:", true)) {
+ csp_parts.push_back(source);
continue;
}
// crbug.com/146487
if (type == Manifest::TYPE_EXTENSION ||
type == Manifest::TYPE_LEGACY_PACKAGED_APP) {
- if (source == "'unsafe-eval'")
+ if (source == "'unsafe-eval'") {
+ csp_parts.push_back(source);
continue;
+ }
+ }
+ if (csp_warnings) {
+ csp_warnings->push_back(ErrorUtils::FormatErrorMessage(
+ manifest_errors::kInvalidCSPInsecureValue, source, directive_name));
}
-
- return false;
}
-
- return true; // Empty values default to 'none', which is secure.
+ // End of CSP directive that was started at the beginning of this method. If
+ // none of the values are secure, the policy will be empty and default to
+ // 'none', which is secure.
+ csp_parts.back() += ";";
Mike West 2014/11/24 11:37:19 Assigning to the result of a method call is iffy.
robwu 2014/11/24 12:19:47 Done.
}
// 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;
+ Manifest::Type type,
+ std::vector<std::string>& csp_parts,
+ std::vector<std::string>* csp_warnings) {
if (directive_name != status->directive_name)
return false;
- status->seen_in_policy = true;
- status->is_secure = HasOnlySecureTokens(tokenizer, type);
+
+ if (!status->seen_in_policy) {
+ status->seen_in_policy = true;
+ GetSecureDirectiveValues(directive_name, tokenizer, type, csp_parts,
+ csp_warnings);
+ } else {
+ // Don't show any errors for duplicate CSP directives, because it will be
+ // ignored by the CSP parser (http://www.w3.org/TR/CSP2/#policy-parsing).
+ GetSecureDirectiveValues(directive_name, tokenizer, type, csp_parts, NULL);
+ }
return true;
}
@@ -170,7 +185,9 @@ bool ContentSecurityPolicyIsLegal(const std::string& policy) {
}
bool ContentSecurityPolicyIsSecure(const std::string& policy,
- Manifest::Type type) {
+ Manifest::Type type,
+ std::string* sanitized_csp,
+ std::vector<InstallWarning>* warnings) {
// See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm.
std::vector<std::string> directives;
base::SplitString(policy, ';', &directives);
@@ -179,6 +196,9 @@ bool ContentSecurityPolicyIsSecure(const std::string& policy,
DirectiveStatus script_src_status(kScriptSrc);
DirectiveStatus object_src_status(kObjectSrc);
+ std::vector<std::string> csp_parts;
+ std::vector<std::string> csp_warnings;
+ std::vector<std::string> default_src_csp_warnings;
for (size_t i = 0; i < directives.size(); ++i) {
std::string& input = directives[i];
base::StringTokenizer tokenizer(input, " \t\r\n");
@@ -188,27 +208,55 @@ bool ContentSecurityPolicyIsSecure(const std::string& policy,
std::string directive_name = tokenizer.token();
base::StringToLowerASCII(&directive_name);
- if (UpdateStatus(directive_name, tokenizer, &default_src_status, type))
+ if (UpdateStatus(directive_name, tokenizer, &default_src_status, type,
+ csp_parts, &default_src_csp_warnings))
continue;
- if (UpdateStatus(directive_name, tokenizer, &script_src_status, type))
+ if (UpdateStatus(directive_name, tokenizer, &script_src_status, type,
+ csp_parts, &csp_warnings))
continue;
- if (UpdateStatus(directive_name, tokenizer, &object_src_status, type))
+ if (UpdateStatus(directive_name, tokenizer, &object_src_status, type,
+ csp_parts, &csp_warnings))
continue;
+
+ // Pass the other CSP directives as-is without further validation.
+ csp_parts.push_back(input + ";");
}
- if (script_src_status.seen_in_policy && !script_src_status.is_secure)
- return false;
+ if (default_src_status.seen_in_policy) {
+ if (!script_src_status.seen_in_policy ||
+ !object_src_status.seen_in_policy) {
+ // Insecure values in default-src are only relevant if either script-src
+ // or object-src is omitted.
+ std::move(default_src_csp_warnings.begin(),
Mike West 2014/11/24 11:37:19 We can't use `std::move` or `std::back_inserter` y
robwu 2014/11/24 12:19:47 Done. Replaced with insert.
+ default_src_csp_warnings.end(),
+ std::back_inserter(csp_warnings));
Mike West 2014/11/24 11:37:19 Don't you also need to inject the default `script-
robwu 2014/11/24 12:19:47 No, they are inferred from default-src.
+ }
Mike West 2014/11/24 11:37:19 I don't see whether you're verifying that the `def
robwu 2014/11/24 12:19:46 GetSecureDirectiveValues (via UpdateStatus) ensure
+ } else {
+ if (!script_src_status.seen_in_policy) {
+ csp_parts.push_back(kScriptSrcDefaultDirective);
+ csp_warnings.push_back(ErrorUtils::FormatErrorMessage(
+ manifest_errors::kInvalidCSPMissingSecureSrc, kScriptSrc));
+ }
+ if (!object_src_status.seen_in_policy) {
+ csp_parts.push_back(kObjectSrcDefaultDirective);
+ csp_warnings.push_back(ErrorUtils::FormatErrorMessage(
+ manifest_errors::kInvalidCSPMissingSecureSrc, kObjectSrc));
+ }
+ }
- if (object_src_status.seen_in_policy && !object_src_status.is_secure)
- return false;
+ if (sanitized_csp)
+ *sanitized_csp = JoinString(csp_parts, ' ');
+ if (csp_warnings.empty())
+ return true;
- 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;
+ if (warnings) {
+ for (std::vector<std::string>::iterator it = csp_warnings.begin();
Mike West 2014/11/24 11:37:19 You can use a C++11 for loop and 'auto' here for c
robwu 2014/11/24 12:19:47 Done.
+ it != csp_warnings.end(); ++it) {
+ warnings->push_back(
+ InstallWarning(*it, manifest_keys::kContentSecurityPolicy));
+ }
}
-
- return default_src_status.seen_in_policy ||
- (script_src_status.seen_in_policy && object_src_status.seen_in_policy);
+ return false;
}
bool ContentSecurityPolicyIsSandboxed(
« no previous file with comments | « extensions/common/csp_validator.h ('k') | extensions/common/csp_validator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698