1 // Copyright (c) 2012 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 "chrome/renderer/chrome_content_renderer_client.h" 6 7 #include "base/strings/utf_string_conversions.h" 8 #include "chrome/common/extensions/extension.h" 9 #include "chrome/common/extensions/extension_builder.h" 10 #include "chrome/common/extensions/extension_manifest_constants.h" 11 #include "content/public/common/webplugininfo.h" 12 #include "testing/gtest/include/gtest/gtest.h" 13 #include "third_party/WebKit/public/platform/WebString.h" 14 #include "third_party/WebKit/public/platform/WebVector.h" 15 #include "third_party/WebKit/public/web/WebPluginParams.h" 16 #include "url/gurl.h" 17 18 using WebKit::WebPluginParams; 19 using WebKit::WebString; 20 using WebKit::WebVector; 21 using chrome::ChromeContentRendererClient; 22 using content::WebPluginInfo; 23 using content::WebPluginMimeType; 24 25 namespace chrome { 26 27 namespace { 28 const bool kNaClRestricted = false; 29 const bool kNaClUnrestricted = true; 30 const bool kExtensionRestricted = false; 31 const bool kExtensionUnrestricted = true; 32 const bool kExtensionNotFromWebStore = false; 33 const bool kExtensionFromWebStore = true; 34 const bool kNotHostedApp = false; 35 const bool kHostedApp = true; 36 37 const char kNaClMimeType[] = "application/x-nacl"; 38 const char kExtensionUrl[] = "chrome-extension://extension_id/background.html"; 39 40 const char kAllowedNaClAppURL1[] = "https://plus.google.com"; 41 const char kAllowedNaClAppURL2[] = "https://plus.sandbox.google.com"; 42 const char kAllowedNaClManifestURL1[] = "https://ssl.gstatic.com/s2/oz/nacl/foo"; 43 const char kAllowedNaClManifestURL2[] = "https://ssl.gstatic.com/photos/nacl/foo"; 44 45 bool AllowsDevInterfaces(const WebPluginParams& params) { 46 for (size_t i = 0; i < params.attributeNames.size(); ++i) { 47 if (params.attributeNames[i] == WebString::fromUTF8("@dev")) 48 return true; 49 } 50 return false; 51 } 52 53 void AddFakeDevAttribute(WebPluginParams* params) { 54 WebVector<WebString> names(static_cast<size_t>(1)); 55 WebVector<WebString> values(static_cast<size_t>(1)); 56 names[0] = WebString::fromUTF8("@dev"); 57 values[0] = WebString(); 58 params->attributeNames.swap(names); 59 params->attributeValues.swap(values); 60 } 61 62 void AddContentTypeHandler(content::WebPluginInfo* info, 63 const char* mime_type, 64 const char* manifest_url) { 65 content::WebPluginMimeType mime_type_info; 66 mime_type_info.mime_type = mime_type; 67 mime_type_info.additional_param_names.push_back(UTF8ToUTF16("nacl")); 68 mime_type_info.additional_param_values.push_back( 69 UTF8ToUTF16(manifest_url)); 70 info->mime_types.push_back(mime_type_info); 71 } 72 } // namespace 73 74 typedef testing::Test ChromeContentRendererClientTest; 75 76 77 scoped_refptr<const extensions::Extension> CreateTestExtension( 78 bool is_unrestricted, bool is_from_webstore, bool is_hosted_app, 79 const std::string& app_url) { 80 extensions::Manifest::Location location = is_unrestricted ? 81 extensions::Manifest::UNPACKED : 82 extensions::Manifest::INTERNAL; 83 int flags = is_from_webstore ? 84 extensions::Extension::FROM_WEBSTORE: 85 extensions::Extension::NO_FLAGS; 86 87 base::DictionaryValue manifest; 88 manifest.SetString("name", "NaCl Extension"); 89 manifest.SetString("version", "1"); 90 manifest.SetInteger("manifest_version", 2); 91 if (is_hosted_app) { 92 base::ListValue* url_list = new base::ListValue(); 93 url_list->Append(base::Value::CreateStringValue(app_url)); 94 manifest.Set(extension_manifest_keys::kWebURLs, url_list); 95 manifest.SetString(extension_manifest_keys::kLaunchWebURL, app_url); 96 } 97 std::string error; 98 return extensions::Extension::Create(base::FilePath(), location, manifest, 99 flags, &error); 100 } 101 102 scoped_refptr<const extensions::Extension> CreateExtension( 103 bool is_unrestricted, bool is_from_webstore) { 104 return CreateTestExtension( 105 is_unrestricted, is_from_webstore, kNotHostedApp, std::string()); 106 } 107 108 scoped_refptr<const extensions::Extension> CreateHostedApp( 109 bool is_unrestricted, bool is_from_webstore, const std::string& app_url) { 110 return CreateTestExtension(is_unrestricted, is_from_webstore, kHostedApp, 111 app_url); 112 } 113 114 TEST_F(ChromeContentRendererClientTest, NaClRestriction) { 115 // Unknown content types have no NaCl module. 116 { 117 WebPluginInfo info; 118 EXPECT_EQ(GURL(), 119 ChromeContentRendererClient::GetNaClContentHandlerURL( 120 "application/x-foo", info)); 121 } 122 // Known content types have a NaCl module. 123 { 124 WebPluginInfo info; 125 AddContentTypeHandler(&info, "application/x-foo", "www.foo.com"); 126 EXPECT_EQ(GURL("www.foo.com"), 127 ChromeContentRendererClient::GetNaClContentHandlerURL( 128 "application/x-foo", info)); 129 } 130 // --enable-nacl allows all NaCl apps, with 'dev' interfaces. 131 { 132 WebPluginParams params; 133 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 134 GURL(), 135 GURL(), 136 kNaClUnrestricted, 137 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 138 ¶ms)); 139 EXPECT_TRUE(AllowsDevInterfaces(params)); 140 } 141 // Unrestricted extensions are allowed without --enable-nacl, with 'dev' 142 // interfaces if called from an extension url. 143 { 144 WebPluginParams params; 145 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 146 GURL(), 147 GURL(kExtensionUrl), 148 kNaClRestricted, 149 CreateExtension(kExtensionUnrestricted, kExtensionNotFromWebStore) 150 .get(), 151 ¶ms)); 152 EXPECT_TRUE(AllowsDevInterfaces(params)); 153 } 154 // CWS extensions are allowed without --enable-nacl, without 'dev' 155 // interfaces if called from an extension url. 156 { 157 WebPluginParams params; 158 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 159 GURL(), 160 GURL(kExtensionUrl), 161 kNaClRestricted, 162 CreateExtension(kExtensionRestricted, kExtensionFromWebStore).get(), 163 ¶ms)); 164 EXPECT_FALSE(AllowsDevInterfaces(params)); 165 } 166 // CWS extensions can't get 'dev' interfaces with --enable-nacl. 167 { 168 WebPluginParams params; 169 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 170 GURL(), 171 GURL(kExtensionUrl), 172 kNaClUnrestricted, 173 CreateExtension(kExtensionRestricted, kExtensionFromWebStore).get(), 174 ¶ms)); 175 EXPECT_FALSE(AllowsDevInterfaces(params)); 176 } 177 // CWS extensions can't get 'dev' interfaces by injecting a fake 178 // '@dev' attribute. 179 { 180 WebPluginParams params; 181 AddFakeDevAttribute(¶ms); 182 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 183 GURL(), 184 GURL(kExtensionUrl), 185 kNaClRestricted, 186 CreateExtension(kExtensionRestricted, kExtensionFromWebStore).get(), 187 ¶ms)); 188 EXPECT_FALSE(AllowsDevInterfaces(params)); 189 } 190 // The NaCl PDF extension is allowed without --enable-nacl, with 'dev' 191 // interfaces, from all URLs. 192 { 193 WebPluginParams params; 194 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 195 GURL("chrome-extension://acadkphlmlegjaadjagenfimbpphcgnh"), 196 GURL(), 197 kNaClRestricted, 198 CreateExtension(kExtensionRestricted, kExtensionFromWebStore).get(), 199 ¶ms)); 200 EXPECT_TRUE(AllowsDevInterfaces(params)); 201 } 202 // Whitelisted URLs are allowed without --enable-nacl, without 'dev' 203 // interfaces. There is a whitelist for the app URL and the manifest URL. 204 { 205 WebPluginParams params; 206 // Whitelisted manifest URL #1, whitelisted app URL root #1 is allowed. 207 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 208 GURL(kAllowedNaClManifestURL1), 209 GURL(kAllowedNaClAppURL1), 210 kNaClRestricted, 211 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 212 ¶ms)); 213 EXPECT_FALSE(AllowsDevInterfaces(params)); 214 // Whitelisted manifest URL #2, whitelisted app URL root #1 is allowed. 215 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 216 GURL(kAllowedNaClManifestURL1), 217 GURL(kAllowedNaClAppURL1), 218 kNaClRestricted, 219 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 220 ¶ms)); 221 EXPECT_FALSE(AllowsDevInterfaces(params)); 222 // Whitelisted manifest URL #1, whitelisted app URL root #2 is allowed. 223 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 224 GURL(kAllowedNaClManifestURL1), 225 GURL(kAllowedNaClAppURL2), 226 kNaClRestricted, 227 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 228 ¶ms)); 229 EXPECT_FALSE(AllowsDevInterfaces(params)); 230 231 // Whitelisted manifest URL, bad app URLs, NOT allowed. 232 EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed( 233 GURL(kAllowedNaClManifestURL1), 234 GURL("http://plus.google.com/foo"), // http scheme 235 kNaClRestricted, 236 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 237 ¶ms)); 238 EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed( 239 GURL(kAllowedNaClManifestURL1), 240 GURL("http://plus.sandbox.google.com/foo"), // http scheme 241 kNaClRestricted, 242 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 243 ¶ms)); 244 EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed( 245 GURL(kAllowedNaClManifestURL1), 246 GURL("https://plus.google.evil.com/foo"), // bad host 247 kNaClRestricted, 248 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 249 ¶ms)); 250 // Whitelisted app URL, bad manifest URL, NOT allowed. 251 EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed( 252 GURL("http://ssl.gstatic.com/s2/oz/nacl/foo"), // http scheme 253 GURL(kAllowedNaClAppURL1), 254 kNaClRestricted, 255 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 256 ¶ms)); 257 EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed( 258 GURL("https://ssl.gstatic.evil.com/s2/oz/nacl/foo"), // bad host 259 GURL(kAllowedNaClAppURL1), 260 kNaClRestricted, 261 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 262 ¶ms)); 263 EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed( 264 GURL("https://ssl.gstatic.com/wrong/s2/oz/nacl/foo"), // bad path 265 GURL(kAllowedNaClAppURL1), 266 kNaClRestricted, 267 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 268 ¶ms)); 269 } 270 // Whitelisted URLs can't get 'dev' interfaces with --enable-nacl. 271 { 272 WebPluginParams params; 273 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 274 GURL(kAllowedNaClManifestURL1), 275 GURL(kAllowedNaClAppURL1), 276 kNaClUnrestricted, 277 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 278 ¶ms)); 279 EXPECT_FALSE(AllowsDevInterfaces(params)); 280 } 281 // Whitelisted URLs can't get 'dev' interfaces by injecting a fake 282 // '@dev' attribute. 283 { 284 WebPluginParams params; 285 AddFakeDevAttribute(¶ms); 286 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 287 GURL(kAllowedNaClManifestURL1), 288 GURL(kAllowedNaClAppURL1), 289 kNaClRestricted, 290 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 291 ¶ms)); 292 EXPECT_FALSE(AllowsDevInterfaces(params)); 293 } 294 // Non-whitelisted URLs are blocked without --enable-nacl. 295 { 296 WebPluginParams params; 297 EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed( 298 GURL(), 299 GURL("https://plus.google.com.evil.com/foo1"), 300 kNaClRestricted, 301 CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(), 302 ¶ms)); 303 EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed( 304 GURL(), 305 GURL("https://plus.google.com.evil.com/foo2"), 306 kNaClRestricted, 307 CreateExtension(kExtensionRestricted, kExtensionFromWebStore).get(), 308 ¶ms)); 309 EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed( 310 GURL(), 311 GURL("https://plus.google.com.evil.com/foo3"), 312 kNaClRestricted, 313 CreateExtension(kExtensionUnrestricted, kExtensionNotFromWebStore) 314 .get(), 315 ¶ms)); 316 EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed( 317 GURL(), 318 GURL("https://plus.google.com.evil.com/foo4"), 319 kNaClRestricted, 320 CreateExtension(kExtensionUnrestricted, kExtensionFromWebStore).get(), 321 ¶ms)); 322 } 323 // Non chrome-extension:// URLs belonging to hosted apps are allowed. 324 { 325 WebPluginParams params; 326 EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed( 327 GURL(), 328 GURL("http://example.com/test.html"), 329 kNaClRestricted, 330 CreateHostedApp(kExtensionRestricted, 331 kExtensionNotFromWebStore, 332 "http://example.com/").get(), 333 ¶ms)); 334 EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed( 335 GURL(), 336 GURL("http://example.evil.com/test.html"), 337 kNaClRestricted, 338 CreateHostedApp(kExtensionRestricted, 339 kExtensionNotFromWebStore, 340 "http://example.com/").get(), 341 ¶ms)); 342 } 343 } 344 345 } // namespace chrome 346