mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-18 15:55:36 +00:00
Bug 1480757 - [1.0] Move safe intent handling utilities from IntentHelper to IntentUtils to make them accessible in GeckoView. r=jchen
This commit is contained in:
parent
70d6c88eae
commit
af25612f9c
@ -12,9 +12,9 @@ import org.mozilla.gecko.util.ActivityResultHandler;
|
|||||||
import org.mozilla.gecko.util.BundleEventListener;
|
import org.mozilla.gecko.util.BundleEventListener;
|
||||||
import org.mozilla.gecko.util.EventCallback;
|
import org.mozilla.gecko.util.EventCallback;
|
||||||
import org.mozilla.gecko.util.GeckoBundle;
|
import org.mozilla.gecko.util.GeckoBundle;
|
||||||
|
import org.mozilla.gecko.util.IntentUtils;
|
||||||
import org.mozilla.gecko.widget.ExternalIntentDuringPrivateBrowsingPromptFragment;
|
import org.mozilla.gecko.widget.ExternalIntentDuringPrivateBrowsingPromptFragment;
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ClipData;
|
import android.content.ClipData;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@ -318,41 +318,21 @@ public final class IntentHelper implements BundleEventListener {
|
|||||||
context.getResources().getString(R.string.share_title));
|
context.getResources().getString(R.string.share_title));
|
||||||
}
|
}
|
||||||
|
|
||||||
Uri uri = normalizeUriScheme(targetURI.indexOf(':') >= 0 ? Uri.parse(targetURI) : new Uri.Builder().scheme(targetURI).build());
|
Uri uri = IntentUtils.normalizeUri(targetURI);
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(mimeType)) {
|
if (!TextUtils.isEmpty(mimeType)) {
|
||||||
Intent intent = getIntentForActionString(action);
|
Intent intent = getIntentForActionString(action);
|
||||||
intent.setDataAndType(uri, mimeType);
|
intent.setDataAndType(uri, mimeType);
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!GeckoAppShell.isUriSafeForScheme(uri)) {
|
if (!IntentUtils.isUriSafeForScheme(targetURI)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String scheme = uri.getScheme();
|
final String scheme = uri.getScheme();
|
||||||
if ("intent".equals(scheme) || "android-app".equals(scheme)) {
|
if ("intent".equals(scheme) || "android-app".equals(scheme)) {
|
||||||
final Intent intent;
|
return IntentUtils.getSafeIntent(uri);
|
||||||
try {
|
|
||||||
intent = Intent.parseUri(targetURI, 0);
|
|
||||||
} catch (final URISyntaxException e) {
|
|
||||||
Log.e(LOGTAG, "Unable to parse URI - " + e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Uri data = intent.getData();
|
|
||||||
if (data != null && "file".equals(normalizeUriScheme(data).getScheme())) {
|
|
||||||
Log.w(LOGTAG, "Blocked intent with \"file://\" data scheme.");
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute our most likely intent, then check to see if there are any
|
// Compute our most likely intent, then check to see if there are any
|
||||||
@ -427,31 +407,6 @@ public final class IntentHelper implements BundleEventListener {
|
|||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// We create a separate method to better encapsulate the @TargetApi use.
|
|
||||||
@TargetApi(15)
|
|
||||||
private static void nullIntentSelector(final Intent intent) {
|
|
||||||
intent.setSelector(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a <code>Uri</code> instance which is equivalent to <code>u</code>,
|
|
||||||
* but with a guaranteed-lowercase scheme as if the API level 16 method
|
|
||||||
* <code>u.normalizeScheme</code> had been called.
|
|
||||||
*
|
|
||||||
* @param u the <code>Uri</code> to normalize.
|
|
||||||
* @return a <code>Uri</code>, which might be <code>u</code>.
|
|
||||||
*/
|
|
||||||
private static Uri normalizeUriScheme(final Uri u) {
|
|
||||||
final String scheme = u.getScheme();
|
|
||||||
final String lower = scheme.toLowerCase(Locale.US);
|
|
||||||
if (lower.equals(scheme)) {
|
|
||||||
return u;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Otherwise, return a new URI with a normalized scheme.
|
|
||||||
return u.buildUpon().scheme(lower).build();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override // BundleEventHandler
|
@Override // BundleEventHandler
|
||||||
public void handleMessage(final String event, final GeckoBundle message,
|
public void handleMessage(final String event, final GeckoBundle message,
|
||||||
final EventCallback callback) {
|
final EventCallback callback) {
|
||||||
|
@ -936,20 +936,6 @@ public class GeckoAppShell
|
|||||||
return type + "/" + subType;
|
return type + "/" + subType;
|
||||||
}
|
}
|
||||||
|
|
||||||
static boolean isUriSafeForScheme(Uri aUri) {
|
|
||||||
// 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 scheme = aUri.getScheme();
|
|
||||||
if ("tel".equals(scheme) || "sms".equals(scheme)) {
|
|
||||||
final String number = aUri.getSchemeSpecificPart();
|
|
||||||
if (number.contains("#") || number.contains("*") || aUri.getFragment() != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@WrapForJNI(calledFrom = "gecko")
|
@WrapForJNI(calledFrom = "gecko")
|
||||||
private static boolean openUriExternal(String targetURI,
|
private static boolean openUriExternal(String targetURI,
|
||||||
String mimeType,
|
String mimeType,
|
||||||
|
@ -6,8 +6,10 @@
|
|||||||
|
|
||||||
package org.mozilla.gecko.util;
|
package org.mozilla.gecko.util;
|
||||||
|
|
||||||
|
import android.annotation.TargetApi;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.CheckResult;
|
import android.support.annotation.CheckResult;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
@ -15,7 +17,9 @@ import android.text.TextUtils;
|
|||||||
|
|
||||||
import org.mozilla.gecko.mozglue.SafeIntent;
|
import org.mozilla.gecko.mozglue.SafeIntent;
|
||||||
|
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.Locale;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
@ -104,4 +108,113 @@ public class IntentUtils {
|
|||||||
final HashMap<String, String> envVars = IntentUtils.getEnvVarMap(intent);
|
final HashMap<String, String> envVars = IntentUtils.getEnvVarMap(intent);
|
||||||
return !TextUtils.isEmpty(envVars.get(IntentUtils.ENV_VAR_IN_AUTOMATION));
|
return !TextUtils.isEmpty(envVars.get(IntentUtils.ENV_VAR_IN_AUTOMATION));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
final String lower = scheme.toLowerCase(Locale.US);
|
||||||
|
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) {
|
||||||
|
final Uri normUri = normalizeUriScheme(
|
||||||
|
aUri.indexOf(':') >= 0
|
||||||
|
? Uri.parse(aUri)
|
||||||
|
: new Uri.Builder().scheme(aUri).build());
|
||||||
|
return normUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
@TargetApi(15)
|
||||||
|
private static void nullIntentSelector(final Intent intent) {
|
||||||
|
intent.setSelector(null);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user