OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "extensions/common/csp_validator.h" | 5 #include "extensions/common/csp_validator.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/strings/string_split.h" | 9 #include "base/strings/string_split.h" |
10 #include "base/strings/string_tokenizer.h" | 10 #include "base/strings/string_tokenizer.h" |
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 | 100 |
101 // Wildcards on subdomains of a TLD are not allowed. | 101 // Wildcards on subdomains of a TLD are not allowed. |
102 size_t registry_length = net::registry_controlled_domains::GetRegistryLength( | 102 size_t registry_length = net::registry_controlled_domains::GetRegistryLength( |
103 host, | 103 host, |
104 net::registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES, | 104 net::registry_controlled_domains::INCLUDE_UNKNOWN_REGISTRIES, |
105 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); | 105 net::registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES); |
106 return registry_length != 0; | 106 return registry_length != 0; |
107 } | 107 } |
108 | 108 |
109 bool HasOnlySecureTokens(base::StringTokenizer& tokenizer, | 109 bool HasOnlySecureTokens(base::StringTokenizer& tokenizer, |
110 Manifest::Type type) { | 110 int options) { |
111 while (tokenizer.GetNext()) { | 111 while (tokenizer.GetNext()) { |
112 std::string source = tokenizer.token(); | 112 std::string source = tokenizer.token(); |
113 base::StringToLowerASCII(&source); | 113 base::StringToLowerASCII(&source); |
114 | 114 |
115 // We might need to relax this whitelist over time. | 115 // We might need to relax this whitelist over time. |
116 if (source == "'self'" || | 116 if (source == "'self'" || |
117 source == "'none'" || | 117 source == "'none'" || |
118 source == "http://127.0.0.1" || | 118 source == "http://127.0.0.1" || |
119 LowerCaseEqualsASCII(source, "blob:") || | 119 LowerCaseEqualsASCII(source, "blob:") || |
120 LowerCaseEqualsASCII(source, "filesystem:") || | 120 LowerCaseEqualsASCII(source, "filesystem:") || |
121 LowerCaseEqualsASCII(source, "http://localhost") || | 121 LowerCaseEqualsASCII(source, "http://localhost") || |
122 StartsWithASCII(source, "http://127.0.0.1:", true) || | 122 StartsWithASCII(source, "http://127.0.0.1:", true) || |
123 StartsWithASCII(source, "http://localhost:", true) || | 123 StartsWithASCII(source, "http://localhost:", true) || |
124 isNonWildcardTLD(source, "https://", true) || | 124 isNonWildcardTLD(source, "https://", true) || |
125 isNonWildcardTLD(source, "chrome://", false) || | 125 isNonWildcardTLD(source, "chrome://", false) || |
126 isNonWildcardTLD(source, | 126 isNonWildcardTLD(source, |
127 std::string(extensions::kExtensionScheme) + | 127 std::string(extensions::kExtensionScheme) + |
128 url::kStandardSchemeSeparator, | 128 url::kStandardSchemeSeparator, |
129 false) || | 129 false) || |
130 StartsWithASCII(source, "chrome-extension-resource:", true)) { | 130 StartsWithASCII(source, "chrome-extension-resource:", true)) { |
131 continue; | 131 continue; |
132 } | 132 } |
133 | 133 |
134 // crbug.com/146487 | 134 if (options & OPTIONS_ALLOW_UNSAFE_EVAL) { |
135 if (type == Manifest::TYPE_EXTENSION || | |
136 type == Manifest::TYPE_LEGACY_PACKAGED_APP) { | |
137 if (source == "'unsafe-eval'") | 135 if (source == "'unsafe-eval'") |
138 continue; | 136 continue; |
139 } | 137 } |
140 | 138 |
141 return false; | 139 return false; |
142 } | 140 } |
143 | 141 |
144 return true; // Empty values default to 'none', which is secure. | 142 return true; // Empty values default to 'none', which is secure. |
145 } | 143 } |
146 | 144 |
147 // Returns true if |directive_name| matches |status.directive_name|. | 145 // Returns true if |directive_name| matches |status.directive_name|. |
148 bool UpdateStatus(const std::string& directive_name, | 146 bool UpdateStatus(const std::string& directive_name, |
149 base::StringTokenizer& tokenizer, | 147 base::StringTokenizer& tokenizer, |
150 DirectiveStatus* status, | 148 DirectiveStatus* status, |
151 Manifest::Type type) { | 149 int options) { |
152 if (status->seen_in_policy) | 150 if (status->seen_in_policy) |
153 return false; | 151 return false; |
154 if (directive_name != status->directive_name) | 152 if (directive_name != status->directive_name) |
155 return false; | 153 return false; |
156 status->seen_in_policy = true; | 154 status->seen_in_policy = true; |
157 status->is_secure = HasOnlySecureTokens(tokenizer, type); | 155 status->is_secure = HasOnlySecureTokens(tokenizer, options); |
158 return true; | 156 return true; |
159 } | 157 } |
160 | 158 |
161 } // namespace | 159 } // namespace |
162 | 160 |
163 bool ContentSecurityPolicyIsLegal(const std::string& policy) { | 161 bool ContentSecurityPolicyIsLegal(const std::string& policy) { |
164 // We block these characters to prevent HTTP header injection when | 162 // We block these characters to prevent HTTP header injection when |
165 // representing the content security policy as an HTTP header. | 163 // representing the content security policy as an HTTP header. |
166 const char kBadChars[] = {',', '\r', '\n', '\0'}; | 164 const char kBadChars[] = {',', '\r', '\n', '\0'}; |
167 | 165 |
168 return policy.find_first_of(kBadChars, 0, arraysize(kBadChars)) == | 166 return policy.find_first_of(kBadChars, 0, arraysize(kBadChars)) == |
169 std::string::npos; | 167 std::string::npos; |
170 } | 168 } |
171 | 169 |
172 bool ContentSecurityPolicyIsSecure(const std::string& policy, | 170 bool ContentSecurityPolicyIsSecure(const std::string& policy, |
173 Manifest::Type type) { | 171 int options) { |
174 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. | 172 // See http://www.w3.org/TR/CSP/#parse-a-csp-policy for parsing algorithm. |
175 std::vector<std::string> directives; | 173 std::vector<std::string> directives; |
176 base::SplitString(policy, ';', &directives); | 174 base::SplitString(policy, ';', &directives); |
177 | 175 |
178 DirectiveStatus default_src_status(kDefaultSrc); | 176 DirectiveStatus default_src_status(kDefaultSrc); |
179 DirectiveStatus script_src_status(kScriptSrc); | 177 DirectiveStatus script_src_status(kScriptSrc); |
180 DirectiveStatus object_src_status(kObjectSrc); | 178 DirectiveStatus object_src_status(kObjectSrc); |
181 | 179 |
182 for (size_t i = 0; i < directives.size(); ++i) { | 180 for (size_t i = 0; i < directives.size(); ++i) { |
183 std::string& input = directives[i]; | 181 std::string& input = directives[i]; |
184 base::StringTokenizer tokenizer(input, " \t\r\n"); | 182 base::StringTokenizer tokenizer(input, " \t\r\n"); |
185 if (!tokenizer.GetNext()) | 183 if (!tokenizer.GetNext()) |
186 continue; | 184 continue; |
187 | 185 |
188 std::string directive_name = tokenizer.token(); | 186 std::string directive_name = tokenizer.token(); |
189 base::StringToLowerASCII(&directive_name); | 187 base::StringToLowerASCII(&directive_name); |
190 | 188 |
191 if (UpdateStatus(directive_name, tokenizer, &default_src_status, type)) | 189 if (UpdateStatus(directive_name, tokenizer, &default_src_status, options)) |
192 continue; | 190 continue; |
193 if (UpdateStatus(directive_name, tokenizer, &script_src_status, type)) | 191 if (UpdateStatus(directive_name, tokenizer, &script_src_status, options)) |
194 continue; | 192 continue; |
195 if (UpdateStatus(directive_name, tokenizer, &object_src_status, type)) | 193 if (UpdateStatus(directive_name, tokenizer, &object_src_status, options)) |
196 continue; | 194 continue; |
197 } | 195 } |
198 | 196 |
199 if (script_src_status.seen_in_policy && !script_src_status.is_secure) | 197 if (script_src_status.seen_in_policy && !script_src_status.is_secure) |
200 return false; | 198 return false; |
201 | 199 |
202 if (object_src_status.seen_in_policy && !object_src_status.is_secure) | 200 if (object_src_status.seen_in_policy && !object_src_status.is_secure) { |
203 return false; | 201 // Note that this does not fully check the object-src source list for |
| 202 // validity but Blink will do this anyway. |
| 203 if (!(options & OPTIONS_ALLOW_INSECURE_OBJECT_SRC)) |
| 204 return false; |
| 205 } |
204 | 206 |
205 if (default_src_status.seen_in_policy && !default_src_status.is_secure) { | 207 if (default_src_status.seen_in_policy && !default_src_status.is_secure) { |
206 return script_src_status.seen_in_policy && | 208 return script_src_status.seen_in_policy && |
207 object_src_status.seen_in_policy; | 209 object_src_status.seen_in_policy; |
208 } | 210 } |
209 | 211 |
210 return default_src_status.seen_in_policy || | 212 return default_src_status.seen_in_policy || |
211 (script_src_status.seen_in_policy && object_src_status.seen_in_policy); | 213 (script_src_status.seen_in_policy && object_src_status.seen_in_policy); |
212 } | 214 } |
213 | 215 |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 } | 250 } |
249 } | 251 } |
250 } | 252 } |
251 | 253 |
252 return seen_sandbox; | 254 return seen_sandbox; |
253 } | 255 } |
254 | 256 |
255 } // namespace csp_validator | 257 } // namespace csp_validator |
256 | 258 |
257 } // namespace extensions | 259 } // namespace extensions |
OLD | NEW |