diff options
| author | Lena <lena@omega> | 2026-08-01 00:00:00 +0000 |
|---|---|---|
| committer | Lena <lena@omega> | 2026-08-01 00:00:00 +0000 |
| commit | 852a8a00273c9128efeed0217a8e5d3fcd8bf780 (patch) | |
| tree | c72f959731fa3c7c809cf1a0222363b6c9c9b03b /app/src/main/java/invalid/lena/scrcpy/Devices.java | |
| parent | f379b93d52bf0dbd3816ec3f64d165314771d2a6 (diff) | |
| download | scrcpy-android-852a8a00273c9128efeed0217a8e5d3fcd8bf780.tar.gz | |
app: harden mirroring lifecycle and state
Diffstat (limited to 'app/src/main/java/invalid/lena/scrcpy/Devices.java')
| -rw-r--r-- | app/src/main/java/invalid/lena/scrcpy/Devices.java | 86 |
1 files changed, 74 insertions, 12 deletions
diff --git a/app/src/main/java/invalid/lena/scrcpy/Devices.java b/app/src/main/java/invalid/lena/scrcpy/Devices.java index 0ba61a3..9ec308c 100644 --- a/app/src/main/java/invalid/lena/scrcpy/Devices.java +++ b/app/src/main/java/invalid/lena/scrcpy/Devices.java @@ -5,10 +5,12 @@ import android.content.Context; import org.json.JSONArray; import org.json.JSONObject; +import java.io.ByteArrayOutputStream; import java.io.File; +import java.io.FileInputStream; import java.io.IOException; +import java.io.InputStream; import java.nio.charset.StandardCharsets; -import java.nio.file.Files; import java.util.ArrayList; import java.util.List; @@ -20,6 +22,8 @@ import java.util.List; public final class Devices { private static final String FILE = "devices.json"; + private static final long MAX_FILE_BYTES = 1024 * 1024; + private static final int MAX_HOST_CHARS = 255; public static final class Device { public final String host; @@ -74,7 +78,7 @@ public final class Devices { port = s.substring(colon + 1); } int p = parsePort(port); - if (host.isEmpty() || p < 0) return null; + if (!validHost(host) || p < 0) return null; return new Device(host, p); } @@ -99,13 +103,29 @@ public final class Devices { json = json.trim(); if (json.isEmpty()) return out; JSONArray arr; - try { arr = new JSONArray(json); } - catch (Exception e) { Log.e(e, "devices: not a json array"); return out; } + try { + arr = new JSONArray(json); + } catch (Exception e) { + throw new IllegalArgumentException("devices: expected a JSON array", e); + } for (int i = 0; i < arr.length(); i++) { try { JSONObject o = arr.getJSONObject(i); - out.add(new Device(o.getString("host"), o.getInt("port"))); + String host = o.getString("host"); + Object portValue = o.get("port"); + if (!(portValue instanceof Integer) && !(portValue instanceof Long)) { + throw new IllegalArgumentException("port is not an integer"); + } + long portLong = ((Number) portValue).longValue(); + if (portLong < 1 || portLong > 65535) { + throw new IllegalArgumentException("port is invalid"); + } + int port = (int) portLong; + if (!validHost(host) || port < 1 || port > 65535) { + throw new IllegalArgumentException("invalid device fields"); + } + out.add(new Device(host, port)); } catch (Exception e) { Log.w("devices: skipping malformed row %d: %s", i, e); } @@ -117,6 +137,9 @@ public final class Devices { try { JSONArray arr = new JSONArray(); for (Device d : devices) { + if (!validHost(d.host) || d.port < 1 || d.port > 65535) { + throw new IllegalArgumentException("invalid saved device"); + } JSONObject o = new JSONObject(); o.put("host", d.host); o.put("port", d.port); @@ -124,23 +147,35 @@ public final class Devices { } return arr.toString(2); } catch (Exception e) { - Log.e(e, "devices: serialize failed"); - return "[]"; + throw new IllegalStateException("devices: serialize failed", e); } } - public static List<Device> load(Context ctx) { + public static List<Device> load(Context ctx) throws IOException { File f = new File(ctx.getFilesDir(), FILE); if (!f.exists()) return new ArrayList<>(); try { - return parse(new String(Files.readAllBytes(f.toPath()), StandardCharsets.UTF_8)); + if (f.length() > MAX_FILE_BYTES) { + throw new IOException("device list is too large: " + f.length()); + } + byte[] data = readLimited(f); + if (data.length == 0) throw new IOException("device list is empty"); + return parse(Wire.decodeUtf8(data)); } catch (Exception e) { - Log.e(e, "devices: load failed"); - return new ArrayList<>(); + if (e instanceof IOException) throw (IOException) e; + throw new IOException("devices: load failed", e); } } - public static void save(Context ctx, List<Device> devices) throws IOException { + // The saved row for an address, or null if there is none. + public static Device find(Context ctx, String host, int port) throws IOException { + for (Device d : load(ctx)) { + if (d.port == port && d.host.equals(host)) return d; + } + return null; + } + + private static void save(Context ctx, List<Device> devices) throws IOException { File f = new File(ctx.getFilesDir(), FILE); AtomicFiles.write(f, serialize(devices).getBytes(StandardCharsets.UTF_8)); } @@ -162,4 +197,31 @@ public final class Devices { save(ctx, list); return list; } + + private static boolean validHost(String host) { + if (host == null || host.isEmpty() || host.length() > MAX_HOST_CHARS) return false; + for (int i = 0; i < host.length(); i++) { + char c = host.charAt(i); + if (Character.isWhitespace(c) || Character.isISOControl(c) || c == '[' || c == ']') { + return false; + } + } + return true; + } + + private static byte[] readLimited(File file) throws IOException { + try (InputStream in = new FileInputStream(file); + ByteArrayOutputStream out = new ByteArrayOutputStream()) { + byte[] buf = new byte[8192]; + long total = 0; + for (;;) { + int n = in.read(buf); + if (n < 0) return out.toByteArray(); + if (n == 0) throw new IOException("device list read made no progress"); + total += n; + if (total > MAX_FILE_BYTES) throw new IOException("device list is too large"); + out.write(buf, 0, n); + } + } + } } |