Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/UrlUtilities.java |
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/UrlUtilities.java b/chrome/android/java/src/org/chromium/chrome/browser/UrlUtilities.java |
| index 1eca2c96dce9aba68b4853176d8e0fab675a370f..4ae28d319e6325f40ae4e77a128e12f79bc6a68a 100644 |
| --- a/chrome/android/java/src/org/chromium/chrome/browser/UrlUtilities.java |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/UrlUtilities.java |
| @@ -11,6 +11,8 @@ import org.chromium.base.CollectionUtil; |
| import java.net.URI; |
| import java.net.URISyntaxException; |
| import java.util.HashSet; |
| +import java.util.regex.Matcher; |
| +import java.util.regex.Pattern; |
| /** |
| * Utilities for working with URIs (and URLs). These methods may be used in security-sensitive |
| @@ -207,6 +209,89 @@ public class UrlUtilities { |
| return nativeGetDomainAndRegistry(uri, includePrivateRegistries); |
| } |
| + // Patterns used in validateIntentUrl. |
| + private static final Pattern DNS_HOSTNAME_PATTERN = |
| + Pattern.compile("^[\\w\\.-]*$"); |
| + private static final Pattern JAVA_PACKAGE_NAME_PATTERN = |
| + Pattern.compile("^[\\w\\.-]*$"); |
| + private static final Pattern URL_SCHEME_PATTERN = |
| + Pattern.compile("^[a-zA-Z]+$"); |
| + |
| + /** |
| + * @param url An Android intent:// URL to validate. |
| + * |
| + * @throws URISyntaxException if url is not a valid Android intent:// |
| + * URL, as specified at |
| + * https://developer.chrome.com/multidevice/android/intents#syntax. |
| + */ |
| + public static boolean validateIntentUrl(String url) { |
| + URI parsed = null; |
| + try { |
| + parsed = new URI(url); |
| + } catch (URISyntaxException e) { |
| + return false; |
| + } |
| + |
| + if (!parsed.getScheme().equals("intent")) return false; |
| + |
| + Matcher m = DNS_HOSTNAME_PATTERN.matcher(parsed.getHost()); |
| + if (!m.matches()) return false; |
| + |
| + if (!parsed.getPath().isEmpty() && !parsed.getPath().equals("/")) { |
| + return false; |
| + } |
| + |
| + String[] parts = parsed.getFragment().split(";"); |
| + if (parts.length < 3 |
| + || !parts[0].equals("Intent") |
| + || !parts[parts.length - 1].equals("end")) { |
| + return false; |
| + } |
| + |
| + boolean seenPackage = false; |
| + boolean seenAction = false; |
| + boolean seenCategory = false; |
| + boolean seenComponent = false; |
| + boolean seenScheme = false; |
| + |
| + for (int i = 1; i < parts.length - 1; ++i) { |
| + // This is OK *only* because no valid package, action, category, |
| + // component, or scheme contains "=". |
| + String[] pair = parts[i].split("="); |
| + if (2 != pair.length) return false; |
|
Yaron
2015/04/24 20:38:17
I think this breaks "extras". see below
|
| + |
| + m = JAVA_PACKAGE_NAME_PATTERN.matcher(pair[1]); |
| + if (pair[0].equals("package")) { |
| + if (seenPackage || !m.matches()) return false; |
| + seenPackage = true; |
| + } else if (pair[0].equals("action")) { |
| + if (seenAction || !m.matches()) return false; |
| + seenAction = true; |
| + } else if (pair[0].equals("category")) { |
| + if (seenCategory || !m.matches()) return false; |
| + seenCategory = true; |
| + } else if (pair[0].equals("component")) { |
| + if (seenComponent || !m.matches()) return false; |
| + seenComponent = true; |
| + } else if (pair[0].equals("scheme")) { |
| + if (seenScheme) return false; |
| + Matcher schemeMatcher = URL_SCHEME_PATTERN.matcher(pair[1]); |
| + if (!schemeMatcher.matches()) return false; |
| + seenScheme = true; |
| + } else { |
| + // Assume we are seeing an Intent Extra. Require that the |
| + // key be a valid Java package name, and that the value also |
| + // fit that rather restrictive pattern. The latter |
| + // requirement may have to be relaxed in the future, but |
| + // beware: Here Lurks Madness. |
|
Yaron
2015/04/24 20:38:17
I guess this is a deliberate "fail closed" but I w
|
| + Matcher keyMatcher = JAVA_PACKAGE_NAME_PATTERN.matcher(pair[0]); |
| + if (!m.matches() || !keyMatcher.matches()) return false; |
| + } |
| + } |
| + |
| + return true; |
| + } |
| + |
| private static native boolean nativeSameDomainOrHost(String primaryUrl, String secondaryUrl, |
| boolean includePrivateRegistries); |
| private static native String nativeGetDomainAndRegistry(String url, |