From 6b6398600392d0aae21f0cd4fdfddc5d0598c031 Mon Sep 17 00:00:00 2001 From: Pieter De Baets Date: Mon, 24 Aug 2026 08:37:33 -0700 Subject: [PATCH] Remove FileIoHandler Summary: This was added ages to serve an internal use-case and is a major security risk if left unguarded in production (D4650999 / S670581). It was never ported to iOS and a platform inconsistency. Changelog: [Android][Changed] Removed FileIoHandler packager message handlers (Not breaking since this is a devtooling-related API change: https://reactnative.dev/releases/versioning-policy#what-is-a-breaking-change) Reviewed By: christophpurrer Differential Revision: D117176703 --- .../ReactAndroid/api/ReactAndroid.api | 6 - .../react/devsupport/DevServerHelper.kt | 2 - .../react/packagerconnection/FileIoHandler.kt | 163 ------------------ 3 files changed, 171 deletions(-) delete mode 100644 packages/react-native/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/FileIoHandler.kt diff --git a/packages/react-native/ReactAndroid/api/ReactAndroid.api b/packages/react-native/ReactAndroid/api/ReactAndroid.api index a78d8fbd869e..f4386d1bd1c7 100644 --- a/packages/react-native/ReactAndroid/api/ReactAndroid.api +++ b/packages/react-native/ReactAndroid/api/ReactAndroid.api @@ -2960,12 +2960,6 @@ public abstract interface class com/facebook/react/modules/websocket/WebSocketMo public abstract fun onMessage (Lokio/ByteString;Lcom/facebook/react/bridge/WritableMap;)V } -public final class com/facebook/react/packagerconnection/FileIoHandler : java/lang/Runnable { - public fun ()V - public final fun handlers ()Ljava/util/Map; - public fun run ()V -} - public final class com/facebook/react/packagerconnection/JSPackagerClient : com/facebook/react/packagerconnection/ReconnectingWebSocket$MessageCallback { public fun (Ljava/lang/String;Lcom/facebook/react/packagerconnection/PackagerConnectionSettings;Ljava/util/Map;)V public fun (Ljava/lang/String;Lcom/facebook/react/packagerconnection/PackagerConnectionSettings;Ljava/util/Map;Lcom/facebook/react/packagerconnection/ReconnectingWebSocket$ConnectionCallback;)V diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.kt index 67ce3a2331f0..81561bc56c68 100644 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.kt +++ b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/devsupport/DevServerHelper.kt @@ -27,7 +27,6 @@ import com.facebook.react.devsupport.interfaces.PackagerStatusCallback import com.facebook.react.modules.debug.interfaces.DeveloperSettings import com.facebook.react.modules.systeminfo.AndroidInfoHelpers.getFriendlyDeviceName import com.facebook.react.modules.systeminfo.AndroidInfoHelpers.getInspectorHostMetadata -import com.facebook.react.packagerconnection.FileIoHandler import com.facebook.react.packagerconnection.JSPackagerClient import com.facebook.react.packagerconnection.NotificationOnlyHandler import com.facebook.react.packagerconnection.PackagerConnectionSettings @@ -160,7 +159,6 @@ public open class DevServerHelper( } } commandListener.customCommandHandlers()?.let { handlers.putAll(it) } - handlers.putAll(FileIoHandler().handlers()) val onPackagerConnectedCallback: ReconnectingWebSocket.ConnectionCallback = object : ReconnectingWebSocket.ConnectionCallback { diff --git a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/FileIoHandler.kt b/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/FileIoHandler.kt deleted file mode 100644 index cd608539e7b4..000000000000 --- a/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/packagerconnection/FileIoHandler.kt +++ /dev/null @@ -1,163 +0,0 @@ -/* - * Copyright (c) Meta Platforms, Inc. and affiliates. - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. - */ - -package com.facebook.react.packagerconnection - -import android.os.Handler -import android.os.Looper -import android.util.Base64 -import com.facebook.common.logging.FLog -import java.io.FileInputStream -import java.io.FileNotFoundException -import java.io.IOException -import org.json.JSONObject - -public class FileIoHandler : Runnable { - - private class TtlFileInputStream(path: String?) { - private val stream = FileInputStream(path) - private var ttl: Long = System.currentTimeMillis() + FILE_TTL - - private fun extendTtl() { - ttl = System.currentTimeMillis() + FILE_TTL - } - - fun expiredTtl(): Boolean = System.currentTimeMillis() >= ttl - - @Throws(IOException::class) - fun read(size: Int): String { - extendTtl() - val buffer = ByteArray(size) - val bytesRead = stream.read(buffer) - return Base64.encodeToString(buffer, 0, bytesRead, Base64.DEFAULT) - } - - @Throws(IOException::class) - fun close() { - stream.close() - } - } - - private var nextHandle = 1 - private val handler = Handler(Looper.getMainLooper()) - private val openFiles: MutableMap = mutableMapOf() - private val requestHandlers: MutableMap = mutableMapOf() - - init { - requestHandlers["fopen"] = - object : RequestOnlyHandler() { - override fun onRequest(params: Any?, responder: Responder) { - synchronized(openFiles) { - try { - val paramsObj = - params as JSONObject? - ?: throw Exception( - "params must be an object { mode: string, filename: string }", - ) - val mode = paramsObj.optString("mode") ?: throw Exception("missing params.mode") - val filename = - paramsObj.optString("filename") ?: throw Exception("missing params.filename") - require(mode == "r") { "unsupported mode: $mode" } - - responder.respond(addOpenFile(filename)) - } catch (e: Exception) { - responder.error(e.toString()) - } - } - } - } - requestHandlers["fclose"] = - object : RequestOnlyHandler() { - override fun onRequest(params: Any?, responder: Responder) { - synchronized(openFiles) { - try { - if (params !is Number) { - throw Exception("params must be a file handle") - } - val stream = - openFiles[params] - ?: throw Exception("invalid file handle, it might have timed out") - - openFiles.remove(params) - stream.close() - responder.respond("") - } catch (e: Exception) { - responder.error(e.toString()) - } - } - } - } - requestHandlers["fread"] = - object : RequestOnlyHandler() { - override fun onRequest(params: Any?, responder: Responder) { - synchronized(openFiles) { - try { - val paramsObj = - params as JSONObject? - ?: throw Exception( - "params must be an object { file: handle, size: number }", - ) - val file = paramsObj.optInt("file") - if (file == 0) { - throw Exception("invalid or missing file handle") - } - val size = paramsObj.optInt("size") - if (size == 0) { - throw Exception("invalid or missing read size") - } - val stream = - openFiles[file] - ?: throw Exception("invalid file handle, it might have timed out") - - responder.respond(stream.read(size)) - } catch (e: Exception) { - responder.error(e.toString()) - } - } - } - } - } - - public fun handlers(): Map = requestHandlers - - @Throws(FileNotFoundException::class) - private fun addOpenFile(filename: String): Int { - val handle = nextHandle++ - openFiles[handle] = TtlFileInputStream(filename) - if (openFiles.size == 1) { - handler.postDelayed(this@FileIoHandler, FILE_TTL) - } - return handle - } - - override fun run() { - // clean up files that are past their expiry date - synchronized(openFiles) { - openFiles.entries.removeAll { (_, stream) -> - if (stream.expiredTtl()) { - try { - stream.close() - } catch (e: IOException) { - FLog.e(TAG, "Failed to close expired file", e) - } - true - } else { - false - } - } - - if (openFiles.isNotEmpty()) { - handler.postDelayed(this, FILE_TTL) - } - } - } - - private companion object { - private val TAG: String = JSPackagerClient::class.java.simpleName - private const val FILE_TTL = 30_000L - } -}