The app handles the https://deeplink.example.com App Link in DeepLinkActivity and performs a sensitive action (a transfer) when the link is opened. The amount parameter is validated correctly (it must be a positive number within an allowed range), so this demo does not depend on an input validation flaw.
The weakness is that the App Link is declared withoutandroid:autoVerify="true", so Android never verifies that the app owns the deeplink.example.com domain. Another app can register the same domain to hijack the link, or invoke this exported handler directly, to reach the sensitive action.
packageorg.owasp.mastestappimportandroid.app.Activityimportandroid.content.Contextimportandroid.content.Intentimportandroid.net.Uriimportandroid.os.Bundle//SUMMARY:Thissampleperformsasensitiveaction(atransfer)throughan//http/httpsAppLinkdeeplink.TheinputISvalidatedcorrectly,sothe//weaknessisNOTinputvalidation(thatiscoveredbyaseparatetest).The//weaknessisthattheAppLinkisnotverified:themanifestismissing//android:autoVerify="true",sotheOSneverconfirmsthatthisappownsthe//deeplink.example.comdomain.Anotherappcanregisterthesamedomainto//hijackthelink(andanysensitivedataitcarries),orinvokethisexported//handlerdirectly,toreachthesensitiveaction.classDeepLinkActivity:Activity(){overridefunonCreate(savedInstanceState:Bundle?){super.onCreate(savedInstanceState)lastDeepLink=intent?.datastartActivity(Intent(this,MainActivity::class.java).apply{addFlags(Intent.FLAG_ACTIVITY_NEW_TASKorIntent.FLAG_ACTIVITY_CLEAR_TOP)})finish()}companionobject{//HoldstheURIthatlaunchedtheappviatheAppLink.varlastDeepLink:Uri?=null}}classMastgTest(privatevalcontext:Context){funmastgTest():String{valdata:Uri=DeepLinkActivity.lastDeepLink?:return""" No deep link processed yet. Trigger the unverified App Link with: adb shell am start -n org.owasp.mastestapp/.DeepLinkActivity -a android.intent.action.VIEW -d "https://deeplink.example.com/transfer?amount=100" Then press Start again to see the result. """.trimIndent()valamountParam=data.getQueryParameter("amount")//TheinputISvalidatedcorrectly:itmustbeapositivenumberwithin//anallowedrange.ThiskeepsthefocusontheAppLinkverification//weakness,notoninputvalidation.valamount=amountParam?.toLongOrNull()if(amount==null||amount<=0||amount>10_000){return"Rejected invalid amount: $amountParam"}returnprocessTransfer(amount)}privatefunprocessTransfer(amount:Long):String{//Sensitiveaction.Eventhoughtheamountisvalidated,thisactionis//reachedthroughanunverifiedAppLinkandcanbehijackedor//triggeredbyanotherapponthedevice.return"Transferred $amount units to the linked account"}}
The App Link is declared in the manifest without android:autoVerify="true":
rules:-id:mastg-android-deeplink-autoverify-missingseverity:WARNINGlanguages:-xmlmetadata:summary:Thisrulelooksforhttp/httpsdeeplinksdeclaredwithoutAppLinksverification.message:'[MASVS-PLATFORM] Deep link intent filter missing android:autoVerify="true", enabling malicious apps to hijack links.'patterns:-pattern:<data...android:scheme="$SCHEME".../>-metavariable-regex:metavariable:$SCHEMEregex:^https?$-pattern-inside:|<activity...>...</activity>-pattern-inside:|<intent-filter...>...<actionandroid:name="android.intent.action.VIEW"/>...</intent-filter>-pattern-inside:|<intent-filter...>...<categoryandroid:name="android.intent.category.BROWSABLE"/>...</intent-filter>-pattern-not-inside:|<intent-filter...android:autoVerify="true"...>...</intent-filter>
The rule flagged the http and https<data> declarations in the DeepLinkActivity<intent-filter>, which is missing the android:autoVerify="true" attribute.
The test case fails because the app declares an http/https App Link without enabling verification. Without android:autoVerify="true", Android does not confirm that the app owns the deeplink.example.com domain, so the app is not its exclusive, verified handler.
Note that the handler validates its input correctly — the issue is not the parameter handling, but that the link itself is unverified. Because the domain is not verified, a malicious app can register the same https://deeplink.example.com intent filter and intercept the deep link (and any sensitive data it carries) or present spoofed content. Since the handler is also exported, any app on the device can invoke it directly to trigger the sensitive action. Use adb to confirm the action is reachable:
After triggering the deep link, tap Start in the app to process it. The result (Transferred 100 units to the linked account) confirms that the sensitive action is reachable through the unverified App Link.