From cfba13b287b8944047aab9c84446d0ec4ee54ca8 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Thu, 18 Jun 2026 09:25:06 +0000 Subject: [PATCH] =?UTF-8?q?=F0=9F=9B=A1=EF=B8=8F=20Sentinel:=20[CRITICAL]?= =?UTF-8?q?=20Fix=20insecure=20deserialization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Replace ObjectInputStream with ValidatingObjectInputStream in Utils.java. * Restrict allowed classes during deserialization. * Document findings in .jules/sentinel.md. Co-authored-by: tomdesair <14034630+tomdesair@users.noreply.github.com> --- .jules/sentinel.md | 4 ++++ src/main/java/me/desair/tus/server/util/Utils.java | 10 ++++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..b96033f --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2024-05-24 - [Insecure Deserialization Fix] +**Vulnerability:** The `Utils.readSerializable` function deserialized objects directly from `ObjectInputStream`, which can lead to insecure deserialization attacks if the stored data is tampered with. +**Learning:** `commons-io` provides `ValidatingObjectInputStream` to limit deserialization to specific allowed classes (`java.lang.*`, `java.util.*`, `me.desair.tus.server.upload.*`, `me.desair.tus.server.checksum.*`). +**Prevention:** Always use `ValidatingObjectInputStream` or equivalent mechanisms to restrict class loading when deserializing data, even if it comes from local storage, to prevent arbitrary code execution in case the storage layer is compromised. diff --git a/src/main/java/me/desair/tus/server/util/Utils.java b/src/main/java/me/desair/tus/server/util/Utils.java index e72af32..28e08ca 100644 --- a/src/main/java/me/desair/tus/server/util/Utils.java +++ b/src/main/java/me/desair/tus/server/util/Utils.java @@ -8,7 +8,6 @@ import jakarta.servlet.http.HttpServletRequest; import java.io.BufferedOutputStream; import java.io.IOException; -import java.io.ObjectInputStream; import java.io.ObjectOutput; import java.io.ObjectOutputStream; import java.io.OutputStream; @@ -22,6 +21,7 @@ import java.util.regex.Pattern; import me.desair.tus.server.HttpHeader; import me.desair.tus.server.checksum.ChecksumAlgorithm; +import org.apache.commons.io.serialization.ValidatingObjectInputStream; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -83,7 +83,13 @@ public static T readSerializable(Path path, Class clazz) throws IOExcepti // Lock will be released when the channel is closed if (lockFileShared(channel) != null) { - try (ObjectInputStream ois = new ObjectInputStream(Channels.newInputStream(channel))) { + try (ValidatingObjectInputStream ois = + new ValidatingObjectInputStream(Channels.newInputStream(channel))) { + ois.accept(clazz); + ois.accept("java.lang.*"); + ois.accept("java.util.*"); + ois.accept("me.desair.tus.server.upload.*"); + ois.accept("me.desair.tus.server.checksum.*"); info = clazz.cast(ois.readObject()); } catch (ClassNotFoundException | java.io.EOFException