Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(331)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/UrlUtilities.java

Issue 1059413004: Add a validator for intent:// URLs. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Why are the presubmit checks different on the bots vs. local? import org.chromium.base.Log; Created 5 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | chrome/android/javatests/src/org/chromium/chrome/browser/UrlUtilitiesTest.java » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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 package org.chromium.chrome.browser; 5 package org.chromium.chrome.browser;
6 6
7 import android.text.TextUtils; 7 import android.text.TextUtils;
8 8
9 import org.chromium.base.CollectionUtil; 9 import org.chromium.base.CollectionUtil;
10 import org.chromium.base.Log;
10 11
12 import java.io.UnsupportedEncodingException;
11 import java.net.URI; 13 import java.net.URI;
12 import java.net.URISyntaxException; 14 import java.net.URISyntaxException;
15 import java.net.URLDecoder;
13 import java.util.HashSet; 16 import java.util.HashSet;
17 import java.util.regex.Matcher;
18 import java.util.regex.Pattern;
14 19
15 /** 20 /**
16 * Utilities for working with URIs (and URLs). These methods may be used in secu rity-sensitive 21 * Utilities for working with URIs (and URLs). These methods may be used in secu rity-sensitive
17 * contexts (after all, origins are the security boundary on the web), and so th e correctness bar 22 * contexts (after all, origins are the security boundary on the web), and so th e correctness bar
18 * must be high. 23 * must be high.
19 */ 24 */
20 public class UrlUtilities { 25 public class UrlUtilities {
26 private static final String TAG = "UrlUtilities";
27
21 /** 28 /**
22 * URI schemes that ContentView can handle. 29 * URI schemes that ContentView can handle.
23 */ 30 */
24 private static final HashSet<String> ACCEPTED_SCHEMES = CollectionUtil.newHa shSet( 31 private static final HashSet<String> ACCEPTED_SCHEMES = CollectionUtil.newHa shSet(
25 "about", "data", "file", "http", "https", "inline", "javascript"); 32 "about", "data", "file", "http", "https", "inline", "javascript");
26 33
27 /** 34 /**
28 * URI schemes that Chrome can download. 35 * URI schemes that Chrome can download.
29 */ 36 */
30 private static final HashSet<String> DOWNLOADABLE_SCHEMES = CollectionUtil.n ewHashSet( 37 private static final HashSet<String> DOWNLOADABLE_SCHEMES = CollectionUtil.n ewHashSet(
(...skipping 169 matching lines...) Expand 10 before | Expand all | Expand 10 after
200 * no subdomains, from the given URI. Returns an empty string if the URI is invalid, has no host 207 * no subdomains, from the given URI. Returns an empty string if the URI is invalid, has no host
201 * (e.g. a file: URI), has multiple trailing dots, is an IP address, has onl y one subcomponent 208 * (e.g. a file: URI), has multiple trailing dots, is an IP address, has onl y one subcomponent
202 * (i.e. no dots other than leading/trailing ones), or is itself a recognize d registry 209 * (i.e. no dots other than leading/trailing ones), or is itself a recognize d registry
203 * identifier. 210 * identifier.
204 */ 211 */
205 public static String getDomainAndRegistry(String uri, boolean includePrivate Registries) { 212 public static String getDomainAndRegistry(String uri, boolean includePrivate Registries) {
206 if (TextUtils.isEmpty(uri)) return uri; 213 if (TextUtils.isEmpty(uri)) return uri;
207 return nativeGetDomainAndRegistry(uri, includePrivateRegistries); 214 return nativeGetDomainAndRegistry(uri, includePrivateRegistries);
208 } 215 }
209 216
217 // Patterns used in validateIntentUrl.
218 private static final Pattern DNS_HOSTNAME_PATTERN =
219 Pattern.compile("^[\\w\\.-]*$");
220 private static final Pattern JAVA_PACKAGE_NAME_PATTERN =
221 Pattern.compile("^[\\w\\.-]*$");
222 private static final Pattern ANDROID_COMPONENT_NAME_PATTERN =
223 Pattern.compile("^[\\w\\./-]*$");
224 private static final Pattern URL_SCHEME_PATTERN =
225 Pattern.compile("^[a-zA-Z]+$");
226
227 /**
228 * @param url An Android intent:// URL to validate.
229 *
230 * @throws URISyntaxException if url is not a valid Android intent://
231 * URL, as specified at
232 * https://developer.chrome.com/multidevice/android/intents#syntax.
233 */
234 public static boolean validateIntentUrl(String url) {
235 if (url == null) {
236 Log.d(TAG, "url was null");
237 return false;
238 }
239
240 URI parsed = null;
241 try {
242 parsed = new URI(url);
243 } catch (URISyntaxException e) {
244 Log.d(TAG, e.toString());
245 return false;
246 }
247
248 if (!parsed.getScheme().equals("intent")) {
249 Log.d(TAG, "scheme was not 'intent'");
250 return false;
251 }
252
253 String hostname = parsed.getHost();
254 if (hostname == null) {
255 Log.d(TAG, "hostname was null for '" + url + "'");
256 return false;
257 }
258 Matcher m = DNS_HOSTNAME_PATTERN.matcher(hostname);
259 if (!m.matches()) {
260 Log.d(TAG, "hostname did not match DNS_HOSTNAME_PATTERN");
261 return false;
262 }
263
264 String path = parsed.getPath();
265 if (path == null || (!path.isEmpty() && !path.equals("/"))) {
266 Log.d(TAG, "path was null or not '/'");
267 return false;
268 }
269
270 // We need to get the raw, unparsed, un-URL-decoded fragment.
271 // parsed.getFragment() returns a URL-decoded fragment, which can
272 // interfere with lexing and parsing Intent extras correctly. Therefore,
273 // we handle the fragment "manually", but first assert that it
274 // URL-decodes correctly.
275 int fragmentStart = url.indexOf('#');
276 if (fragmentStart == -1 || fragmentStart == url.length() - 1) {
277 Log.d(TAG, "Could not find '#'");
278 return false;
279 }
280 String fragment = url.substring(url.indexOf('#') + 1);
281 try {
282 String f = parsed.getFragment();
283 if (f == null) {
284 Log.d(TAG, "Could not get fragment from parsed URL");
285 return false;
286 }
287 if (!URLDecoder.decode(fragment, "UTF-8").equals(f)) {
288 Log.d(TAG, "Parsed fragment does not equal lexed fragment");
289 return false;
290 }
291 } catch (UnsupportedEncodingException e) {
292 Log.d(TAG, e.toString());
293 return false;
294 }
295
296 // Now lex and parse the correctly-encoded fragment.
297 String[] parts = fragment.split(";");
298 if (parts.length < 3
299 || !parts[0].equals("Intent")
300 || !parts[parts.length - 1].equals("end")) {
301 Log.d(TAG, "Invalid fragment (not enough parts, lacking Intent, "
302 + "or lacking end");
303 return false;
304 }
305
306 boolean seenPackage = false;
307 boolean seenAction = false;
308 boolean seenCategory = false;
309 boolean seenComponent = false;
310 boolean seenScheme = false;
311
312 for (int i = 1; i < parts.length - 1; ++i) {
313 // This is OK *only* because no valid package, action, category,
314 // component, or scheme contains (unencoded) "=".
315 String[] pair = parts[i].split("=");
316 if (2 != pair.length) {
317 Log.d(TAG, "Invalid key=value pair '" + parts[i] + "'");
318 return false;
319 }
320
321 m = JAVA_PACKAGE_NAME_PATTERN.matcher(pair[1]);
322 if (pair[0].equals("package")) {
323 if (seenPackage || !m.matches()) {
324 Log.d(TAG, "Invalid package '" + pair[1] + "'");
325 return false;
326 }
327 seenPackage = true;
328 } else if (pair[0].equals("action")) {
329 if (seenAction || !m.matches()) {
330 Log.d(TAG, "Invalid action '" + pair[1] + "'");
331 return false;
332 }
333 seenAction = true;
334 } else if (pair[0].equals("category")) {
335 if (seenCategory || !m.matches()) {
336 Log.d(TAG, "Invalid category '" + pair[1] + "'");
337 return false;
338 }
339 seenCategory = true;
340 } else if (pair[0].equals("component")) {
341 Matcher componentMatcher = ANDROID_COMPONENT_NAME_PATTERN.matche r(pair[1]);
342 if (seenComponent || !componentMatcher.matches()) {
343 Log.d(TAG, "Invalid component '" + pair[1] + "'");
344 return false;
345 }
346 seenComponent = true;
347 } else if (pair[0].equals("scheme")) {
348 if (seenScheme) return false;
349 Matcher schemeMatcher = URL_SCHEME_PATTERN.matcher(pair[1]);
350 if (!schemeMatcher.matches()) {
351 Log.d(TAG, "Invalid scheme '" + pair[1] + "'");
352 return false;
353 }
354 seenScheme = true;
355 } else {
356 // Assume we are seeing an Intent Extra. Up above, we ensured
357 // that the #Intent... fragment was correctly URL-encoded;
358 // beyond that, there is no further validation we can do. Extras
359 // are blobs to us.
360 continue;
361 }
362 }
363
364 return true;
365 }
366
210 private static native boolean nativeSameDomainOrHost(String primaryUrl, Stri ng secondaryUrl, 367 private static native boolean nativeSameDomainOrHost(String primaryUrl, Stri ng secondaryUrl,
211 boolean includePrivateRegistries); 368 boolean includePrivateRegistries);
212 private static native String nativeGetDomainAndRegistry(String url, 369 private static native String nativeGetDomainAndRegistry(String url,
213 boolean includePrivateRegistries); 370 boolean includePrivateRegistries);
214 public static native boolean nativeIsGoogleSearchUrl(String url); 371 public static native boolean nativeIsGoogleSearchUrl(String url);
215 public static native boolean nativeIsGoogleHomePageUrl(String url); 372 public static native boolean nativeIsGoogleHomePageUrl(String url);
216 private static native String nativeFixupUrl(String url, String desiredTld); 373 private static native String nativeFixupUrl(String url, String desiredTld);
217 } 374 }
OLDNEW
« no previous file with comments | « no previous file | chrome/android/javatests/src/org/chromium/chrome/browser/UrlUtilitiesTest.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698