summaryrefslogtreecommitdiffstats
path: root/extensions
diff options
context:
space:
mode:
authorrob <rob@robwu.nl>2015-08-10 16:36:45 -0700
committerCommit bot <commit-bot@chromium.org>2015-08-10 23:37:18 +0000
commit8131e678f35f3984805c049fcc66cc2d3ecc2d2d (patch)
treed9d10ff327422ec50d4416f67f02ab01d95050e8 /extensions
parentcaa7fbff19c6451b185d70edd0e05516c3e856f9 (diff)
downloadchromium_src-8131e678f35f3984805c049fcc66cc2d3ecc2d2d.zip
chromium_src-8131e678f35f3984805c049fcc66cc2d3ecc2d2d.tar.gz
chromium_src-8131e678f35f3984805c049fcc66cc2d3ecc2d2d.tar.bz2
Support hash-source CSP directive in extensions/apps
Support CSP hashes (http://www.w3.org/TR/2015/CR-CSP2-20150721/#script-src-hash-usage). The validation is strict and follows the standard by the letter. Blink also accepts sha1 and URL-encoded base64, but the extension's CSP validator only accepts the syntax as specified by the CSP2 standard. This allows the Blink implementation to become standard-compliant in the future without breaking extensions. Also, the CSP validator will now preserve the case of the CSP tokens, mainly because base64 is case-sensitive. And base::StringToLowerASCII is deprecated, which is another reason to change to base::ToLowerASCII. BUG=446036 Review URL: https://codereview.chromium.org/1285523002 Cr-Commit-Position: refs/heads/master@{#342737}
Diffstat (limited to 'extensions')
-rw-r--r--extensions/common/csp_validator.cc68
-rw-r--r--extensions/common/csp_validator_unittest.cc42
2 files changed, 84 insertions, 26 deletions
diff --git a/extensions/common/csp_validator.cc b/extensions/common/csp_validator.cc
index d19b983..842fc80 100644
--- a/extensions/common/csp_validator.cc
+++ b/extensions/common/csp_validator.cc
@@ -43,6 +43,15 @@ const char* const kSandboxedPluginTypes[] = {
"application/x-pnacl"
};
+// List of CSP hash-source prefixes that are accepted. Blink is a bit more
+// lenient, but we only accept standard hashes to be forward-compatible.
+// http://www.w3.org/TR/2015/CR-CSP2-20150721/#hash_algo
+const char* const kHashSourcePrefixes[] = {
+ "'sha256-",
+ "'sha384-",
+ "'sha512-"
+};
+
struct DirectiveStatus {
explicit DirectiveStatus(const char* name)
: directive_name(name), seen_in_policy(false) {}
@@ -113,6 +122,30 @@ bool isNonWildcardTLD(const std::string& url,
return registry_length != 0;
}
+// Checks whether the source is a syntactically valid hash.
+bool IsHashSource(const std::string& source) {
+ size_t hash_end = source.length() - 1;
+ if (source.empty() || source[hash_end] != '\'') {
+ return false;
+ }
+
+ for (const char* prefix : kHashSourcePrefixes) {
+ if (base::StartsWith(source, prefix,
+ base::CompareCase::INSENSITIVE_ASCII)) {
+ for (size_t i = strlen(prefix); i < hash_end; ++i) {
+ const char c = source[i];
+ // The hash must be base64-encoded. Do not allow any other characters.
+ if (!base::IsAsciiAlpha(c) && !base::IsAsciiDigit(c) && c != '+' &&
+ c != '/' && c != '=') {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
InstallWarning CSPInstallWarning(const std::string& csp_warning) {
return InstallWarning(csp_warning, manifest_keys::kContentSecurityPolicy);
}
@@ -124,38 +157,39 @@ void GetSecureDirectiveValues(const std::string& directive_name,
std::vector<InstallWarning>* warnings) {
sane_csp_parts->push_back(directive_name);
while (tokenizer->GetNext()) {
- std::string source = tokenizer->token();
- base::StringToLowerASCII(&source);
+ std::string source_literal = tokenizer->token();
+ std::string source_lower = base::ToLowerASCII(source_literal);
bool is_secure_csp_token = false;
// We might need to relax this whitelist over time.
- if (source == "'self'" || source == "'none'" ||
- source == "http://127.0.0.1" ||
- base::LowerCaseEqualsASCII(source, "blob:") ||
- base::LowerCaseEqualsASCII(source, "filesystem:") ||
- base::LowerCaseEqualsASCII(source, "http://localhost") ||
- base::StartsWith(source, "http://127.0.0.1:",
+ if (source_lower == "'self'" || source_lower == "'none'" ||
+ source_lower == "http://127.0.0.1" || source_lower == "blob:" ||
+ source_lower == "filesystem:" || source_lower == "http://localhost" ||
+ base::StartsWith(source_lower, "http://127.0.0.1:",
base::CompareCase::SENSITIVE) ||
- base::StartsWith(source, "http://localhost:",
+ base::StartsWith(source_lower, "http://localhost:",
base::CompareCase::SENSITIVE) ||
- isNonWildcardTLD(source, "https://", true) ||
- isNonWildcardTLD(source, "chrome://", false) ||
- isNonWildcardTLD(source, std::string(extensions::kExtensionScheme) +
- url::kStandardSchemeSeparator,
+ isNonWildcardTLD(source_lower, "https://", true) ||
+ isNonWildcardTLD(source_lower, "chrome://", false) ||
+ isNonWildcardTLD(source_lower,
+ std::string(extensions::kExtensionScheme) +
+ url::kStandardSchemeSeparator,
false) ||
- base::StartsWith(source, "chrome-extension-resource:",
+ IsHashSource(source_literal) ||
+ base::StartsWith(source_lower, "chrome-extension-resource:",
base::CompareCase::SENSITIVE)) {
is_secure_csp_token = true;
} else if ((options & OPTIONS_ALLOW_UNSAFE_EVAL) &&
- source == "'unsafe-eval'") {
+ source_lower == "'unsafe-eval'") {
is_secure_csp_token = true;
}
if (is_secure_csp_token) {
- sane_csp_parts->push_back(source);
+ sane_csp_parts->push_back(source_literal);
} else if (warnings) {
warnings->push_back(CSPInstallWarning(ErrorUtils::FormatErrorMessage(
- manifest_errors::kInvalidCSPInsecureValue, source, directive_name)));
+ manifest_errors::kInvalidCSPInsecureValue, source_literal,
+ directive_name)));
}
}
// End of CSP directive that was started at the beginning of this method. If
diff --git a/extensions/common/csp_validator_unittest.cc b/extensions/common/csp_validator_unittest.cc
index a9728f9..ed80024 100644
--- a/extensions/common/csp_validator_unittest.cc
+++ b/extensions/common/csp_validator_unittest.cc
@@ -311,9 +311,9 @@ TEST(ExtensionCSPValidator, IsSecure) {
"default-src 'self' http://127.0.0.1;", OPTIONS_ALLOW_UNSAFE_EVAL));
EXPECT_TRUE(CheckSanitizeCSP(
"default-src 'self' http://localhost;", OPTIONS_ALLOW_UNSAFE_EVAL));
- EXPECT_TRUE(CheckSanitizeCSP(
- "default-src 'self' http://lOcAlHoSt;", OPTIONS_ALLOW_UNSAFE_EVAL,
- "default-src 'self' http://localhost;"));
+ EXPECT_TRUE(CheckSanitizeCSP("default-src 'self' http://lOcAlHoSt;",
+ OPTIONS_ALLOW_UNSAFE_EVAL,
+ "default-src 'self' http://lOcAlHoSt;"));
EXPECT_TRUE(CheckSanitizeCSP(
"default-src 'self' http://127.0.0.1:9999;", OPTIONS_ALLOW_UNSAFE_EVAL));
EXPECT_TRUE(CheckSanitizeCSP(
@@ -333,16 +333,14 @@ TEST(ExtensionCSPValidator, IsSecure) {
"default-src 'self' blob:;", OPTIONS_ALLOW_UNSAFE_EVAL));
EXPECT_TRUE(CheckSanitizeCSP(
"default-src 'self' blob:http://example.com/XXX",
- OPTIONS_ALLOW_UNSAFE_EVAL,
- "default-src 'self';",
- InsecureValueWarning("default-src", "blob:http://example.com/xxx")));
+ OPTIONS_ALLOW_UNSAFE_EVAL, "default-src 'self';",
+ InsecureValueWarning("default-src", "blob:http://example.com/XXX")));
EXPECT_TRUE(CheckSanitizeCSP(
"default-src 'self' filesystem:;", OPTIONS_ALLOW_UNSAFE_EVAL));
EXPECT_TRUE(CheckSanitizeCSP(
"default-src 'self' filesystem:http://example.com/XX",
- OPTIONS_ALLOW_UNSAFE_EVAL,
- "default-src 'self';",
- InsecureValueWarning("default-src", "filesystem:http://example.com/xx")));
+ OPTIONS_ALLOW_UNSAFE_EVAL, "default-src 'self';",
+ InsecureValueWarning("default-src", "filesystem:http://example.com/XX")));
EXPECT_TRUE(CheckSanitizeCSP(
"default-src 'self' https://*.googleapis.com;",
@@ -393,6 +391,32 @@ TEST(ExtensionCSPValidator, IsSecure) {
OPTIONS_ALLOW_INSECURE_OBJECT_SRC,
"script-src; object-src *; plugin-types application/pdf;",
InsecureValueWarning("script-src", "*")));
+
+ EXPECT_TRUE(CheckSanitizeCSP(
+ "default-src; script-src"
+ " 'sha256-hndjYvzUzy2Ykuad81Cwsl1FOXX/qYs/aDVyUyNZwBw='"
+ " 'sha384-bSVm1i3sjPBRM4TwZtYTDjk9JxZMExYHWbFmP1SxDhJH4ue0Wu9OPOkY5hcqRcS"
+ "t'"
+ " 'sha512-440MmBLtj9Kp5Bqloogn9BqGDylY8vFsv5/zXL1zH2fJVssCoskRig4gyM+9Kqw"
+ "vCSapSz5CVoUGHQcxv43UQg==';",
+ OPTIONS_NONE));
+
+ // Reject non-standard algorithms, even if they are still supported by Blink.
+ EXPECT_TRUE(CheckSanitizeCSP(
+ "default-src; script-src 'sha1-eYyYGmKWdhpUewohaXk9o8IaLSw=';",
+ OPTIONS_NONE, "default-src; script-src;",
+ InsecureValueWarning("script-src",
+ "'sha1-eYyYGmKWdhpUewohaXk9o8IaLSw='")));
+
+ EXPECT_TRUE(CheckSanitizeCSP(
+ "default-src; script-src 'sha256-hndjYvzUzy2Ykuad81Cwsl1FOXX/qYs/aDVyUyNZ"
+ "wBw= sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng=';",
+ OPTIONS_NONE, "default-src; script-src;",
+ InsecureValueWarning(
+ "script-src", "'sha256-hndjYvzUzy2Ykuad81Cwsl1FOXX/qYs/aDVyUyNZwBw="),
+ InsecureValueWarning(
+ "script-src",
+ "sha256-qznLcsROx4GACP2dm0UCKCzCG+HiZ1guq6ZZDob/Tng='")));
}
TEST(ExtensionCSPValidator, IsSandboxed) {