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