Overview

Medium
Severity
2.9
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactIncorrect authorization in WebAPKs
DescriptionIncorrect authorization in WebAPKs
ComponentWebAPKs
Bug ClassAuthorization Error
Action tierwatch — logic/policy bug — security-relevant, no memory-unsafety
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker502242455
Fix commit9f55f22780c0 (chromium/src) +3/-143
CWECWE-863
CVSS vectorCVSS:3.1/AV:L/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-04-13 (per vendor advisory)
Fix landed2026-08-21 (public git)
Disclosed2026-09-22
Patch gap32 days — fix public in source before the advisory

Background

WebAPK
An Android APK that wraps a Progressive Web App and is granted elevated, browser-trusted privileges (such as running without browser UI) once Chrome confirms it is a legitimate, Chrome-minted package.
`WebApkValidator`
The client-side class in components/webapk/android/libs/client/.../WebApkValidator.java that decides whether an installed package should be treated as a trusted WebAPK, normally by checking its signing key or comment signature.
Signature-based validation
The intended trust check (verifyV1WebApk / verifyCommentSignedWebApk) that ties WebAPK trust to a cryptographic key rather than to attacker-controllable metadata like package name or URL.
Allowlist bypass
A special-case code path that grants trust based on matching a hardcoded identifier instead of performing the cryptographic check.

Root Cause Analysis

WebApkValidator contained a legacy special case, verifyMapsLite, that returned a trusted ValidationResult.MAPS_LITE verdict whenever a package’s name equaled the hardcoded MAPSLITE_PACKAGE_NAME ("com.google.android.apps.mapslite") and its START_URL / SCOPE metadata began with MAPSLITE_URL_PREFIX ("https://www.google.com/maps"). Unlike the verifyV1WebApk and verifyCommentSignedWebApk paths, this check verified no signature, so any package that adopted that package name and metadata prefixes was accepted as a valid WebAPK — violating the invariant that WebAPK trust must derive from a Chrome-controlled signing key. Because the dispatch method returned before reaching the signature-based checks, an attacker-installed package could satisfy the allowlist and receive WebAPK authorization it was never entitled to.

The fix removes verifyMapsLite, the MAPS_LITE enum value, the switch case that handled it, and the associated constants and deprecation string, so every package must now pass a cryptographic verification path.

The fix works because trust decisions are again gated solely on signature checks that attacker-supplied metadata cannot forge.

Key insight
The core mistake was authorizing a package as a trusted WebAPK based on an attacker-controllable package name and URL prefix instead of a cryptographic signature; the fix eliminates that unsigned allowlist entirely, forcing all packages back through signature-based validation.

Attack Path

  1. Craft a lookalike package An attacker builds an APK whose package name is set to com.google.android.apps.mapslite, since the allowlist keyed trust off this exact string. (inferred)
  2. Set matching metadata The attacker populates the manifest metadata so START_URL and SCOPE both begin with MAPSLITE_URL_PREFIX ("https://www.google.com/maps"), the only other conditions verifyMapsLite enforced.
  3. Install and be validated Once installed, Chrome’s WebApkValidator reaches verifyMapsLite, which returns true and short-circuits to the ValidationResult.MAPS_LITE verdict before any signature check runs.
  4. Obtain WebAPK authorization The package is treated as a legitimate WebAPK, gaining the elevated, browser-trusted handling normally reserved for Chrome-signed WebAPKs. (inferred)

Impact Assessment

An attacker who can get a suitably named and configured package installed gains incorrect WebAPK authorization within Chrome on Android, bypassing the intended signature-based trust check by satisfying only a package-name and URL-prefix allowlist. The precondition is control of an installed package adopting the com.google.android.apps.mapslite identity with matching START_URL/SCOPE metadata; the advisory classifies this as incorrect authorization in WebAPKs and states no exploited-in-the-wild status. Any further escalation of the WebAPK privileges into broader capability is not demonstrated by the patch. (inferred)

Changed Functions

FunctionChangeNotes
switch
components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java
modified
if
components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java
modified

Files Changed

  • components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidatorTest.java
  • components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java
  • components/browser_ui/strings/android/browser_ui_strings_grd/IDS_WEBAPK_MAPSGO_DEPRECATION_WARNING.png.sha1
  • components/browser_ui/strings/android/browser_ui_strings.grd

Audit Directions

  • Non-cryptographic allowlists
    Search validation and authorization code for trust decisions keyed on hardcoded package names, URLs, or metadata prefixes rather than signatures, especially early-return special cases that precede the real check.
  • Legacy/deprecated special cases
    Review remaining WebAPK and app-trust paths for other deprecated exceptions (like the removed Maps Lite case) that may still grant privileges without full verification.
  • Verdict dispatch ordering
    Audit switch/dispatch methods that return a trusted result before invoking verifyV1WebApk / verifyCommentSignedWebApk, ensuring no branch short-circuits the signature-based validation.
From 9f55f22780c010abc558718a6528957ad907c034 Mon Sep 17 00:00:00 2001
From: Yaron Friedman <[email protected]>
Date: Thu, 20 Aug 2026 17:18:18 -0700
Subject: [PATCH] Remove Maps Lite allowlist from WebApkValidator

Remove the deprecated Maps Lite allowlist and associated deprecation warning string and unit tests.

Fixed: 502242455

TAG=agy
CONV=3a08567f-d60b-44f5-a7dc-378d70a721f1

Change-Id: If328144efd28dbe8053beec0a872f0437bf9174a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8269601
Reviewed-by: Daniel Murphy <[email protected]>
Commit-Queue: Yaron Friedman <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1683570}
---
diff --git a/components/browser_ui/strings/android/browser_ui_strings.grd b/components/browser_ui/strings/android/browser_ui_strings.grd
index 9f789aa0..ec8bcce 100644
--- a/components/browser_ui/strings/android/browser_ui_strings.grd
+++ b/components/browser_ui/strings/android/browser_ui_strings.grd
@@ -929,9 +929,6 @@
       </message>
 
       <!-- WebApk outofdate warning string -->
-      <message name="IDS_WEBAPK_MAPSGO_DEPRECATION_WARNING" desc="The toast message when user trying to launch the deprecated Maps Go WebAPK.">
-        '<ph name="APP_NAME">%1$s<ex>Maps Go</ex></ph>' is out of date. Please update the app.
-      </message>
       <message name="IDS_WEBAPK_DEPRECATION_WARNING" desc="The toast message when user trying to launch the deprecated WebAPKs.">
         '<ph name="APP_NAME">%1$s<ex>Maps Go</ex></ph>' is out of date. Please re-install the app.
       </message>
diff --git a/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_WEBAPK_MAPSGO_DEPRECATION_WARNING.png.sha1 b/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_WEBAPK_MAPSGO_DEPRECATION_WARNING.png.sha1
deleted file mode 100644
index 3ae1237..0000000
--- a/components/browser_ui/strings/android/browser_ui_strings_grd/IDS_WEBAPK_MAPSGO_DEPRECATION_WARNING.png.sha1
+++ /dev/null
@@ -1 +0,0 @@
-a6da12e61f0b81f118d2cb0747b852228aeafc73
\ No newline at end of file
diff --git a/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java b/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java
index 2b50f4e3..3993884 100644
--- a/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java
+++ b/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java
@@ -6,7 +6,6 @@
 
 import static org.chromium.build.NullUtil.assumeNonNull;
 import static org.chromium.components.webapk.lib.common.WebApkConstants.WEBAPK_PACKAGE_PREFIX;
-import static org.chromium.components.webapk.lib.common.WebApkMetaDataKeys.SCOPE;
 import static org.chromium.components.webapk.lib.common.WebApkMetaDataKeys.START_URL;
 import static org.chromium.components.webapk.lib.common.WebApkMetaDataKeys.WEB_MANIFEST_URL;
 
@@ -55,9 +54,6 @@
 public class WebApkValidator {
     private static final String TAG = "WebApkValidator";
     private static final String KEY_FACTORY = "EC"; // aka "ECDSA"
-    private static final String MAPSLITE_PACKAGE_NAME = "com.google.android.apps.mapslite";
-    private static final String MAPSLITE_URL_PREFIX =
-            "https://www.google.com/maps"; // Matches scope.
     private static final boolean DEBUG = false;
 
     private static byte @Nullable [] sExpectedSignature;
@@ -68,14 +64,12 @@
     @IntDef({
         ValidationResult.FAILURE,
         ValidationResult.V1_WEB_APK,
-        ValidationResult.MAPS_LITE,
         ValidationResult.COMMENT_SIGNED
     })
     @Retention(RetentionPolicy.SOURCE)
     private @interface ValidationResult {
         int FAILURE = 0;
         int V1_WEB_APK = 1;
-        int MAPS_LITE = 2;
         int COMMENT_SIGNED = 3;
     }
 
@@ -184,11 +178,6 @@
                 switch (result) {
                     case ValidationResult.FAILURE:
                         continue;
-                    case ValidationResult.MAPS_LITE:
-                        String name = info.loadLabel(context.getPackageManager()).toString();
-                        showDeprecationWarning(
-                                context, name, R.string.webapk_mapsgo_deprecation_warning);
-                        return false;
                     case ValidationResult.V1_WEB_APK:
                         int shellApkVersion =
                                 IntentUtils.safeGetInt(
@@ -336,13 +325,6 @@
         if (verifyV1WebApk(packageInfo, webappPackageName)) {
             return ValidationResult.V1_WEB_APK;
         }
-        if (verifyMapsLite(packageInfo, webappPackageName)) {
-            if (DEBUG) {
-                Log.d(TAG, "Matches Maps Lite");
-            }
-
-            return ValidationResult.MAPS_LITE;
-        }
         if (verifyCommentSignedWebApk(packageInfo)) {
             return ValidationResult.COMMENT_SIGNED;
         }
@@ -428,27 +410,6 @@
         return false;
     }
 
-    private static boolean verifyMapsLite(PackageInfo packageInfo, String webappPackageName) {
-        if (hasSplits(packageInfo) || !webappPackageName.equals(MAPSLITE_PACKAGE_NAME)) {
-            return false;
-        }
-        String startUrl = packageInfo.applicationInfo.metaData.getString(START_URL);
-        if (startUrl == null || !startUrl.startsWith(MAPSLITE_URL_PREFIX)) {
-            if (DEBUG) {
-                Log.d(TAG, "mapslite invalid startUrl prefix");
-            }
-            return false;
-        }
-        String scope = packageInfo.applicationInfo.metaData.getString(SCOPE);
-        if (scope == null || !scope.equals(MAPSLITE_URL_PREFIX)) {
-            if (DEBUG) {
-                Log.d(TAG, "mapslite invalid scope prefix");
-            }
-            return false;
-        }
-        return true;
-    }
-
     /** Verify that the comment signed webapk matches the public key. */
     private static boolean verifyCommentSignedWebApk(PackageInfo packageInfo) {
         if (hasSplits(packageInfo)) {
diff --git a/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidatorTest.java b/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidatorTest.java
index 075259c..eb82c5b 100644
--- a/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidatorTest.java
+++ b/components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidatorTest.java
@@ -51,8 +51,6 @@
     private static final String URL_WITHOUT_WEBAPK = "https://www.other.com";
     private static final String TEST_DATA_DIR = "webapks/";
     private static final String TEST_STARTURL = "https://non-empty.com/starturl";
-    private static final String MAPSLITE_PACKAGE_NAME = "com.google.android.apps.mapslite";
-    private static final String MAPSLITE_EXAMPLE_STARTURL = "https://www.google.com/maps";
     private static final String MANIFEST_URL = "https://www.foo.com/manifest.json";
     private static final int SHELL_VERSION = 100;
 
@@ -322,69 +320,6 @@
     }
 
     /**
-     * Tests {@link WebApkValidator.isValidWebApk} returns true if the package name is maps lite and
-     * the start url matches the correct prefix.
-     */
-    @Test
-    public void testIsValidWebApkForMapsLite() {
-        mPackageManager.addPackage(
-                newPackageInfoWithBrowserSignature(
-                        MAPSLITE_PACKAGE_NAME,
-                        new Signature(SIGNATURE_1),
-                        MAPSLITE_EXAMPLE_STARTURL,
-                        null));
-        mPackageManager.addPackage(
-                newPackageInfoWithBrowserSignature(
-                        MAPSLITE_PACKAGE_NAME + ".other",
-                        new Signature(SIGNATURE_1),
-                        MAPSLITE_EXAMPLE_STARTURL,
-                        null));
-
-        assertTrue(
-                WebApkValidator.isValidWebApk(
-                        RuntimeEnvironment.application, MAPSLITE_PACKAGE_NAME));
-        assertFalse(
-                WebApkValidator.isValidWebApk(
-                        RuntimeEnvironment.application, MAPSLITE_PACKAGE_NAME + ".other"));
-        assertFalse(
-                WebApkValidator.isValidWebApk(
-                        RuntimeEnvironment.application, MAPSLITE_PACKAGE_NAME + ".notfound"));
-    }
-
-    /** Tests {@link WebApkValidator.canWebApkHandleUrl} returns false and shows a toast. */
-    @Test
-    public void testMapsLiteWebApkShowsWarning() {
-        // Invalid MapsLite WebAPK is not verified and does not show toast.
-        addWebApkResolveInfoWithPackageName(
-                MAPSLITE_EXAMPLE_STARTURL, MAPSLITE_PACKAGE_NAME + ".other", SIGNATURE_1);
-        assertFalse(
-                WebApkValidator.canWebApkHandleUrl(
-                        RuntimeEnvironment.application,
-                        MAPSLITE_PACKAGE_NAME + ".other",
-                        MAPSLITE_EXAMPLE_STARTURL,
-                        0));
-        assertNull(ShadowToast.getLatestToast());
-
-        // Valid MapsLite WebAPK returns false as "not handled" and shows a toast.
-        addWebApkResolveInfoWithPackageName(
-                MAPSLITE_EXAMPLE_STARTURL, MAPSLITE_PACKAGE_NAME, SIGNATURE_1);
-        assertFalse(
-                WebApkValidator.canWebApkHandleUrl(
-                        RuntimeEnvironment.application,
-                        MAPSLITE_PACKAGE_NAME,
-                        MAPSLITE_EXAMPLE_STARTURL,
-                        0));
-        assertNotNull(ShadowToast.getLatestToast());
-        // assertTextFromLatestToast(R.string.copied);
-        TextView textView = (TextView) ShadowToast.getLatestToast().getView();
-        String actualText = textView == null ? "" : textView.getText().toString();
-        assertEquals(
-                ContextUtils.getApplicationContext()
-                        .getString(R.string.webapk_mapsgo_deprecation_warning, ""),
-                actualText);
-    }
-
-    /**
      * Tests {@link WebApkValidator.canWebApkHandleUrl} returns false and shows a toast when the
      * shell version is out-of-date (older than the min_version).
      */
@@ -420,19 +355,6 @@
     }
 
     /**
-     * Tests {@link WebApkValidator.isValidWebApk} returns false when the startUrl is not correct.
-     */
-    @Test
-    public void testIsNotValidWebApkForMapsLiteBadStartUrl() {
-        mPackageManager.addPackage(
-                newPackageInfoWithBrowserSignature(
-                        MAPSLITE_PACKAGE_NAME, new Signature(SIGNATURE_1), TEST_STARTURL, null));
-        assertFalse(
-                WebApkValidator.isValidWebApk(
-                        RuntimeEnvironment.application, MAPSLITE_PACKAGE_NAME));
-    }
-
-    /**
      * Tests {@link WebApkValidator.isValidWebApk} returns false if a WebAPK has more than 2
      * signatures, even if the second one matches the expected signature.
      */
@@ -562,28 +484,9 @@
                         RuntimeEnvironment.application, INVALID_WEBAPK_PACKAGE_NAME));
     }
 
-    /** Tests {@link WebApkValidator.isValidV1WebApk} returns false if the package name is maps lite. */
-    @Test
-    public void testIsValidV1WebApkFalseForMapsLite() {
-        mPackageManager.addPackage(
-                newPackageInfoWithBrowserSignature(
-                        MAPSLITE_PACKAGE_NAME,
-                        new Signature(SIGNATURE_1),
-                        MAPSLITE_EXAMPLE_STARTURL,
-                        null));
-        mPackageManager.addPackage(
-                newPackageInfoWithBrowserSignature(
-                        MAPSLITE_PACKAGE_NAME + ".other",
-                        new Signature(SIGNATURE_1),
-                        MAPSLITE_EXAMPLE_STARTURL,
-                        null));
-
-        assertFalse(
-                WebApkValidator.isValidV1WebApk(
-                        RuntimeEnvironment.application, MAPSLITE_PACKAGE_NAME));
-    }
-
-    /** Tests {@link WebApkValidator#queryBoundWebApkForManifestUrl()} for a valid installed entry. */
+    /**
+     * Tests {@link WebApkValidator#queryBoundWebApkForManifestUrl()} for a valid installed entry.
+     */
     @Test
     public void testQueryBoundWebApkForManifestUrl() {
         mPackageManager.addPackage(
Loading diff…

Original Bug Report

reported by [email protected]

Potential cross-origin notification content leak via spoofed Maps Lite WebAPK

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the Chrome Security team.

Overview: Chrome for Android may incorrectly route web push notifications to a malicious sideloaded application spoofing the deprecated Maps Lite WebAPK. This occurs due to a lack of signature verification and scope validation in WebApkValidator, potentially allowing the malicious app to intercept sensitive notification data from arbitrary origins.

Affected files:

  • components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java
  • chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java
  • chrome/android/java/src/org/chromium/chrome/browser/webapps/WebApkServiceClient.java
  • chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkIdentityServiceClient.java
  • chrome/android/java/src/org/chromium/chrome/browser/webapps/ChromeWebApkHost.java

Estimated timestamp from git blame: 2024-02-21

Description

There is a potential vulnerability in how Chrome for Android validates WebAPKs before delegating push notifications. The WebApkValidator contains specific logic to identify the Maps Lite WebAPK (com.google.android.apps.mapslite). However, the verifyMapsLite method only checks the package name and internal metadata, entirely omitting cryptographic signature verification.

Furthermore, when querying for a WebAPK to handle a specific notification scope URL, Chrome relies on the Android PackageManager’s intent resolution. A malicious app can declare intent filters for arbitrary origins while spoofing the Maps Lite package name and metadata. Because verifyMapsLite assumes the app is valid based on metadata alone, and doesn’t cross-check the queried URL against the verified scope, Chrome trusts the app as a legitimate WebAPK for the target origin.

The attacker can further bypass runtime host checks by implementing IIdentityService to return Chrome’s package name. Consequently, Chrome will serialize the android.app.Notification object—containing sensitive cross-origin data like 2FA codes or messages—and pass it directly to the attacker’s IWebApkApi service.

Potential Attack Scenario (Unverified)

These are suggested steps an attacker might follow to exploit this issue:

  1. Create a Malicious App: The attacker creates an Android app with the package name com.google.android.apps.mapslite.
  2. Spoof Metadata: In the AndroidManifest.xml, the attacker adds <meta-data> for START_URL and SCOPE, setting them to https://www.google.com/maps to satisfy verifyMapsLite.
  3. Target Intent Filters: The attacker adds an <intent-filter> for android.intent.action.VIEW and CATEGORY_BROWSABLE targeting the victim’s origin (e.g., https://mail.example.com).
  4. Implement Spoofed Services: The attacker implements IIdentityService to return com.android.chrome and IWebApkApi to receive the incoming notifications.
  5. Installation: The attacker convinces the victim to sideload this APK (this requires the legitimate Google Maps Go app to be absent from the device).
  6. Triggering: The victim visits https://mail.example.com in Chrome and allows notifications. When a push notification arrives, Chrome delegates it to the malicious app via WebApkServiceClient.notifyNotificationWithChannel, exposing the contents.

Code References

  • components/webapk/android/libs/client/src/org/chromium/components/webapk/lib/client/WebApkValidator.java:
    • verifyMapsLite() checks package name and metadata but lacks signature verification.
    • queryFirstWebApkPackage() relies on PackageManager to find apps claiming to handle the URL, allowing the attacker’s arbitrary intent filter to be selected.
  • chrome/android/webapk/libs/client/src/org/chromium/webapk/lib/client/WebApkIdentityServiceClient.java:
    • checkBrowserBacksWebApkAsync() trusts the unauthenticated IIdentityService of the WebAPK to determine the host browser.
  • chrome/android/java/src/org/chromium/chrome/browser/notifications/NotificationPlatformBridge.java:
    • displayNotificationInternal() delegates the notification based on the flawed validation.

Suggested Fix

  1. Enforce Signature Verification: Add strict cryptographic signature verification to verifyMapsLite(), ensuring the package is genuinely signed by Google, similar to how V1 WebAPKs are verified. Alternatively, since Maps Lite is deprecated (as seen in canWebApkHandleUrl), consider removing support for it entirely.
  2. Strict Scope Validation: Ensure that when a WebAPK is selected via queryFirstWebApkPackage, the actual queried URL is strictly validated against the WebAPK’s authenticated SCOPE, rather than relying solely on the presence of an intent filter.

Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker