Commit 3eaf3ef6 authored by Matt Giuca's avatar Matt Giuca Committed by Commit Bot

Manifest parser: Refactor the "within scope" checking into ParseURL.

This makes a slight change to error messages: notably, we no longer
explicitly check if the URL is same origin with the document URL (which
is implied by being within scope), hence same-origin errors are now
reported as not-within-scope errors.

Bug: 1101909, 1101910, 1101911, 1101912
Change-Id: I492688f667d7cd351bbc1dfe1ce39d5276d9fa93
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2280961Reviewed-by: default avatarDominick Ng <dominickn@chromium.org>
Reviewed-by: default avatarMatt Giuca <mgiuca@chromium.org>
Commit-Queue: Matt Giuca <mgiuca@chromium.org>
Cr-Commit-Position: refs/heads/master@{#785285}
parent 5431980a
...@@ -40,6 +40,12 @@ bool VerifyFiles(const Vector<mojom::blink::ManifestFileFilterPtr>& files) { ...@@ -40,6 +40,12 @@ bool VerifyFiles(const Vector<mojom::blink::ManifestFileFilterPtr>& files) {
return true; return true;
} }
// Determines whether |url| is within scope of |scope|.
bool URLIsWithinScope(const KURL& url, const KURL& scope) {
return SecurityOrigin::AreSameOrigin(url, scope) &&
url.GetPath().StartsWith(scope.GetPath());
}
} // anonymous namespace } // anonymous namespace
ManifestParser::ManifestParser(const String& data, ManifestParser::ManifestParser(const String& data,
...@@ -209,7 +215,7 @@ base::Optional<RGBA32> ManifestParser::ParseColor(const JSONObject* object, ...@@ -209,7 +215,7 @@ base::Optional<RGBA32> ManifestParser::ParseColor(const JSONObject* object,
KURL ManifestParser::ParseURL(const JSONObject* object, KURL ManifestParser::ParseURL(const JSONObject* object,
const String& key, const String& key,
const KURL& base_url, const KURL& base_url,
ParseURLOriginRestrictions origin_restriction) { ParseURLRestrictions origin_restriction) {
base::Optional<String> url_str = ParseString(object, key, NoTrim); base::Optional<String> url_str = ParseString(object, key, NoTrim);
if (!url_str.has_value()) if (!url_str.has_value())
return KURL(); return KURL();
...@@ -221,14 +227,25 @@ KURL ManifestParser::ParseURL(const JSONObject* object, ...@@ -221,14 +227,25 @@ KURL ManifestParser::ParseURL(const JSONObject* object,
} }
switch (origin_restriction) { switch (origin_restriction) {
case ParseURLOriginRestrictions::kSameOriginOnly: case ParseURLRestrictions::kNoRestrictions:
return resolved;
case ParseURLRestrictions::kSameOriginOnly:
if (!SecurityOrigin::AreSameOrigin(resolved, document_url_)) { if (!SecurityOrigin::AreSameOrigin(resolved, document_url_)) {
AddErrorInfo("property '" + key + AddErrorInfo("property '" + key +
"' ignored, should be same origin as document."); "' ignored, should be same origin as document.");
return KURL(); return KURL();
} }
return resolved; return resolved;
case ParseURLOriginRestrictions::kNoRestrictions: case ParseURLRestrictions::kWithinScope:
if (!URLIsWithinScope(resolved, manifest_->scope)) {
AddErrorInfo("property '" + key +
"' ignored, should be within scope of the manifest.");
return KURL();
}
// Within scope implies same origin as document URL.
DCHECK(SecurityOrigin::AreSameOrigin(resolved, document_url_));
return resolved; return resolved;
} }
...@@ -248,13 +265,13 @@ String ManifestParser::ParseShortName(const JSONObject* object) { ...@@ -248,13 +265,13 @@ String ManifestParser::ParseShortName(const JSONObject* object) {
KURL ManifestParser::ParseStartURL(const JSONObject* object) { KURL ManifestParser::ParseStartURL(const JSONObject* object) {
return ParseURL(object, "start_url", manifest_url_, return ParseURL(object, "start_url", manifest_url_,
ParseURLOriginRestrictions::kSameOriginOnly); ParseURLRestrictions::kSameOriginOnly);
} }
KURL ManifestParser::ParseScope(const JSONObject* object, KURL ManifestParser::ParseScope(const JSONObject* object,
const KURL& start_url) { const KURL& start_url) {
KURL scope = ParseURL(object, "scope", manifest_url_, KURL scope = ParseURL(object, "scope", manifest_url_,
ParseURLOriginRestrictions::kSameOriginOnly); ParseURLRestrictions::kNoRestrictions);
// This will change to remove the |document_url_| fallback in the future. // This will change to remove the |document_url_| fallback in the future.
// See https://github.com/w3c/manifest/issues/668. // See https://github.com/w3c/manifest/issues/668.
...@@ -264,8 +281,7 @@ KURL ManifestParser::ParseScope(const JSONObject* object, ...@@ -264,8 +281,7 @@ KURL ManifestParser::ParseScope(const JSONObject* object,
if (scope.IsEmpty()) if (scope.IsEmpty())
return KURL(default_value.BaseAsString()); return KURL(default_value.BaseAsString());
if (!SecurityOrigin::AreSameOrigin(default_value, scope) || if (!URLIsWithinScope(default_value, scope)) {
!default_value.GetPath().StartsWith(scope.GetPath())) {
AddErrorInfo( AddErrorInfo(
"property 'scope' ignored. Start url should be within scope " "property 'scope' ignored. Start url should be within scope "
"of scope URL."); "of scope URL.");
...@@ -273,6 +289,7 @@ KURL ManifestParser::ParseScope(const JSONObject* object, ...@@ -273,6 +289,7 @@ KURL ManifestParser::ParseScope(const JSONObject* object,
} }
DCHECK(scope.IsValid()); DCHECK(scope.IsValid());
DCHECK(SecurityOrigin::AreSameOrigin(scope, document_url_));
return scope; return scope;
} }
...@@ -305,7 +322,7 @@ WebScreenOrientationLockType ManifestParser::ParseOrientation( ...@@ -305,7 +322,7 @@ WebScreenOrientationLockType ManifestParser::ParseOrientation(
KURL ManifestParser::ParseIconSrc(const JSONObject* icon) { KURL ManifestParser::ParseIconSrc(const JSONObject* icon) {
return ParseURL(icon, "src", manifest_url_, return ParseURL(icon, "src", manifest_url_,
ParseURLOriginRestrictions::kNoRestrictions); ParseURLRestrictions::kNoRestrictions);
} }
String ManifestParser::ParseIconType(const JSONObject* icon) { String ManifestParser::ParseIconType(const JSONObject* icon) {
...@@ -442,16 +459,9 @@ String ManifestParser::ParseShortcutDescription(const JSONObject* shortcut) { ...@@ -442,16 +459,9 @@ String ManifestParser::ParseShortcutDescription(const JSONObject* shortcut) {
KURL ManifestParser::ParseShortcutUrl(const JSONObject* shortcut) { KURL ManifestParser::ParseShortcutUrl(const JSONObject* shortcut) {
KURL shortcut_url = ParseURL(shortcut, "url", manifest_url_, KURL shortcut_url = ParseURL(shortcut, "url", manifest_url_,
ParseURLOriginRestrictions::kSameOriginOnly); ParseURLRestrictions::kWithinScope);
if (shortcut_url.IsNull()) { if (shortcut_url.IsNull())
AddErrorInfo("property 'url' of 'shortcut' not present."); AddErrorInfo("property 'url' of 'shortcut' not present.");
} else if (!shortcut_url.GetString().StartsWith(
manifest_->scope.GetString())) {
AddErrorInfo(
"property 'url' of 'shortcut' ignored. url should be within scope of "
"the manifest.");
return KURL();
}
return shortcut_url; return shortcut_url;
} }
...@@ -673,7 +683,7 @@ ManifestParser::ParseShareTarget(const JSONObject* object) { ...@@ -673,7 +683,7 @@ ManifestParser::ParseShareTarget(const JSONObject* object) {
auto share_target = mojom::blink::ManifestShareTarget::New(); auto share_target = mojom::blink::ManifestShareTarget::New();
share_target->action = ParseURL(share_target_object, "action", manifest_url_, share_target->action = ParseURL(share_target_object, "action", manifest_url_,
ParseURLOriginRestrictions::kSameOriginOnly); ParseURLRestrictions::kSameOriginOnly);
if (!share_target->action.IsValid()) { if (!share_target->action.IsValid()) {
AddErrorInfo( AddErrorInfo(
"property 'share_target' ignored. Property 'action' is " "property 'share_target' ignored. Property 'action' is "
...@@ -775,7 +785,7 @@ ManifestParser::ParseFileHandler(const JSONObject* file_handler) { ...@@ -775,7 +785,7 @@ ManifestParser::ParseFileHandler(const JSONObject* file_handler) {
mojom::blink::ManifestFileHandlerPtr entry = mojom::blink::ManifestFileHandlerPtr entry =
mojom::blink::ManifestFileHandler::New(); mojom::blink::ManifestFileHandler::New();
entry->action = ParseURL(file_handler, "action", manifest_url_, entry->action = ParseURL(file_handler, "action", manifest_url_,
ParseURLOriginRestrictions::kSameOriginOnly); ParseURLRestrictions::kSameOriginOnly);
if (!entry->action.IsValid()) { if (!entry->action.IsValid()) {
AddErrorInfo("FileHandler ignored. Property 'action' is invalid."); AddErrorInfo("FileHandler ignored. Property 'action' is invalid.");
return base::nullopt; return base::nullopt;
...@@ -920,7 +930,7 @@ ManifestParser::ParseProtocolHandler(const JSONObject* object) { ...@@ -920,7 +930,7 @@ ManifestParser::ParseProtocolHandler(const JSONObject* object) {
return base::nullopt; return base::nullopt;
} }
protocol_handler->url = ParseURL(object, "url", manifest_url_, protocol_handler->url = ParseURL(object, "url", manifest_url_,
ParseURLOriginRestrictions::kSameOriginOnly); ParseURLRestrictions::kSameOriginOnly);
bool is_valid_url = protocol_handler->url.IsValid(); bool is_valid_url = protocol_handler->url.IsValid();
if (is_valid_url) { if (is_valid_url) {
const char kToken[] = "%s"; const char kToken[] = "%s";
...@@ -954,7 +964,7 @@ String ManifestParser::ParseRelatedApplicationPlatform( ...@@ -954,7 +964,7 @@ String ManifestParser::ParseRelatedApplicationPlatform(
base::Optional<KURL> ManifestParser::ParseRelatedApplicationURL( base::Optional<KURL> ManifestParser::ParseRelatedApplicationURL(
const JSONObject* application) { const JSONObject* application) {
return ParseURL(application, "url", manifest_url_, return ParseURL(application, "url", manifest_url_,
ParseURLOriginRestrictions::kNoRestrictions); ParseURLRestrictions::kNoRestrictions);
} }
String ManifestParser::ParseRelatedApplicationId( String ManifestParser::ParseRelatedApplicationId(
......
...@@ -51,10 +51,13 @@ class MODULES_EXPORT ManifestParser { ...@@ -51,10 +51,13 @@ class MODULES_EXPORT ManifestParser {
// Used to indicate whether to strip whitespace when parsing a string. // Used to indicate whether to strip whitespace when parsing a string.
enum TrimType { Trim, NoTrim }; enum TrimType { Trim, NoTrim };
// Indicate whether a parsed URL should be restricted to document origin. // Indicate restrictions to be placed on the parsed URL with respect to the
enum class ParseURLOriginRestrictions { // document URL or manifest scope.
enum class ParseURLRestrictions {
kNoRestrictions = 0, kNoRestrictions = 0,
kSameOriginOnly, kSameOriginOnly, // Parsed URLs must be same origin as the document URL.
kWithinScope, // Parsed URLs must be within scope of the manifest scope
// (implies same origin as document URL).
}; };
// Helper function to parse booleans present on a given |dictionary| in a // Helper function to parse booleans present on a given |dictionary| in a
...@@ -99,7 +102,7 @@ class MODULES_EXPORT ManifestParser { ...@@ -99,7 +102,7 @@ class MODULES_EXPORT ManifestParser {
KURL ParseURL(const JSONObject* object, KURL ParseURL(const JSONObject* object,
const String& key, const String& key,
const KURL& base_url, const KURL& base_url,
ParseURLOriginRestrictions origin_restriction); ParseURLRestrictions origin_restriction);
// Parses the 'name' field of the manifest, as defined in: // Parses the 'name' field of the manifest, as defined in:
// https://w3c.github.io/manifest/#dfn-steps-for-processing-the-name-member // https://w3c.github.io/manifest/#dfn-steps-for-processing-the-name-member
......
...@@ -373,7 +373,9 @@ TEST_F(ManifestParserTest, ScopeParseRules) { ...@@ -373,7 +373,9 @@ TEST_F(ManifestParserTest, ScopeParseRules) {
EXPECT_EQ( EXPECT_EQ(
"property 'start_url' ignored, should be same origin as document.", "property 'start_url' ignored, should be same origin as document.",
errors()[0]); errors()[0]);
EXPECT_EQ("property 'scope' ignored, should be same origin as document.", EXPECT_EQ(
"property 'scope' ignored. Start url should be within scope "
"of scope URL.",
errors()[1]); errors()[1]);
} }
...@@ -1389,7 +1391,7 @@ TEST_F(ManifestParserTest, ShortcutUrlParseRules) { ...@@ -1389,7 +1391,7 @@ TEST_F(ManifestParserTest, ShortcutUrlParseRules) {
KURL("http://foo.com/landing/manifest.json"), DefaultDocumentUrl()); KURL("http://foo.com/landing/manifest.json"), DefaultDocumentUrl());
EXPECT_TRUE(manifest->shortcuts.IsEmpty()); EXPECT_TRUE(manifest->shortcuts.IsEmpty());
EXPECT_EQ(2u, GetErrorCount()); EXPECT_EQ(2u, GetErrorCount());
EXPECT_EQ("property 'url' ignored, should be same origin as document.", EXPECT_EQ("property 'url' ignored, should be within scope of the manifest.",
errors()[0]); errors()[0]);
EXPECT_EQ("property 'url' of 'shortcut' not present.", errors()[1]); EXPECT_EQ("property 'url' of 'shortcut' not present.", errors()[1]);
} }
...@@ -1405,11 +1407,10 @@ TEST_F(ManifestParserTest, ShortcutUrlParseRules) { ...@@ -1405,11 +1407,10 @@ TEST_F(ManifestParserTest, ShortcutUrlParseRules) {
KURL("http://foo.com/landing/index.html")); KURL("http://foo.com/landing/index.html"));
EXPECT_TRUE(manifest->shortcuts.IsEmpty()); EXPECT_TRUE(manifest->shortcuts.IsEmpty());
ASSERT_EQ(manifest->scope.GetString(), "http://foo.com/landing"); ASSERT_EQ(manifest->scope.GetString(), "http://foo.com/landing");
EXPECT_EQ(1u, GetErrorCount()); EXPECT_EQ(2u, GetErrorCount());
EXPECT_EQ( EXPECT_EQ("property 'url' ignored, should be within scope of the manifest.",
"property 'url' of 'shortcut' ignored. url should be within scope of "
"the manifest.",
errors()[0]); errors()[0]);
EXPECT_EQ("property 'url' of 'shortcut' not present.", errors()[1]);
} }
// Shortcut url should be within the manifest scope. // Shortcut url should be within the manifest scope.
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment