From 265994042efb883fa3d485446eed12e3b436d837 Mon Sep 17 00:00:00 2001 From: XuqmGroup Date: Tue, 23 Jun 2026 11:27:21 +0800 Subject: [PATCH] fix(sdk-webview): reject file picker result when MIME type doesn't match accepted types Some devices show .doc files in the picker even when only .docx was requested (accept=".docx"). Passing the .doc URI to the WebView renderer could cause a native crash. Now we check the actual MIME type of the selected file against the accepted list and cancel the upload if it does not match, preventing the renderer from receiving an unexpected file type. Co-Authored-By: Claude Sonnet 4.6 --- .../java/com/xuqm/sdk/webview/XWebViewView.kt | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/sdk-webview/src/main/java/com/xuqm/sdk/webview/XWebViewView.kt b/sdk-webview/src/main/java/com/xuqm/sdk/webview/XWebViewView.kt index ac98b89..d22be73 100644 --- a/sdk-webview/src/main/java/com/xuqm/sdk/webview/XWebViewView.kt +++ b/sdk-webview/src/main/java/com/xuqm/sdk/webview/XWebViewView.kt @@ -360,6 +360,7 @@ fun XWebViewView( // camera capture val pendingFileCallback = remember { mutableStateOf>?>(null) } val pendingCameraUri = remember { mutableStateOf(null) } + val pendingAcceptMimes = remember { mutableStateOf?>(null) } val takePictureLauncher = rememberLauncherForActivityResult( ActivityResultContracts.TakePicture() @@ -374,7 +375,28 @@ fun XWebViewView( val pickContentLauncher = rememberLauncherForActivityResult(GetContentWithMimeTypes()) { uri -> val cb = pendingFileCallback.value pendingFileCallback.value = null - cb?.onReceiveValue(if (uri != null) arrayOf(uri) else null) + if (uri == null) { + cb?.onReceiveValue(null) + return@rememberLauncherForActivityResult + } + // Validate that the selected file's actual MIME type is within the accepted list. + // Some devices show extra file types in the picker (e.g. .doc when only .docx was + // requested). Passing an unsupported type to the WebView renderer can cause a native + // crash. We accept the file only when its MIME type matches, or fall back to passing + // it through when we can't determine the type (to avoid blocking legitimate uploads). + val acceptedMimes = pendingAcceptMimes.value + val actualMime = uri.let { context.contentResolver.getType(it) } + val allowed = acceptedMimes.isNullOrEmpty() || + acceptedMimes.any { accepted -> + accepted == "*/*" || + (actualMime != null && (actualMime == accepted || actualMime.startsWith(accepted.substringBefore("*")))) + } + if (!allowed) { + android.util.Log.w("XWV", "Rejected file: actualMime=$actualMime not in accepted=$acceptedMimes") + cb?.onReceiveValue(null) + } else { + cb?.onReceiveValue(arrayOf(uri)) + } } val shouldLaunchCamera = remember { mutableStateOf(false) } @@ -605,6 +627,7 @@ fun XWebViewView( pendingFileCallback.value = filePathCallback val acceptMimes = resolvePickerMimeTypes(fileChooserParams.acceptTypes) + pendingAcceptMimes.value = acceptMimes val isCameraCapture = fileChooserParams.isCaptureEnabled && fileChooserParams.acceptTypes.any { it.contains("image") } val isImageOnly = acceptMimes.all { it.startsWith("image/") || it == "image/*" }