1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "extensions/common/manifest_handlers/shared_module_info.h" 6 7#include "base/lazy_instance.h" 8#include "base/memory/scoped_ptr.h" 9#include "base/strings/string_number_conversions.h" 10#include "base/strings/string_util.h" 11#include "base/strings/utf_string_conversions.h" 12#include "base/version.h" 13#include "components/crx_file/id_util.h" 14#include "extensions/common/constants.h" 15#include "extensions/common/error_utils.h" 16#include "extensions/common/manifest_constants.h" 17#include "extensions/common/permissions/permission_set.h" 18#include "extensions/common/permissions/permissions_data.h" 19 20namespace extensions { 21 22namespace keys = manifest_keys; 23namespace values = manifest_values; 24namespace errors = manifest_errors; 25 26namespace { 27 28const char kSharedModule[] = "shared_module"; 29 30static base::LazyInstance<SharedModuleInfo> g_empty_shared_module_info = 31 LAZY_INSTANCE_INITIALIZER; 32 33const SharedModuleInfo& GetSharedModuleInfo(const Extension* extension) { 34 SharedModuleInfo* info = static_cast<SharedModuleInfo*>( 35 extension->GetManifestData(kSharedModule)); 36 if (!info) 37 return g_empty_shared_module_info.Get(); 38 return *info; 39} 40 41} // namespace 42 43SharedModuleInfo::SharedModuleInfo() { 44} 45 46SharedModuleInfo::~SharedModuleInfo() { 47} 48 49// static 50void SharedModuleInfo::ParseImportedPath(const std::string& path, 51 std::string* import_id, 52 std::string* import_relative_path) { 53 std::vector<std::string> tokens; 54 Tokenize(path, std::string("/"), &tokens); 55 if (tokens.size() > 2 && tokens[0] == kModulesDir && 56 crx_file::id_util::IdIsValid(tokens[1])) { 57 *import_id = tokens[1]; 58 *import_relative_path = tokens[2]; 59 for (size_t i = 3; i < tokens.size(); ++i) 60 *import_relative_path += "/" + tokens[i]; 61 } 62} 63 64// static 65bool SharedModuleInfo::IsImportedPath(const std::string& path) { 66 std::vector<std::string> tokens; 67 Tokenize(path, std::string("/"), &tokens); 68 if (tokens.size() > 2 && tokens[0] == kModulesDir && 69 crx_file::id_util::IdIsValid(tokens[1])) { 70 return true; 71 } 72 return false; 73} 74 75// static 76bool SharedModuleInfo::IsSharedModule(const Extension* extension) { 77 CHECK(extension); 78 return extension->manifest()->is_shared_module(); 79} 80 81// static 82bool SharedModuleInfo::IsExportAllowed(const Extension* extension, 83 const std::string& relative_path) { 84 return GetSharedModuleInfo(extension). 85 exported_set_.MatchesURL(extension->url().Resolve(relative_path)); 86} 87 88// static 89bool SharedModuleInfo::IsExportAllowedByWhitelist(const Extension* extension, 90 const std::string& other_id) { 91 // Sanity check. In case the caller did not check |extension| to make sure it 92 // is a shared module, we do not want it to appear that the extension with 93 // |other_id| importing |extension| is valid. 94 if (!SharedModuleInfo::IsSharedModule(extension)) 95 return false; 96 const SharedModuleInfo& info = GetSharedModuleInfo(extension); 97 if (info.export_whitelist_.empty()) 98 return true; 99 if (info.export_whitelist_.find(other_id) != info.export_whitelist_.end()) 100 return true; 101 return false; 102} 103 104// static 105bool SharedModuleInfo::ImportsExtensionById(const Extension* extension, 106 const std::string& other_id) { 107 const SharedModuleInfo& info = GetSharedModuleInfo(extension); 108 for (size_t i = 0; i < info.imports_.size(); i++) { 109 if (info.imports_[i].extension_id == other_id) 110 return true; 111 } 112 return false; 113} 114 115// static 116bool SharedModuleInfo::ImportsModules(const Extension* extension) { 117 return GetSharedModuleInfo(extension).imports_.size() > 0; 118} 119 120// static 121const std::vector<SharedModuleInfo::ImportInfo>& SharedModuleInfo::GetImports( 122 const Extension* extension) { 123 return GetSharedModuleInfo(extension).imports_; 124} 125 126bool SharedModuleInfo::Parse(const Extension* extension, 127 base::string16* error) { 128 bool has_import = extension->manifest()->HasKey(keys::kImport); 129 bool has_export = extension->manifest()->HasKey(keys::kExport); 130 if (!has_import && !has_export) 131 return true; 132 133 if (has_import && has_export) { 134 *error = base::ASCIIToUTF16(errors::kInvalidImportAndExport); 135 return false; 136 } 137 138 if (has_export) { 139 const base::DictionaryValue* export_value = NULL; 140 if (!extension->manifest()->GetDictionary(keys::kExport, &export_value)) { 141 *error = base::ASCIIToUTF16(errors::kInvalidExport); 142 return false; 143 } 144 const base::ListValue* resources_list = NULL; 145 if (!export_value->GetList(keys::kResources, &resources_list)) { 146 *error = base::ASCIIToUTF16(errors::kInvalidExportResources); 147 return false; 148 } 149 if (export_value->HasKey(keys::kWhitelist)) { 150 const base::ListValue* whitelist = NULL; 151 if (!export_value->GetList(keys::kWhitelist, &whitelist)) { 152 *error = base::ASCIIToUTF16(errors::kInvalidExportWhitelist); 153 return false; 154 } 155 for (size_t i = 0; i < whitelist->GetSize(); ++i) { 156 std::string extension_id; 157 if (!whitelist->GetString(i, &extension_id) || 158 !crx_file::id_util::IdIsValid(extension_id)) { 159 *error = ErrorUtils::FormatErrorMessageUTF16( 160 errors::kInvalidExportWhitelistString, base::IntToString(i)); 161 return false; 162 } 163 export_whitelist_.insert(extension_id); 164 } 165 } 166 for (size_t i = 0; i < resources_list->GetSize(); ++i) { 167 std::string resource_path; 168 if (!resources_list->GetString(i, &resource_path)) { 169 *error = ErrorUtils::FormatErrorMessageUTF16( 170 errors::kInvalidExportResourcesString, base::IntToString(i)); 171 return false; 172 } 173 const GURL& resolved_path = extension->url().Resolve(resource_path); 174 if (!resolved_path.is_valid()) { 175 *error = ErrorUtils::FormatErrorMessageUTF16( 176 errors::kInvalidExportResourcesString, base::IntToString(i)); 177 return false; 178 } 179 exported_set_.AddPattern( 180 URLPattern(URLPattern::SCHEME_EXTENSION, resolved_path.spec())); 181 } 182 } 183 184 if (has_import) { 185 const base::ListValue* import_list = NULL; 186 if (!extension->manifest()->GetList(keys::kImport, &import_list)) { 187 *error = base::ASCIIToUTF16(errors::kInvalidImport); 188 return false; 189 } 190 for (size_t i = 0; i < import_list->GetSize(); ++i) { 191 const base::DictionaryValue* import_entry = NULL; 192 if (!import_list->GetDictionary(i, &import_entry)) { 193 *error = base::ASCIIToUTF16(errors::kInvalidImport); 194 return false; 195 } 196 std::string extension_id; 197 imports_.push_back(ImportInfo()); 198 if (!import_entry->GetString(keys::kId, &extension_id) || 199 !crx_file::id_util::IdIsValid(extension_id)) { 200 *error = ErrorUtils::FormatErrorMessageUTF16( 201 errors::kInvalidImportId, base::IntToString(i)); 202 return false; 203 } 204 imports_.back().extension_id = extension_id; 205 if (import_entry->HasKey(keys::kMinimumVersion)) { 206 std::string min_version; 207 if (!import_entry->GetString(keys::kMinimumVersion, &min_version)) { 208 *error = ErrorUtils::FormatErrorMessageUTF16( 209 errors::kInvalidImportVersion, base::IntToString(i)); 210 return false; 211 } 212 imports_.back().minimum_version = min_version; 213 Version v(min_version); 214 if (!v.IsValid()) { 215 *error = ErrorUtils::FormatErrorMessageUTF16( 216 errors::kInvalidImportVersion, base::IntToString(i)); 217 return false; 218 } 219 } 220 } 221 } 222 return true; 223} 224 225 226SharedModuleHandler::SharedModuleHandler() { 227} 228 229SharedModuleHandler::~SharedModuleHandler() { 230} 231 232bool SharedModuleHandler::Parse(Extension* extension, base::string16* error) { 233 scoped_ptr<SharedModuleInfo> info(new SharedModuleInfo); 234 if (!info->Parse(extension, error)) 235 return false; 236 extension->SetManifestData(kSharedModule, info.release()); 237 return true; 238} 239 240bool SharedModuleHandler::Validate( 241 const Extension* extension, 242 std::string* error, 243 std::vector<InstallWarning>* warnings) const { 244 // Extensions that export resources should not have any permissions of their 245 // own, instead they rely on the permissions of the extensions which import 246 // them. 247 if (SharedModuleInfo::IsSharedModule(extension) && 248 !extension->permissions_data()->active_permissions()->IsEmpty()) { 249 *error = errors::kInvalidExportPermissions; 250 return false; 251 } 252 return true; 253} 254 255const std::vector<std::string> SharedModuleHandler::Keys() const { 256 static const char* keys[] = { 257 keys::kExport, 258 keys::kImport 259 }; 260 return std::vector<std::string>(keys, keys + arraysize(keys)); 261} 262 263} // namespace extensions 264