Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
android:compileSdkVersion="35"
android:compileSdkVersionCodename="15"
package="org.owasp.mastestapp"
platformBuildVersionCode="35"
platformBuildVersionName="15">
<uses-sdk
android:minSdkVersion="29"
android:targetSdkVersion="35"/>
<uses-permission android:name="android.permission.INTERNET"/>
<permission
android:name="org.owasp.mastestapp.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
android:protectionLevel="signature"/>
<uses-permission android:name="org.owasp.mastestapp.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"/>
<application
android:theme="@style/Theme.MASTestApp"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:debuggable="true"
android:allowBackup="true"
android:supportsRtl="true"
android:extractNativeLibs="false"
android:usesCleartextTraffic="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:appComponentFactory="androidx.core.app.CoreComponentFactory">
<activity
android:theme="@style/Theme.MASTestApp"
android:name="org.owasp.mastestapp.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:scheme="vulnerable-app"
android:host="deeplink"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name="androidx.compose.ui.tooling.PreviewActivity"
android:exported="true"/>
<activity
android:name="androidx.activity.ComponentActivity"
android:exported="true"/>
<provider
android:name="androidx.startup.InitializationProvider"
android:exported="false"
android:authorities="org.owasp.mastestapp.androidx-startup">
<meta-data
android:name="androidx.emoji2.text.EmojiCompatInitializer"
android:value="androidx.startup"/>
<meta-data
android:name="androidx.lifecycle.ProcessLifecycleInitializer"
android:value="androidx.startup"/>
<meta-data
android:name="androidx.profileinstaller.ProfileInstallerInitializer"
android:value="androidx.startup"/>
</provider>
<receiver
android:name="androidx.profileinstaller.ProfileInstallReceiver"
android:permission="android.permission.DUMP"
android:enabled="true"
android:exported="true"
android:directBootAware="false">
<intent-filter>
<action android:name="androidx.profileinstaller.action.INSTALL_PROFILE"/>
</intent-filter>
<intent-filter>
<action android:name="androidx.profileinstaller.action.SKIP_FILE"/>
</intent-filter>
<intent-filter>
<action android:name="androidx.profileinstaller.action.SAVE_PROFILE"/>
</intent-filter>
<intent-filter>
<action android:name="androidx.profileinstaller.action.BENCHMARK_OPERATION"/>
</intent-filter>
</receiver>
</application>
</manifest>
32 changes: 32 additions & 0 deletions demos/android/MASVS-PLATFORM/MASTG-DEMO-XXXA/MASTG-DEMO-XXXA.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
platform: android
title: Unvalidated URL from Deep Link Loaded in WebView with semgrep
id: MASTG-DEMO-XXXA
code: [kotlin]
test: MASTG-TEST-XXXA
status: new
Comment on lines +1 to +7
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The demo front matter uses placeholder IDs (id: MASTG-DEMO-XXXA, test: MASTG-TEST-XXXA). Demos must use the final numeric demo ID in both the folder name and the id: field, and test: should reference the actual numeric test ID.

Copilot uses AI. Check for mistakes.
---

### Sample

The following is a sample code file that contains a function to handle a deep link, which insecurely loads a URL into a WebView.

{{ MastgTest.kt # MastgTest_reversed.java }}

### Steps

Let's run @MASTG-TOOL-0110 rules against the sample code.

{{ ../../../../rules/mastg-android-unvalidated-deeplink-data.yml }}

{{ run.sh }}

### Observation

The output file shows usage of dangerous data flow from a source `getQueryParameter` to a sink `loadUrl`.

{{ output.txt }}

### Evaluation

The test fails because the app loads a user-controllable URL from a deep link directly into a WebView without validation.
32 changes: 32 additions & 0 deletions demos/android/MASVS-PLATFORM/MASTG-DEMO-XXXA/MastgTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package org.owasp.mastestapp

import android.annotation.SuppressLint
import android.content.Context
import android.net.Uri
import android.webkit.WebView
import androidx.activity.ComponentActivity

class MastgTest(private val context: Context) {

fun mastgTest(): String {
return """
This app is vulnerable to deep link attacks.

Test with:
adb shell am start -a android.intent.action.VIEW -d "vulnerable-app://deeplink?url=https://example.com"
""".trimIndent()
}

@SuppressLint("SetJavaScriptEnabled")
fun processDeepLinkAndLoad(uri: Uri?) {
if (uri == null) return

val url = uri.getQueryParameter("url")
if (url != null) {
val webView = WebView(context)
webView.settings.javaScriptEnabled = true
webView.loadUrl(url)
(context as ComponentActivity).setContentView(webView)
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(context as ComponentActivity).setContentView(webView) will throw a ClassCastException if context is not a ComponentActivity (for example if a ContextWrapper is passed). For a demo that should reliably run, pass in an Activity/ComponentActivity explicitly, or use a safe cast with a clear error path.

Suggested change
(context as ComponentActivity).setContentView(webView)
(context as? ComponentActivity)?.setContentView(webView)

Copilot uses AI. Check for mistakes.
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package org.owasp.mastestapp;

import android.content.Context;
import android.net.Uri;
import android.webkit.WebView;
import androidx.activity.ComponentActivity;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;

/* compiled from: MastgTest.kt */
@Metadata(d1 = {"\u0000$\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\b\u0007\u0018\u00002\u00020\u0001B\u000f\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0004\b\u0004\u0010\u0005J\u0006\u0010\u0006\u001a\u00020\u0007J\u0012\u0010\b\u001a\u00020\t2\b\u0010\n\u001a\u0004\u0018\u00010\u000bH\u0007R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\f"}, d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "<init>", "(Landroid/content/Context;)V", "mastgTest", "", "processDeepLinkAndLoad", "", "uri", "Landroid/net/Uri;", "app_debug"}, k = 1, mv = {2, 0, 0}, xi = 48)
/* loaded from: classes3.dex */
public final class MastgTest {
public static final int $stable = 8;
private final Context context;

public MastgTest(Context context) {
Intrinsics.checkNotNullParameter(context, "context");
this.context = context;
}

public final String mastgTest() {
return "This app is vulnerable to deep link attacks.\n\nTest with:\nadb shell am start -a android.intent.action.VIEW -d \"vulnerable-app://deeplink?url=https://example.com\"";
}

public final void processDeepLinkAndLoad(Uri uri) {
String url;
if (uri != null && (url = uri.getQueryParameter("url")) != null) {
WebView webView = new WebView(this.context);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl(url);
Context context = this.context;
Intrinsics.checkNotNull(context, "null cannot be cast to non-null type androidx.activity.ComponentActivity");
((ComponentActivity) context).setContentView(webView);
}
}
}
12 changes: 12 additions & 0 deletions demos/android/MASVS-PLATFORM/MASTG-DEMO-XXXA/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@



┌────────────────┐
│ 1 Code Finding │
└────────────────┘

MastgTest_reversed.java
❯❱ android-unvalidated-deeplink-data
[MASVS-PLATFORM] Unvalidated deep link query parameters are directly loaded into a WebView.

31┆ webView.loadUrl(url);
1 change: 1 addition & 0 deletions demos/android/MASVS-PLATFORM/MASTG-DEMO-XXXA/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NO_COLOR=true semgrep -c ../../../../rules/mastg-android-unvalidated-deeplink-data.yml ./MastgTest_reversed.java --text -o output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0"
android:compileSdkVersion="35"
android:compileSdkVersionCodename="15"
package="org.owasp.mastestapp"
platformBuildVersionCode="35"
platformBuildVersionName="15">
<uses-sdk
android:minSdkVersion="29"
android:targetSdkVersion="35"/>
<uses-permission android:name="android.permission.INTERNET"/>
<permission
android:name="org.owasp.mastestapp.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"
android:protectionLevel="signature"/>
<uses-permission android:name="org.owasp.mastestapp.DYNAMIC_RECEIVER_NOT_EXPORTED_PERMISSION"/>
<application
android:theme="@style/Theme.MASTestApp"
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:debuggable="true"
android:allowBackup="true"
android:supportsRtl="true"
android:extractNativeLibs="false"
android:usesCleartextTraffic="true"
android:roundIcon="@mipmap/ic_launcher_round"
android:appComponentFactory="androidx.core.app.CoreComponentFactory">
<activity
android:theme="@style/Theme.MASTestApp"
android:name="org.owasp.mastestapp.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:scheme="vulnerable-app"
android:host="deeplink"/>
Comment on lines +38 to +39
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This demo is intended to illustrate missing App Links verification (android:autoVerify), but the intent-filter shown uses a custom scheme (android:scheme="vulnerable-app"). App Links are HTTPS URLs; android:autoVerify is not applicable to custom schemes. Update the sample to use an https scheme + host (and keep android:autoVerify absent) so the demo and rule match the real failure mode.

Suggested change
android:scheme="vulnerable-app"
android:host="deeplink"/>
android:scheme="https"
android:host="vulnerable-app.example"/>

Copilot uses AI. Check for mistakes.
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name="androidx.compose.ui.tooling.PreviewActivity"
android:exported="true"/>
<activity
android:name="androidx.activity.ComponentActivity"
android:exported="true"/>
<provider
android:name="androidx.startup.InitializationProvider"
android:exported="false"
android:authorities="org.owasp.mastestapp.androidx-startup">
<meta-data
android:name="androidx.emoji2.text.EmojiCompatInitializer"
android:value="androidx.startup"/>
<meta-data
android:name="androidx.lifecycle.ProcessLifecycleInitializer"
android:value="androidx.startup"/>
<meta-data
android:name="androidx.profileinstaller.ProfileInstallerInitializer"
android:value="androidx.startup"/>
</provider>
<receiver
android:name="androidx.profileinstaller.ProfileInstallReceiver"
android:permission="android.permission.DUMP"
android:enabled="true"
android:exported="true"
android:directBootAware="false">
<intent-filter>
<action android:name="androidx.profileinstaller.action.INSTALL_PROFILE"/>
</intent-filter>
<intent-filter>
<action android:name="androidx.profileinstaller.action.SKIP_FILE"/>
</intent-filter>
<intent-filter>
<action android:name="androidx.profileinstaller.action.SAVE_PROFILE"/>
</intent-filter>
<intent-filter>
<action android:name="androidx.profileinstaller.action.BENCHMARK_OPERATION"/>
</intent-filter>
</receiver>
</application>
</manifest>
32 changes: 32 additions & 0 deletions demos/android/MASVS-PLATFORM/MASTG-DEMO-XXXB/MASTG-DEMO-XXXB.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
platform: android
title: Deep Link Intent Filter Missing android:autoVerify with semgrep
id: MASTG-DEMO-XXXB
code: [kotlin]
test: MASTG-TEST-XXXB
status: new
Comment on lines +1 to +7
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The demo front matter uses placeholder IDs (id: MASTG-DEMO-XXXB, test: MASTG-TEST-XXXB). Demos must use the final numeric demo ID in both the folder name and the id: field, and test: should reference the actual numeric test ID it supports.

Copilot uses AI. Check for mistakes.
---

### Sample

The following is a sample `AndroidManifest.xml` snippet that defines a deep link intent filter without the `android:autoVerify="true"` attribute.

{{ AndroidManifest_reversed.xml }}

### Steps

Let's run @MASTG-TOOL-0110 rules against the sample manifest.

{{ ../../../../rules/mastg-android-autoverify-missing.yml }}

{{ run.sh }}

### Observation

The rule has identified that the deep link intent filter is missing the `android:autoVerify="true"` attribute.

{{ output.txt }}

### Evaluation

The test fails because the app does not enforce Android App Links verification. Without `android:autoVerify="true"`, malicious apps may intercept the app's deep links, leading to phishing or hijacking attacks.
19 changes: 19 additions & 0 deletions demos/android/MASVS-PLATFORM/MASTG-DEMO-XXXB/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@


┌────────────────┐
│ 1 Code Finding │
└────────────────┘

AndroidManifest_reversed.xml
❯❱ android-autoverify-missing
[MASVS-PLATFORM] Deep link intent filter missing android:autoVerify="true",enabling
malicious apps to hijack links.

33┆ <intent-filter>
34┆ <action android:name="android.intent.action.VIEW"/>
35┆ <category android:name="android.intent.category.DEFAULT"/>
36┆ <category android:name="android.intent.category.BROWSABLE"/>
37┆ <data
38┆ android:scheme="vulnerable-app"
39┆ android:host="deeplink"/>
40┆ </intent-filter>
1 change: 1 addition & 0 deletions demos/android/MASVS-PLATFORM/MASTG-DEMO-XXXB/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
NO_COLOR=true semgrep -c ../../../../rules/mastg-android-autoverify-missing.yml ../MASTG-DEMO-0062/AndroidManifest_reversed.xml --text -o output.txt
Copy link

Copilot AI Feb 10, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run.sh references ../MASTG-DEMO-0062/AndroidManifest_reversed.xml instead of the manifest shipped in this demo folder, which makes the demo non-self-contained and couples it to another demo's layout. Point the command at ./AndroidManifest_reversed.xml (or include the needed file locally) so the demo can run independently.

Suggested change
NO_COLOR=true semgrep -c ../../../../rules/mastg-android-autoverify-missing.yml ../MASTG-DEMO-0062/AndroidManifest_reversed.xml --text -o output.txt
NO_COLOR=true semgrep -c ../../../../rules/mastg-android-autoverify-missing.yml ./AndroidManifest_reversed.xml --text -o output.txt

Copilot uses AI. Check for mistakes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.MASTestApp"
tools:targetApi="31">
<!-- Vulnerable deep link Activity: exported with no validation -->
<activity
android:name=".DeepLinkActivity"
android:exported="true"
android:theme="@style/Theme.MASTestApp">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="mastestapp" android:host="toggle" />
</intent-filter>
</activity>
<activity
android:name=".MainActivity"
android:exported="true"
android:windowSoftInputMode="adjustResize"
android:theme="@style/Theme.MASTestApp">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>
Loading
Loading