1 // Copyright 2018 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 "extensions/common/cors_util.h"
6 
7 #include <utility>
8 
9 #include "base/strings/string_number_conversions.h"
10 #include "build/chromeos_buildflags.h"
11 #include "content/public/common/url_constants.h"
12 #include "extensions/common/constants.h"
13 #include "extensions/common/extension.h"
14 #include "extensions/common/extension_urls.h"
15 #include "extensions/common/permissions/permissions_data.h"
16 #include "extensions/common/url_pattern_set.h"
17 
18 namespace extensions {
19 
20 namespace {
21 
GetEffectivePort(const std::string & port_string)22 uint16_t GetEffectivePort(const std::string& port_string) {
23   int port_int = 0;
24   bool success = base::StringToInt(port_string, &port_int);
25   // The URLPattern should verify that |port| is a number or "*", so conversion
26   // should never fail.
27   DCHECK(success) << port_string;
28   return port_int;
29 }
30 
AddURLPatternSetToList(const URLPatternSet & pattern_set,std::vector<network::mojom::CorsOriginPatternPtr> * list,network::mojom::CorsOriginAccessMatchPriority priority)31 void AddURLPatternSetToList(
32     const URLPatternSet& pattern_set,
33     std::vector<network::mojom::CorsOriginPatternPtr>* list,
34     network::mojom::CorsOriginAccessMatchPriority priority) {
35   static const char* const kSchemes[] = {
36     content::kChromeUIScheme,
37 #if BUILDFLAG(IS_CHROMEOS_ASH)
38     content::kExternalFileScheme,
39 #endif
40     extensions::kExtensionScheme,
41     url::kFileScheme,
42     url::kFtpScheme,
43     url::kHttpScheme,
44     url::kHttpsScheme,
45   };
46   for (const URLPattern& pattern : pattern_set) {
47     for (const char* const scheme : kSchemes) {
48       if (!pattern.MatchesScheme(scheme))
49         continue;
50       network::mojom::CorsDomainMatchMode domain_match_mode =
51           pattern.match_subdomains()
52               ? network::mojom::CorsDomainMatchMode::kAllowSubdomains
53               : network::mojom::CorsDomainMatchMode::kDisallowSubdomains;
54       network::mojom::CorsPortMatchMode port_match_mode =
55           (pattern.port() == "*")
56               ? network::mojom::CorsPortMatchMode::kAllowAnyPort
57               : network::mojom::CorsPortMatchMode::kAllowOnlySpecifiedPort;
58       uint16_t port =
59           (port_match_mode ==
60            network::mojom::CorsPortMatchMode::kAllowOnlySpecifiedPort)
61               ? GetEffectivePort(pattern.port())
62               : 0u;
63       list->push_back(network::mojom::CorsOriginPattern::New(
64           scheme, pattern.host(), port, domain_match_mode, port_match_mode,
65           priority));
66     }
67   }
68 }
69 
70 }  // namespace
71 
72 std::vector<network::mojom::CorsOriginPatternPtr>
CreateCorsOriginAccessAllowList(const Extension & extension,PermissionsData::EffectiveHostPermissionsMode mode)73 CreateCorsOriginAccessAllowList(
74     const Extension& extension,
75     PermissionsData::EffectiveHostPermissionsMode mode) {
76   std::vector<network::mojom::CorsOriginPatternPtr> allow_list;
77 
78   // Permissions declared by the extension.
79   URLPatternSet origin_permissions =
80       extension.permissions_data()->GetEffectiveHostPermissions(mode);
81   AddURLPatternSetToList(
82       origin_permissions, &allow_list,
83       network::mojom::CorsOriginAccessMatchPriority::kDefaultPriority);
84 
85   // Hosts exempted from the enterprise policy blocklist.
86   // This set intersection is necessary to prevent an enterprise policy from
87   // granting a host permission the extension didn't ask for.
88   URLPatternSet policy_allowed_host_patterns =
89       URLPatternSet::CreateIntersection(
90           extension.permissions_data()->policy_allowed_hosts(),
91           origin_permissions, URLPatternSet::IntersectionBehavior::kDetailed);
92   AddURLPatternSetToList(
93       policy_allowed_host_patterns, &allow_list,
94       network::mojom::CorsOriginAccessMatchPriority::kMediumPriority);
95 
96   return allow_list;
97 }
98 
99 std::vector<network::mojom::CorsOriginPatternPtr>
CreateCorsOriginAccessBlockList(const Extension & extension)100 CreateCorsOriginAccessBlockList(const Extension& extension) {
101   std::vector<network::mojom::CorsOriginPatternPtr> block_list;
102 
103   // Hosts blocked by enterprise policy.
104   AddURLPatternSetToList(
105       extension.permissions_data()->policy_blocked_hosts(), &block_list,
106       network::mojom::CorsOriginAccessMatchPriority::kLowPriority);
107 
108   GURL webstore_launch_url = extension_urls::GetWebstoreLaunchURL();
109   block_list.push_back(network::mojom::CorsOriginPattern::New(
110       webstore_launch_url.scheme(), webstore_launch_url.host(), /*port=*/0,
111       network::mojom::CorsDomainMatchMode::kAllowSubdomains,
112       network::mojom::CorsPortMatchMode::kAllowAnyPort,
113       network::mojom::CorsOriginAccessMatchPriority::kHighPriority));
114 
115   // TODO(devlin): Should we also block the webstore update URL here? See
116   // https://crbug.com/826946 for a related instance.
117   return block_list;
118 }
119 
120 }  // namespace extensions
121