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
21using blink::WebPluginParams;
22using blink::WebString;
23using blink::WebVector;
24using content::WebPluginInfo;
25using content::WebPluginMimeType;
26
27namespace {
28const bool kNaClRestricted = false;
29const bool kNaClUnrestricted = true;
30const bool kExtensionRestricted = false;
31const bool kExtensionUnrestricted = true;
32const bool kExtensionNotFromWebStore = false;
33const bool kExtensionFromWebStore = true;
34const bool kNotHostedApp = false;
35const bool kHostedApp = true;
36
37const char kExtensionUrl[] = "chrome-extension://extension_id/background.html";
38
39const char kPhotosAppURL1[] = "https://foo.plus.google.com";
40const char kPhotosAppURL2[] = "https://foo.plus.sandbox.google.com";
41const char kPhotosManifestURL1[] = "https://ssl.gstatic.com/s2/oz/nacl/foo";
42const char kPhotosManifestURL2[] = "https://ssl.gstatic.com/photos/nacl/foo";
43
44const char kChatAppURL1[] = "https://foo.talkgadget.google.com/hangouts/foo";
45const char kChatAppURL2[] = "https://foo.plus.google.com/hangouts/foo";
46const char kChatAppURL3[] = "https://foo.plus.sandbox.google.com/hangouts/foo";
47const char kChatManifestFS1[] =
48  "filesystem:https://foo.talkgadget.google.com/foo";
49const char kChatManifestFS2[] = "filesystem:https://foo.plus.google.com/foo";
50const char kChatManifestFS3[] =
51  "filesystem:https://foo.plus.sandbox.google.com/foo";
52
53bool 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
61void 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
70void 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
82typedef testing::Test ChromeContentRendererClientTest;
83
84
85scoped_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
110scoped_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
116scoped_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
122TEST_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
377TEST_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
394TEST_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