[Android] Apply IntentHandler internal-scheme guard to brave:// (#36370)

Aligns brave:// with chrome:// in IntentHandler.intentHasUnsafeInternalScheme.
The upstream guard is unaware of the Brave alias because the brave:// →
chrome:// rewrite happens after the intent layer.

BraveIntentHandlerClassAdapter redirects the upstream call site through
BraveIntentHandler.intentHasUnsafeInternalScheme. The Brave method defers to
upstream first, then additionally rejects brave:// under the same category
guard upstream uses. A new BraveIntentHandlerInternal helper holds the
bytecode stub used to back-call the (private) upstream method.

Resolves: https://github.com/brave/brave-browser/issues/55473
This commit is contained in:
Serg
2026-05-12 22:07:15 +01:00
committed by GitHub
parent d1d734153b
commit 3effe93f7d
7 changed files with 104 additions and 0 deletions
+1
View File
@@ -701,6 +701,7 @@
*** getUrlForWebapp(...);
*** isJavascriptSchemeOrInvalidUrl(...);
*** extractUrlFromIntent(...);
*** intentHasUnsafeInternalScheme(...);
}
-keep class org.chromium.chrome.browser.BraveIntentHandler {
@@ -20,12 +20,15 @@ import org.chromium.chrome.browser.search_engines.TemplateUrlServiceFactory;
import org.chromium.chrome.browser.searchwidget.SearchWidgetProvider;
import org.chromium.content_public.browser.BrowserStartupController;
import java.util.Locale;
import java.util.concurrent.Callable;
@NullMarked
public class BraveIntentHandler {
private static final String TAG = "BraveIntentHandler";
private static final String BRAVE_SCHEME = "brave";
/** An extra to indicate that the intent was triggered from an app widget Leo button. */
public static final String EXTRA_INVOKED_FROM_APP_WIDGET_LEO =
"com.android.brave.invoked_from_app_widget_leo";
@@ -146,4 +149,22 @@ public class BraveIntentHandler {
assert false;
return false;
}
/**
* Bytecode-redirected from {@link IntentHandler#intentHasUnsafeInternalScheme}. Defers to the
* upstream check (which handles chrome://, chrome-native://, devtools://, distiller://,
* about://) and additionally blocks brave://, since it is a display alias for chrome:// and
* gets rewritten to chrome:// deeper in the navigation stack — too late to protect this guard.
*/
public static boolean intentHasUnsafeInternalScheme(
@Nullable String scheme, @Nullable String url, Intent intent) {
if (BraveIntentHandlerInternal.intentHasUnsafeInternalScheme(scheme, url, intent)) {
return true;
}
return scheme != null
&& BRAVE_SCHEME.equals(scheme.toLowerCase(Locale.US))
&& (intent.hasCategory(Intent.CATEGORY_BROWSABLE)
|| intent.hasCategory(Intent.CATEGORY_DEFAULT)
|| intent.getCategories() == null);
}
}
@@ -0,0 +1,28 @@
/* Copyright (c) 2026 The Brave Authors. All rights reserved.
* 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 https://mozilla.org/MPL/2.0/. */
package org.chromium.chrome.browser;
import android.content.Intent;
import org.chromium.build.annotations.NullMarked;
import org.chromium.build.annotations.Nullable;
/**
* Holds same-named stubs that bytecode-redirect to private members of {@link IntentHandler}, so
* {@link BraveIntentHandler} can back-call them despite Java visibility rules. The stub bodies are
* never executed: {@code BraveIntentHandlerClassAdapter} rewrites each call site to invoke the
* upstream method (which it also bumps to public at bytecode time).
*/
@NullMarked
final class BraveIntentHandlerInternal {
private BraveIntentHandlerInternal() {}
static boolean intentHasUnsafeInternalScheme(
@Nullable String scheme, @Nullable String url, Intent intent) {
assert false;
return false;
}
}