diff options
author | skerner@chromium.org <skerner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-04 21:25:31 +0000 |
---|---|---|
committer | skerner@chromium.org <skerner@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-04 21:25:31 +0000 |
commit | 542258c2719723200e46a45fdd7dc571ed5b90cf (patch) | |
tree | 5a7754d98ef347c17328696e62ec93bde6738d89 /chrome | |
parent | 7b1c0376a17429471d2163c31ed4dbb05a9e819f (diff) | |
download | chromium_src-542258c2719723200e46a45fdd7dc571ed5b90cf.zip chromium_src-542258c2719723200e46a45fdd7dc571ed5b90cf.tar.gz chromium_src-542258c2719723200e46a45fdd7dc571ed5b90cf.tar.bz2 |
Give a helpful warning message if a url patern contains a port.
BUG=32160
TEST=ExtensionURLPatternTest.Ports,ExtensionManifestTest.*
Review URL: http://codereview.chromium.org/2835034
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76967 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
48 files changed, 629 insertions, 181 deletions
diff --git a/chrome/browser/background_application_list_model_unittest.cc b/chrome/browser/background_application_list_model_unittest.cc index 27e8514..4f4a249 100644 --- a/chrome/browser/background_application_list_model_unittest.cc +++ b/chrome/browser/background_application_list_model_unittest.cc @@ -90,7 +90,7 @@ static scoped_refptr<Extension> CreateExtension(const std::string& name, std::string error; scoped_refptr<Extension> extension = Extension::Create( bogus_file_path().AppendASCII(name), Extension::INVALID, manifest, false, - &error); + true, &error); // Cannot ASSERT_* here because that attempts an illegitimate return. // Cannot EXPECT_NE here because that assumes non-pointers unlike EXPECT_EQ EXPECT_TRUE(extension.get() != NULL) << error; diff --git a/chrome/browser/extensions/convert_user_script.cc b/chrome/browser/extensions/convert_user_script.cc index 0b45da8..f0ec459 100644 --- a/chrome/browser/extensions/convert_user_script.cc +++ b/chrome/browser/extensions/convert_user_script.cc @@ -147,7 +147,12 @@ scoped_refptr<Extension> ConvertUserScriptToExtension( } scoped_refptr<Extension> extension = Extension::Create( - temp_dir.path(), Extension::INTERNAL, *root, false, error); + temp_dir.path(), + Extension::INTERNAL, + *root, + false, // Do not require key + false, // Disable strict checks + error); if (!extension) { NOTREACHED() << "Could not init extension " << *error; return NULL; diff --git a/chrome/browser/extensions/convert_web_app.cc b/chrome/browser/extensions/convert_web_app.cc index fe082d9..66e3e8f 100644 --- a/chrome/browser/extensions/convert_web_app.cc +++ b/chrome/browser/extensions/convert_web_app.cc @@ -168,7 +168,12 @@ scoped_refptr<Extension> ConvertWebAppToExtension( // Finally, create the extension object to represent the unpacked directory. std::string error; scoped_refptr<Extension> extension = Extension::Create( - temp_dir.path(), Extension::INTERNAL, *root, false, &error); + temp_dir.path(), + Extension::INTERNAL, + *root, + false, // Don't require a key. + true, // Enable strict error checks. + &error); if (!extension) { LOG(ERROR) << error; return NULL; diff --git a/chrome/browser/extensions/crx_installer.cc b/chrome/browser/extensions/crx_installer.cc index 1f494b0..39365fd 100644 --- a/chrome/browser/extensions/crx_installer.cc +++ b/chrome/browser/extensions/crx_installer.cc @@ -374,7 +374,11 @@ void CrxInstaller::CompleteInstall() { // lazily and based on the Extension's root path at that moment. std::string error; extension_ = extension_file_util::LoadExtension( - version_dir, install_source_, true, &error); + version_dir, + install_source_, + true, // Require key + false, // Disable strict error checks + &error); CHECK(error.empty()) << error; ReportSuccessFromFileThread(); diff --git a/chrome/browser/extensions/extension_context_menu_api.cc b/chrome/browser/extensions/extension_context_menu_api.cc index 78f4f03..d8e93f2 100644 --- a/chrome/browser/extensions/extension_context_menu_api.cc +++ b/chrome/browser/extensions/extension_context_menu_api.cc @@ -138,7 +138,10 @@ bool ExtensionContextMenuFunction::ParseURLPatterns( return false; URLPattern pattern(ExtensionMenuManager::kAllowedSchemes); - if (URLPattern::PARSE_SUCCESS != pattern.Parse(tmp)) { + // TODO(skerner): Consider enabling strict pattern parsing + // if this extension's location indicates that it is under development. + if (URLPattern::PARSE_SUCCESS != pattern.Parse(tmp, + URLPattern::PARSE_LENIENT)) { error_ = ExtensionErrorUtils::FormatErrorMessage(kInvalidURLPatternError, tmp); return false; diff --git a/chrome/browser/extensions/extension_creator.cc b/chrome/browser/extensions/extension_creator.cc index a211071..e21728f 100644 --- a/chrome/browser/extensions/extension_creator.cc +++ b/chrome/browser/extensions/extension_creator.cc @@ -60,6 +60,7 @@ bool ExtensionCreator::InitializeInput( extension_file_util::LoadExtension(extension_dir, Extension::INTERNAL, false, // key not required + true, // enable strict error checks &error_message_)); if (!extension.get()) return false; // LoadExtension already set error_message_. diff --git a/chrome/browser/extensions/extension_icon_manager_unittest.cc b/chrome/browser/extensions/extension_icon_manager_unittest.cc index 0a4189c..6bb2445 100644 --- a/chrome/browser/extensions/extension_icon_manager_unittest.cc +++ b/chrome/browser/extensions/extension_icon_manager_unittest.cc @@ -111,7 +111,7 @@ TEST_F(ExtensionIconManagerTest, LoadRemoveLoad) { scoped_refptr<Extension> extension(Extension::Create( manifest_path.DirName(), Extension::INVALID, *manifest.get(), - false, NULL)); + false, true, NULL)); ASSERT_TRUE(extension.get()); TestIconManager icon_manager(this); diff --git a/chrome/browser/extensions/extension_info_map_unittest.cc b/chrome/browser/extensions/extension_info_map_unittest.cc index ba5a0b2..7d82dd2 100644 --- a/chrome/browser/extensions/extension_info_map_unittest.cc +++ b/chrome/browser/extensions/extension_info_map_unittest.cc @@ -42,7 +42,8 @@ static scoped_refptr<Extension> CreateExtension(const std::string& name) { std::string error; scoped_refptr<Extension> extension = Extension::Create( - path.AppendASCII(name), Extension::INVALID, manifest, false, &error); + path.AppendASCII(name), Extension::INVALID, manifest, false, true, + &error); EXPECT_TRUE(extension) << error; return extension; @@ -64,7 +65,7 @@ static scoped_refptr<Extension> LoadManifest(const std::string& dir, std::string error; scoped_refptr<Extension> extension = Extension::Create( path, Extension::INVALID, *static_cast<DictionaryValue*>(result.get()), - false, &error); + false, true, &error); EXPECT_TRUE(extension) << error; return extension; diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc index 2022c11..1aa8840 100644 --- a/chrome/browser/extensions/extension_prefs.cc +++ b/chrome/browser/extensions/extension_prefs.cc @@ -488,7 +488,6 @@ void ExtensionPrefs::SetLastPingDayImpl(const Time& time, SavePrefsAndNotify(); } - bool ExtensionPrefs::GetGrantedPermissions( const std::string& extension_id, bool* full_access, @@ -511,11 +510,20 @@ bool ExtensionPrefs::GetGrantedPermissions( // "permissions" array and from the content script "matches" arrays, // so the URLPattern needs to accept valid schemes from both types. for (std::set<std::string>::iterator i = host_permissions.begin(); - i != host_permissions.end(); ++i) - host_extent->AddPattern(URLPattern( + i != host_permissions.end(); ++i) { + URLPattern pattern( Extension::kValidHostPermissionSchemes | - UserScript::kValidUserScriptSchemes, - *i)); + UserScript::kValidUserScriptSchemes); + + // Parse without strict checks, so that new strict checks do not + // fail on a pattern in an installed extension. + if (URLPattern::PARSE_SUCCESS != pattern.Parse( + *i, URLPattern::PARSE_LENIENT)) { + NOTREACHED(); // Corrupt prefs? Hand editing? + } else { + host_extent->AddPattern(pattern); + } + } return true; } diff --git a/chrome/browser/extensions/extension_prefs_unittest.cc b/chrome/browser/extensions/extension_prefs_unittest.cc index fd83f56..71b4bb1 100644 --- a/chrome/browser/extensions/extension_prefs_unittest.cc +++ b/chrome/browser/extensions/extension_prefs_unittest.cc @@ -619,13 +619,13 @@ class ExtensionPrefsPreferencesBase : public ExtensionPrefsTest { ext1_scoped_ = Extension::Create( prefs_.temp_dir().AppendASCII("ext1_"), Extension::EXTERNAL_PREF, - simple_dict, false, &error); + simple_dict, false, true, &error); ext2_scoped_ = Extension::Create( prefs_.temp_dir().AppendASCII("ext2_"), Extension::EXTERNAL_PREF, - simple_dict, false, &error); + simple_dict, false, true, &error); ext3_scoped_ = Extension::Create( prefs_.temp_dir().AppendASCII("ext3_"), Extension::EXTERNAL_PREF, - simple_dict, false, &error); + simple_dict, false, true, &error); ext1_ = ext1_scoped_.get(); ext2_ = ext2_scoped_.get(); diff --git a/chrome/browser/extensions/extension_service.cc b/chrome/browser/extensions/extension_service.cc index 6116e99..7d6d5e10 100644 --- a/chrome/browser/extensions/extension_service.cc +++ b/chrome/browser/extensions/extension_service.cc @@ -201,6 +201,7 @@ void ExtensionServiceBackend::LoadSingleExtension( extension_path, Extension::LOAD, false, // Don't require id + Extension::ShouldDoStrictErrorChecking(Extension::LOAD), &error)); if (!extension) { @@ -833,7 +834,8 @@ void ExtensionService::LoadComponentExtensions() { it->root_directory, Extension::COMPONENT, *static_cast<DictionaryValue*>(manifest.get()), - true, // require key + true, // Require key + Extension::ShouldDoStrictErrorChecking(Extension::COMPONENT), &error)); if (!extension.get()) { NOTREACHED() << error; @@ -878,7 +880,11 @@ void ExtensionService::LoadAllExtensions() { std::string error; scoped_refptr<const Extension> extension( extension_file_util::LoadExtension( - info->extension_path, info->extension_location, false, &error)); + info->extension_path, + info->extension_location, + false, // Don't require key + Extension::ShouldDoStrictErrorChecking(info->extension_location), + &error)); if (extension.get()) { extensions_info->at(i)->extension_manifest.reset( @@ -985,7 +991,7 @@ void ExtensionService::LoadAllExtensions() { } void ExtensionService::LoadInstalledExtension(const ExtensionInfo& info, - bool write_to_prefs) { + bool write_to_prefs) { std::string error; scoped_refptr<const Extension> extension(NULL); if (!extension_prefs_->IsExtensionAllowedByPolicy(info.extension_id)) { @@ -993,8 +999,12 @@ void ExtensionService::LoadInstalledExtension(const ExtensionInfo& info, } else if (info.extension_manifest.get()) { bool require_key = info.extension_location != Extension::LOAD; extension = Extension::Create( - info.extension_path, info.extension_location, *info.extension_manifest, - require_key, &error); + info.extension_path, + info.extension_location, + *info.extension_manifest, + require_key, + Extension::ShouldDoStrictErrorChecking(info.extension_location), + &error); } else { error = errors::kManifestUnreadable; } diff --git a/chrome/browser/extensions/extension_special_storage_policy_unittest.cc b/chrome/browser/extensions/extension_special_storage_policy_unittest.cc index 9597fb8..3a77b25 100644 --- a/chrome/browser/extensions/extension_special_storage_policy_unittest.cc +++ b/chrome/browser/extensions/extension_special_storage_policy_unittest.cc @@ -28,7 +28,7 @@ class ExtensionSpecialStoragePolicyTest : public testing::Test { manifest.Set(keys::kWebURLs, list); std::string error; scoped_refptr<Extension> protected_app = Extension::Create( - path, Extension::INVALID, manifest, false, &error); + path, Extension::INVALID, manifest, false, true, &error); EXPECT_TRUE(protected_app.get()) << error; return protected_app; } @@ -52,7 +52,7 @@ class ExtensionSpecialStoragePolicyTest : public testing::Test { manifest.Set(keys::kWebURLs, list); std::string error; scoped_refptr<Extension> unlimited_app = Extension::Create( - path, Extension::INVALID, manifest, false, &error); + path, Extension::INVALID, manifest, false, true, &error); EXPECT_TRUE(unlimited_app.get()) << error; return unlimited_app; } diff --git a/chrome/browser/extensions/extension_ui_unittest.cc b/chrome/browser/extensions/extension_ui_unittest.cc index 5980109..b2ae1c5 100644 --- a/chrome/browser/extensions/extension_ui_unittest.cc +++ b/chrome/browser/extensions/extension_ui_unittest.cc @@ -42,7 +42,7 @@ namespace { EXPECT_EQ("", error); scoped_refptr<Extension> extension(Extension::Create( - path, Extension::INVALID, *extension_data, true, &error)); + path, Extension::INVALID, *extension_data, true, true, &error)); EXPECT_TRUE(extension.get()); EXPECT_EQ("", error); diff --git a/chrome/browser/extensions/extension_webrequest_api.cc b/chrome/browser/extensions/extension_webrequest_api.cc index d2567a9..a68f2b3 100644 --- a/chrome/browser/extensions/extension_webrequest_api.cc +++ b/chrome/browser/extensions/extension_webrequest_api.cc @@ -131,7 +131,8 @@ bool ExtensionWebRequestEventRouter::RequestFilter::InitFromValue( std::string url; URLPattern pattern(URLPattern::SCHEME_ALL); if (!urls_value->GetString(i, &url) || - pattern.Parse(url) != URLPattern::PARSE_SUCCESS) + pattern.Parse(url, URLPattern::PARSE_STRICT) != + URLPattern::PARSE_SUCCESS) return false; urls.AddPattern(pattern); } diff --git a/chrome/browser/extensions/image_loading_tracker_unittest.cc b/chrome/browser/extensions/image_loading_tracker_unittest.cc index 4e41d32..e43d8d5 100644 --- a/chrome/browser/extensions/image_loading_tracker_unittest.cc +++ b/chrome/browser/extensions/image_loading_tracker_unittest.cc @@ -75,7 +75,7 @@ class ImageLoadingTrackerTest : public testing::Test, return NULL; return Extension::Create( - test_file, Extension::INVALID, *valid_value, false, &error); + test_file, Extension::INVALID, *valid_value, false, true, &error); } SkBitmap image_; diff --git a/chrome/browser/extensions/sandboxed_extension_unpacker.cc b/chrome/browser/extensions/sandboxed_extension_unpacker.cc index 008c2fc..d7a9b1b 100644 --- a/chrome/browser/extensions/sandboxed_extension_unpacker.cc +++ b/chrome/browser/extensions/sandboxed_extension_unpacker.cc @@ -181,7 +181,12 @@ void SandboxedExtensionUnpacker::OnUnpackExtensionSucceeded( } extension_ = Extension::Create( - extension_root_, Extension::INTERNAL, *final_manifest, true, &error); + extension_root_, + Extension::INTERNAL, + *final_manifest, + true, // Require key + false, // Disable strict error checks + &error); if (!extension_.get()) { ReportFailure( diff --git a/chrome/browser/extensions/test_extension_prefs.cc b/chrome/browser/extensions/test_extension_prefs.cc index 0975f27..a8c8a66 100644 --- a/chrome/browser/extensions/test_extension_prefs.cc +++ b/chrome/browser/extensions/test_extension_prefs.cc @@ -98,7 +98,7 @@ scoped_refptr<Extension> TestExtensionPrefs::AddExtensionWithManifest( FilePath path = extensions_dir_.AppendASCII(name); std::string errors; scoped_refptr<Extension> extension = Extension::Create( - path, location, manifest, false, &errors); + path, location, manifest, false, true, &errors); EXPECT_TRUE(extension); if (!extension) return NULL; diff --git a/chrome/browser/extensions/user_script_master.cc b/chrome/browser/extensions/user_script_master.cc index eae62b4..c62f452 100644 --- a/chrome/browser/extensions/user_script_master.cc +++ b/chrome/browser/extensions/user_script_master.cc @@ -107,7 +107,8 @@ bool UserScriptMaster::ScriptReloader::ParseMetadataHeader( script->set_description(value); } else if (GetDeclarationValue(line, kMatchDeclaration, &value)) { URLPattern pattern(UserScript::kValidUserScriptSchemes); - if (URLPattern::PARSE_SUCCESS != pattern.Parse(value)) + if (URLPattern::PARSE_SUCCESS != + pattern.Parse(value, URLPattern::PARSE_LENIENT)) return false; script->add_url_pattern(pattern); } else if (GetDeclarationValue(line, kRunAtDeclaration, &value)) { diff --git a/chrome/browser/sync/glue/extension_util_unittest.cc b/chrome/browser/sync/glue/extension_util_unittest.cc index 946ca5a..2ca92dd 100644 --- a/chrome/browser/sync/glue/extension_util_unittest.cc +++ b/chrome/browser/sync/glue/extension_util_unittest.cc @@ -71,7 +71,7 @@ scoped_refptr<Extension> MakeExtension( std::string error; scoped_refptr<Extension> extension = Extension::Create( - extension_path, location, source, false, &error); + extension_path, location, source, false, true, &error); #if defined(OS_CHROMEOS) if (num_plugins > 0) { // plugins are illegal in extensions on chrome os. EXPECT_FALSE(extension); @@ -386,7 +386,7 @@ scoped_refptr<Extension> MakeSyncableExtension( source.SetString(extension_manifest_keys::kName, name); std::string error; scoped_refptr<Extension> extension = Extension::Create( - extension_path, Extension::INTERNAL, source, false, &error); + extension_path, Extension::INTERNAL, source, false, true, &error); EXPECT_TRUE(extension); EXPECT_EQ("", error); return extension; diff --git a/chrome/browser/sync/glue/theme_util_unittest.cc b/chrome/browser/sync/glue/theme_util_unittest.cc index 575ae86..dfa3ea3 100644 --- a/chrome/browser/sync/glue/theme_util_unittest.cc +++ b/chrome/browser/sync/glue/theme_util_unittest.cc @@ -33,7 +33,7 @@ scoped_refptr<Extension> MakeThemeExtension(const FilePath& extension_path, source.SetString(extension_manifest_keys::kVersion, "0.0.0.0"); std::string error; scoped_refptr<Extension> extension = Extension::Create( - extension_path, Extension::INTERNAL, source, false, &error); + extension_path, Extension::INTERNAL, source, false, true, &error); EXPECT_TRUE(extension); EXPECT_EQ("", error); return extension; diff --git a/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc b/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc index ec661bf..ed51a36 100644 --- a/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc +++ b/chrome/browser/sync/util/extensions_activity_monitor_unittest.cc @@ -73,7 +73,8 @@ class BookmarkAPIEventGenerator { input.SetString(keys::kVersion, kTestExtensionVersion); input.SetString(keys::kName, kTestExtensionName); scoped_refptr<Extension> extension(Extension::Create( - FilePath(extension_path), Extension::INVALID, input, false, &error)); + FilePath(extension_path), Extension::INVALID, input, false, true, + &error)); bookmarks_function->set_name(T::function_name()); base::WaitableEvent done_event(false, false); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, @@ -131,7 +132,8 @@ class ExtensionsActivityMonitorTest : public testing::Test { input.SetString(keys::kVersion, kTestExtensionVersion); input.SetString(keys::kName, kTestExtensionName); scoped_refptr<Extension> extension(Extension::Create( - FilePath(extension_path), Extension::INVALID, input, false, &error)); + FilePath(extension_path), Extension::INVALID, input, false, true, + &error)); EXPECT_EQ("", error); return extension->id(); } diff --git a/chrome/browser/themes/browser_theme_pack_unittest.cc b/chrome/browser/themes/browser_theme_pack_unittest.cc index d13945c..72219f0 100644 --- a/chrome/browser/themes/browser_theme_pack_unittest.cc +++ b/chrome/browser/themes/browser_theme_pack_unittest.cc @@ -406,7 +406,8 @@ TEST_F(BrowserThemePackTest, CanBuildAndReadPack) { EXPECT_EQ("", error); ASSERT_TRUE(valid_value.get()); scoped_refptr<Extension> extension(Extension::Create( - star_gazing_path, Extension::INVALID, *valid_value, true, &error)); + star_gazing_path, Extension::INVALID, *valid_value, true, true, + &error)); ASSERT_TRUE(extension.get()); ASSERT_EQ("", error); diff --git a/chrome/browser/ui/cocoa/extensions/extension_install_prompt_controller_unittest.mm b/chrome/browser/ui/cocoa/extensions/extension_install_prompt_controller_unittest.mm index fb393af..f11aeb4 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_install_prompt_controller_unittest.mm +++ b/chrome/browser/ui/cocoa/extensions/extension_install_prompt_controller_unittest.mm @@ -61,7 +61,7 @@ public: } extension_ = Extension::Create( - path.DirName(), Extension::INVALID, *value, false, &error); + path.DirName(), Extension::INVALID, *value, false, true, &error); if (!extension_.get()) { LOG(ERROR) << error; return; diff --git a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller_unittest.mm b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller_unittest.mm index 1cafd26..5115d7d 100644 --- a/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller_unittest.mm +++ b/chrome/browser/ui/cocoa/extensions/extension_installed_bubble_controller_unittest.mm @@ -101,7 +101,7 @@ class ExtensionInstalledBubbleControllerTest : public CocoaTest { std::string error; return Extension::Create( - path, Extension::INVALID, extension_input_value, false, &error); + path, Extension::INVALID, extension_input_value, false, true, &error); } // Allows us to create the window and browser for testing. diff --git a/chrome/common/extensions/extension.cc b/chrome/common/extensions/extension.cc index c979d24..ce77fa3 100644 --- a/chrome/common/extensions/extension.cc +++ b/chrome/common/extensions/extension.cc @@ -268,9 +268,11 @@ scoped_refptr<Extension> Extension::Create(const FilePath& path, Location location, const DictionaryValue& value, bool require_key, + bool strict_error_checks, std::string* error) { scoped_refptr<Extension> extension = new Extension(path, location); - if (!extension->InitFromValue(value, require_key, error)) + + if (!extension->InitFromValue(value, require_key, strict_error_checks, error)) return NULL; return extension; } @@ -515,7 +517,9 @@ bool Extension::GenerateId(const std::string& input, std::string* output) { // Helper method that loads a UserScript object from a dictionary in the // content_script list of the manifest. bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, - int definition_index, std::string* error, + int definition_index, + URLPattern::ParseOption parse_strictness, + std::string* error, UserScript* result) { // run_at if (content_script->HasKey(keys::kRunAt)) { @@ -566,8 +570,11 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, for (size_t j = 0; j < matches->GetSize(); ++j) { std::string match_str; if (!matches->GetString(j, &match_str)) { - *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidMatch, - base::IntToString(definition_index), base::IntToString(j)); + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidMatch, + base::IntToString(definition_index), + base::IntToString(j), + errors::kExpectString); return false; } @@ -575,9 +582,14 @@ bool Extension::LoadUserScriptHelper(const DictionaryValue* content_script, if (CanExecuteScriptEverywhere()) pattern.set_valid_schemes(URLPattern::SCHEME_ALL); - if (URLPattern::PARSE_SUCCESS != pattern.Parse(match_str)) { - *error = ExtensionErrorUtils::FormatErrorMessage(errors::kInvalidMatch, - base::IntToString(definition_index), base::IntToString(j)); + URLPattern::ParseResult parse_result = pattern.Parse(match_str, + parse_strictness); + if (parse_result != URLPattern::PARSE_SUCCESS) { + *error = ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidMatch, + base::IntToString(definition_index), + base::IntToString(j), + URLPattern::GetParseResultString(parse_result)); return false; } @@ -878,6 +890,7 @@ bool Extension::LoadExtent(const DictionaryValue* manifest, ExtensionExtent* extent, const char* list_error, const char* value_error, + URLPattern::ParseOption parse_strictness, std::string* error) { Value* temp = NULL; if (!manifest->Get(key, &temp)) @@ -893,34 +906,52 @@ bool Extension::LoadExtent(const DictionaryValue* manifest, std::string pattern_string; if (!pattern_list->GetString(i, &pattern_string)) { *error = ExtensionErrorUtils::FormatErrorMessage(value_error, - base::UintToString(i)); + base::UintToString(i), + errors::kExpectString); return false; } URLPattern pattern(kValidWebExtentSchemes); - URLPattern::ParseResult result = pattern.Parse(pattern_string); - if (result == URLPattern::PARSE_ERROR_EMPTY_PATH) { + URLPattern::ParseResult parse_result = pattern.Parse(pattern_string, + parse_strictness); + if (parse_result == URLPattern::PARSE_ERROR_EMPTY_PATH) { pattern_string += "/"; - result = pattern.Parse(pattern_string); + parse_result = pattern.Parse(pattern_string, parse_strictness); } - if (URLPattern::PARSE_SUCCESS != result) { - *error = ExtensionErrorUtils::FormatErrorMessage(value_error, - base::UintToString(i)); + + if (parse_result != URLPattern::PARSE_SUCCESS) { + *error = ExtensionErrorUtils::FormatErrorMessage( + value_error, + base::UintToString(i), + URLPattern::GetParseResultString(parse_result)); return false; } - // Do not allow authors to claim "<all_urls>" or "*" for host. - if (pattern.match_all_urls() || pattern.host().empty()) { - *error = ExtensionErrorUtils::FormatErrorMessage(value_error, - base::UintToString(i)); + // Do not allow authors to claim "<all_urls>". + if (pattern.match_all_urls()) { + *error = ExtensionErrorUtils::FormatErrorMessage( + value_error, + base::UintToString(i), + errors::kCannotClaimAllURLsInExtent); + return false; + } + + // Do not allow authors to claim "*" for host. + if (pattern.host().empty()) { + *error = ExtensionErrorUtils::FormatErrorMessage( + value_error, + base::UintToString(i), + errors::kCannotClaimAllHostsInExtent); return false; } // We do not allow authors to put wildcards in their paths. Instead, we // imply one at the end. if (pattern.path().find('*') != std::string::npos) { - *error = ExtensionErrorUtils::FormatErrorMessage(value_error, - base::UintToString(i)); + *error = ExtensionErrorUtils::FormatErrorMessage( + value_error, + base::UintToString(i), + errors::kNoWildCardsInPaths); return false; } pattern.SetPath(pattern.path() + '*'); @@ -993,15 +1024,32 @@ bool Extension::LoadLaunchURL(const DictionaryValue* manifest, // process isolation, we must insert any provided value into the component // app's launch url and web extent. if (id() == extension_misc::kWebStoreAppId) { - GURL gallery_url(CommandLine::ForCurrentProcess()-> - GetSwitchValueASCII(switches::kAppsGalleryURL)); - if (gallery_url.is_valid()) { - launch_web_url_ = gallery_url.spec(); + std::string gallery_url_str = CommandLine::ForCurrentProcess()-> + GetSwitchValueASCII(switches::kAppsGalleryURL); + + // Empty string means option was not used. + if (!gallery_url_str.empty()) { + GURL gallery_url(gallery_url_str); + if (!gallery_url.is_valid()) { + LOG(WARNING) << "Invalid url given in switch " + << switches::kAppsGalleryURL; + } else { + if (gallery_url.has_port()) { + LOG(WARNING) << "URLs passed to switch " << switches::kAppsGalleryURL + << " should not contain a port. Removing it."; + + GURL::Replacements remove_port; + remove_port.ClearPort(); + gallery_url = gallery_url.ReplaceComponents(remove_port); + } + + launch_web_url_ = gallery_url.spec(); - URLPattern pattern(kValidWebExtentSchemes); - pattern.Parse(gallery_url.spec()); - pattern.SetPath(pattern.path() + '*'); - extent_.AddPattern(pattern); + URLPattern pattern(kValidWebExtentSchemes); + pattern.Parse(gallery_url.spec(), URLPattern::PARSE_STRICT); + pattern.SetPath(pattern.path() + '*'); + extent_.AddPattern(pattern); + } } } @@ -1278,7 +1326,12 @@ GURL Extension::GetBaseURLFromExtensionId(const std::string& extension_id) { } bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, - std::string* error) { + bool strict_error_checks, std::string* error) { + // When strict error checks are enabled, make URL pattern parsing strict. + URLPattern::ParseOption parse_strictness = + (strict_error_checks ? URLPattern::PARSE_STRICT + : URLPattern::PARSE_LENIENT); + if (source.HasKey(keys::kPublicKey)) { std::string public_key_bytes; if (!source.GetString(keys::kPublicKey, @@ -1646,7 +1699,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, } UserScript script; - if (!LoadUserScriptHelper(content_script, i, error, &script)) + if (!LoadUserScriptHelper(content_script, i, parse_strictness, error, + &script)) return false; // Failed to parse script context definition. script.set_extension_id(id()); if (converted_from_user_script_) { @@ -1714,7 +1768,8 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, if (!LoadIsApp(manifest_value_.get(), error) || !LoadExtent(manifest_value_.get(), keys::kWebURLs, &extent_, - errors::kInvalidWebURLs, errors::kInvalidWebURL, error) || + errors::kInvalidWebURLs, errors::kInvalidWebURL, + parse_strictness, error) || !EnsureNotHybridApp(manifest_value_.get(), error) || !LoadLaunchURL(manifest_value_.get(), error) || !LoadLaunchContainer(manifest_value_.get(), error)) { @@ -1810,7 +1865,9 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, URLPattern pattern = URLPattern(CanExecuteScriptEverywhere() ? URLPattern::SCHEME_ALL : kValidHostPermissionSchemes); - if (URLPattern::PARSE_SUCCESS == pattern.Parse(permission_str)) { + URLPattern::ParseResult parse_result = pattern.Parse(permission_str, + parse_strictness); + if (parse_result == URLPattern::PARSE_SUCCESS) { if (!CanSpecifyHostPermission(pattern)) { *error = ExtensionErrorUtils::FormatErrorMessage( errors::kInvalidPermissionScheme, base::IntToString(i)); @@ -1829,6 +1886,10 @@ bool Extension::InitFromValue(const DictionaryValue& source, bool require_key, // backwards compatability (http://crbug.com/42742). // TODO(jstritar): We can improve error messages by adding better // validation of API permissions here. + // TODO(skerner): Consider showing the reason |permission_str| is not + // a valid URL pattern if it is almost valid. For example, if it has + // a valid scheme, and failed to parse because it has a port, show an + // error. } } diff --git a/chrome/common/extensions/extension.h b/chrome/common/extensions/extension.h index 772d72c..49850aa 100644 --- a/chrome/common/extensions/extension.h +++ b/chrome/common/extensions/extension.h @@ -114,10 +114,21 @@ class Extension : public base::RefCountedThreadSafe<Extension> { const int message_id; }; + // |strict_error_checks| enables extra error checking, such as + // checks that URL patterns do not contain ports. This error + // checking may find an error that a previous version of + // chrome did not flag. To avoid errors in installed extensions + // when chrome is upgraded, strict error checking is only enabled + // when loading extensions as a developer would (such as loading + // an unpacked extension), or when loading an extension that is + // tied to a specific version of chrome (such as a component + // extension). Most callers will set |strict_error_checks| to + // Extension::ShouldDoStrictErrorChecking(location). static scoped_refptr<Extension> Create(const FilePath& path, Location location, const DictionaryValue& value, bool require_key, + bool strict_error_checks, std::string* error); // Return the update url used by gallery/webstore extensions. @@ -236,6 +247,17 @@ class Extension : public base::RefCountedThreadSafe<Extension> { IsExternalLocation(location); } + // Whether extensions with |location| should be loaded with strict + // error checking. Strict error checks may flag errors older versions + // of chrome did not detect. To avoid breaking installed extensions, + // strict checks are disabled unless the location indicates that the + // developer is loading the extension, or the extension is a component + // of chrome. + static inline bool ShouldDoStrictErrorChecking(Location location) { + return location == Extension::LOAD || + location == Extension::COMPONENT; + } + // See Type definition above. Type GetType() const; @@ -513,7 +535,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // its manifest, but if |require_key| is |false|, a temporary ID will be // generated based on the path. bool InitFromValue(const DictionaryValue& value, bool require_key, - std::string* error); + bool strict_error_checks, std::string* error); // Helper function for implementing HasCachedImage/GetCachedImage. A return // value of NULL means there is no matching image cached (we allow caching an @@ -525,6 +547,7 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // dictionary in the content_script list of the manifest. bool LoadUserScriptHelper(const DictionaryValue* content_script, int definition_index, + URLPattern::ParseOption parse_strictness, std::string* error, UserScript* result); @@ -539,9 +562,13 @@ class Extension : public base::RefCountedThreadSafe<Extension> { // Helpers to load various chunks of the manifest. bool LoadIsApp(const DictionaryValue* manifest, std::string* error); - bool LoadExtent(const DictionaryValue* manifest, const char* key, - ExtensionExtent* extent, const char* list_error, - const char* value_error, std::string* error); + bool LoadExtent(const DictionaryValue* manifest, + const char* key, + ExtensionExtent* extent, + const char* list_error, + const char* value_error, + URLPattern::ParseOption parse_strictness, + std::string* error); bool LoadLaunchContainer(const DictionaryValue* manifest, std::string* error); bool LoadLaunchURL(const DictionaryValue* manifest, std::string* error); bool EnsureNotHybridApp(const DictionaryValue* manifest, std::string* error); diff --git a/chrome/common/extensions/extension_constants.cc b/chrome/common/extensions/extension_constants.cc index e30ff97..96fd349 100644 --- a/chrome/common/extensions/extension_constants.cc +++ b/chrome/common/extensions/extension_constants.cc @@ -100,6 +100,10 @@ const char* kAppsNotEnabled = const char* kCannotAccessPage = "Cannot access contents of url \"*\". " "Extension manifest must request permission to access this host."; +const char* kCannotClaimAllHostsInExtent = + "Cannot claim all hosts ('*') in an extent."; +const char* kCannotClaimAllURLsInExtent = + "Cannot claim all URLs in an extent."; const char* kCannotScriptGallery = "The extensions gallery cannot be scripted."; const char* kChromeVersionTooLow = @@ -109,6 +113,7 @@ const char* kDisabledByPolicy = const char* kDevToolsExperimental = "You must request the 'experimental' permission in order to use the" " DevTools API."; +const char* kExpectString = "Expect string value."; const char* kExperimentalFlagRequired = "Loading extensions with 'experimental' permission requires" " --enable-experimental-extension-apis command line flag."; @@ -171,7 +176,7 @@ const char* kInvalidLaunchWidthContainer = const char* kInvalidManifest = "Manifest file is invalid."; const char* kInvalidMatch = - "Invalid value for 'content_scripts[*].matches[*]'."; + "Invalid value for 'content_scripts[*].matches[*]': *"; const char* kInvalidMatchCount = "Invalid value for 'content_scripts[*].matches'. There must be at least" "one match specified."; @@ -269,7 +274,7 @@ const char* kInvalidVersion = "Required value 'version' is missing or invalid. It must be between 1-4 " "dot-separated integers each between 0 and 65536."; const char* kInvalidWebURL = - "Invalid value for 'app.urls[*]'."; + "Invalid value for 'app.urls[*]': *"; const char* kInvalidWebURLs = "Invalid value for 'app.urls'."; const char* kInvalidZipHash = @@ -296,6 +301,8 @@ const char* kMissingFile = "At least one js or css file is required for 'content_scripts[*]'."; const char* kMultipleOverrides = "An extension cannot override more than one page."; +const char* kNoWildCardsInPaths = + "Wildcards are not allowed in extent URL pattern paths."; const char* kOneUISurfaceOnly = "Only one of 'browser_action', 'page_action', and 'app' can be specified."; const char* kReservedMessageFound = diff --git a/chrome/common/extensions/extension_constants.h b/chrome/common/extensions/extension_constants.h index 756a888..bad2a5b 100644 --- a/chrome/common/extensions/extension_constants.h +++ b/chrome/common/extensions/extension_constants.h @@ -100,11 +100,14 @@ namespace extension_manifest_values { namespace extension_manifest_errors { extern const char* kAppsNotEnabled; extern const char* kCannotAccessPage; + extern const char* kCannotClaimAllHostsInExtent; + extern const char* kCannotClaimAllURLsInExtent; extern const char* kCannotScriptGallery; extern const char* kChromeVersionTooLow; extern const char* kDevToolsExperimental; extern const char* kDisabledByPolicy; extern const char* kExperimentalFlagRequired; + extern const char* kExpectString; extern const char* kHostedAppsCannotIncludeExtensionFeatures; extern const char* kInvalidAllFrames; extern const char* kInvalidBackground; @@ -197,6 +200,7 @@ namespace extension_manifest_errors { extern const char* kManifestUnreadable; extern const char* kMissingFile; extern const char* kMultipleOverrides; + extern const char* kNoWildCardsInPaths; extern const char* kOneUISurfaceOnly; extern const char* kReservedMessageFound; extern const char* kSidebarExperimental; diff --git a/chrome/common/extensions/extension_file_util.cc b/chrome/common/extensions/extension_file_util.cc index 0a8197a..6e9a4eb 100644 --- a/chrome/common/extensions/extension_file_util.cc +++ b/chrome/common/extensions/extension_file_util.cc @@ -89,6 +89,7 @@ void UninstallExtension(const FilePath& extensions_dir, scoped_refptr<Extension> LoadExtension(const FilePath& extension_path, Extension::Location location, bool require_key, + bool strict_error_checks, std::string* error) { FilePath manifest_path = extension_path.Append(Extension::kManifestFilename); @@ -124,7 +125,12 @@ scoped_refptr<Extension> LoadExtension(const FilePath& extension_path, return NULL; scoped_refptr<Extension> extension(Extension::Create( - extension_path, location, *manifest, require_key, error)); + extension_path, + location, + *manifest, + require_key, + strict_error_checks, + error)); if (!extension.get()) return NULL; diff --git a/chrome/common/extensions/extension_file_util.h b/chrome/common/extensions/extension_file_util.h index 04de555..066ee80 100644 --- a/chrome/common/extensions/extension_file_util.h +++ b/chrome/common/extensions/extension_file_util.h @@ -40,6 +40,7 @@ void UninstallExtension(const FilePath& extensions_dir, scoped_refptr<Extension> LoadExtension(const FilePath& extension_root, Extension::Location location, bool require_key, + bool strict_error_checks, std::string* error); // Returns true if the given extension object is valid and consistent. diff --git a/chrome/common/extensions/extension_file_util_unittest.cc b/chrome/common/extensions/extension_file_util_unittest.cc index 3f59da6..d3cabff 100644 --- a/chrome/common/extensions/extension_file_util_unittest.cc +++ b/chrome/common/extensions/extension_file_util_unittest.cc @@ -79,7 +79,7 @@ TEST(ExtensionFileUtil, LoadExtensionWithValidLocales) { std::string error; scoped_refptr<Extension> extension(extension_file_util::LoadExtension( - install_dir, Extension::LOAD, false, &error)); + install_dir, Extension::LOAD, false, true, &error)); ASSERT_TRUE(extension != NULL); EXPECT_EQ("The first extension that I made.", extension->description()); } @@ -95,7 +95,7 @@ TEST(ExtensionFileUtil, LoadExtensionWithoutLocalesFolder) { std::string error; scoped_refptr<Extension> extension(extension_file_util::LoadExtension( - install_dir, Extension::LOAD, false, &error)); + install_dir, Extension::LOAD, false, true, &error)); ASSERT_FALSE(extension == NULL); EXPECT_TRUE(error.empty()); } @@ -153,7 +153,7 @@ TEST(ExtensionFileUtil, LoadExtensionGivesHelpfullErrorOnMissingManifest) { std::string error; scoped_refptr<Extension> extension(extension_file_util::LoadExtension( - install_dir, Extension::LOAD, false, &error)); + install_dir, Extension::LOAD, false, true, &error)); ASSERT_TRUE(extension == NULL); ASSERT_FALSE(error.empty()); ASSERT_STREQ("Manifest file is missing or unreadable.", error.c_str()); @@ -170,7 +170,7 @@ TEST(ExtensionFileUtil, LoadExtensionGivesHelpfullErrorOnBadManifest) { std::string error; scoped_refptr<Extension> extension(extension_file_util::LoadExtension( - install_dir, Extension::LOAD, false, &error)); + install_dir, Extension::LOAD, false, true, &error)); ASSERT_TRUE(extension == NULL); ASSERT_FALSE(error.empty()); ASSERT_STREQ("Manifest is not valid JSON. " @@ -186,7 +186,7 @@ TEST(ExtensionFileUtil, FailLoadingNonUTF8Scripts) { std::string error; scoped_refptr<Extension> extension(extension_file_util::LoadExtension( - install_dir, Extension::LOAD, false, &error)); + install_dir, Extension::LOAD, false, true, &error)); ASSERT_TRUE(extension == NULL); ASSERT_STREQ("Could not load file 'bad_encoding.js' for content script. " "It isn't UTF-8 encoded.", error.c_str()); diff --git a/chrome/common/extensions/extension_manifests_unittest.cc b/chrome/common/extensions/extension_manifests_unittest.cc index a6bc71f..63172a9 100644 --- a/chrome/common/extensions/extension_manifests_unittest.cc +++ b/chrome/common/extensions/extension_manifests_unittest.cc @@ -7,6 +7,7 @@ #include "base/file_util.h" #include "base/path_service.h" #include "base/scoped_ptr.h" +#include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "chrome/common/chrome_paths.h" @@ -42,31 +43,41 @@ class ExtensionManifestTest : public testing::Test { scoped_refptr<Extension> LoadExtensionWithLocation( DictionaryValue* value, Extension::Location location, + bool strict_error_checks, std::string* error) { FilePath path; PathService::Get(chrome::DIR_TEST_DATA, &path); path = path.AppendASCII("extensions").AppendASCII("manifest_tests"); - return Extension::Create(path.DirName(), location, *value, false, error); + return Extension::Create(path.DirName(), location, *value, false, + strict_error_checks, error); } scoped_refptr<Extension> LoadExtension(const std::string& name, std::string* error) { - return LoadExtensionWithLocation(name, Extension::INTERNAL, error); + return LoadExtensionWithLocation(name, Extension::INTERNAL, false, error); + } + + scoped_refptr<Extension> LoadExtensionStrict(const std::string& name, + std::string* error) { + return LoadExtensionWithLocation(name, Extension::INTERNAL, true, error); } scoped_refptr<Extension> LoadExtension(DictionaryValue* value, std::string* error) { - return LoadExtensionWithLocation(value, Extension::INTERNAL, error); + // Loading as an installed extension disables strict error checks. + return LoadExtensionWithLocation(value, Extension::INTERNAL, false, error); } scoped_refptr<Extension> LoadExtensionWithLocation( const std::string& name, Extension::Location location, + bool strict_error_checks, std::string* error) { scoped_ptr<DictionaryValue> value(LoadManifestFile(name, error)); if (!value.get()) return NULL; - return LoadExtensionWithLocation(value.get(), location, error); + return LoadExtensionWithLocation(value.get(), location, + strict_error_checks, error); } scoped_refptr<Extension> LoadAndExpectSuccess(const std::string& name) { @@ -77,6 +88,14 @@ class ExtensionManifestTest : public testing::Test { return extension; } + scoped_refptr<Extension> LoadStrictAndExpectSuccess(const std::string& name) { + std::string error; + scoped_refptr<Extension> extension = LoadExtensionStrict(name, &error); + EXPECT_TRUE(extension) << name; + EXPECT_EQ("", error) << name; + return extension; + } + scoped_refptr<Extension> LoadAndExpectSuccess(DictionaryValue* manifest, const std::string& name) { std::string error; @@ -104,6 +123,13 @@ class ExtensionManifestTest : public testing::Test { VerifyExpectedError(extension.get(), name, error, expected_error); } + void LoadAndExpectErrorStrict(const std::string& name, + const std::string& expected_error) { + std::string error; + scoped_refptr<Extension> extension(LoadExtensionStrict(name, &error)); + VerifyExpectedError(extension.get(), name, error, expected_error); + } + void LoadAndExpectError(DictionaryValue* manifest, const std::string& name, const std::string& expected_error) { @@ -129,18 +155,52 @@ TEST_F(ExtensionManifestTest, ValidApp) { TEST_F(ExtensionManifestTest, AppWebUrls) { LoadAndExpectError("web_urls_wrong_type.json", errors::kInvalidWebURLs); - LoadAndExpectError("web_urls_invalid_1.json", - ExtensionErrorUtils::FormatErrorMessage( - errors::kInvalidWebURL, "0")); - LoadAndExpectError("web_urls_invalid_2.json", - ExtensionErrorUtils::FormatErrorMessage( - errors::kInvalidWebURL, "0")); - LoadAndExpectError("web_urls_invalid_3.json", - ExtensionErrorUtils::FormatErrorMessage( - errors::kInvalidWebURL, "0")); - LoadAndExpectError("web_urls_invalid_4.json", - ExtensionErrorUtils::FormatErrorMessage( - errors::kInvalidWebURL, "0")); + LoadAndExpectError( + "web_urls_invalid_1.json", + ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidWebURL, + base::IntToString(0), + errors::kExpectString)); + + LoadAndExpectError( + "web_urls_invalid_2.json", + ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidWebURL, + base::IntToString(0), + URLPattern::GetParseResultString( + URLPattern::PARSE_ERROR_MISSING_SCHEME_SEPARATOR))); + + LoadAndExpectError( + "web_urls_invalid_3.json", + ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidWebURL, + base::IntToString(0), + errors::kNoWildCardsInPaths)); + + LoadAndExpectError( + "web_urls_invalid_4.json", + ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidWebURL, + base::IntToString(0), + errors::kCannotClaimAllURLsInExtent)); + + LoadAndExpectError( + "web_urls_invalid_5.json", + ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidWebURL, + base::IntToString(1), + errors::kCannotClaimAllHostsInExtent)); + + // Ports in app.urls only raise an error when loading as a + // developer would. + LoadAndExpectSuccess("web_urls_invalid_has_port.json"); + LoadAndExpectErrorStrict( + "web_urls_invalid_has_port.json", + ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidWebURL, + base::IntToString(1), + URLPattern::GetParseResultString(URLPattern::PARSE_ERROR_HAS_COLON))); + scoped_refptr<Extension> extension( LoadAndExpectSuccess("web_urls_default.json")); @@ -238,13 +298,45 @@ TEST_F(ExtensionManifestTest, ChromeResourcesPermissionValidOnlyForComponents) { extension = LoadExtensionWithLocation( "permission_chrome_resources_url.json", Extension::COMPONENT, + true, // Strict error checking &error); EXPECT_EQ("", error); } -TEST_F(ExtensionManifestTest, ChromeURLContentScriptInvalid) { - LoadAndExpectError("content_script_chrome_url_invalid.json", - errors::kInvalidMatch); +TEST_F(ExtensionManifestTest, InvalidContentScriptMatchPattern) { + + // chrome:// urls are not allowed. + LoadAndExpectError( + "content_script_chrome_url_invalid.json", + ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidMatch, + base::IntToString(0), + base::IntToString(0), + URLPattern::GetParseResultString( + URLPattern::PARSE_ERROR_INVALID_SCHEME))); + + // Match paterns must be strings. + LoadAndExpectError( + "content_script_match_pattern_not_string.json", + ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidMatch, + base::IntToString(0), + base::IntToString(0), + errors::kExpectString)); + + // Ports in match patterns cause an error, but only when loading + // in developer mode. + LoadAndExpectSuccess("forbid_ports_in_content_scripts.json"); + + // Loading as a developer would should give an error. + LoadAndExpectErrorStrict( + "forbid_ports_in_content_scripts.json", + ExtensionErrorUtils::FormatErrorMessage( + errors::kInvalidMatch, + base::IntToString(1), + base::IntToString(0), + URLPattern::GetParseResultString( + URLPattern::PARSE_ERROR_HAS_COLON))); } TEST_F(ExtensionManifestTest, ExperimentalPermission) { @@ -444,3 +536,14 @@ TEST_F(ExtensionManifestTest, TtsProvider) { EXPECT_EQ("en-US", extension->tts_voices()[0].locale); EXPECT_EQ("female", extension->tts_voices()[0].gender); } + +TEST_F(ExtensionManifestTest, ForbidPortsInPermissions) { + // Loading as a user would shoud not trigger an error. + LoadAndExpectSuccess("forbid_ports_in_permissions.json"); + + // Ideally, loading as a developer would give an error. + // To ensure that we do not error out on a valid permission + // in a future version of chrome, validation is to loose + // to flag this case. + LoadStrictAndExpectSuccess("forbid_ports_in_permissions.json"); +} diff --git a/chrome/common/extensions/extension_set_unittest.cc b/chrome/common/extensions/extension_set_unittest.cc index 1b85ac5..a68e1d7 100644 --- a/chrome/common/extensions/extension_set_unittest.cc +++ b/chrome/common/extensions/extension_set_unittest.cc @@ -37,10 +37,11 @@ scoped_refptr<Extension> CreateTestExtension(const std::string& name, } const bool kRequireKey = false; + const bool kStrictErrorChecks = true; std::string error; scoped_refptr<Extension> extension( Extension::Create(path, Extension::INTERNAL, manifest, kRequireKey, - &error)); + kStrictErrorChecks, &error)); EXPECT_TRUE(extension.get()) << error; return extension; } diff --git a/chrome/common/extensions/extension_unittest.cc b/chrome/common/extensions/extension_unittest.cc index 92d60fd..ab3c3a2 100644 --- a/chrome/common/extensions/extension_unittest.cc +++ b/chrome/common/extensions/extension_unittest.cc @@ -105,7 +105,7 @@ TEST(ExtensionTest, InitFromValueInvalid) { EXPECT_EQ("", error); EXPECT_EQ(0, error_code); ASSERT_TRUE(valid_value.get()); - ASSERT_TRUE(extension.InitFromValue(*valid_value, true, &error)); + ASSERT_TRUE(extension.InitFromValue(*valid_value, true, false, &error)); ASSERT_EQ("", error); EXPECT_EQ("en_US", extension.default_locale()); @@ -114,33 +114,33 @@ TEST(ExtensionTest, InitFromValueInvalid) { // Test missing and invalid versions input_value.reset(valid_value->DeepCopy()); input_value->Remove(keys::kVersion, NULL); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_EQ(errors::kInvalidVersion, error); input_value->SetInteger(keys::kVersion, 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_EQ(errors::kInvalidVersion, error); // Test missing and invalid names. input_value.reset(valid_value->DeepCopy()); input_value->Remove(keys::kName, NULL); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_EQ(errors::kInvalidName, error); input_value->SetInteger(keys::kName, 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_EQ(errors::kInvalidName, error); // Test invalid description input_value.reset(valid_value->DeepCopy()); input_value->SetInteger(keys::kDescription, 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_EQ(errors::kInvalidDescription, error); // Test invalid icons input_value.reset(valid_value->DeepCopy()); input_value->SetInteger(keys::kIcons, 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_EQ(errors::kInvalidIcons, error); // Test invalid icon paths @@ -149,13 +149,13 @@ TEST(ExtensionTest, InitFromValueInvalid) { input_value->GetDictionary(keys::kIcons, &icons); ASSERT_FALSE(NULL == icons); icons->SetInteger(base::IntToString(128), 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidIconPath)); // Test invalid user scripts list input_value.reset(valid_value->DeepCopy()); input_value->SetInteger(keys::kContentScripts, 42); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_EQ(errors::kInvalidContentScriptsList, error); // Test invalid user script item @@ -164,7 +164,7 @@ TEST(ExtensionTest, InitFromValueInvalid) { input_value->GetList(keys::kContentScripts, &content_scripts); ASSERT_FALSE(NULL == content_scripts); content_scripts->Set(0, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidContentScript)); // Test missing and invalid matches array @@ -173,25 +173,25 @@ TEST(ExtensionTest, InitFromValueInvalid) { DictionaryValue* user_script = NULL; content_scripts->GetDictionary(0, &user_script); user_script->Remove(keys::kMatches, NULL); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatches)); user_script->Set(keys::kMatches, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatches)); ListValue* matches = new ListValue; user_script->Set(keys::kMatches, matches); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatchCount)); // Test invalid match element matches->Set(0, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatch)); matches->Set(0, Value::CreateStringValue("chrome://*/*")); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMatch)); // Test missing and invalid files array @@ -200,67 +200,68 @@ TEST(ExtensionTest, InitFromValueInvalid) { content_scripts->GetDictionary(0, &user_script); user_script->Remove(keys::kJs, NULL); user_script->Remove(keys::kCss, NULL); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kMissingFile)); user_script->Set(keys::kJs, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidJsList)); user_script->Set(keys::kCss, new ListValue); user_script->Set(keys::kJs, new ListValue); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kMissingFile)); user_script->Remove(keys::kCss, NULL); ListValue* files = new ListValue; user_script->Set(keys::kJs, files); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kMissingFile)); // Test invalid file element files->Set(0, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidJs)); user_script->Remove(keys::kJs, NULL); // Test the css element user_script->Set(keys::kCss, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidCssList)); // Test invalid file element ListValue* css_files = new ListValue; user_script->Set(keys::kCss, css_files); css_files->Set(0, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidCss)); // Test missing and invalid permissions array input_value.reset(valid_value->DeepCopy()); - EXPECT_TRUE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_TRUE(extension.InitFromValue(*input_value, true, false, &error)); + ListValue* permissions = NULL; input_value->GetList(keys::kPermissions, &permissions); ASSERT_FALSE(NULL == permissions); permissions = new ListValue; input_value->Set(keys::kPermissions, permissions); - EXPECT_TRUE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_TRUE(extension.InitFromValue(*input_value, true, false, &error)); input_value->Set(keys::kPermissions, Value::CreateIntegerValue(9)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidPermissions)); input_value.reset(valid_value->DeepCopy()); input_value->GetList(keys::kPermissions, &permissions); permissions->Set(0, Value::CreateIntegerValue(24)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidPermission)); // We allow unknown API permissions, so this will be valid until we better // distinguish between API and host permissions. permissions->Set(0, Value::CreateStringValue("www.google.com")); - EXPECT_TRUE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_TRUE(extension.InitFromValue(*input_value, true, false, &error)); // Multiple page actions are not allowed. input_value.reset(valid_value->DeepCopy()); @@ -271,36 +272,36 @@ TEST(ExtensionTest, InitFromValueInvalid) { action_list->Append(action->DeepCopy()); action_list->Append(action); input_value->Set(keys::kPageActions, action_list); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_STREQ(errors::kInvalidPageActionsListSize, error.c_str()); // Test invalid options page url. input_value.reset(valid_value->DeepCopy()); input_value->Set(keys::kOptionsPage, Value::CreateNullValue()); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidOptionsPage)); // Test invalid/empty default locale. input_value.reset(valid_value->DeepCopy()); input_value->Set(keys::kDefaultLocale, Value::CreateIntegerValue(5)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidDefaultLocale)); input_value->Set(keys::kDefaultLocale, Value::CreateStringValue("")); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidDefaultLocale)); // Test invalid minimum_chrome_version. input_value.reset(valid_value->DeepCopy()); input_value->Set(keys::kMinimumChromeVersion, Value::CreateIntegerValue(42)); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kInvalidMinimumChromeVersion)); #if !defined(OS_MACOSX) // TODO(aa): The version isn't stamped into the unit test binary on mac. input_value->Set(keys::kMinimumChromeVersion, Value::CreateStringValue("88.8")); - EXPECT_FALSE(extension.InitFromValue(*input_value, true, &error)); + EXPECT_FALSE(extension.InitFromValue(*input_value, true, false, &error)); EXPECT_TRUE(MatchPattern(error, errors::kChromeVersionTooLow)); #endif } @@ -321,7 +322,7 @@ TEST(ExtensionTest, InitFromValueValid) { input_value.SetString(keys::kVersion, "1.0.0.0"); input_value.SetString(keys::kName, "my extension"); - EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); EXPECT_EQ("", error); EXPECT_TRUE(Extension::IdIsValid(extension.id())); EXPECT_EQ("1.0.0.0", extension.VersionString()); @@ -336,12 +337,12 @@ TEST(ExtensionTest, InitFromValueValid) { // We allow unknown API permissions, so this will be valid until we better // distinguish between API and host permissions. - EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); input_value.Remove(keys::kPermissions, NULL); // Test with an options page. input_value.SetString(keys::kOptionsPage, "options.html"); - EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); EXPECT_EQ("", error); EXPECT_EQ("chrome-extension", extension.options_url().scheme()); EXPECT_EQ("/options.html", extension.options_url().path()); @@ -350,14 +351,14 @@ TEST(ExtensionTest, InitFromValueValid) { // from being loaded. ListValue* empty_list = new ListValue; input_value.Set(keys::kPageActions, empty_list); - EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); EXPECT_EQ("", error); #if !defined(OS_MACOSX) // TODO(aa): The version isn't stamped into the unit test binary on mac. // Test with a minimum_chrome_version. input_value.SetString(keys::kMinimumChromeVersion, "1.0"); - EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); EXPECT_EQ("", error); // The minimum chrome version is not stored in the Extension object. #endif @@ -387,7 +388,7 @@ TEST(ExtensionTest, InitFromValueValidNameInRTL) { // No strong RTL characters in name. std::wstring name(L"Dictionary (by Google)"); input_value.SetString(keys::kName, WideToUTF16Hack(name)); - EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); EXPECT_EQ("", error); std::wstring localized_name(name); base::i18n::AdjustStringForLocaleDirection(&localized_name); @@ -396,7 +397,7 @@ TEST(ExtensionTest, InitFromValueValidNameInRTL) { // Strong RTL characters in name. name = L"Dictionary (\x05D1\x05D2"L" Google)"; input_value.SetString(keys::kName, WideToUTF16Hack(name)); - EXPECT_TRUE(extension.InitFromValue(input_value, false, &error)); + EXPECT_TRUE(extension.InitFromValue(input_value, false, false, &error)); EXPECT_EQ("", error); localized_name = name; base::i18n::AdjustStringForLocaleDirection(&localized_name); @@ -420,7 +421,7 @@ TEST(ExtensionTest, GetResourceURLAndPath) { input_value.SetString(keys::kVersion, "1.0.0.0"); input_value.SetString(keys::kName, "my extension"); scoped_refptr<Extension> extension(Extension::Create( - path, Extension::INVALID, input_value, false, NULL)); + path, Extension::INVALID, input_value, false, true, NULL)); EXPECT_TRUE(extension.get()); EXPECT_EQ(extension->url().spec() + "bar/baz.js", @@ -679,7 +680,7 @@ TEST(ExtensionTest, UpdateUrls) { input_value.SetString(keys::kUpdateURL, url.spec()); scoped_refptr<Extension> extension(Extension::Create( - path, Extension::INVALID, input_value, false, &error)); + path, Extension::INVALID, input_value, false, true, &error)); EXPECT_TRUE(extension.get()) << error; } @@ -703,7 +704,7 @@ TEST(ExtensionTest, UpdateUrls) { input_value.SetString(keys::kUpdateURL, invalid[i]); scoped_refptr<Extension> extension(Extension::Create( - path, Extension::INVALID, input_value, false, &error)); + path, Extension::INVALID, input_value, false, true, &error)); EXPECT_FALSE(extension.get()); EXPECT_TRUE(MatchPattern(error, errors::kInvalidUpdateURL)); } @@ -751,7 +752,7 @@ static scoped_refptr<Extension> LoadManifest(const std::string& dir, scoped_refptr<Extension> extension = Extension::Create( path.DirName(), Extension::INVALID, - *static_cast<DictionaryValue*>(result.get()), false, &error); + *static_cast<DictionaryValue*>(result.get()), false, true, &error); EXPECT_TRUE(extension) << error; return extension; } @@ -972,7 +973,7 @@ TEST(ExtensionTest, ImageCaching) { values.SetString(keys::kName, "test"); values.SetString(keys::kVersion, "0.1"); scoped_refptr<Extension> extension(Extension::Create( - path, Extension::INVALID, values, false, &errors)); + path, Extension::INVALID, values, false, true, &errors)); ASSERT_TRUE(extension.get()); // Create an ExtensionResource pointing at an icon. @@ -1056,7 +1057,7 @@ TEST(ExtensionTest, OldUnlimitedStoragePermission) { // is present. std::string errors; scoped_refptr<Extension> extension(Extension::Create( - extension_path, Extension::INVALID, dictionary, false, &errors)); + extension_path, Extension::INVALID, dictionary, false, true, &errors)); EXPECT_TRUE(extension.get()); EXPECT_TRUE(extension->HasApiPermission( Extension::kUnlimitedStoragePermission)); diff --git a/chrome/common/extensions/extension_unpacker.cc b/chrome/common/extensions/extension_unpacker.cc index 25919cf..fee4b54 100644 --- a/chrome/common/extensions/extension_unpacker.cc +++ b/chrome/common/extensions/extension_unpacker.cc @@ -177,7 +177,12 @@ bool ExtensionUnpacker::Run() { // EXTENSION. std::string error; scoped_refptr<Extension> extension(Extension::Create( - temp_install_dir_, Extension::INVALID, *parsed_manifest_, false, &error)); + temp_install_dir_, + Extension::INVALID, + *parsed_manifest_, + false, // Do not require a key + false, // Do not enable strict error checks + &error)); if (!extension.get()) { SetError(error); return false; diff --git a/chrome/common/extensions/url_pattern.cc b/chrome/common/extensions/url_pattern.cc index 6dc3578..5eb0beb 100644 --- a/chrome/common/extensions/url_pattern.cc +++ b/chrome/common/extensions/url_pattern.cc @@ -11,10 +11,14 @@ #include "googleurl/src/gurl.h" #include "googleurl/src/url_util.h" +const char URLPattern::kAllUrlsPattern[] = "<all_urls>"; + +namespace { + // TODO(aa): Consider adding chrome-extension? What about more obscure ones // like data: and javascript: ? // Note: keep this array in sync with kValidSchemeMasks. -static const char* kValidSchemes[] = { +const char* kValidSchemes[] = { chrome::kHttpScheme, chrome::kHttpsScheme, chrome::kFileScheme, @@ -22,7 +26,7 @@ static const char* kValidSchemes[] = { chrome::kChromeUIScheme, }; -static const int kValidSchemeMasks[] = { +const int kValidSchemeMasks[] = { URLPattern::SCHEME_HTTP, URLPattern::SCHEME_HTTPS, URLPattern::SCHEME_FILE, @@ -33,11 +37,34 @@ static const int kValidSchemeMasks[] = { COMPILE_ASSERT(arraysize(kValidSchemes) == arraysize(kValidSchemeMasks), must_keep_these_arrays_in_sync); -static const char kPathSeparator[] = "/"; +const char* kParseSuccess = "Success."; +const char* kParseErrorMissingSchemeSeparator = "Missing scheme separator."; +const char* kParseErrorInvalidScheme = "Invalid scheme."; +const char* kParseErrorWrongSchemeType = "Wrong scheme type."; +const char* kParseErrorEmptyHost = "Host can not be empty."; +const char* kParseErrorInvalidHostWildcard = "Invalid host wildcard."; +const char* kParseErrorEmptyPath = "Empty path."; +const char* kParseErrorHasColon = + "Ports are not supported in URL patterns. ':' may not be used in a host."; + +// Message explaining each URLPattern::ParseResult. +const char* kParseResultMessages[] = { + kParseSuccess, + kParseErrorMissingSchemeSeparator, + kParseErrorInvalidScheme, + kParseErrorWrongSchemeType, + kParseErrorEmptyHost, + kParseErrorInvalidHostWildcard, + kParseErrorEmptyPath, + kParseErrorHasColon +}; -const char URLPattern::kAllUrlsPattern[] = "<all_urls>"; +COMPILE_ASSERT(URLPattern::NUM_PARSE_RESULTS == arraysize(kParseResultMessages), + must_add_message_for_each_parse_result); + +const char kPathSeparator[] = "/"; -static bool IsStandardScheme(const std::string& scheme) { +bool IsStandardScheme(const std::string& scheme) { // "*" gets the same treatment as a standard scheme. if (scheme == "*") return true; @@ -46,6 +73,8 @@ static bool IsStandardScheme(const std::string& scheme) { url_parse::Component(0, static_cast<int>(scheme.length()))); } +} // namespace + URLPattern::URLPattern() : valid_schemes_(SCHEME_NONE), match_all_urls_(false), @@ -58,14 +87,21 @@ URLPattern::URLPattern(int valid_schemes) URLPattern::URLPattern(int valid_schemes, const std::string& pattern) : valid_schemes_(valid_schemes), match_all_urls_(false), match_subdomains_(false) { - if (PARSE_SUCCESS != Parse(pattern)) + + // Strict error checking is used, because this constructor is only + // appropriate when we know |pattern| is valid. + if (PARSE_SUCCESS != Parse(pattern, PARSE_STRICT)) NOTREACHED() << "URLPattern is invalid: " << pattern; } URLPattern::~URLPattern() { } -URLPattern::ParseResult URLPattern::Parse(const std::string& pattern) { +URLPattern::ParseResult URLPattern::Parse(const std::string& pattern, + ParseOption strictness) { + CHECK(strictness == PARSE_LENIENT || + strictness == PARSE_STRICT); + // Special case pattern to match every valid URL. if (pattern == kAllUrlsPattern) { match_all_urls_ = true; @@ -142,6 +178,9 @@ URLPattern::ParseResult URLPattern::Parse(const std::string& pattern) { SetPath(pattern.substr(path_start_pos)); + if (strictness == PARSE_STRICT && host_.find(':') != std::string::npos) + return PARSE_ERROR_HAS_COLON; + return PARSE_SUCCESS; } @@ -310,3 +349,9 @@ std::vector<URLPattern> URLPattern::ConvertToExplicitSchemes() const { return result; } + +// static +const char* URLPattern::GetParseResultString( + URLPattern::ParseResult parse_result) { + return kParseResultMessages[parse_result]; +} diff --git a/chrome/common/extensions/url_pattern.h b/chrome/common/extensions/url_pattern.h index 723dc2c..edd92d4 100644 --- a/chrome/common/extensions/url_pattern.h +++ b/chrome/common/extensions/url_pattern.h @@ -91,15 +91,23 @@ class URLPattern { SCHEME_ALL = -1, }; + // Options for URLPattern::Parse(). + enum ParseOption { + PARSE_LENIENT, + PARSE_STRICT + }; + // Error codes returned from Parse(). enum ParseResult { - PARSE_SUCCESS, + PARSE_SUCCESS = 0, PARSE_ERROR_MISSING_SCHEME_SEPARATOR, PARSE_ERROR_INVALID_SCHEME, PARSE_ERROR_WRONG_SCHEME_SEPARATOR, PARSE_ERROR_EMPTY_HOST, PARSE_ERROR_INVALID_HOST_WILDCARD, PARSE_ERROR_EMPTY_PATH, + PARSE_ERROR_HAS_COLON, // Only checked when strict checks are enabled. + NUM_PARSE_RESULTS }; // The <all_urls> string pattern. @@ -149,8 +157,16 @@ class URLPattern { // Initializes this instance by parsing the provided string. Returns // URLPattern::PARSE_SUCCESS on success, or an error code otherwise. On // failure, this instance will have some intermediate values and is in an - // invalid state. - ParseResult Parse(const std::string& pattern_str); + // invalid state. Adding error checks to URLPattern::Parse() can cause + // patterns in installed extensions to fail. If an installed extension + // uses a pattern that was valid but fails a new error check, the + // extension will fail to load when chrome is auto-updated. To avoid + // this, new parse checks are enabled only when |strictness| is + // OPTION_STRICT. OPTION_STRICT should be used when loading in developer + // mode, or when an extension's patterns are controlled by chrome (such + // as component extensions). + ParseResult Parse(const std::string& pattern_str, + ParseOption strictness); // Sets the scheme for pattern matches. This can be a single '*' if the // pattern matches all valid schemes (as defined by the valid_schemes_ @@ -205,6 +221,9 @@ class URLPattern { }; }; + // Get an error string for a ParseResult. + static const char* GetParseResultString(URLPattern::ParseResult parse_result); + private: #if !(defined(_MSC_VER) && _MSC_VER >= 1600) friend class std::vector<URLPattern>; diff --git a/chrome/common/extensions/url_pattern_unittest.cc b/chrome/common/extensions/url_pattern_unittest.cc index 33bf8a0..cc9c605 100644 --- a/chrome/common/extensions/url_pattern_unittest.cc +++ b/chrome/common/extensions/url_pattern_unittest.cc @@ -29,6 +29,7 @@ TEST(ExtensionURLPatternTest, ParseInvalid) { { "http:///", URLPattern::PARSE_ERROR_EMPTY_HOST }, { "http://*foo/bar", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD }, { "http://foo.*.bar/baz", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD }, + { "http://fo.*.ba:123/baz", URLPattern::PARSE_ERROR_INVALID_HOST_WILDCARD }, { "http:/bar", URLPattern::PARSE_ERROR_WRONG_SCHEME_SEPARATOR }, { "http://bar", URLPattern::PARSE_ERROR_EMPTY_PATH }, }; @@ -36,15 +37,55 @@ TEST(ExtensionURLPatternTest, ParseInvalid) { for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kInvalidPatterns); ++i) { URLPattern pattern(URLPattern::SCHEME_ALL); EXPECT_EQ(kInvalidPatterns[i].expected_result, - pattern.Parse(kInvalidPatterns[i].pattern)) + pattern.Parse(kInvalidPatterns[i].pattern, + URLPattern::PARSE_LENIENT)) << kInvalidPatterns[i].pattern; } }; +TEST(ExtensionURLPatternTest, Colons) { + const struct { + const char* pattern; + URLPattern::ParseResult expected_result; + } kTestPatterns[] = { + { "http://foo:1234/", URLPattern::PARSE_ERROR_HAS_COLON }, + { "http://foo:1234/bar", URLPattern::PARSE_ERROR_HAS_COLON }, + { "http://*.foo:1234/", URLPattern::PARSE_ERROR_HAS_COLON }, + { "http://*.foo:1234/bar", URLPattern::PARSE_ERROR_HAS_COLON }, + { "http://:1234/", URLPattern::PARSE_ERROR_HAS_COLON }, + { "http://foo:/", URLPattern::PARSE_ERROR_HAS_COLON }, + { "http://*.foo:/", URLPattern::PARSE_ERROR_HAS_COLON }, + { "http://foo:com/", URLPattern::PARSE_ERROR_HAS_COLON }, + + // Port-like strings in the path should not trigger a warning. + { "http://*/:1234", URLPattern::PARSE_SUCCESS }, + { "http://*.foo/bar:1234", URLPattern::PARSE_SUCCESS }, + { "http://foo/bar:1234/path", URLPattern::PARSE_SUCCESS }, + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(kTestPatterns); ++i) { + URLPattern pattern(URLPattern::SCHEME_ALL); + + // Without |strict_error_checks|, expect success. + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse(kTestPatterns[i].pattern, + URLPattern::PARSE_LENIENT)) + << "Got unexpected error for URL pattern: " + << kTestPatterns[i].pattern; + + EXPECT_EQ(kTestPatterns[i].expected_result, + pattern.Parse(kTestPatterns[i].pattern, + URLPattern::PARSE_STRICT)) + << "Got unexpected result for URL pattern: " + << kTestPatterns[i].pattern; + } +}; + // all pages for a given scheme TEST(ExtensionURLPatternTest, Match1) { URLPattern pattern(kAllSchemes); - EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*/*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://*/*", URLPattern::PARSE_STRICT)); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -60,7 +101,8 @@ TEST(ExtensionURLPatternTest, Match1) { // all domains TEST(ExtensionURLPatternTest, Match2) { URLPattern pattern(kAllSchemes); - EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("https://*/foo*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("https://*/foo*", URLPattern::PARSE_STRICT)); EXPECT_EQ("https", pattern.scheme()); EXPECT_EQ("", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -76,7 +118,8 @@ TEST(ExtensionURLPatternTest, Match2) { TEST(URLPatternTest, Match3) { URLPattern pattern(kAllSchemes); EXPECT_EQ(URLPattern::PARSE_SUCCESS, - pattern.Parse("http://*.google.com/foo*bar")); + pattern.Parse("http://*.google.com/foo*bar", + URLPattern::PARSE_STRICT)); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("google.com", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -93,7 +136,7 @@ TEST(URLPatternTest, Match3) { TEST(ExtensionURLPatternTest, Match5) { URLPattern pattern(kAllSchemes); EXPECT_EQ(URLPattern::PARSE_SUCCESS, - pattern.Parse("file:///foo?bar\\*baz")); + pattern.Parse("file:///foo?bar\\*baz", URLPattern::PARSE_STRICT)); EXPECT_EQ("file", pattern.scheme()); EXPECT_EQ("", pattern.host()); EXPECT_FALSE(pattern.match_subdomains()); @@ -107,7 +150,7 @@ TEST(ExtensionURLPatternTest, Match5) { TEST(ExtensionURLPatternTest, Match6) { URLPattern pattern(kAllSchemes); EXPECT_EQ(URLPattern::PARSE_SUCCESS, - pattern.Parse("http://127.0.0.1/*")); + pattern.Parse("http://127.0.0.1/*", URLPattern::PARSE_STRICT)); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("127.0.0.1", pattern.host()); EXPECT_FALSE(pattern.match_subdomains()); @@ -120,7 +163,8 @@ TEST(ExtensionURLPatternTest, Match6) { TEST(ExtensionURLPatternTest, Match7) { URLPattern pattern(kAllSchemes); EXPECT_EQ(URLPattern::PARSE_SUCCESS, - pattern.Parse("http://*.0.0.1/*")); // allowed, but useless + pattern.Parse("http://*.0.0.1/*", + URLPattern::PARSE_STRICT)); // allowed, but useless EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("0.0.1", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -136,7 +180,8 @@ TEST(ExtensionURLPatternTest, Match8) { // The below is the ASCII encoding of the following URL: // http://*.\xe1\x80\xbf/a\xc2\x81\xe1* EXPECT_EQ(URLPattern::PARSE_SUCCESS, - pattern.Parse("http://*.xn--gkd/a%C2%81%E1*")); + pattern.Parse("http://*.xn--gkd/a%C2%81%E1*", + URLPattern::PARSE_STRICT)); EXPECT_EQ("http", pattern.scheme()); EXPECT_EQ("xn--gkd", pattern.host()); EXPECT_TRUE(pattern.match_subdomains()); @@ -152,7 +197,7 @@ TEST(ExtensionURLPatternTest, Match8) { TEST(ExtensionURLPatternTest, Match9) { URLPattern pattern(kAllSchemes); EXPECT_EQ(URLPattern::PARSE_SUCCESS, - pattern.Parse("chrome://favicon/*")); + pattern.Parse("chrome://favicon/*", URLPattern::PARSE_STRICT)); EXPECT_EQ("chrome", pattern.scheme()); EXPECT_EQ("favicon", pattern.host()); EXPECT_FALSE(pattern.match_subdomains()); @@ -166,7 +211,8 @@ TEST(ExtensionURLPatternTest, Match9) { // *:// TEST(ExtensionURLPatternTest, Match10) { URLPattern pattern(kAllSchemes); - EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("*://*/*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("*://*/*", URLPattern::PARSE_STRICT)); EXPECT_TRUE(pattern.MatchesScheme("http")); EXPECT_TRUE(pattern.MatchesScheme("https")); EXPECT_FALSE(pattern.MatchesScheme("chrome")); @@ -183,7 +229,8 @@ TEST(ExtensionURLPatternTest, Match10) { // <all_urls> TEST(ExtensionURLPatternTest, Match11) { URLPattern pattern(kAllSchemes); - EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("<all_urls>")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("<all_urls>", URLPattern::PARSE_STRICT)); EXPECT_TRUE(pattern.MatchesScheme("chrome")); EXPECT_TRUE(pattern.MatchesScheme("http")); EXPECT_TRUE(pattern.MatchesScheme("https")); @@ -199,7 +246,8 @@ TEST(ExtensionURLPatternTest, Match11) { // SCHEME_ALL matches all schemes. TEST(ExtensionURLPatternTest, Match12) { URLPattern pattern(URLPattern::SCHEME_ALL); - EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("<all_urls>")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("<all_urls>", URLPattern::PARSE_STRICT)); EXPECT_TRUE(pattern.MatchesScheme("chrome")); EXPECT_TRUE(pattern.MatchesScheme("http")); EXPECT_TRUE(pattern.MatchesScheme("https")); @@ -238,7 +286,8 @@ TEST(ExtensionURLPatternTest, Match13) { for (size_t i = 0; i < arraysize(kMatch13UrlPatternTestCases); ++i) { URLPattern pattern(URLPattern::SCHEME_ALL); EXPECT_EQ(URLPattern::PARSE_SUCCESS, - pattern.Parse(kMatch13UrlPatternTestCases[i].pattern)) + pattern.Parse(kMatch13UrlPatternTestCases[i].pattern, + URLPattern::PARSE_STRICT)) << " while parsing " << kMatch13UrlPatternTestCases[i].pattern; EXPECT_TRUE(pattern.MatchesUrl( GURL(kMatch13UrlPatternTestCases[i].matches))) @@ -247,7 +296,8 @@ TEST(ExtensionURLPatternTest, Match13) { // Negative test. URLPattern pattern(URLPattern::SCHEME_ALL); - EXPECT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("data:*")); + EXPECT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("data:*", URLPattern::PARSE_STRICT)); EXPECT_FALSE(pattern.MatchesUrl(GURL("about:blank"))); }; @@ -272,7 +322,8 @@ TEST(ExtensionURLPatternTest, GetAsString) { for (size_t i = 0; i < arraysize(kGetAsStringTestCases); ++i) { URLPattern pattern(URLPattern::SCHEME_ALL); EXPECT_EQ(URLPattern::PARSE_SUCCESS, - pattern.Parse(kGetAsStringTestCases[i].pattern)); + pattern.Parse(kGetAsStringTestCases[i].pattern, + URLPattern::PARSE_STRICT)); EXPECT_STREQ(kGetAsStringTestCases[i].pattern, pattern.GetAsString().c_str()); } diff --git a/chrome/common/extensions/user_script.cc b/chrome/common/extensions/user_script.cc index b66dee94..488922d 100644 --- a/chrome/common/extensions/user_script.cc +++ b/chrome/common/extensions/user_script.cc @@ -195,7 +195,8 @@ void UserScript::Unpickle(const ::Pickle& pickle, void** iter) { std::string pattern_str; URLPattern pattern(valid_schemes); CHECK(pickle.ReadString(iter, &pattern_str)); - CHECK(URLPattern::PARSE_SUCCESS == pattern.Parse(pattern_str)); + CHECK(URLPattern::PARSE_SUCCESS == + pattern.Parse(pattern_str, URLPattern::PARSE_LENIENT)); url_patterns_.push_back(pattern); } diff --git a/chrome/common/extensions/user_script_unittest.cc b/chrome/common/extensions/user_script_unittest.cc index 297de7b..bec6436 100644 --- a/chrome/common/extensions/user_script_unittest.cc +++ b/chrome/common/extensions/user_script_unittest.cc @@ -71,7 +71,8 @@ TEST(ExtensionUserScriptTest, Match5) { TEST(ExtensionUserScriptTest, Match6) { URLPattern pattern(kAllSchemes); - ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern.Parse("http://*/foo*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, + pattern.Parse("http://*/foo*", URLPattern::PARSE_STRICT)); UserScript script; script.add_url_pattern(pattern); @@ -87,7 +88,8 @@ TEST(ExtensionUserScriptTest, UrlPatternGlobInteraction) { URLPattern pattern(kAllSchemes); ASSERT_EQ(URLPattern::PARSE_SUCCESS, - pattern.Parse("http://www.google.com/*")); + pattern.Parse("http://www.google.com/*", + URLPattern::PARSE_STRICT)); script.add_url_pattern(pattern); script.add_glob("*bar*"); @@ -115,8 +117,10 @@ TEST(ExtensionUserScriptTest, UrlPatternGlobInteraction) { TEST(ExtensionUserScriptTest, Pickle) { URLPattern pattern1(kAllSchemes); URLPattern pattern2(kAllSchemes); - ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern1.Parse("http://*/foo*")); - ASSERT_EQ(URLPattern::PARSE_SUCCESS, pattern2.Parse("http://bar/baz*")); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, + pattern1.Parse("http://*/foo*", URLPattern::PARSE_STRICT)); + ASSERT_EQ(URLPattern::PARSE_SUCCESS, + pattern2.Parse("http://bar/baz*", URLPattern::PARSE_STRICT)); UserScript script1; script1.js_scripts().push_back(UserScript::File( diff --git a/chrome/common/render_messages.cc b/chrome/common/render_messages.cc index 3d1f767..8cdcf51 100644 --- a/chrome/common/render_messages.cc +++ b/chrome/common/render_messages.cc @@ -834,7 +834,7 @@ bool ParamTraits<URLPattern>::Read(const Message* m, void** iter, return false; p->set_valid_schemes(valid_schemes); - return URLPattern::PARSE_SUCCESS == p->Parse(spec); + return URLPattern::PARSE_SUCCESS == p->Parse(spec, URLPattern::PARSE_LENIENT); } void ParamTraits<URLPattern>::Log(const param_type& p, std::string* l) { diff --git a/chrome/common/render_messages_params.cc b/chrome/common/render_messages_params.cc index fa0a38e..149e521 100644 --- a/chrome/common/render_messages_params.cc +++ b/chrome/common/render_messages_params.cc @@ -327,10 +327,15 @@ scoped_refptr<Extension> ViewMsg_ExtensionLoaded_Params::ConvertToExtension() const { // Extensions that are loaded unpacked won't have a key. const bool kRequireKey = false; + + // The extension may have been loaded in a way that does not require + // strict error checks to pass. Do not do strict checks here. + const bool kStrictErrorChecks = false; std::string error; scoped_refptr<Extension> extension( - Extension::Create(path, location, *manifest, kRequireKey, &error)); + Extension::Create(path, location, *manifest, kRequireKey, + kStrictErrorChecks, &error)); if (!extension.get()) LOG(ERROR) << "Error deserializing extension: " << error; diff --git a/chrome/test/data/extensions/manifest_tests/content_script_match_pattern_not_string.json b/chrome/test/data/extensions/manifest_tests/content_script_match_pattern_not_string.json new file mode 100644 index 0000000..830ca39 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/content_script_match_pattern_not_string.json @@ -0,0 +1,9 @@ +{ + "name": "test", + "version": "1", + "content_scripts": [ + { + "matches": [12345] + } + ] +} diff --git a/chrome/test/data/extensions/manifest_tests/forbid_ports_in_content_scripts.json b/chrome/test/data/extensions/manifest_tests/forbid_ports_in_content_scripts.json new file mode 100644 index 0000000..fdb6411 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/forbid_ports_in_content_scripts.json @@ -0,0 +1,14 @@ +{ + "name": "test", + "version": "1", + "content_scripts": [ + { + "matches": ["http://*.no.port.okay/path/*"], + "js": ["javascript.js"] + }, + { + "matches": ["http://*.has.port.error:12345/path/*"], + "js": ["javascript.js"] + } + ] +} diff --git a/chrome/test/data/extensions/manifest_tests/forbid_ports_in_permissions.json b/chrome/test/data/extensions/manifest_tests/forbid_ports_in_permissions.json new file mode 100644 index 0000000..9c5b870 --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/forbid_ports_in_permissions.json @@ -0,0 +1,8 @@ +{ + "name": "test", + "version": "1", + "permissions": [ + "http://*.no.port.okay/path/*", + "http://*.has.port.error:12345/path/*" + ] +} diff --git a/chrome/test/data/extensions/manifest_tests/web_urls_invalid_5.json b/chrome/test/data/extensions/manifest_tests/web_urls_invalid_5.json new file mode 100644 index 0000000..ca3e4fe --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/web_urls_invalid_5.json @@ -0,0 +1,13 @@ +{ + "name": "test", + "version": "1", + "app": { + "urls": [ + "http://*.example.com/path", + "https://*/path" + ], + "launch": { + "web_url": "http://www.google.com/foo.html" + } + } +} diff --git a/chrome/test/data/extensions/manifest_tests/web_urls_invalid_has_port.json b/chrome/test/data/extensions/manifest_tests/web_urls_invalid_has_port.json new file mode 100644 index 0000000..9ae851b --- /dev/null +++ b/chrome/test/data/extensions/manifest_tests/web_urls_invalid_has_port.json @@ -0,0 +1,16 @@ +{ + "name": "test", + "version": "1", + "app": { + "urls": [ + "http://www.google.com/mail/", + "http://www.google.com:12345/foobar/" + ], + "launch": { + "web_url": "http://www.google.com/mail/" + } + }, + "permissions": [ + "notifications" + ] +} diff --git a/chrome/test/live_sync/live_extensions_sync_test_base.cc b/chrome/test/live_sync/live_extensions_sync_test_base.cc index 1fb31c8..97e245a 100644 --- a/chrome/test/live_sync/live_extensions_sync_test_base.cc +++ b/chrome/test/live_sync/live_extensions_sync_test_base.cc @@ -48,7 +48,7 @@ scoped_refptr<Extension> CreateExtension( std::string error; scoped_refptr<Extension> extension = Extension::Create(extension_dir, - Extension::INTERNAL, source, false, &error); + Extension::INTERNAL, source, false, true, &error); if (!error.empty()) { LOG(WARNING) << error; return NULL; |