1 // Copyright 2014 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/common/extensions/permissions/chrome_permission_message_provider.h"
6 
7 #include <memory>
8 #include <vector>
9 
10 #include "base/macros.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "base/values.h"
14 #include "chrome/common/extensions/manifest_tests/chrome_manifest_test.h"
15 #include "chrome/grit/generated_resources.h"
16 #include "extensions/common/permissions/permission_set.h"
17 #include "extensions/common/permissions/permissions_data.h"
18 #include "extensions/common/permissions/permissions_info.h"
19 #include "extensions/common/permissions/settings_override_permission.h"
20 #include "extensions/common/permissions/usb_device_permission.h"
21 #include "extensions/common/url_pattern_set.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "ui/base/l10n/l10n_util.h"
24 
25 namespace extensions {
26 
27 // Tests that ChromePermissionMessageProvider provides correct permission
28 // messages for given permissions.
29 // NOTE: No extensions are created as part of these tests. Integration tests
30 // that test the messages are generated properly for extensions can be found in
31 // chrome/browser/extensions/permission_messages_unittest.cc.
32 class ChromePermissionMessageProviderUnittest : public ChromeManifestTest {
33  public:
ChromePermissionMessageProviderUnittest()34   ChromePermissionMessageProviderUnittest()
35       : message_provider_(new ChromePermissionMessageProvider()) {}
~ChromePermissionMessageProviderUnittest()36   ~ChromePermissionMessageProviderUnittest() override {}
37 
38  protected:
GetMessages(const APIPermissionSet & permissions,Manifest::Type type)39   PermissionMessages GetMessages(const APIPermissionSet& permissions,
40                                  Manifest::Type type) {
41     return message_provider_->GetPermissionMessages(
42         message_provider_->GetAllPermissionIDs(
43             PermissionSet(permissions.Clone(), ManifestPermissionSet(),
44                           URLPatternSet(), URLPatternSet()),
45             type));
46   }
47 
GetManagementUIPermissionIDs(const APIPermissionSet & api_permissions,const ManifestPermissionSet & manifest_permissions,Manifest::Type type)48   PermissionMessages GetManagementUIPermissionIDs(
49       const APIPermissionSet& api_permissions,
50       const ManifestPermissionSet& manifest_permissions,
51       Manifest::Type type) {
52     return message_provider_->GetPermissionMessages(
53         message_provider_->GetManagementUIPermissionIDs(
54             PermissionSet(api_permissions.Clone(), manifest_permissions.Clone(),
55                           URLPatternSet(), URLPatternSet()),
56             type));
57   }
58 
IsPrivilegeIncrease(const APIPermissionSet & granted_permissions,const URLPatternSet & granted_hosts,const APIPermissionSet & requested_permissions,const URLPatternSet & requested_hosts)59   bool IsPrivilegeIncrease(const APIPermissionSet& granted_permissions,
60                            const URLPatternSet& granted_hosts,
61                            const APIPermissionSet& requested_permissions,
62                            const URLPatternSet& requested_hosts) {
63     return message_provider_->IsPrivilegeIncrease(
64         PermissionSet(granted_permissions.Clone(), ManifestPermissionSet(),
65                       granted_hosts.Clone(), URLPatternSet()),
66         PermissionSet(requested_permissions.Clone(), ManifestPermissionSet(),
67                       requested_hosts.Clone(), URLPatternSet()),
68         Manifest::TYPE_EXTENSION);
69   }
70 
message_provider()71   ChromePermissionMessageProvider* message_provider() {
72     return message_provider_.get();
73   }
74 
75  private:
76   std::unique_ptr<ChromePermissionMessageProvider> message_provider_;
77 
78   DISALLOW_COPY_AND_ASSIGN(ChromePermissionMessageProviderUnittest);
79 };
80 
81 // Checks that if an app has a superset and a subset permission, only the
82 // superset permission message is displayed if they are both present.
TEST_F(ChromePermissionMessageProviderUnittest,SupersetOverridesSubsetPermission)83 TEST_F(ChromePermissionMessageProviderUnittest,
84        SupersetOverridesSubsetPermission) {
85   {
86     APIPermissionSet permissions;
87     permissions.insert(APIPermission::kTab);
88     PermissionMessages messages =
89         GetMessages(permissions, Manifest::TYPE_PLATFORM_APP);
90     ASSERT_EQ(1U, messages.size());
91     EXPECT_EQ(
92         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ),
93         messages.front().message());
94   }
95   {
96     APIPermissionSet permissions;
97     permissions.insert(APIPermission::kTopSites);
98     PermissionMessages messages =
99         GetMessages(permissions, Manifest::TYPE_PLATFORM_APP);
100     ASSERT_EQ(1U, messages.size());
101     EXPECT_EQ(l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_TOPSITES),
102               messages.front().message());
103   }
104   {
105     APIPermissionSet permissions;
106     permissions.insert(APIPermission::kTab);
107     permissions.insert(APIPermission::kTopSites);
108     PermissionMessages messages =
109         GetMessages(permissions, Manifest::TYPE_PLATFORM_APP);
110     ASSERT_EQ(1U, messages.size());
111     EXPECT_EQ(
112         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ),
113         messages.front().message());
114   }
115 }
116 
117 // Checks that when permissions are merged into a single message, their details
118 // are merged as well.
TEST_F(ChromePermissionMessageProviderUnittest,WarningsAndDetailsCoalesceTogether)119 TEST_F(ChromePermissionMessageProviderUnittest,
120        WarningsAndDetailsCoalesceTogether) {
121   // kTab and kTopSites should be merged into a single message.
122   APIPermissionSet permissions;
123   permissions.insert(APIPermission::kTab);
124   permissions.insert(APIPermission::kTopSites);
125   // The USB device permission message has a non-empty details string.
126   std::unique_ptr<UsbDevicePermission> usb(new UsbDevicePermission(
127       PermissionsInfo::GetInstance()->GetByID(APIPermission::kUsbDevice)));
128   std::unique_ptr<base::ListValue> devices_list(new base::ListValue());
129   devices_list->Append(
130       UsbDevicePermissionData(0x02ad, 0x138c, -1, -1).ToValue());
131   devices_list->Append(
132       UsbDevicePermissionData(0x02ad, 0x138d, -1, -1).ToValue());
133   ASSERT_TRUE(usb->FromValue(devices_list.get(), nullptr, nullptr));
134   permissions.insert(std::move(usb));
135 
136   PermissionMessages messages =
137       GetMessages(permissions, Manifest::TYPE_EXTENSION);
138 
139   ASSERT_EQ(2U, messages.size());
140   auto it = messages.begin();
141   const PermissionMessage& message0 = *it++;
142   EXPECT_EQ(
143       l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ),
144       message0.message());
145   EXPECT_TRUE(message0.submessages().empty());
146   const PermissionMessage& message1 = *it++;
147   EXPECT_EQ(
148       l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_USB_DEVICE_LIST),
149       message1.message());
150   EXPECT_FALSE(message1.submessages().empty());
151 }
152 
153 // Check that if IDN domains are provided in host permissions, then those
154 // domains are converted to punycode.
TEST_F(ChromePermissionMessageProviderUnittest,IDNDomainsInHostPermissionsArePunycoded)155 TEST_F(ChromePermissionMessageProviderUnittest,
156        IDNDomainsInHostPermissionsArePunycoded) {
157   extensions::URLPatternSet explicit_hosts;
158 
159   explicit_hosts.AddPattern(
160       URLPattern(URLPattern::SCHEME_ALL, "https://ɡoogle.com/"));
161   explicit_hosts.AddPattern(
162       URLPattern(URLPattern::SCHEME_ALL, "https://*oogle.com/"));
163   extensions::PermissionSet permissions(
164       APIPermissionSet(), ManifestPermissionSet(), std::move(explicit_hosts),
165       URLPatternSet());
166 
167   PermissionMessages messages = message_provider()->GetPermissionMessages(
168       message_provider()->GetAllPermissionIDs(permissions,
169                                               Manifest::TYPE_EXTENSION));
170 
171   ASSERT_EQ(1U, messages.size());
172   EXPECT_EQ(l10n_util::GetStringFUTF16(
173                 IDS_EXTENSION_PROMPT_WARNING_2_HOSTS,
174                 base::ASCIIToUTF16("all xn--oogle-qmc.com sites"),
175                 base::ASCIIToUTF16("xn--oogle-qmc.com")),
176             messages.front().message());
177 }
178 
179 // Checks whether powerful permissions are returned correctly.
TEST_F(ChromePermissionMessageProviderUnittest,PowerfulPermissions)180 TEST_F(ChromePermissionMessageProviderUnittest, PowerfulPermissions) {
181   {
182     APIPermissionSet permissions;
183     permissions.insert(APIPermission::kTab);
184     PermissionMessages messages = GetManagementUIPermissionIDs(
185         permissions, ManifestPermissionSet(), Manifest::TYPE_EXTENSION);
186     ASSERT_EQ(1U, messages.size());
187     EXPECT_EQ(
188         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ),
189         messages.front().message());
190   }
191   {
192     APIPermissionSet permissions;
193     permissions.insert(APIPermission::kBookmark);
194     PermissionMessages messages = GetManagementUIPermissionIDs(
195         permissions, ManifestPermissionSet(), Manifest::TYPE_EXTENSION);
196     ASSERT_EQ(0U, messages.size());
197   }
198   {
199     APIPermissionSet permissions;
200     permissions.insert(APIPermission::kTab);
201     permissions.insert(APIPermission::kBookmark);
202     PermissionMessages messages = GetManagementUIPermissionIDs(
203         permissions, ManifestPermissionSet(), Manifest::TYPE_EXTENSION);
204     ASSERT_EQ(1U, messages.size());
205     EXPECT_EQ(
206         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_HISTORY_READ),
207         messages.front().message());
208   }
209   {
210     scoped_refptr<Extension> extension =
211         ManifestTest::LoadAndExpectSuccess("automation_desktop_true.json");
212     ASSERT_TRUE(extension.get());
213     ManifestPermissionSet manifest_permissions = extension->permissions_data()
214                                                      ->active_permissions()
215                                                      .manifest_permissions()
216                                                      .Clone();
217     APIPermissionSet permissions;
218     permissions.insert(APIPermission::kTab);
219     permissions.insert(APIPermission::kBookmark);
220     permissions.insert(APIPermission::kDebugger);
221     PermissionMessages messages = GetManagementUIPermissionIDs(
222         permissions, manifest_permissions, Manifest::TYPE_EXTENSION);
223     ASSERT_EQ(2U, messages.size());
224     EXPECT_EQ(l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_DEBUGGER),
225               messages.front().message());
226     EXPECT_EQ(
227         l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_FULL_ACCESS),
228         messages[1].message());
229   }
230   {
231     scoped_refptr<Extension> extension = ManifestTest::LoadAndExpectSuccess(
232         "automation_all_hosts_interact_true.json");
233     ASSERT_TRUE(extension.get());
234     ManifestPermissionSet manifest_permissions = extension->permissions_data()
235                                                      ->active_permissions()
236                                                      .manifest_permissions()
237                                                      .Clone();
238     APIPermissionSet permissions;
239     permissions.insert(APIPermission::kTab);
240     PermissionMessages messages = GetManagementUIPermissionIDs(
241         permissions, manifest_permissions, Manifest::TYPE_EXTENSION);
242     ASSERT_EQ(1U, messages.size());
243     EXPECT_EQ(l10n_util::GetStringUTF16(IDS_EXTENSION_PROMPT_WARNING_ALL_HOSTS),
244               messages.front().message());
245   }
246 }
247 
248 // Checks that granted hosts that may cause API permission messages are
249 // processed as part of IsPrivilegeIncrease. Regression test for
250 // crbug.com/1014505.
TEST_F(ChromePermissionMessageProviderUnittest,PrivilegeIncreaseAllUrls)251 TEST_F(ChromePermissionMessageProviderUnittest, PrivilegeIncreaseAllUrls) {
252   APIPermissionSet granted_permissions;
253   granted_permissions.insert(APIPermission::kWebRequest);
254 
255   extensions::URLPatternSet granted_hosts;
256   granted_hosts.AddPattern(URLPattern(URLPattern::SCHEME_ALL, "<all_urls>"));
257 
258   APIPermissionSet requested_permissions;
259   requested_permissions.insert(APIPermission::kWebRequest);
260   requested_permissions.insert(APIPermission::kDeclarativeNetRequest);
261 
262   extensions::URLPatternSet requested_hosts;
263   requested_hosts.AddPattern(URLPattern(URLPattern::SCHEME_ALL, "<all_urls>"));
264 
265   // While |kDeclarativeNetRequest| would cause a permission message, the
266   // inclusion of <all_urls> for both granted and request permissions should
267   // subsume the permission message for |kDeclarativeNetRequest| with its own
268   // message. Since this message would be identical between
269   // |granted_permissions| and |requested_permissions|, there should not be a
270   // privilege increase.
271   EXPECT_FALSE(IsPrivilegeIncrease(granted_permissions, granted_hosts,
272                                    requested_permissions, requested_hosts));
273 }
274 
275 }  // namespace extensions
276