From 0661ed6baccdbbf69b2fdf58e4d8749819d0f519 Mon Sep 17 00:00:00 2001
From: ScreaM <70141504+ScreaMy7@users.noreply.github.com>
Date: Wed, 6 Aug 2025 11:52:14 +0000
Subject: [PATCH 01/12] draft
---
.../AndroidManifest_reversed.xml | 78 +++++++++
.../MASTG-DEMO-0061/MASTG-DEMO-0061.md | 33 ++++
.../MainActivityKt_reversed.java | 143 ++++++++++++++++
.../MASVS-CODE/MASTG-DEMO-0061/MastgTest.kt | 93 ++++++++++
.../MASTG-DEMO-0061/MastgTest_reversed.java | 159 ++++++++++++++++++
.../MASVS-CODE/MASTG-DEMO-0061/output.txt | 13 ++
.../android/MASVS-CODE/MASTG-DEMO-0061/run.sh | 2 +
...-android-sql-injection-contentprovider.yml | 14 ++
.../android/MASVS-CODE/MASTG-TEST-0288.md | 33 ++++
tests/android/MASVS-CODE/MASTG-TEST-0025.md | 3 +
10 files changed, 571 insertions(+)
create mode 100644 demos/android/MASVS-CODE/MASTG-DEMO-0061/AndroidManifest_reversed.xml
create mode 100644 demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md
create mode 100644 demos/android/MASVS-CODE/MASTG-DEMO-0061/MainActivityKt_reversed.java
create mode 100644 demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest.kt
create mode 100644 demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest_reversed.java
create mode 100644 demos/android/MASVS-CODE/MASTG-DEMO-0061/output.txt
create mode 100644 demos/android/MASVS-CODE/MASTG-DEMO-0061/run.sh
create mode 100644 rules/mastg-android-sql-injection-contentprovider.yml
create mode 100644 tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/AndroidManifest_reversed.xml b/demos/android/MASVS-CODE/MASTG-DEMO-0061/AndroidManifest_reversed.xml
new file mode 100644
index 00000000000..f1f5809cf7a
--- /dev/null
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0061/AndroidManifest_reversed.xml
@@ -0,0 +1,78 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md
new file mode 100644
index 00000000000..2f40f97050e
--- /dev/null
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md
@@ -0,0 +1,33 @@
+---
+platform: android
+title: Dangerous Permissions in the AndroidManifest with semgrep
+id: MASTG-DEMO-0061
+code: [kotlin]
+test: MASTG-TEST-0288
+status: new
+profiles: [L1, L2]
+---
+
+### Sample
+
+The following code implements a vulnerable `ContentProvider` that appends user-controlled input from the URI path directly into a SQL.
+
+{{ MastgTest.kt # MastgTest_reversed.kt }}
+
+### Steps
+
+Let’s run our @MASTG-TOOL-0110 rule against the sample code.
+
+{{ ../../../../rules/mastg-android-sql-injection-contentprovider.yml }}
+
+{{ run.sh }}
+
+### Observation
+
+The rule has identified the use of untrusted input from `Uri.getPathSegments().get(...)` being concatenated and passed into `SQLiteQueryBuilder.appendWhere(...)`, which is a known vector for SQL injection in exported `ContentProviders`.
+
+{{ output.txt }}
+
+### Evaluation
+
+This test case fails because the application constructs a SQL `WHERE` clause by directly appending untrusted user input from the URI without any validation or sanitization. This approach allows attackers to perform SQL injection by crafting a malicious `content://` URI to manipulate the query logic.
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MainActivityKt_reversed.java b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MainActivityKt_reversed.java
new file mode 100644
index 00000000000..802d57e3429
--- /dev/null
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MainActivityKt_reversed.java
@@ -0,0 +1,143 @@
+package org.owasp.mastestapp;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import androidx.compose.foundation.layout.PaddingKt;
+import androidx.compose.material3.TextKt;
+import androidx.compose.runtime.Composer;
+import androidx.compose.runtime.ComposerKt;
+import androidx.compose.runtime.MutableState;
+import androidx.compose.runtime.ProvidableCompositionLocal;
+import androidx.compose.runtime.RecomposeScopeImplKt;
+import androidx.compose.runtime.ScopeUpdateScope;
+import androidx.compose.runtime.SnapshotStateKt__SnapshotStateKt;
+import androidx.compose.runtime.internal.ComposableLambdaKt;
+import androidx.compose.ui.Modifier;
+import androidx.compose.ui.graphics.Color;
+import androidx.compose.ui.platform.AndroidCompositionLocals_androidKt;
+import androidx.compose.ui.platform.TestTagKt;
+import androidx.compose.ui.text.TextLayoutResult;
+import androidx.compose.ui.text.TextStyle;
+import androidx.compose.ui.text.font.FontFamily;
+import androidx.compose.ui.text.font.FontStyle;
+import androidx.compose.ui.text.font.FontWeight;
+import androidx.compose.ui.text.style.TextAlign;
+import androidx.compose.ui.text.style.TextDecoration;
+import androidx.compose.ui.unit.Dp;
+import androidx.compose.ui.unit.TextUnitKt;
+import kotlin.Metadata;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function0;
+import kotlin.jvm.functions.Function1;
+import kotlin.jvm.functions.Function2;
+import kotlin.jvm.internal.Intrinsics;
+
+/* compiled from: MainActivity.kt */
+@Metadata(d1 = {"\u0000\u0010\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0002\n\u0002\b\u0003\u001a\r\u0010\u0002\u001a\u00020\u0003H\u0007¢\u0006\u0002\u0010\u0004\"\u000e\u0010\u0000\u001a\u00020\u0001X\u0086T¢\u0006\u0002\n\u0000¨\u0006\u0005²\u0006\n\u0010\u0006\u001a\u00020\u0001X\u008a\u008e\u0002"}, d2 = {"MASTG_TEXT_TAG", "", "MainScreen", "", "(Landroidx/compose/runtime/Composer;I)V", "app_debug", "displayString"}, k = 2, mv = {2, 0, 0}, xi = 48)
+/* loaded from: classes3.dex */
+public final class MainActivityKt {
+ public static final String MASTG_TEXT_TAG = "mastgTestText";
+
+ /* JADX INFO: Access modifiers changed from: private */
+ public static final Unit MainScreen$lambda$6(int i, Composer composer, int i2) {
+ MainScreen(composer, RecomposeScopeImplKt.updateChangedFlags(i | 1));
+ return Unit.INSTANCE;
+ }
+
+ public static final void MainScreen(Composer $composer, final int $changed) {
+ Object value$iv;
+ Composer $composer2 = $composer.startRestartGroup(2010408835);
+ ComposerKt.sourceInformation($composer2, "C(MainScreen)37@1171L63,38@1266L7,50@1613L280,41@1323L570:MainActivity.kt#vyvp3i");
+ if ($changed != 0 || !$composer2.getSkipping()) {
+ $composer2.startReplaceGroup(1922816064);
+ ComposerKt.sourceInformation($composer2, "CC(remember):MainActivity.kt#9igjgp");
+ Object it$iv = $composer2.rememberedValue();
+ if (it$iv == Composer.INSTANCE.getEmpty()) {
+ value$iv = SnapshotStateKt__SnapshotStateKt.mutableStateOf$default("Click \"Start\" to run the test.", null, 2, null);
+ $composer2.updateRememberedValue(value$iv);
+ } else {
+ value$iv = it$iv;
+ }
+ final MutableState displayString$delegate = (MutableState) value$iv;
+ $composer2.endReplaceGroup();
+ ProvidableCompositionLocal localContext = AndroidCompositionLocals_androidKt.getLocalContext();
+ ComposerKt.sourceInformationMarkerStart($composer2, 2023513938, "CC:CompositionLocal.kt#9igjgp");
+ Object objConsume = $composer2.consume(localContext);
+ ComposerKt.sourceInformationMarkerEnd($composer2);
+ Context context = (Context) objConsume;
+ final MastgTest mastgTestClass = new MastgTest(context);
+ BaseScreenKt.BaseScreen(new Function0() { // from class: org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda1
+ @Override // kotlin.jvm.functions.Function0
+ public final Object invoke() {
+ return MainActivityKt.MainScreen$lambda$5(mastgTestClass, displayString$delegate);
+ }
+ }, ComposableLambdaKt.rememberComposableLambda(2027959928, true, new Function2() { // from class: org.owasp.mastestapp.MainActivityKt.MainScreen.2
+ @Override // kotlin.jvm.functions.Function2
+ public /* bridge */ /* synthetic */ Unit invoke(Composer composer, Integer num) {
+ invoke(composer, num.intValue());
+ return Unit.INSTANCE;
+ }
+
+ public final void invoke(Composer $composer3, int $changed2) {
+ ComposerKt.sourceInformation($composer3, "C51@1623L264:MainActivity.kt#vyvp3i");
+ if (($changed2 & 11) != 2 || !$composer3.getSkipping()) {
+ TextKt.m2715Text4IGK_g(MainActivityKt.MainScreen$lambda$1(displayString$delegate), TestTagKt.testTag(PaddingKt.m681padding3ABfNKs(Modifier.INSTANCE, Dp.m6664constructorimpl(16)), MainActivityKt.MASTG_TEXT_TAG), Color.INSTANCE.m4219getWhite0d7_KjU(), TextUnitKt.getSp(16), (FontStyle) null, (FontWeight) null, (FontFamily) FontFamily.INSTANCE.getMonospace(), 0L, (TextDecoration) null, (TextAlign) null, 0L, 0, false, 0, 0, (Function1 super TextLayoutResult, Unit>) null, (TextStyle) null, $composer3, 3504, 0, 130992);
+ } else {
+ $composer3.skipToGroupEnd();
+ }
+ }
+ }, $composer2, 54), $composer2, 48, 0);
+ } else {
+ $composer2.skipToGroupEnd();
+ }
+ ScopeUpdateScope scopeUpdateScopeEndRestartGroup = $composer2.endRestartGroup();
+ if (scopeUpdateScopeEndRestartGroup != null) {
+ scopeUpdateScopeEndRestartGroup.updateScope(new Function2() { // from class: org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda2
+ @Override // kotlin.jvm.functions.Function2
+ public final Object invoke(Object obj, Object obj2) {
+ return MainActivityKt.MainScreen$lambda$6($changed, (Composer) obj, ((Integer) obj2).intValue());
+ }
+ });
+ }
+ }
+
+ /* JADX INFO: Access modifiers changed from: private */
+ public static final String MainScreen$lambda$1(MutableState mutableState) {
+ MutableState $this$getValue$iv = mutableState;
+ return $this$getValue$iv.getValue();
+ }
+
+ /* JADX INFO: Access modifiers changed from: private */
+ public static final Unit MainScreen$lambda$5(final MastgTest mastgTestClass, final MutableState displayString$delegate) {
+ Intrinsics.checkNotNullParameter(mastgTestClass, "$mastgTestClass");
+ Intrinsics.checkNotNullParameter(displayString$delegate, "$displayString$delegate");
+ new Thread(new Runnable() { // from class: org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda0
+ @Override // java.lang.Runnable
+ public final void run() {
+ MainActivityKt.MainScreen$lambda$5$lambda$4(mastgTestClass, displayString$delegate);
+ }
+ }).start();
+ return Unit.INSTANCE;
+ }
+
+ /* JADX INFO: Access modifiers changed from: private */
+ public static final void MainScreen$lambda$5$lambda$4(MastgTest mastgTestClass, final MutableState displayString$delegate) {
+ Intrinsics.checkNotNullParameter(mastgTestClass, "$mastgTestClass");
+ Intrinsics.checkNotNullParameter(displayString$delegate, "$displayString$delegate");
+ final String result = mastgTestClass.mastgTest();
+ new Handler(Looper.getMainLooper()).post(new Runnable() { // from class: org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3
+ @Override // java.lang.Runnable
+ public final void run() {
+ MainActivityKt.MainScreen$lambda$5$lambda$4$lambda$3(result, displayString$delegate);
+ }
+ });
+ }
+
+ /* JADX INFO: Access modifiers changed from: private */
+ public static final void MainScreen$lambda$5$lambda$4$lambda$3(String result, MutableState displayString$delegate) {
+ Intrinsics.checkNotNullParameter(result, "$result");
+ Intrinsics.checkNotNullParameter(displayString$delegate, "$displayString$delegate");
+ displayString$delegate.setValue(result);
+ }
+}
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest.kt b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest.kt
new file mode 100644
index 00000000000..6752b7bffd6
--- /dev/null
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest.kt
@@ -0,0 +1,93 @@
+package org.owasp.mastestapp
+
+import android.content.ContentProvider
+import android.content.ContentValues
+import android.content.Context
+import android.content.UriMatcher
+import android.database.Cursor
+import android.database.sqlite.SQLiteDatabase
+import android.database.sqlite.SQLiteOpenHelper
+import android.database.sqlite.SQLiteQueryBuilder
+import android.net.Uri
+import android.util.Log
+
+class MastgTest(private val context: Context) {
+
+ fun mastgTest(): String {
+ val sensitiveString = "Hello from the OWASP MASTG Test app."
+ Log.d("MASTG-TEST", sensitiveString)
+ return sensitiveString
+ }
+
+ // 🔥 Vulnerable ContentProvider with path-based SQL injection
+ class StudentProvider : ContentProvider() {
+
+ companion object {
+ const val AUTHORITY = "org.owasp.mastestapp.provider"
+ const val STUDENTS = 1
+ const val STUDENT_ID = 2
+ val uriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
+ addURI(AUTHORITY, "students", STUDENTS)
+ addURI(AUTHORITY, "students/#", STUDENT_ID)
+ }
+ }
+
+ private lateinit var dbHelper: DatabaseHelper
+
+ override fun onCreate(): Boolean {
+ dbHelper = DatabaseHelper(context!!)
+ return true
+ }
+
+ override fun query(
+ uri: Uri,
+ projection: Array?,
+ selection: String?,
+ selectionArgs: Array?,
+ sortOrder: String?
+ ): Cursor? {
+ val db = dbHelper.readableDatabase
+ val qb = SQLiteQueryBuilder()
+ qb.tables = "students"
+
+ when (uriMatcher.match(uri)) {
+ STUDENTS -> {
+ // No filtering — all rows
+ }
+ STUDENT_ID -> {
+ // 🚨 Vulnerable: unvalidated input from path used in query
+ val id = uri.getPathSegments().get(1)
+ qb.appendWhere("id=" + id)
+ Log.e("SQLI", "Injected ID segment: $id")
+ }
+ else -> throw IllegalArgumentException("Unknown URI: $uri")
+ }
+
+ val cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder)
+ cursor.setNotificationUri(context!!.contentResolver, uri)
+ return cursor
+ }
+
+ override fun getType(uri: Uri): String? = null
+ override fun insert(uri: Uri, values: ContentValues?): Uri? = null
+ override fun delete(uri: Uri, selection: String?, selectionArgs: Array?): Int = 0
+ override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?): Int = 0
+ }
+
+ // 📦 DB helper for student data
+ class DatabaseHelper(context: Context) :
+ SQLiteOpenHelper(context, "students.db", null, 1) {
+
+ override fun onCreate(db: SQLiteDatabase) {
+ db.execSQL("CREATE TABLE students (id INTEGER PRIMARY KEY, name TEXT)")
+ db.execSQL("INSERT INTO students (name) VALUES ('Alice')")
+ db.execSQL("INSERT INTO students (name) VALUES ('Bob')")
+ db.execSQL("INSERT INTO students (name) VALUES ('Charlie')")
+ }
+
+ override fun onUpgrade(db: SQLiteDatabase, oldVersion: Int, newVersion: Int) {
+ db.execSQL("DROP TABLE IF EXISTS students")
+ onCreate(db)
+ }
+ }
+}
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest_reversed.java b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest_reversed.java
new file mode 100644
index 00000000000..b805af2c7a8
--- /dev/null
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest_reversed.java
@@ -0,0 +1,159 @@
+package org.owasp.mastestapp;
+
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.UriMatcher;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteQueryBuilder;
+import android.net.Uri;
+import android.util.Log;
+import kotlin.Metadata;
+import kotlin.jvm.internal.DefaultConstructorMarker;
+import kotlin.jvm.internal.Intrinsics;
+
+/* compiled from: MastgTest.kt */
+@Metadata(d1 = {"\u0000\u001a\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0002\b\u0003\b\u0007\u0018\u00002\u00020\u0001:\u0002\b\tB\u000f\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0004\b\u0004\u0010\u0005J\u0006\u0010\u0006\u001a\u00020\u0007R\u000e\u0010\u0002\u001a\u00020\u0003X\u0082\u0004¢\u0006\u0002\n\u0000¨\u0006\n"}, d2 = {"Lorg/owasp/mastestapp/MastgTest;", "", "context", "Landroid/content/Context;", "", "(Landroid/content/Context;)V", "mastgTest", "", "StudentProvider", "DatabaseHelper", "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() {
+ Log.d("MASTG-TEST", "Hello from the OWASP MASTG Test app.");
+ return "Hello from the OWASP MASTG Test app.";
+ }
+
+ /* compiled from: MastgTest.kt */
+ @Metadata(d1 = {"\u0000>\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u000b\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\u0011\n\u0002\u0010\u000e\n\u0002\b\u0007\n\u0002\u0018\u0002\n\u0000\n\u0002\u0010\b\n\u0002\b\u0005\b\u0007\u0018\u0000 \u001c2\u00020\u0001:\u0001\u001cB\t\b\u0007¢\u0006\u0004\b\u0002\u0010\u0003J\b\u0010\u0006\u001a\u00020\u0007H\u0016JK\u0010\b\u001a\u0004\u0018\u00010\t2\u0006\u0010\n\u001a\u00020\u000b2\u000e\u0010\f\u001a\n\u0012\u0004\u0012\u00020\u000e\u0018\u00010\r2\b\u0010\u000f\u001a\u0004\u0018\u00010\u000e2\u000e\u0010\u0010\u001a\n\u0012\u0004\u0012\u00020\u000e\u0018\u00010\r2\b\u0010\u0011\u001a\u0004\u0018\u00010\u000eH\u0016¢\u0006\u0002\u0010\u0012J\u0012\u0010\u0013\u001a\u0004\u0018\u00010\u000e2\u0006\u0010\n\u001a\u00020\u000bH\u0016J\u001c\u0010\u0014\u001a\u0004\u0018\u00010\u000b2\u0006\u0010\n\u001a\u00020\u000b2\b\u0010\u0015\u001a\u0004\u0018\u00010\u0016H\u0016J1\u0010\u0017\u001a\u00020\u00182\u0006\u0010\n\u001a\u00020\u000b2\b\u0010\u000f\u001a\u0004\u0018\u00010\u000e2\u0010\u0010\u0010\u001a\f\u0012\u0006\b\u0001\u0012\u00020\u000e\u0018\u00010\rH\u0016¢\u0006\u0002\u0010\u0019J;\u0010\u001a\u001a\u00020\u00182\u0006\u0010\n\u001a\u00020\u000b2\b\u0010\u0015\u001a\u0004\u0018\u00010\u00162\b\u0010\u000f\u001a\u0004\u0018\u00010\u000e2\u0010\u0010\u0010\u001a\f\u0012\u0006\b\u0001\u0012\u00020\u000e\u0018\u00010\rH\u0016¢\u0006\u0002\u0010\u001bR\u000e\u0010\u0004\u001a\u00020\u0005X\u0082.¢\u0006\u0002\n\u0000¨\u0006\u001d"}, d2 = {"Lorg/owasp/mastestapp/MastgTest$StudentProvider;", "Landroid/content/ContentProvider;", "", "()V", "dbHelper", "Lorg/owasp/mastestapp/MastgTest$DatabaseHelper;", "onCreate", "", "query", "Landroid/database/Cursor;", "uri", "Landroid/net/Uri;", "projection", "", "", "selection", "selectionArgs", "sortOrder", "(Landroid/net/Uri;[Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;Ljava/lang/String;)Landroid/database/Cursor;", "getType", "insert", "values", "Landroid/content/ContentValues;", "delete", "", "(Landroid/net/Uri;Ljava/lang/String;[Ljava/lang/String;)I", "update", "(Landroid/net/Uri;Landroid/content/ContentValues;Ljava/lang/String;[Ljava/lang/String;)I", "Companion", "app_debug"}, k = 1, mv = {2, 0, 0}, xi = 48)
+ public static final class StudentProvider extends ContentProvider {
+ public static final String AUTHORITY = "org.owasp.mastestapp.provider";
+ public static final int STUDENTS = 1;
+ public static final int STUDENT_ID = 2;
+ private static final UriMatcher uriMatcher;
+ private DatabaseHelper dbHelper;
+
+ /* renamed from: Companion, reason: from kotlin metadata */
+ public static final Companion INSTANCE = new Companion(null);
+ public static final int $stable = 8;
+
+ /* compiled from: MastgTest.kt */
+ @Metadata(d1 = {"\u0000\"\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0003\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\b\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\u0003\b\u0086\u0003\u0018\u00002\u00020\u0001B\t\b\u0002¢\u0006\u0004\b\u0002\u0010\u0003R\u000e\u0010\u0004\u001a\u00020\u0005X\u0086T¢\u0006\u0002\n\u0000R\u000e\u0010\u0006\u001a\u00020\u0007X\u0086T¢\u0006\u0002\n\u0000R\u000e\u0010\b\u001a\u00020\u0007X\u0086T¢\u0006\u0002\n\u0000R\u0011\u0010\t\u001a\u00020\n¢\u0006\b\n\u0000\u001a\u0004\b\u000b\u0010\f¨\u0006\r"}, d2 = {"Lorg/owasp/mastestapp/MastgTest$StudentProvider$Companion;", "", "", "()V", "AUTHORITY", "", "STUDENTS", "", "STUDENT_ID", "uriMatcher", "Landroid/content/UriMatcher;", "getUriMatcher", "()Landroid/content/UriMatcher;", "app_debug"}, k = 1, mv = {2, 0, 0}, xi = 48)
+ public static final class Companion {
+ public /* synthetic */ Companion(DefaultConstructorMarker defaultConstructorMarker) {
+ this();
+ }
+
+ private Companion() {
+ }
+
+ public final UriMatcher getUriMatcher() {
+ return StudentProvider.uriMatcher;
+ }
+ }
+
+ static {
+ UriMatcher $this$uriMatcher_u24lambda_u240 = new UriMatcher(-1);
+ $this$uriMatcher_u24lambda_u240.addURI(AUTHORITY, "students", 1);
+ $this$uriMatcher_u24lambda_u240.addURI(AUTHORITY, "students/#", 2);
+ uriMatcher = $this$uriMatcher_u24lambda_u240;
+ }
+
+ @Override // android.content.ContentProvider
+ public boolean onCreate() {
+ Context context = getContext();
+ Intrinsics.checkNotNull(context);
+ this.dbHelper = new DatabaseHelper(context);
+ return true;
+ }
+
+ @Override // android.content.ContentProvider
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
+ Intrinsics.checkNotNullParameter(uri, "uri");
+ DatabaseHelper databaseHelper = this.dbHelper;
+ if (databaseHelper == null) {
+ Intrinsics.throwUninitializedPropertyAccessException("dbHelper");
+ databaseHelper = null;
+ }
+ SQLiteDatabase db = databaseHelper.getReadableDatabase();
+ SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
+ qb.setTables("students");
+ switch (uriMatcher.match(uri)) {
+ case 1:
+ break;
+ case 2:
+ String id = uri.getPathSegments().get(1);
+ qb.appendWhere("id=" + id);
+ Log.e("SQLI", "Injected ID segment: " + id);
+ break;
+ default:
+ throw new IllegalArgumentException("Unknown URI: " + uri);
+ }
+ Cursor cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder);
+ Context context = getContext();
+ Intrinsics.checkNotNull(context);
+ cursor.setNotificationUri(context.getContentResolver(), uri);
+ return cursor;
+ }
+
+ @Override // android.content.ContentProvider
+ public String getType(Uri uri) {
+ Intrinsics.checkNotNullParameter(uri, "uri");
+ return null;
+ }
+
+ @Override // android.content.ContentProvider
+ public Uri insert(Uri uri, ContentValues values) {
+ Intrinsics.checkNotNullParameter(uri, "uri");
+ return null;
+ }
+
+ @Override // android.content.ContentProvider
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ Intrinsics.checkNotNullParameter(uri, "uri");
+ return 0;
+ }
+
+ @Override // android.content.ContentProvider
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ Intrinsics.checkNotNullParameter(uri, "uri");
+ return 0;
+ }
+ }
+
+ /* compiled from: MastgTest.kt */
+ @Metadata(d1 = {"\u0000(\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0003\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\b\n\u0002\b\u0002\b\u0007\u0018\u00002\u00020\u0001B\u000f\u0012\u0006\u0010\u0002\u001a\u00020\u0003¢\u0006\u0004\b\u0004\u0010\u0005J\u0010\u0010\u0006\u001a\u00020\u00072\u0006\u0010\b\u001a\u00020\tH\u0016J \u0010\n\u001a\u00020\u00072\u0006\u0010\b\u001a\u00020\t2\u0006\u0010\u000b\u001a\u00020\f2\u0006\u0010\r\u001a\u00020\fH\u0016¨\u0006\u000e"}, d2 = {"Lorg/owasp/mastestapp/MastgTest$DatabaseHelper;", "Landroid/database/sqlite/SQLiteOpenHelper;", "context", "Landroid/content/Context;", "", "(Landroid/content/Context;)V", "onCreate", "", "db", "Landroid/database/sqlite/SQLiteDatabase;", "onUpgrade", "oldVersion", "", "newVersion", "app_debug"}, k = 1, mv = {2, 0, 0}, xi = 48)
+ public static final class DatabaseHelper extends SQLiteOpenHelper {
+ public static final int $stable = 0;
+
+ /* JADX WARN: 'super' call moved to the top of the method (can break code semantics) */
+ public DatabaseHelper(Context context) {
+ super(context, "students.db", (SQLiteDatabase.CursorFactory) null, 1);
+ Intrinsics.checkNotNullParameter(context, "context");
+ }
+
+ @Override // android.database.sqlite.SQLiteOpenHelper
+ public void onCreate(SQLiteDatabase db) throws SQLException {
+ Intrinsics.checkNotNullParameter(db, "db");
+ db.execSQL("CREATE TABLE students (id INTEGER PRIMARY KEY, name TEXT)");
+ db.execSQL("INSERT INTO students (name) VALUES ('Alice')");
+ db.execSQL("INSERT INTO students (name) VALUES ('Bob')");
+ db.execSQL("INSERT INTO students (name) VALUES ('Charlie')");
+ }
+
+ @Override // android.database.sqlite.SQLiteOpenHelper
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) throws SQLException {
+ Intrinsics.checkNotNullParameter(db, "db");
+ db.execSQL("DROP TABLE IF EXISTS students");
+ onCreate(db);
+ }
+ }
+}
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/output.txt b/demos/android/MASVS-CODE/MASTG-DEMO-0061/output.txt
new file mode 100644
index 00000000000..b4bf6b625dc
--- /dev/null
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0061/output.txt
@@ -0,0 +1,13 @@
+
+
+┌────────────────┐
+│ 1 Code Finding │
+└────────────────┘
+
+ MastgTest_reversed.java
+ ❯❱ mastg-android-sqli-contentprovider
+ Possible SQL Injection: Unvalidated user input from `Uri.getPathSegments()` used in
+ `SQLiteQueryBuilder.appendWhere()
+
+ 94┆ qb.appendWhere("id=" + id);
+
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/run.sh b/demos/android/MASVS-CODE/MASTG-DEMO-0061/run.sh
new file mode 100644
index 00000000000..e1f9f69eed3
--- /dev/null
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0061/run.sh
@@ -0,0 +1,2 @@
+#!/bin/bash
+NO_COLOR=true semgrep -c ../../../../rules/mastg-android-sql-injection-contentprovider.yml ./MastgTest_reversed.java > output.txt
\ No newline at end of file
diff --git a/rules/mastg-android-sql-injection-contentprovider.yml b/rules/mastg-android-sql-injection-contentprovider.yml
new file mode 100644
index 00000000000..16123a15553
--- /dev/null
+++ b/rules/mastg-android-sql-injection-contentprovider.yml
@@ -0,0 +1,14 @@
+rules:
+ - id: mastg-android-sqli-contentprovider
+ severity: WARNING
+ languages:
+ - java
+ metadata:
+ summary: This rule inspects the possible SQL injection in android content providers.
+ message: "Possible SQL Injection: Unvalidated user input from `Uri.getPathSegments()` used in `SQLiteQueryBuilder.appendWhere()"
+ patterns:
+ - pattern: |
+ $QB.appendWhere("..." + $VAR);
+ - pattern-inside: |
+ $VAR = $URI.getPathSegments().get(...);
+ ...
\ No newline at end of file
diff --git a/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md b/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
new file mode 100644
index 00000000000..310c799b168
--- /dev/null
+++ b/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
@@ -0,0 +1,33 @@
+---
+platform: android
+title: SQL Injection in ContentProvider
+id: MASTG-TEST-0288
+weakness: [MASVS-CODE-4,MSTG-PLATFORM-2]
+profiles: [L1, L2]
+---
+
+## Overview
+
+Android apps can expose structured data through `ContentProvider` components. If these providers build SQL queries using untrusted input from URIs without proper validation or parameterization, they become vulnerable to SQL injection attacks.
+
+## Steps
+
+To identify and test for SQL injection vulnerabilities in `ContentProvider`s:
+
+1. Run @MASTG-TOOL-0110 rule against the code file to detect unvalidated use of `Uri.getPathSegments()` inside `appendWhere()`.
+
+## Observation
+
+The following vulnerable pattern was found in the app:
+
+```
+String id = uri.getPathSegments().get(1);
+qb.appendWhere("id=" + id);
+```
+This code uses untrusted input from the URI path directly in a SQL query, enabling potential SQL injection.
+
+## Evaluation
+
+The test fails if:
+- Untrusted user input (e.g., from `getPathSegments()`) is directly concatenated into SQL statements.
+- The app uses `appendWhere()` or builds queries unsafely without sanitization or parameterization.
diff --git a/tests/android/MASVS-CODE/MASTG-TEST-0025.md b/tests/android/MASVS-CODE/MASTG-TEST-0025.md
index 1ee9197255d..1d44e44d69b 100644
--- a/tests/android/MASVS-CODE/MASTG-TEST-0025.md
+++ b/tests/android/MASVS-CODE/MASTG-TEST-0025.md
@@ -9,6 +9,9 @@ masvs_v1_levels:
- L1
- L2
profiles: [L1, L2]
+status: deprecated
+covered_by: [MASTG-TEST-0288]
+deprecation_note: New version available in MASTG V2
---
## Overview
From 200cd94e4973ca71792d7731edaed7a4bf3d7f54 Mon Sep 17 00:00:00 2001
From: ScreaM <70141504+ScreaMy7@users.noreply.github.com>
Date: Wed, 6 Aug 2025 12:04:21 +0000
Subject: [PATCH 02/12] md fix
---
demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md | 2 +-
tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md | 4 +++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md
index 2f40f97050e..7c073424556 100644
--- a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md
@@ -1,6 +1,6 @@
---
platform: android
-title: Dangerous Permissions in the AndroidManifest with semgrep
+title: Injection flaws in android Content providers
id: MASTG-DEMO-0061
code: [kotlin]
test: MASTG-TEST-0288
diff --git a/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md b/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
index 310c799b168..88e98e3e3c3 100644
--- a/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
+++ b/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
@@ -20,14 +20,16 @@ To identify and test for SQL injection vulnerabilities in `ContentProvider`s:
The following vulnerable pattern was found in the app:
-```
+```java
String id = uri.getPathSegments().get(1);
qb.appendWhere("id=" + id);
```
+
This code uses untrusted input from the URI path directly in a SQL query, enabling potential SQL injection.
## Evaluation
The test fails if:
+
- Untrusted user input (e.g., from `getPathSegments()`) is directly concatenated into SQL statements.
- The app uses `appendWhere()` or builds queries unsafely without sanitization or parameterization.
From ef93867c0d544d8c678fd6e69119b37477cf5c42 Mon Sep 17 00:00:00 2001
From: ScreaM <70141504+ScreaMy7@users.noreply.github.com>
Date: Wed, 6 Aug 2025 12:11:49 +0000
Subject: [PATCH 03/12] small fix
---
demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md
index 7c073424556..85bff973e6e 100644
--- a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md
@@ -16,7 +16,7 @@ The following code implements a vulnerable `ContentProvider` that appends user-c
### Steps
-Let’s run our @MASTG-TOOL-0110 rule against the sample code.
+Let's run our @MASTG-TOOL-0110 rule against the sample code.
{{ ../../../../rules/mastg-android-sql-injection-contentprovider.yml }}
From ff677736cc10cf8d91a11ecbfc6acb761bb0ac64 Mon Sep 17 00:00:00 2001
From: ScreaM <70141504+ScreaMy7@users.noreply.github.com>
Date: Fri, 8 Aug 2025 11:17:23 +0000
Subject: [PATCH 04/12] fix test-beta
---
tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md | 14 +++++---------
1 file changed, 5 insertions(+), 9 deletions(-)
diff --git a/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md b/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
index 88e98e3e3c3..bc38fb5b182 100644
--- a/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
+++ b/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
@@ -1,32 +1,28 @@
---
-platform: android
title: SQL Injection in ContentProvider
+platform: android
id: MASTG-TEST-0288
-weakness: [MASVS-CODE-4,MSTG-PLATFORM-2]
+weakness:
profiles: [L1, L2]
---
## Overview
-Android apps can expose structured data through `ContentProvider` components. If these providers build SQL queries using untrusted input from URIs without proper validation or parameterization, they become vulnerable to SQL injection attacks.
+Android applications can share structured data via `ContentProvider` components. However, if these providers create SQL queries using untrusted input from URIs without adequate validation or parameterization, they risk becoming susceptible to SQL injection attacks.
## Steps
-To identify and test for SQL injection vulnerabilities in `ContentProvider`s:
-
-1. Run @MASTG-TOOL-0110 rule against the code file to detect unvalidated use of `Uri.getPathSegments()` inside `appendWhere()`.
+1. Run @MASTG-TOOL-0110 rule against the code file to detect SQL injection caused by unvalidated use of `Uri.getPathSegments()` inside `appendWhere()`.
## Observation
-The following vulnerable pattern was found in the app:
+This code uses untrusted input from the URI path directly in a SQL query, enabling potential SQL injection.
```java
String id = uri.getPathSegments().get(1);
qb.appendWhere("id=" + id);
```
-This code uses untrusted input from the URI path directly in a SQL query, enabling potential SQL injection.
-
## Evaluation
The test fails if:
From 28a04d431ba893f63c1d24d31cd677e5a2d9ed87 Mon Sep 17 00:00:00 2001
From: ScreaM <70141504+ScreaMy7@users.noreply.github.com>
Date: Fri, 8 Aug 2025 17:20:08 +0530
Subject: [PATCH 05/12] added weakness
---
tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md b/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
index bc38fb5b182..6a70bef1206 100644
--- a/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
+++ b/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
@@ -2,7 +2,7 @@
title: SQL Injection in ContentProvider
platform: android
id: MASTG-TEST-0288
-weakness:
+weakness: MASWE-0084
profiles: [L1, L2]
---
From 903768b727a362c12388a2d80a27cd81d5586912 Mon Sep 17 00:00:00 2001
From: ScreaM <70141504+ScreaMy7@users.noreply.github.com>
Date: Fri, 12 Sep 2025 13:49:16 +0000
Subject: [PATCH 06/12] added fix and best practise
---
best-practices/MASTG-BEST-0015.md | 23 +++
.../MainActivityKt_reversed.java | 143 ------------------
.../AndroidManifest_reversed.xml | 0
.../MASTG-DEMO-0062.md} | 4 +-
.../MastgTest.kt | 15 +-
.../MastgTest_reversed.java | 0
.../output.txt | 0
.../run.sh | 0
...-android-sql-injection-contentprovider.yml | 2 +-
...{MASTG-TEST-0288.md => MASTG-TEST-0292.md} | 6 +-
10 files changed, 40 insertions(+), 153 deletions(-)
create mode 100644 best-practices/MASTG-BEST-0015.md
delete mode 100644 demos/android/MASVS-CODE/MASTG-DEMO-0061/MainActivityKt_reversed.java
rename demos/android/MASVS-CODE/{MASTG-DEMO-0061 => MASTG-DEMO-0062}/AndroidManifest_reversed.xml (100%)
rename demos/android/MASVS-CODE/{MASTG-DEMO-0061/MASTG-DEMO-0061.md => MASTG-DEMO-0062/MASTG-DEMO-0062.md} (82%)
rename demos/android/MASVS-CODE/{MASTG-DEMO-0061 => MASTG-DEMO-0062}/MastgTest.kt (87%)
rename demos/android/MASVS-CODE/{MASTG-DEMO-0061 => MASTG-DEMO-0062}/MastgTest_reversed.java (100%)
rename demos/android/MASVS-CODE/{MASTG-DEMO-0061 => MASTG-DEMO-0062}/output.txt (100%)
rename demos/android/MASVS-CODE/{MASTG-DEMO-0061 => MASTG-DEMO-0062}/run.sh (100%)
rename tests-beta/android/MASVS-CODE/{MASTG-TEST-0288.md => MASTG-TEST-0292.md} (91%)
diff --git a/best-practices/MASTG-BEST-0015.md b/best-practices/MASTG-BEST-0015.md
new file mode 100644
index 00000000000..3736d967670
--- /dev/null
+++ b/best-practices/MASTG-BEST-0015.md
@@ -0,0 +1,23 @@
+---
+title: Prevent SQL Injection in ContentProviders
+alias: prevent-sqli-contentprovider
+id: MASTG-BEST-0015
+platform: android
+---
+
+The `ContentProvider` enables Android applications to share data with other applications and system components. If a `ContentProvider` constructs SQL queries using untrusted input from URIs, IPC calls, or Intents without validation or parameterization, it becomes vulnerable to SQL injection. Attackers can take advantage of this vulnerability to bypass access controls and extract sensitive data. Improper handling of URI path segments, query parameters, or `selection` arguments in `ContentProvider` queries can lead to arbitrary SQL execution.
+
+- Use Parameterized Queries : Instead of building SQL using string concatenation, use `selection` and `selectionArgs` parameters.
+
+For example:
+
+```kotlin
+
+ val idSegment = uri.getPathSegments()[1]
+ val selection = "id = ?"
+ val selectionArgs = arrayOf(idSegment)
+ val cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder)
+
+```
+
+Refer to ["Protect against malicious input"](https://developer.android.com/guide/topics/providers/content-provider-basics#Injection) for more information.
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MainActivityKt_reversed.java b/demos/android/MASVS-CODE/MASTG-DEMO-0061/MainActivityKt_reversed.java
deleted file mode 100644
index 802d57e3429..00000000000
--- a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MainActivityKt_reversed.java
+++ /dev/null
@@ -1,143 +0,0 @@
-package org.owasp.mastestapp;
-
-import android.content.Context;
-import android.os.Handler;
-import android.os.Looper;
-import androidx.compose.foundation.layout.PaddingKt;
-import androidx.compose.material3.TextKt;
-import androidx.compose.runtime.Composer;
-import androidx.compose.runtime.ComposerKt;
-import androidx.compose.runtime.MutableState;
-import androidx.compose.runtime.ProvidableCompositionLocal;
-import androidx.compose.runtime.RecomposeScopeImplKt;
-import androidx.compose.runtime.ScopeUpdateScope;
-import androidx.compose.runtime.SnapshotStateKt__SnapshotStateKt;
-import androidx.compose.runtime.internal.ComposableLambdaKt;
-import androidx.compose.ui.Modifier;
-import androidx.compose.ui.graphics.Color;
-import androidx.compose.ui.platform.AndroidCompositionLocals_androidKt;
-import androidx.compose.ui.platform.TestTagKt;
-import androidx.compose.ui.text.TextLayoutResult;
-import androidx.compose.ui.text.TextStyle;
-import androidx.compose.ui.text.font.FontFamily;
-import androidx.compose.ui.text.font.FontStyle;
-import androidx.compose.ui.text.font.FontWeight;
-import androidx.compose.ui.text.style.TextAlign;
-import androidx.compose.ui.text.style.TextDecoration;
-import androidx.compose.ui.unit.Dp;
-import androidx.compose.ui.unit.TextUnitKt;
-import kotlin.Metadata;
-import kotlin.Unit;
-import kotlin.jvm.functions.Function0;
-import kotlin.jvm.functions.Function1;
-import kotlin.jvm.functions.Function2;
-import kotlin.jvm.internal.Intrinsics;
-
-/* compiled from: MainActivity.kt */
-@Metadata(d1 = {"\u0000\u0010\n\u0000\n\u0002\u0010\u000e\n\u0000\n\u0002\u0010\u0002\n\u0002\b\u0003\u001a\r\u0010\u0002\u001a\u00020\u0003H\u0007¢\u0006\u0002\u0010\u0004\"\u000e\u0010\u0000\u001a\u00020\u0001X\u0086T¢\u0006\u0002\n\u0000¨\u0006\u0005²\u0006\n\u0010\u0006\u001a\u00020\u0001X\u008a\u008e\u0002"}, d2 = {"MASTG_TEXT_TAG", "", "MainScreen", "", "(Landroidx/compose/runtime/Composer;I)V", "app_debug", "displayString"}, k = 2, mv = {2, 0, 0}, xi = 48)
-/* loaded from: classes3.dex */
-public final class MainActivityKt {
- public static final String MASTG_TEXT_TAG = "mastgTestText";
-
- /* JADX INFO: Access modifiers changed from: private */
- public static final Unit MainScreen$lambda$6(int i, Composer composer, int i2) {
- MainScreen(composer, RecomposeScopeImplKt.updateChangedFlags(i | 1));
- return Unit.INSTANCE;
- }
-
- public static final void MainScreen(Composer $composer, final int $changed) {
- Object value$iv;
- Composer $composer2 = $composer.startRestartGroup(2010408835);
- ComposerKt.sourceInformation($composer2, "C(MainScreen)37@1171L63,38@1266L7,50@1613L280,41@1323L570:MainActivity.kt#vyvp3i");
- if ($changed != 0 || !$composer2.getSkipping()) {
- $composer2.startReplaceGroup(1922816064);
- ComposerKt.sourceInformation($composer2, "CC(remember):MainActivity.kt#9igjgp");
- Object it$iv = $composer2.rememberedValue();
- if (it$iv == Composer.INSTANCE.getEmpty()) {
- value$iv = SnapshotStateKt__SnapshotStateKt.mutableStateOf$default("Click \"Start\" to run the test.", null, 2, null);
- $composer2.updateRememberedValue(value$iv);
- } else {
- value$iv = it$iv;
- }
- final MutableState displayString$delegate = (MutableState) value$iv;
- $composer2.endReplaceGroup();
- ProvidableCompositionLocal localContext = AndroidCompositionLocals_androidKt.getLocalContext();
- ComposerKt.sourceInformationMarkerStart($composer2, 2023513938, "CC:CompositionLocal.kt#9igjgp");
- Object objConsume = $composer2.consume(localContext);
- ComposerKt.sourceInformationMarkerEnd($composer2);
- Context context = (Context) objConsume;
- final MastgTest mastgTestClass = new MastgTest(context);
- BaseScreenKt.BaseScreen(new Function0() { // from class: org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda1
- @Override // kotlin.jvm.functions.Function0
- public final Object invoke() {
- return MainActivityKt.MainScreen$lambda$5(mastgTestClass, displayString$delegate);
- }
- }, ComposableLambdaKt.rememberComposableLambda(2027959928, true, new Function2() { // from class: org.owasp.mastestapp.MainActivityKt.MainScreen.2
- @Override // kotlin.jvm.functions.Function2
- public /* bridge */ /* synthetic */ Unit invoke(Composer composer, Integer num) {
- invoke(composer, num.intValue());
- return Unit.INSTANCE;
- }
-
- public final void invoke(Composer $composer3, int $changed2) {
- ComposerKt.sourceInformation($composer3, "C51@1623L264:MainActivity.kt#vyvp3i");
- if (($changed2 & 11) != 2 || !$composer3.getSkipping()) {
- TextKt.m2715Text4IGK_g(MainActivityKt.MainScreen$lambda$1(displayString$delegate), TestTagKt.testTag(PaddingKt.m681padding3ABfNKs(Modifier.INSTANCE, Dp.m6664constructorimpl(16)), MainActivityKt.MASTG_TEXT_TAG), Color.INSTANCE.m4219getWhite0d7_KjU(), TextUnitKt.getSp(16), (FontStyle) null, (FontWeight) null, (FontFamily) FontFamily.INSTANCE.getMonospace(), 0L, (TextDecoration) null, (TextAlign) null, 0L, 0, false, 0, 0, (Function1 super TextLayoutResult, Unit>) null, (TextStyle) null, $composer3, 3504, 0, 130992);
- } else {
- $composer3.skipToGroupEnd();
- }
- }
- }, $composer2, 54), $composer2, 48, 0);
- } else {
- $composer2.skipToGroupEnd();
- }
- ScopeUpdateScope scopeUpdateScopeEndRestartGroup = $composer2.endRestartGroup();
- if (scopeUpdateScopeEndRestartGroup != null) {
- scopeUpdateScopeEndRestartGroup.updateScope(new Function2() { // from class: org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda2
- @Override // kotlin.jvm.functions.Function2
- public final Object invoke(Object obj, Object obj2) {
- return MainActivityKt.MainScreen$lambda$6($changed, (Composer) obj, ((Integer) obj2).intValue());
- }
- });
- }
- }
-
- /* JADX INFO: Access modifiers changed from: private */
- public static final String MainScreen$lambda$1(MutableState mutableState) {
- MutableState $this$getValue$iv = mutableState;
- return $this$getValue$iv.getValue();
- }
-
- /* JADX INFO: Access modifiers changed from: private */
- public static final Unit MainScreen$lambda$5(final MastgTest mastgTestClass, final MutableState displayString$delegate) {
- Intrinsics.checkNotNullParameter(mastgTestClass, "$mastgTestClass");
- Intrinsics.checkNotNullParameter(displayString$delegate, "$displayString$delegate");
- new Thread(new Runnable() { // from class: org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda0
- @Override // java.lang.Runnable
- public final void run() {
- MainActivityKt.MainScreen$lambda$5$lambda$4(mastgTestClass, displayString$delegate);
- }
- }).start();
- return Unit.INSTANCE;
- }
-
- /* JADX INFO: Access modifiers changed from: private */
- public static final void MainScreen$lambda$5$lambda$4(MastgTest mastgTestClass, final MutableState displayString$delegate) {
- Intrinsics.checkNotNullParameter(mastgTestClass, "$mastgTestClass");
- Intrinsics.checkNotNullParameter(displayString$delegate, "$displayString$delegate");
- final String result = mastgTestClass.mastgTest();
- new Handler(Looper.getMainLooper()).post(new Runnable() { // from class: org.owasp.mastestapp.MainActivityKt$$ExternalSyntheticLambda3
- @Override // java.lang.Runnable
- public final void run() {
- MainActivityKt.MainScreen$lambda$5$lambda$4$lambda$3(result, displayString$delegate);
- }
- });
- }
-
- /* JADX INFO: Access modifiers changed from: private */
- public static final void MainScreen$lambda$5$lambda$4$lambda$3(String result, MutableState displayString$delegate) {
- Intrinsics.checkNotNullParameter(result, "$result");
- Intrinsics.checkNotNullParameter(displayString$delegate, "$displayString$delegate");
- displayString$delegate.setValue(result);
- }
-}
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/AndroidManifest_reversed.xml b/demos/android/MASVS-CODE/MASTG-DEMO-0062/AndroidManifest_reversed.xml
similarity index 100%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0061/AndroidManifest_reversed.xml
rename to demos/android/MASVS-CODE/MASTG-DEMO-0062/AndroidManifest_reversed.xml
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md b/demos/android/MASVS-CODE/MASTG-DEMO-0062/MASTG-DEMO-0062.md
similarity index 82%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md
rename to demos/android/MASVS-CODE/MASTG-DEMO-0062/MASTG-DEMO-0062.md
index 85bff973e6e..c527d742936 100644
--- a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MASTG-DEMO-0061.md
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0062/MASTG-DEMO-0062.md
@@ -30,4 +30,6 @@ The rule has identified the use of untrusted input from `Uri.getPathSegments().g
### Evaluation
-This test case fails because the application constructs a SQL `WHERE` clause by directly appending untrusted user input from the URI without any validation or sanitization. This approach allows attackers to perform SQL injection by crafting a malicious `content://` URI to manipulate the query logic.
+This test case fails because the application constructs a SQL `WHERE` clause by directly appending untrusted user input from the URI without any validation or sanitization. This approach allows attackers to perform SQL injection by crafting a malicious `content://` URI to manipulate the query logic. For example, the following content query command can be used to list all names:
+
+`content query --uri content://org.owasp.mastestapp.provider/students --where "name='Bob' OR '1'='1'"`
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest.kt b/demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest.kt
similarity index 87%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest.kt
rename to demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest.kt
index 6752b7bffd6..814274fb5d5 100644
--- a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest.kt
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest.kt
@@ -14,12 +14,15 @@ import android.util.Log
class MastgTest(private val context: Context) {
fun mastgTest(): String {
- val sensitiveString = "Hello from the OWASP MASTG Test app."
- Log.d("MASTG-TEST", sensitiveString)
- return sensitiveString
+ return """
+ This app is vulnerable to SQLI in content provider.
+
+ Test on adb shell with:
+ # content query --uri content://org.owasp.mastestapp.provider/students --where "name='Bob' OR '1'='1'"
+ """.trimIndent()
}
- // 🔥 Vulnerable ContentProvider with path-based SQL injection
+ // Vulnerable ContentProvider with path-based SQL injection
class StudentProvider : ContentProvider() {
companion object {
@@ -55,7 +58,7 @@ class MastgTest(private val context: Context) {
// No filtering — all rows
}
STUDENT_ID -> {
- // 🚨 Vulnerable: unvalidated input from path used in query
+ // Vulnerable: unvalidated input from path used in query
val id = uri.getPathSegments().get(1)
qb.appendWhere("id=" + id)
Log.e("SQLI", "Injected ID segment: $id")
@@ -74,7 +77,7 @@ class MastgTest(private val context: Context) {
override fun update(uri: Uri, values: ContentValues?, selection: String?, selectionArgs: Array?): Int = 0
}
- // 📦 DB helper for student data
+ // DB helper for student data
class DatabaseHelper(context: Context) :
SQLiteOpenHelper(context, "students.db", null, 1) {
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest_reversed.java b/demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest_reversed.java
similarity index 100%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0061/MastgTest_reversed.java
rename to demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest_reversed.java
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/output.txt b/demos/android/MASVS-CODE/MASTG-DEMO-0062/output.txt
similarity index 100%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0061/output.txt
rename to demos/android/MASVS-CODE/MASTG-DEMO-0062/output.txt
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0061/run.sh b/demos/android/MASVS-CODE/MASTG-DEMO-0062/run.sh
similarity index 100%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0061/run.sh
rename to demos/android/MASVS-CODE/MASTG-DEMO-0062/run.sh
diff --git a/rules/mastg-android-sql-injection-contentprovider.yml b/rules/mastg-android-sql-injection-contentprovider.yml
index 16123a15553..9d4ce949fd3 100644
--- a/rules/mastg-android-sql-injection-contentprovider.yml
+++ b/rules/mastg-android-sql-injection-contentprovider.yml
@@ -5,7 +5,7 @@ rules:
- java
metadata:
summary: This rule inspects the possible SQL injection in android content providers.
- message: "Possible SQL Injection: Unvalidated user input from `Uri.getPathSegments()` used in `SQLiteQueryBuilder.appendWhere()"
+ message: "Possible SQL Injection: Unvalidated user input from `Uri.getPathSegments()` used in `SQLiteQueryBuilder.appendWhere()`"
patterns:
- pattern: |
$QB.appendWhere("..." + $VAR);
diff --git a/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md b/tests-beta/android/MASVS-CODE/MASTG-TEST-0292.md
similarity index 91%
rename from tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
rename to tests-beta/android/MASVS-CODE/MASTG-TEST-0292.md
index 6a70bef1206..48198168da7 100644
--- a/tests-beta/android/MASVS-CODE/MASTG-TEST-0288.md
+++ b/tests-beta/android/MASVS-CODE/MASTG-TEST-0292.md
@@ -1,8 +1,10 @@
---
title: SQL Injection in ContentProvider
platform: android
-id: MASTG-TEST-0288
-weakness: MASWE-0084
+id: MASTG-TEST-0292
+type: [static]
+weakness: MASWE-0086
+best-practices: [MASTG-BEST-0015]
profiles: [L1, L2]
---
From 52a416dbecbde73f8fbfd7b6747d7c1074d4d38e Mon Sep 17 00:00:00 2001
From: ScreaM <70141504+ScreaMy7@users.noreply.github.com>
Date: Fri, 12 Sep 2025 14:24:28 +0000
Subject: [PATCH 07/12] another fix
---
demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest.kt b/demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest.kt
index 814274fb5d5..6160088cb32 100644
--- a/demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest.kt
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest.kt
@@ -15,7 +15,7 @@ class MastgTest(private val context: Context) {
fun mastgTest(): String {
return """
- This app is vulnerable to SQLI in content provider.
+ This app's content provider is vulnerable to SQLI.
Test on adb shell with:
# content query --uri content://org.owasp.mastestapp.provider/students --where "name='Bob' OR '1'='1'"
From 40896f10ade954d30c7b5384765ed0989011b54a Mon Sep 17 00:00:00 2001
From: ScreaM <70141504+ScreaMy7@users.noreply.github.com>
Date: Tue, 4 Nov 2025 07:19:41 +0000
Subject: [PATCH 08/12] added new tech and outputs.
---
...{MASTG-BEST-0015.md => MASTG-BEST-XXXX.md} | 2 +-
.../AndroidManifest_reversed.xml | 0
.../MASTG-DEMO-00XX.md} | 16 ++--
.../MastgTest.kt | 0
.../MastgTest_reversed.java | 0
.../output.txt | 0
.../run.sh | 0
techniques/android/MASTG-TECH-XXXX.md | 83 +++++++++++++++++++
...{MASTG-TEST-0292.md => MASTG-TEST-02XX.md} | 4 +-
9 files changed, 97 insertions(+), 8 deletions(-)
rename best-practices/{MASTG-BEST-0015.md => MASTG-BEST-XXXX.md} (98%)
rename demos/android/MASVS-CODE/{MASTG-DEMO-0062 => MASTG-DEMO-00XX}/AndroidManifest_reversed.xml (100%)
rename demos/android/MASVS-CODE/{MASTG-DEMO-0062/MASTG-DEMO-0062.md => MASTG-DEMO-00XX/MASTG-DEMO-00XX.md} (70%)
rename demos/android/MASVS-CODE/{MASTG-DEMO-0062 => MASTG-DEMO-00XX}/MastgTest.kt (100%)
rename demos/android/MASVS-CODE/{MASTG-DEMO-0062 => MASTG-DEMO-00XX}/MastgTest_reversed.java (100%)
rename demos/android/MASVS-CODE/{MASTG-DEMO-0062 => MASTG-DEMO-00XX}/output.txt (100%)
rename demos/android/MASVS-CODE/{MASTG-DEMO-0062 => MASTG-DEMO-00XX}/run.sh (100%)
create mode 100644 techniques/android/MASTG-TECH-XXXX.md
rename tests-beta/android/MASVS-CODE/{MASTG-TEST-0292.md => MASTG-TEST-02XX.md} (94%)
diff --git a/best-practices/MASTG-BEST-0015.md b/best-practices/MASTG-BEST-XXXX.md
similarity index 98%
rename from best-practices/MASTG-BEST-0015.md
rename to best-practices/MASTG-BEST-XXXX.md
index 3736d967670..d31a09f8345 100644
--- a/best-practices/MASTG-BEST-0015.md
+++ b/best-practices/MASTG-BEST-XXXX.md
@@ -1,7 +1,7 @@
---
title: Prevent SQL Injection in ContentProviders
alias: prevent-sqli-contentprovider
-id: MASTG-BEST-0015
+id: MASTG-BEST-XXXX
platform: android
---
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0062/AndroidManifest_reversed.xml b/demos/android/MASVS-CODE/MASTG-DEMO-00XX/AndroidManifest_reversed.xml
similarity index 100%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0062/AndroidManifest_reversed.xml
rename to demos/android/MASVS-CODE/MASTG-DEMO-00XX/AndroidManifest_reversed.xml
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0062/MASTG-DEMO-0062.md b/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md
similarity index 70%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0062/MASTG-DEMO-0062.md
rename to demos/android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md
index c527d742936..dc8984a9aa1 100644
--- a/demos/android/MASVS-CODE/MASTG-DEMO-0062/MASTG-DEMO-0062.md
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md
@@ -1,11 +1,10 @@
---
platform: android
title: Injection flaws in android Content providers
-id: MASTG-DEMO-0061
+id: MASTG-DEMO-00XX
code: [kotlin]
-test: MASTG-TEST-0288
+test: MASTG-TEST-02XX
status: new
-profiles: [L1, L2]
---
### Sample
@@ -30,6 +29,13 @@ The rule has identified the use of untrusted input from `Uri.getPathSegments().g
### Evaluation
-This test case fails because the application constructs a SQL `WHERE` clause by directly appending untrusted user input from the URI without any validation or sanitization. This approach allows attackers to perform SQL injection by crafting a malicious `content://` URI to manipulate the query logic. For example, the following content query command can be used to list all names:
+This test case fails because the application constructs a SQL `WHERE` clause by directly appending untrusted user input from the URI without any validation or sanitization. This approach allows attackers to perform SQL injection by crafting a malicious `content://` URI to manipulate the query logic. For example, the following content query command can be used to list all names:
-`content query --uri content://org.owasp.mastestapp.provider/students --where "name='Bob' OR '1'='1'"`
+```bash
+$ content query --uri content://org.owasp.mastestapp.provider/students --where "name='Bob' OR '1'='1'"
+Row: 0 id=1, name=Alice
+Row: 1 id=2, name=Bob
+Row: 2 id=3, name=Charlie
+```
+
+Refer to @MASTG-TECH-XXX, to know more on using content query.
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest.kt b/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MastgTest.kt
similarity index 100%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest.kt
rename to demos/android/MASVS-CODE/MASTG-DEMO-00XX/MastgTest.kt
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest_reversed.java b/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MastgTest_reversed.java
similarity index 100%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0062/MastgTest_reversed.java
rename to demos/android/MASVS-CODE/MASTG-DEMO-00XX/MastgTest_reversed.java
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0062/output.txt b/demos/android/MASVS-CODE/MASTG-DEMO-00XX/output.txt
similarity index 100%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0062/output.txt
rename to demos/android/MASVS-CODE/MASTG-DEMO-00XX/output.txt
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-0062/run.sh b/demos/android/MASVS-CODE/MASTG-DEMO-00XX/run.sh
similarity index 100%
rename from demos/android/MASVS-CODE/MASTG-DEMO-0062/run.sh
rename to demos/android/MASVS-CODE/MASTG-DEMO-00XX/run.sh
diff --git a/techniques/android/MASTG-TECH-XXXX.md b/techniques/android/MASTG-TECH-XXXX.md
new file mode 100644
index 00000000000..ac20d5c3dba
--- /dev/null
+++ b/techniques/android/MASTG-TECH-XXXX.md
@@ -0,0 +1,83 @@
+---
+title: Testing Content URI Injection and Path-Segment Abuse
+platform: android
+---
+
+Android `ContentProvider`s make structured data available to other applications through `content://` URIs. They specify an authority (a unique identifier), one or more paths (tables/resources), and carry out CRUD operations (`query`, `insert`, `update`, `delete`). Clients interact with them via `ContentResolver` or directly from the device shell. The accessibility of a provider is contingent upon its `exported` setting and any permissions declared in the application's manifest (refer to @MASTG-TECH-0117).
+
+## What They Are
+
+- Interface for cross-app data access and IPC on Android.
+- Identified by a URI: `content:///` or `content:////`.
+- Backed by storage such as SQLite; many apps use `SQLiteQueryBuilder` in `query`.
+- Access control via `android:exported` and read/write permissions; signature-level permissions can restrict access to trusted apps only.
+
+## Using Content query
+
+Use @MASTG-TOOL-0004 to interact with providers on a device or emulator via the `content` command:
+
+- Query rows
+
+```bash
+$ adb shell content query --uri content://org.owasp.mastestapp.provider/students
+$ adb shell content query --uri content://org.owasp.mastestapp.provider/students --where "name='Bob'"
+```
+
+- Insert a row
+
+```bash
+$ adb shell content insert \
+ --uri content://org.owasp.mastestapp.provider/students \
+ --bind name:s:"Eve"
+```
+
+- Update rows
+
+```bash
+$ adb shell content update \
+ --uri content://org.owasp.mastestapp.provider/students \
+ --where "id=1" \
+ --bind name:s:"Alice Jr"
+```
+
+- Delete rows
+
+```bash
+$ adb shell content delete --uri content://org.owasp.mastestapp.provider/students --where "id=3"
+```
+
+## Inputs To Validate
+
+- URI path segments
+ - Risk: values from `Uri.getPathSegments()` / `lastPathSegment` concatenated into SQL (for example, `appendWhere("id=" + id)`).
+ - Safer: parse numeric IDs with `ContentUris.parseId(uri)`; strictly validate/whitelist path segments; never concatenate untrusted data into SQL.
+
+## Injection Flaw Testing
+
+Injection vulnerabilities in `ContentProvider`s usually arise when untrusted input (such as a path segment from `Uri.getPathSegments()` or a selection string provided by the caller) is directly concatenated into SQL queries rather than being parameterized. A frequent point of concern is `SQLiteQueryBuilder.appendWhere(...)`. The potential risk is particularly significant for exported providers or those that offer extensive read permissions. As a tester, you can investigate the behavior using the Android shell; a positive indication of an issue is when a query retrieves more rows than expected or circumvents filtering.
+
+```bash
+$ adb shell content query --uri content://org.owasp.mastestapp.provider/students
+Row: 0 id=1, name=Alice
+Row: 1 id=2, name=Bob
+Row: 2 id=3, name=Charlie
+```
+
+**Injection probe**(only if applicable, to detect unsafe string concatenation in selection logic)
+
+```bash
+$ content query --uri content://org.owasp.mastestapp.provider/students --where "name='Bob' OR '1'='1'"
+Row: 0 id=1, name=Alice
+Row: 1 id=2, name=Bob
+Row: 2 id=3, name=Charlie
+```
+
+If results exceed the intended filter, the content provider may be concatenating untrusted input instead of using parameterized selections.
+
+## Observe logs
+
+Use @MASTG-TOOL-0004 to look for `SQLiteException`, syntax errors, or provider log statements that indicate raw string concatenation or leaking SQL statements.
+
+```bash
+$ adb logcat | grep -i -E "sqlite|contentresolver|provider"
+```
diff --git a/tests-beta/android/MASVS-CODE/MASTG-TEST-0292.md b/tests-beta/android/MASVS-CODE/MASTG-TEST-02XX.md
similarity index 94%
rename from tests-beta/android/MASVS-CODE/MASTG-TEST-0292.md
rename to tests-beta/android/MASVS-CODE/MASTG-TEST-02XX.md
index 48198168da7..38f1c1010d6 100644
--- a/tests-beta/android/MASVS-CODE/MASTG-TEST-0292.md
+++ b/tests-beta/android/MASVS-CODE/MASTG-TEST-02XX.md
@@ -1,10 +1,10 @@
---
title: SQL Injection in ContentProvider
platform: android
-id: MASTG-TEST-0292
+id: MASTG-TEST-02XX
type: [static]
weakness: MASWE-0086
-best-practices: [MASTG-BEST-0015]
+best-practices: [MASTG-BEST-XXXX]
profiles: [L1, L2]
---
From 3e352f66ad3f5c153e941c3c99434b65f212f001 Mon Sep 17 00:00:00 2001
From: ScreaM <70141504+ScreaMy7@users.noreply.github.com>
Date: Mon, 26 Jan 2026 16:55:31 +0530
Subject: [PATCH 09/12] lint fix and added Prepared statement
---
best-practices/MASTG-BEST-XXXX.md | 8 ++++----
.../android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md | 2 +-
techniques/android/MASTG-TECH-XXXX.md | 4 ++--
3 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/best-practices/MASTG-BEST-XXXX.md b/best-practices/MASTG-BEST-XXXX.md
index d31a09f8345..09160d44374 100644
--- a/best-practices/MASTG-BEST-XXXX.md
+++ b/best-practices/MASTG-BEST-XXXX.md
@@ -7,17 +7,17 @@ platform: android
The `ContentProvider` enables Android applications to share data with other applications and system components. If a `ContentProvider` constructs SQL queries using untrusted input from URIs, IPC calls, or Intents without validation or parameterization, it becomes vulnerable to SQL injection. Attackers can take advantage of this vulnerability to bypass access controls and extract sensitive data. Improper handling of URI path segments, query parameters, or `selection` arguments in `ContentProvider` queries can lead to arbitrary SQL execution.
-- Use Parameterized Queries : Instead of building SQL using string concatenation, use `selection` and `selectionArgs` parameters.
+- **Use Parameterized Queries** : Instead of building SQL using string concatenation, use `selection` and `selectionArgs` parameters.
For example:
```kotlin
-
val idSegment = uri.getPathSegments()[1]
val selection = "id = ?"
val selectionArgs = arrayOf(idSegment)
val cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder)
-
```
-Refer to ["Protect against malicious input"](https://developer.android.com/guide/topics/providers/content-provider-basics#Injection) for more information.
+- **Use Prepared Statements** : When performing insert, update, or delete operations, use SQLite prepared statements (for example, `SQLiteStatement` or `SQLiteDatabase` methods that support argument binding) instead of dynamically constructed SQL. Prepared statements ensure that untrusted input is bound as parameters and cannot alter the structure of the SQL query, effectively preventing SQL injection even when input originates from URIs or IPC calls.
+
+Refer to ["Protect against malicious input"](https://developer.android.com/guide/topics/providers/content-provider-basics#Injection) for more information.
\ No newline at end of file
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md b/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md
index dc8984a9aa1..cb3d035dceb 100644
--- a/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md
@@ -29,7 +29,7 @@ The rule has identified the use of untrusted input from `Uri.getPathSegments().g
### Evaluation
-This test case fails because the application constructs a SQL `WHERE` clause by directly appending untrusted user input from the URI without any validation or sanitization. This approach allows attackers to perform SQL injection by crafting a malicious `content://` URI to manipulate the query logic. For example, the following content query command can be used to list all names:
+This test case fails because the application constructs a SQL `WHERE` clause by directly appending untrusted user input from the URI without any validation or sanitization. This approach allows attackers to perform SQL injection by crafting a malicious `content://` URI to manipulate the query logic. For example, the following content query command can be used to list all names:
```bash
$ content query --uri content://org.owasp.mastestapp.provider/students --where "name='Bob' OR '1'='1'"
diff --git a/techniques/android/MASTG-TECH-XXXX.md b/techniques/android/MASTG-TECH-XXXX.md
index ac20d5c3dba..98c1bfd1dfb 100644
--- a/techniques/android/MASTG-TECH-XXXX.md
+++ b/techniques/android/MASTG-TECH-XXXX.md
@@ -49,8 +49,8 @@ $ adb shell content delete --uri content://org.owasp.mastestapp.provider/student
## Inputs To Validate
- URI path segments
- - Risk: values from `Uri.getPathSegments()` / `lastPathSegment` concatenated into SQL (for example, `appendWhere("id=" + id)`).
- - Safer: parse numeric IDs with `ContentUris.parseId(uri)`; strictly validate/whitelist path segments; never concatenate untrusted data into SQL.
+ - Risk: values from `Uri.getPathSegments()` / `lastPathSegment` concatenated into SQL (for example, `appendWhere("id=" + id)`).
+ - Safer: parse numeric IDs with `ContentUris.parseId(uri)`; strictly validate/whitelist path segments; never concatenate untrusted data into SQL.
## Injection Flaw Testing
From ff3d8c61714ae2f7e72063492a348748772fbda5 Mon Sep 17 00:00:00 2001
From: ScreaM <70141504+ScreaMy7@users.noreply.github.com>
Date: Mon, 26 Jan 2026 16:56:11 +0530
Subject: [PATCH 10/12] lint fix
---
best-practices/MASTG-BEST-XXXX.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/best-practices/MASTG-BEST-XXXX.md b/best-practices/MASTG-BEST-XXXX.md
index 09160d44374..2651c411654 100644
--- a/best-practices/MASTG-BEST-XXXX.md
+++ b/best-practices/MASTG-BEST-XXXX.md
@@ -20,4 +20,4 @@ For example:
- **Use Prepared Statements** : When performing insert, update, or delete operations, use SQLite prepared statements (for example, `SQLiteStatement` or `SQLiteDatabase` methods that support argument binding) instead of dynamically constructed SQL. Prepared statements ensure that untrusted input is bound as parameters and cannot alter the structure of the SQL query, effectively preventing SQL injection even when input originates from URIs or IPC calls.
-Refer to ["Protect against malicious input"](https://developer.android.com/guide/topics/providers/content-provider-basics#Injection) for more information.
\ No newline at end of file
+Refer to ["Protect against malicious input"](https://developer.android.com/guide/topics/providers/content-provider-basics#Injection) for more information.
From 9eecdbe923b9bd29f1433497533cb2cc55e8b1b0 Mon Sep 17 00:00:00 2001
From: ScreaM <70141504+ScreaMy7@users.noreply.github.com>
Date: Fri, 30 Jan 2026 01:42:38 +0530
Subject: [PATCH 11/12] spell fix
---
.../android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md b/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md
index cb3d035dceb..bdf4b9ffc21 100644
--- a/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md
+++ b/demos/android/MASVS-CODE/MASTG-DEMO-00XX/MASTG-DEMO-00XX.md
@@ -1,6 +1,6 @@
---
platform: android
-title: Injection flaws in android Content providers
+title: Injection flaws in Android Content providers
id: MASTG-DEMO-00XX
code: [kotlin]
test: MASTG-TEST-02XX
@@ -9,9 +9,9 @@ status: new
### Sample
-The following code implements a vulnerable `ContentProvider` that appends user-controlled input from the URI path directly into a SQL.
+The following code implements a vulnerable `ContentProvider` that appends user-controlled input from the URI path directly into a SQL query.
-{{ MastgTest.kt # MastgTest_reversed.kt }}
+{{ MastgTest.kt # MastgTest_reversed.java }}
### Steps
@@ -38,4 +38,4 @@ Row: 1 id=2, name=Bob
Row: 2 id=3, name=Charlie
```
-Refer to @MASTG-TECH-XXX, to know more on using content query.
+Refer to @MASTG-TECH-XXXX, to know more on using content query.
From f078291f027023398a943652e968b1ff94428de2 Mon Sep 17 00:00:00 2001
From: Carlos Holguera
Date: Tue, 3 Feb 2026 12:54:18 +0100
Subject: [PATCH 12/12] Fix lint issue
---
best-practices/MASTG-BEST-XXXX.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/best-practices/MASTG-BEST-XXXX.md b/best-practices/MASTG-BEST-XXXX.md
index 2651c411654..3e81885d8dc 100644
--- a/best-practices/MASTG-BEST-XXXX.md
+++ b/best-practices/MASTG-BEST-XXXX.md
@@ -18,6 +18,6 @@ For example:
val cursor = qb.query(db, projection, selection, selectionArgs, null, null, sortOrder)
```
-- **Use Prepared Statements** : When performing insert, update, or delete operations, use SQLite prepared statements (for example, `SQLiteStatement` or `SQLiteDatabase` methods that support argument binding) instead of dynamically constructed SQL. Prepared statements ensure that untrusted input is bound as parameters and cannot alter the structure of the SQL query, effectively preventing SQL injection even when input originates from URIs or IPC calls.
+- **Use Prepared Statements**: When performing insert, update, or delete operations, use SQLite prepared statements (for example, `SQLiteStatement` or `SQLiteDatabase` methods that support argument binding) instead of dynamically constructed SQL. Prepared statements ensure that untrusted input is bound as parameters and cannot alter the structure of the SQL query, effectively preventing SQL injection even when input originates from URIs or IPC calls.
Refer to ["Protect against malicious input"](https://developer.android.com/guide/topics/providers/content-provider-basics#Injection) for more information.