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 "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         &params));
    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         &params));
    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         &params));
    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         &params));
    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(&params);
    182     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
    183         GURL(),
    184         GURL(kExtensionUrl),
    185         kNaClRestricted,
    186         CreateExtension(kExtensionRestricted, kExtensionFromWebStore).get(),
    187         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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         &params));
    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(&params);
    286     EXPECT_TRUE(ChromeContentRendererClient::IsNaClAllowed(
    287         GURL(kAllowedNaClManifestURL1),
    288         GURL(kAllowedNaClAppURL1),
    289         kNaClRestricted,
    290         CreateExtension(kExtensionRestricted, kExtensionNotFromWebStore).get(),
    291         &params));
    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         &params));
    303     EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
    304         GURL(),
    305         GURL("https://plus.google.com.evil.com/foo2"),
    306         kNaClRestricted,
    307         CreateExtension(kExtensionRestricted, kExtensionFromWebStore).get(),
    308         &params));
    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         &params));
    316     EXPECT_FALSE(ChromeContentRendererClient::IsNaClAllowed(
    317         GURL(),
    318         GURL("https://plus.google.com.evil.com/foo4"),
    319         kNaClRestricted,
    320         CreateExtension(kExtensionUnrestricted, kExtensionFromWebStore).get(),
    321         &params));
    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         &params));
    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         &params));
    342   }
    343 }
    344 
    345 }  // namespace chrome
    346