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