[Android] Remote access: fix security drift on video-list and download routes

Nicolas Pomepuy git at videolan.org
Thu Sep 10 04:41:21 UTC 2026


vlc-android | branch: master | Nicolas Pomepuy <nicolas at videolabs.io> | Wed Sep  9 15:00:03 2026 +0200| [8e84a658d67fc9a9f79f440183bc6495b67c06a5] | committer: Nicolas Pomepuy

Remote access: fix security drift on video-list and download routes

> https://code.videolan.org/videolan/vlc-android/commit/8e84a658d67fc9a9f79f440183bc6495b67c06a5
---

 .../routing/RemoteAccessRoutingFile.kt               |  1 +
 .../routing/RemoteAccessRoutingUtils.kt              | 20 ++++++++++++++++++++
 .../routing/RemoteAccessRoutingVideo.kt              |  5 +----
 3 files changed, 22 insertions(+), 4 deletions(-)

diff --git a/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingFile.kt b/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingFile.kt
index 89cd88e8c5..b72fd3ab65 100644
--- a/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingFile.kt
+++ b/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingFile.kt
@@ -466,6 +466,7 @@ fun Route.authenticatedFileRouting(appContext: Context, scope: CoroutineScope, s
     }
     // Download a file previously prepared
     get("/download") {
+        if (!checkPermission(settings) { settings.getBoolean(REMOTE_ACCESS_FILE_BROWSER_CONTENT, false) }) return at get
         val requested = call.request.queryParameters["file"] ?: run {
             call.respond(HttpStatusCode.BadRequest, "Missing file parameter")
             return at get
diff --git a/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingUtils.kt b/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingUtils.kt
index fd009d2a00..c3f8089483 100644
--- a/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingUtils.kt
+++ b/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingUtils.kt
@@ -25,6 +25,7 @@
 package org.videolan.vlc.remoteaccessserver.routing
 
 import android.content.Context
+import android.content.SharedPreferences
 import android.text.format.Formatter
 import android.util.Log
 import androidx.core.net.toUri
@@ -34,7 +35,9 @@ import io.ktor.http.HttpStatusCode
 import io.ktor.http.content.OutgoingContent
 import io.ktor.http.content.TextContent
 import io.ktor.server.application.ApplicationCall
+import io.ktor.server.application.call
 import io.ktor.server.response.respond
+import io.ktor.util.pipeline.PipelineContext
 import kotlinx.coroutines.*
 import org.videolan.medialibrary.interfaces.media.MediaWrapper
 import org.videolan.medialibrary.media.MediaLibraryItem
@@ -46,6 +49,7 @@ import org.videolan.resources.util.observeLiveDataUntil
 import org.videolan.tools.livedata.LiveDataset
 import org.videolan.vlc.remoteaccessserver.RemoteAccessOTP
 import org.videolan.vlc.remoteaccessserver.RemoteAccessServer
+import org.videolan.vlc.remoteaccessserver.RemoteAccessSession.verifyLogin
 import org.videolan.vlc.remoteaccessserver.websockets.RemoteAccessWebSockets
 import org.videolan.vlc.util.*
 import java.io.File
@@ -264,3 +268,19 @@ internal fun File.isSafelyWithin(parentDir: File): Boolean {
     val canonicalChild = this.canonicalFile.path
     return canonicalChild == canonicalParent || canonicalChild.startsWith(canonicalParent + File.separator)
 }
+
+/**
+ * Verifies session authentication and evaluates a permission predicate.
+ * Responds with HTTP 403 Forbidden and returns false if permission check fails.
+ */
+internal suspend inline fun PipelineContext<Unit, ApplicationCall>.checkPermission(
+    settings: SharedPreferences,
+    predicate: () -> Boolean
+): Boolean {
+    verifyLogin(settings)
+    if (!predicate()) {
+        call.respond(HttpStatusCode.Forbidden)
+        return false
+    }
+    return true
+}
diff --git a/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingVideo.kt b/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingVideo.kt
index f727b94e88..864016ebc4 100644
--- a/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingVideo.kt
+++ b/application/remote-access-server/src/main/java/org/videolan/vlc/remoteaccessserver/routing/RemoteAccessRoutingVideo.kt
@@ -43,10 +43,7 @@ import org.videolan.vlc.remoteaccessserver.utils.serveVideos
 fun Route.videoRouting(appContext: Context, settings: SharedPreferences) {
     // List of all the videos
     get("/video-list") {
-        if (!settings.serveVideos(appContext)) {
-            call.respond(HttpStatusCode.Forbidden)
-            return at get
-        }
+        if (!checkPermission(settings) { settings.serveVideos(appContext) }) return at get
         val grouping = call.request.queryParameters["grouping"]?.toInt() ?: 0
         val groupId = call.request.queryParameters["group"]?.toLong() ?: 0L
         val folderId = call.request.queryParameters["folder"]?.toLong() ?: 0L



More information about the Android mailing list