Commit 2b1c14dd authored by Liquan(Max) Gu's avatar Liquan(Max) Gu Committed by Commit Bot

[PaymentHandler] Block insecure content

The Payment Handler of the Chrome Custom Tab solution blocks the
insecure content. The Payment Handler of the bottom-sheet solution also
requires the same check. This CL is to add the check.

The security check was implemented according to the spec:
https://www.w3.org/TR/payment-handler/#authorized-payment-apps

After this change, opening a payment app that has invalid or insecure
content (invalid url, insecure content, http) or interstitial page
would cause an error in the payment app. And the payment handler would
be hidden right away in these cases.

Bug: 999196

Change-Id: I664803b0bd7ccc128cc8d0c61dd889fcb964b733
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1874519
Commit-Queue: Liquan (Max) Gu <maxlg@chromium.org>
Reviewed-by: default avatarRouslan Solomakhin <rouslan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#709610}
parent d572c730
......@@ -1189,13 +1189,7 @@ public class PaymentRequestImpl
mPaymentHandlerUi = new PaymentHandlerCoordinator();
ChromeActivity chromeActivity = ChromeActivity.fromWebContents(mWebContents);
if (chromeActivity == null) return false;
return mPaymentHandlerUi.show(
chromeActivity, this::onPaymentHandlerUiDismissed, url, mIsIncognito);
}
private void onPaymentHandlerUiDismissed() {
ensureHideAndResetPaymentHandlerUi();
ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(mWebContents);
return mPaymentHandlerUi.show(chromeActivity, url, mIsIncognito);
}
@Override
......
......@@ -45,7 +45,7 @@ public class ServiceWorkerPaymentAppBridge implements PaymentAppFactory.PaymentA
private static boolean sCanMakePaymentForTesting;
/** The interface for checking whether there is an installed SW payment app. */
static public interface HasServiceWorkerPaymentAppsCallback {
public static interface HasServiceWorkerPaymentAppsCallback {
/**
* Called to return checking result.
*
......@@ -55,7 +55,7 @@ public class ServiceWorkerPaymentAppBridge implements PaymentAppFactory.PaymentA
}
/** The interface for getting all installed SW payment apps' information. */
static public interface GetServiceWorkerPaymentAppsInfoCallback {
public static interface GetServiceWorkerPaymentAppsInfoCallback {
/**
* Called to return installed SW payment apps' information.
*
......@@ -272,20 +272,27 @@ public class ServiceWorkerPaymentAppBridge implements PaymentAppFactory.PaymentA
// Notify closing payment app window so as to abort payment if unsecure.
WebContents webContents = tab.getWebContents();
if (!SslValidityChecker.isValidPageInPaymentHandlerWindow(webContents)) {
ServiceWorkerPaymentAppBridgeJni.get().onClosingPaymentAppWindow(webContents,
PaymentEventResponseType.PAYMENT_HANDLER_INSECURE_NAVIGATION);
onClosingPaymentAppWindowForInsecureNavigation(webContents);
}
}
@Override
public void onDidAttachInterstitialPage(Tab tab) {
ServiceWorkerPaymentAppBridgeJni.get().onClosingPaymentAppWindow(
tab.getWebContents(),
PaymentEventResponseType.PAYMENT_HANDLER_INSECURE_NAVIGATION);
onClosingPaymentAppWindowForInsecureNavigation(tab.getWebContents());
}
});
}
/**
* Notify closing the opened payment app window for insecure navigation.
*
* @param webContents The web contents in the opened window.
*/
public static void onClosingPaymentAppWindowForInsecureNavigation(WebContents webContents) {
ServiceWorkerPaymentAppBridgeJni.get().onClosingPaymentAppWindow(
webContents, PaymentEventResponseType.PAYMENT_HANDLER_INSECURE_NAVIGATION);
}
/**
* Notify closing the opened payment app window.
*
......
......@@ -26,16 +26,6 @@ import java.net.URI;
*/
public class PaymentHandlerCoordinator {
private Runnable mHider;
private WebContents mWebContents;
/** Observer for the dismissal of the payment-handler UI. */
public interface DismissObserver {
/**
* Called after the user has dismissed the payment-handler UI by swiping it down or tapping
* on the scrim behind the UI.
*/
void onDismissed();
}
/** Constructs the payment-handler component coordinator. */
public PaymentHandlerCoordinator() {
......@@ -46,37 +36,38 @@ public class PaymentHandlerCoordinator {
* Shows the payment-handler UI.
*
* @param chromeActivity The activity where the UI should be shown.
* @param dismissObserver The observer to be notified when the user has dismissed the UI.
* @param url The url of the payment handler app, i.e., that of
* "PaymentRequestEvent.openWindow(url)".
* @param isIncognito Whether the tab is in incognito mode.
* @return Whether the payment-handler UI was shown. Can be false if the UI was suppressed.
*/
public boolean show(ChromeActivity activity, DismissObserver dismissObserver, URI url,
boolean isIncognito) {
public boolean show(ChromeActivity activity, URI url, boolean isIncognito) {
assert mHider == null : "Already showing payment-handler UI";
WebContents webContents =
WebContentsFactory.createWebContents(isIncognito, /*initiallyHidden=*/false);
ContentView webContentView = ContentView.createContentView(activity, webContents);
webContents.initialize(ChromeVersionInfo.getProductVersion(),
ViewAndroidDelegate.createBasicDelegate(webContentView), webContentView,
activity.getWindowAndroid(), WebContents.createDefaultInternalsHolder());
webContents.getNavigationController().loadUrl(new LoadUrlParams(url.toString()));
PropertyModel model = new PropertyModel.Builder(PaymentHandlerProperties.ALL_KEYS).build();
PaymentHandlerMediator mediator =
new PaymentHandlerMediator(model, this::hide, dismissObserver);
new PaymentHandlerMediator(model, this::hide, webContents);
BottomSheetController bottomSheetController = activity.getBottomSheetController();
bottomSheetController.getBottomSheet().addObserver(mediator);
mWebContents = WebContentsFactory.createWebContents(isIncognito, /*initiallyHidden=*/false);
ContentView webContentView = ContentView.createContentView(activity, mWebContents);
mWebContents.initialize(ChromeVersionInfo.getProductVersion(),
ViewAndroidDelegate.createBasicDelegate(webContentView), webContentView,
activity.getWindowAndroid(), WebContents.createDefaultInternalsHolder());
mWebContents.getNavigationController().loadUrl(new LoadUrlParams(url.toString()));
webContents.addObserver(mediator);
PaymentHandlerView view = new PaymentHandlerView(
activity, bottomSheetController.getBottomSheet(), mWebContents, webContentView);
activity, bottomSheetController.getBottomSheet(), webContents, webContentView);
PropertyModelChangeProcessor changeProcessor =
PropertyModelChangeProcessor.create(model, view, PaymentHandlerViewBinder::bind);
mHider = () -> {
changeProcessor.destroy();
bottomSheetController.getBottomSheet().removeObserver(mediator);
bottomSheetController.hideContent(/*content=*/view, /*animate=*/true);
mWebContents.destroy();
webContents.destroy();
};
boolean result = bottomSheetController.requestShowContent(view, /*animate=*/true);
if (result) bottomSheetController.expandSheet();
......
......@@ -4,34 +4,59 @@
package org.chromium.chrome.browser.payments.handler;
import org.chromium.chrome.browser.payments.handler.PaymentHandlerCoordinator.DismissObserver;
import org.chromium.chrome.browser.compositor.bottombar.OverlayPanel.StateChangeReason;
import org.chromium.chrome.browser.payments.ServiceWorkerPaymentAppBridge;
import org.chromium.chrome.browser.payments.SslValidityChecker;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.BottomSheetContent;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheet.SheetState;
import org.chromium.chrome.browser.widget.bottomsheet.EmptyBottomSheetObserver;
import org.chromium.chrome.browser.widget.bottomsheet.BottomSheetObserver;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsObserver;
import org.chromium.ui.modelutil.PropertyModel;
/**
* PaymentHandler mediator, which is responsible for receiving events from the view and notifies the
* backend (the coordinator).
*/
/* package */ class PaymentHandlerMediator extends EmptyBottomSheetObserver {
/* package */ class PaymentHandlerMediator
extends WebContentsObserver implements BottomSheetObserver {
private final PropertyModel mModel;
private final Runnable mHider;
private final DismissObserver mDismissObserver;
/**
* Build a new mediator that handle events from outside the payment handler component.
* @param model The {@link PaymentHandlerProperties} that holds all the view state for the
* payment handler component.
* @param hider The callback to clean up {@link PaymentHandlerCoordinator} when the sheet is
* hidden.
* @param webContents The web-contents that loads the payment app.
*/
/* package */ PaymentHandlerMediator(
PropertyModel model, Runnable hider, DismissObserver dismissObserver) {
PropertyModel model, Runnable hider, WebContents webContents) {
super(webContents);
mModel = model;
mHider = hider;
mDismissObserver = dismissObserver;
}
// EmptyBottomSheetObserver:
/**
* Hide the bottom-sheet if weak-ref of web-contents refers to null.
* @return Return true if the sheet is hidden.
*/
private boolean hideSheetIfWebContentsNotExist() {
if (mWebContents != null) return false;
// TODO(maxlg): how to inform the service worker when the web-contents is missing.
mHider.run();
return true;
}
// BottomSheetObserver:
@Override
public void onSheetStateChanged(@SheetState int newState) {
if (hideSheetIfWebContentsNotExist()) return;
switch (newState) {
case SheetState.HIDDEN:
ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(mWebContents.get());
mHider.run();
mDismissObserver.onDismissed();
break;
}
}
......@@ -40,4 +65,50 @@ import org.chromium.ui.modelutil.PropertyModel;
public void onSheetOffsetChanged(float heightFraction, float offsetPx) {
mModel.set(PaymentHandlerProperties.BOTTOM_SHEET_HEIGHT_FRACTION, heightFraction);
}
@Override
public void onSheetOpened(@StateChangeReason int reason) {}
@Override
public void onSheetClosed(@StateChangeReason int reason) {
// This is invoked when the sheet returns to the peek state, but Payment Handler doesn't
// have a peek state.
}
@Override
public void onLoadUrl(String url) {}
@Override
public void onSheetFullyPeeked() {}
@Override
public void onSheetContentChanged(BottomSheetContent newContent) {}
// WebContentsObserver:
@Override
public void didFinishLoad(long frameId, String validatedUrl, boolean isMainFrame) {
if (hideSheetIfWebContentsNotExist()) return;
if (!SslValidityChecker.isValidPageInPaymentHandlerWindow(mWebContents.get())) {
ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindowForInsecureNavigation(
mWebContents.get());
mHider.run();
}
}
@Override
public void didAttachInterstitialPage() {
if (hideSheetIfWebContentsNotExist()) return;
ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindowForInsecureNavigation(
mWebContents.get());
mHider.run();
}
@Override
public void didFailLoad(
boolean isMainFrame, int errorCode, String description, String failingUrl) {
if (hideSheetIfWebContentsNotExist()) return;
// TODO(crbug.com/1017926): Respond to service worker with the net error.
ServiceWorkerPaymentAppBridge.onClosingPaymentAppWindow(mWebContents.get());
mHider.run();
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment