Home | History | Annotate | Download | only in renderer
      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         &params));
    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         &params));
    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         &params));
    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         &params));
    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(&params);
    190     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
    191         GURL(),
    192         GURL(kExtensionUrl),
    193         kNaClRestricted,
    194         CreateExtension(kExtensionRestricted, kExtensionFromWebStore).get(),
    195         &params));
    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         &params));
    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         &params));
    221     EXPECT_FALSE(AllowsDevInterfaces(params));
    222     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
    223         GURL(kPhotosManifestURL1),
    224         GURL(kPhotosAppURL2),
    225         kNaClRestricted,
    226         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
    227         &params));
    228     EXPECT_FALSE(AllowsDevInterfaces(params));
    229     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
    230         GURL(kPhotosManifestURL2),
    231         GURL(kPhotosAppURL1),
    232         kNaClRestricted,
    233         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
    234         &params));
    235     EXPECT_FALSE(AllowsDevInterfaces(params));
    236     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
    237         GURL(kPhotosManifestURL2),
    238         GURL(kPhotosAppURL2),
    239         kNaClRestricted,
    240         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
    241         &params));
    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         &params));
    250     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
    251         GURL(kChatManifestFS2),
    252         GURL(kChatAppURL2),
    253         kNaClRestricted,
    254         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
    255         &params));
    256     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
    257         GURL(kChatManifestFS3),
    258         GURL(kChatAppURL3),
    259         kNaClRestricted,
    260         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
    261         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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(&params);
    318     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
    319         GURL(kPhotosManifestURL1),
    320         GURL(kPhotosAppURL1),
    321         kNaClRestricted,
    322         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
    323         &params));
    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         &params));
    335     EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
    336         GURL(),
    337         GURL("https://plus.google.com.evil.com/foo2"),
    338         kNaClRestricted,
    339         CreateExtension(kExtensionRestricted, kExtensionFromWebStore).get(),
    340         &params));
    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         &params));
    348     EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
    349         GURL(),
    350         GURL("https://talkgadget.google.com.evil.com/foo4"),
    351         kNaClRestricted,
    352         CreateExtension(kExtensionUnrestricted, kExtensionFromWebStore).get(),
    353         &params));
    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         &params));
    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         &params));
    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