Skip to content

Validate deep-link args when resolving the start location#200

Merged
jayohms merged 3 commits into
mainfrom
security/validate-deeplink-start-location
Jul 2, 2026
Merged

Validate deep-link args when resolving the start location#200
jayohms merged 3 commits into
mainfrom
security/validate-deeplink-start-location

Conversation

@mbarta

@mbarta mbarta commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Problem

NavigatorHost.ensureDeeplinkStartLocationValid() validates the start location only inside the deepLinkExtras intent extra (android-support-nav:controller:deepLinkExtras).

AndroidX NavController also reads a second extra — deepLinkArgs (android-support-nav:controller:deepLinkArgs) — and merges it over deepLinkExtras when assembling each destination's arguments (arguments.putAll(globalArgs) followed by arguments.putAll(deepLinkArgs[index]), so the later write wins). Because an exported Activity's launch intent is externally controllable, a location (or any other argument) supplied via deepLinkArgs overrides the validated value and becomes the navigator's start destination — loading an unvalidated URL into the host's WebView.

Fix

ensureDeeplinkStartLocationValid() now sanitizes the launching intent's deepLinkArgs in addition to validating deepLinkExtras:

  • each deepLinkArgs per-destination bundle is emptied so it can't override the validated start location;
  • a deepLinkExtras start location whose host doesn't match the configured start host is reverted to the configured start location, as before.

Sanitization applies to every launching intent. Intent-origin signals are too weak to gate on — callingPackage is only set for startActivityForResult launches, and Activity.referrer is backed by the attacker-settable EXTRA_REFERRER — so there is no reliable way to distinguish app-produced intents from external ones. Unconditional sanitization keeps legitimate same-host start locations working while preventing an external intent from steering the navigator to an arbitrary URL.

Tests

NavigatorHostTest covers:

  • an off-host start location is reverted to the configured one;
  • a same-host start location is preserved;
  • deepLinkArgs are emptied so they can't override the validated start location.

🤖 Generated with Claude Code

NavigatorHost.ensureDeeplinkStartLocationValid() validated only the `location`
inside the deepLinkExtras intent extra. AndroidX NavController also reads a
separate deepLinkArgs extra and merges it over deepLinkExtras when building the
start destination's arguments (last write wins), so a value supplied via
deepLinkArgs on an externally-launched intent could override the validated
start location.

Neutralize the externally-supplied deepLinkArgs by replacing each
per-destination argument bundle with an empty one, in addition to the existing
deepLinkExtras host validation. This is a rewrite (not a removal), so the
deepLinkIds navigation path and the validated start location are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@mbarta
mbarta marked this pull request as draft June 18, 2026 15:15
@mbarta mbarta self-assigned this Jun 23, 2026
@mbarta
mbarta requested review from jayohms and jhutarek June 23, 2026 12:37
@mbarta
mbarta marked this pull request as ready for review June 23, 2026 12:37
mbarta and others added 2 commits June 24, 2026 12:39
Trust intents the app produced itself (verified via the calling package or
referrer, rejecting the spoofable referrer extras) and leave their deep-link
extras untouched. Any other intent keeps the existing sanitization: empty
deepLinkArgs and revert an off-host start location to the configured one.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Origin signals are too weak to gate on: callingPackage is only set
for startActivityForResult launches, and Activity.referrer is backed
by the attacker-settable EXTRA_REFERRER. Sanitizing every launching
intent is simpler and costs nothing — same-host start locations still
pass through, and app-produced deep links keep working.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jayohms
jayohms merged commit 0f9c77b into main Jul 2, 2026
1 check passed
@jayohms
jayohms deleted the security/validate-deeplink-start-location branch July 2, 2026 16:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

2 participants