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) 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) 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.