diff options
Diffstat (limited to 'mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java')
-rw-r--r-- | mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java new file mode 100644 index 0000000000..36fde18a02 --- /dev/null +++ b/mobile/android/geckoview/src/main/java/org/mozilla/gecko/util/IntentUtils.java @@ -0,0 +1,116 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, you can obtain one at http://mozilla.org/MPL/2.0/. + */ + +package org.mozilla.gecko.util; + +import android.content.Intent; +import android.net.Uri; +import java.net.URISyntaxException; +import java.util.Locale; + +/** Utilities for Intents. */ +public class IntentUtils { + private IntentUtils() {} + + /** + * Return a Uri instance which is equivalent to uri, but with a guaranteed-lowercase scheme as if + * the API level 16 method Uri.normalizeScheme had been called. + * + * @param uri The URI string to normalize. + * @return The corresponding normalized Uri. + */ + private static Uri normalizeUriScheme(final Uri uri) { + final String scheme = uri.getScheme(); + if (scheme == null) { + return uri; + } + final String lower = scheme.toLowerCase(Locale.ROOT); + if (lower.equals(scheme)) { + return uri; + } + + // Otherwise, return a new URI with a normalized scheme. + return uri.buildUpon().scheme(lower).build(); + } + + /** + * Return a normalized Uri instance that corresponds to the given URI string with cross-API-level + * compatibility. + * + * @param aUri The URI string to normalize. + * @return The corresponding normalized Uri. + */ + public static Uri normalizeUri(final String aUri) { + return normalizeUriScheme( + aUri.indexOf(':') >= 0 ? Uri.parse(aUri) : new Uri.Builder().scheme(aUri).build()); + } + + public static boolean isUriSafeForScheme(final String aUri) { + return isUriSafeForScheme(normalizeUri(aUri)); + } + + /** + * Verify whether the given URI is considered safe to load in respect to its scheme. Unsafe URIs + * should be blocked from further handling. + * + * @param aUri The URI instance to test. + * @return Whether the provided URI is considered safe in respect to its scheme. + */ + public static boolean isUriSafeForScheme(final Uri aUri) { + final String scheme = aUri.getScheme(); + if ("tel".equals(scheme) || "sms".equals(scheme)) { + // Bug 794034 - We don't want to pass MWI or USSD codes to the + // dialer, and ensure the Uri class doesn't parse a URI + // containing a fragment ('#') + final String number = aUri.getSchemeSpecificPart(); + if (number.contains("#") || number.contains("*") || aUri.getFragment() != null) { + return false; + } + } + + if (("intent".equals(scheme) || "android-app".equals(scheme))) { + // Bug 1356893 - Rject intents with file data schemes. + return getSafeIntent(aUri) != null; + } + + return true; + } + + /** + * Create a safe intent for the given URI. Intents with file data schemes are considered unsafe. + * + * @param aUri The URI for the intent. + * @return A safe intent for the given URI or null if URI is considered unsafe. + */ + public static Intent getSafeIntent(final Uri aUri) { + final Intent intent; + try { + intent = Intent.parseUri(aUri.toString(), 0); + } catch (final URISyntaxException e) { + return null; + } + + final Uri data = intent.getData(); + if (data != null && "file".equals(normalizeUriScheme(data).getScheme())) { + return null; + } + + // Only open applications which can accept arbitrary data from a browser. + intent.addCategory(Intent.CATEGORY_BROWSABLE); + + // Prevent site from explicitly opening our internal activities, + // which can leak data. + intent.setComponent(null); + nullIntentSelector(intent); + + return intent; + } + + // We create a separate method to better encapsulate the @TargetApi use. + private static void nullIntentSelector(final Intent intent) { + intent.setSelector(null); + } +} |