CVE-2025-26430 AI-generated analysis
platform/packages/apps/Settings, SPA bridge activity (SpaAppBridgeActivity) / app navigation
SpaAppBridgeActivity.getDestinationForApp now validates that the package name taken from the incoming "package:" intent URI matches a package-name regex before interpolating it into the SPA navigation route, returning null otherwise.
Root cause
In src/com/android/settings/spa/SpaAppBridgeActivity.kt, getDestinationForApp() took intent.data?.schemeSpecificPart from the bridge activity's intent (the bridge sits behind an activity-alias so SpaActivity need not be exported) and directly built the SPA destination string "$destinationPrefix/$packageName/${UserHandle.myUserId()}". The scheme-specific part was not checked to be a well-formed package name, so a value containing route-significant characters such as "/" or "#" (as in the new test "package:package.name/10#") would alter the structure of the destination route passed to startSpaActivityFromBridge instead of being a single package-name segment.
How the fix works
- src/com/android/settings/spa/SpaAppBridgeActivity.kt:getDestinationForApp: the schemeSpecificPart is filtered with takeIf { Regex("^([a-zA-Z]\\w*\\.)*[a-zA-Z]\\w*$").matches(it) }; only dot-separated identifiers (word characters, each segment starting with a letter) are accepted. Anything else makes the expression null and the function returns null, so the startSpaActivityFromBridge lambda in onCreate yields a null destination; onCreate then calls finish() as before.
- tests/spa_unit/src/com/android/settings/spa/SpaAppBridgeActivityTest.kt: adds getDestinationForApp_hasMalformedPackageName asserting that "package:package.name/10#" produces a null destination.
Completeness
- Covers the single place in SpaAppBridgeActivity where the URI scheme-specific part is interpolated into the route; the package segment is now validated and the trailing user-id segment comes from UserHandle.myUserId().
- The whole-string regex rejects any '/', '#', '?', ':' or whitespace, so the package name cannot span or terminate route segments. It does not check that the package exists.
- Only this bridge activity is changed. Other SPA bridge/route-building paths (e.g. SpaBridgeActivity.startSpaActivityFromBridge itself, or other destination builders) are not touched by this commit; the material does not show whether they take untrusted route components.
Behavior changes
- For intents to the SPA app bridge whose package: URI does not match the regex, getDestinationForApp now returns null instead of a route containing the raw string; the activity still calls finish().
Per major
- Android 14: not affected per the bulletin; at android-security-14.0.0_r29 only 1/6 lines of the patched region are present.
- Android 15: same fix: 484b4be8f363 in android-security-15.0.0_r8 (Change-Id Ife615c78c48a2d6f5b938c880c9c2128c5551644); the regex line is present in the android-security-15.0.0_r17 code (5/6 lines of patched region).
- Android 16: not affected per the bulletin (not listed); 5/6 lines of the patched region present at android-security-16.0.0_r8.
- Android 17: not affected per the bulletin (not listed); 5/6 lines of the patched region present at android-security-17.0.0_r1.
Unknowns
- The material does not show how startSpaActivityFromBridge / the SPA navigation layer parses the destination string or handles a null destination, so the concrete effect of an injected route is not shown.
- The manifest activity-alias entries that export this bridge and their intent filters are not in the material.
- Whether the 16 and 17 trees contain the exact regex line (5/6 match) is not shown.