• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
AllowsDevInterfaces(const WebPluginParams & params)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 
AddFakeDevAttribute(WebPluginParams * params)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 
AddContentTypeHandler(content::WebPluginInfo * info,const char * mime_type,const char * manifest_url)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(UTF8ToUTF16("nacl"));
76   mime_type_info.additional_param_values.push_back(
77       UTF8ToUTF16(manifest_url));
78   info->mime_types.push_back(mime_type_info);
79 }
80 }  // namespace
81 
82 typedef testing::Test ChromeContentRendererClientTest;
83 
84 
CreateTestExtension(bool is_unrestricted,bool is_from_webstore,bool is_hosted_app,const std::string & app_url)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(base::Value::CreateStringValue(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 
CreateExtension(bool is_unrestricted,bool is_from_webstore)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 
CreateHostedApp(bool is_unrestricted,bool is_from_webstore,const std::string & app_url)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 
TEST_F(ChromeContentRendererClientTest,NaClRestriction)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 
TEST_F(ChromeContentRendererClientTest,AllowPepperMediaStreamAPI)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 
TEST_F(ChromeContentRendererClientTest,ShouldSuppressErrorPage)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(GURL("http://example.com")));
400   EXPECT_TRUE(client.ShouldSuppressErrorPage(GURL("http://example.com/n")));
401 }
402