diff --git a/cache/src/main/java/dev/openrs2/cache/Js5Compression.kt b/cache/src/main/java/dev/openrs2/cache/Js5Compression.kt index 3b6d40cf..3a13c346 100644 --- a/cache/src/main/java/dev/openrs2/cache/Js5Compression.kt +++ b/cache/src/main/java/dev/openrs2/cache/Js5Compression.kt @@ -7,7 +7,7 @@ import dev.openrs2.crypto.xteaEncrypt import io.netty.buffer.ByteBuf import io.netty.buffer.ByteBufInputStream import io.netty.buffer.ByteBufOutputStream -import java.io.EOFException +import java.io.IOException public object Js5Compression { public fun compress(input: ByteBuf, type: Js5CompressionType, key: XteaKey = XteaKey.ZERO): ByteBuf { @@ -102,16 +102,18 @@ public object Js5Compression { public fun uncompress(input: ByteBuf, key: XteaKey = XteaKey.ZERO): ByteBuf { val typeId = input.readUnsignedByte().toInt() val type = Js5CompressionType.fromOrdinal(typeId) - require(type != null) { - "Invalid compression type: $typeId" - } + ?: throw IOException("Invalid compression type: $typeId") val len = input.readInt() - require(len >= 0) { - "Length is negative: $len" + if (len < 0) { + throw IOException("Length is negative: $len") } if (type == Js5CompressionType.NONE) { + if (input.readableBytes() < len) { + throw IOException("Data truncated") + } + input.readBytes(len).use { output -> if (!key.isZero) { output.xteaDecrypt(0, len, key) @@ -120,10 +122,15 @@ public object Js5Compression { } } - decrypt(input, len + 4, key).use { plaintext -> + val lenWithUncompressedLen = len + 4 + if (input.readableBytes() < lenWithUncompressedLen) { + throw IOException("Compressed data truncated") + } + + decrypt(input, lenWithUncompressedLen, key).use { plaintext -> val uncompressedLen = plaintext.readInt() - require(uncompressedLen >= 0) { - "Uncompressed length is negative: $uncompressedLen" + if (uncompressedLen < 0) { + throw IOException("Uncompressed length is negative: $uncompressedLen") } plaintext.alloc().buffer(uncompressedLen, uncompressedLen).use { output -> @@ -132,7 +139,7 @@ public object Js5Compression { while (remaining > 0) { val n = output.writeBytes(inputStream, remaining) if (n == -1) { - throw EOFException() + throw IOException("Uncompressed data truncated") } remaining -= n } diff --git a/cache/src/test/java/dev/openrs2/cache/Js5CompressionTest.kt b/cache/src/test/java/dev/openrs2/cache/Js5CompressionTest.kt index eb86f320..b3954434 100644 --- a/cache/src/test/java/dev/openrs2/cache/Js5CompressionTest.kt +++ b/cache/src/test/java/dev/openrs2/cache/Js5CompressionTest.kt @@ -5,7 +5,7 @@ import dev.openrs2.crypto.XteaKey import io.netty.buffer.ByteBuf import io.netty.buffer.Unpooled import org.junit.jupiter.api.assertThrows -import java.io.EOFException +import java.io.IOException import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertNotEquals @@ -260,7 +260,7 @@ object Js5CompressionTest { @Test fun testInvalidType() { read("invalid-type.dat").use { compressed -> - assertThrows { + assertThrows { Js5Compression.uncompress(compressed).release() } } @@ -269,7 +269,7 @@ object Js5CompressionTest { @Test fun testInvalidLength() { read("invalid-length.dat").use { compressed -> - assertThrows { + assertThrows { Js5Compression.uncompress(compressed).release() } } @@ -278,7 +278,7 @@ object Js5CompressionTest { @Test fun testInvalidUncompressedLength() { read("invalid-uncompressed-length.dat").use { compressed -> - assertThrows { + assertThrows { Js5Compression.uncompress(compressed).release() } } @@ -287,7 +287,7 @@ object Js5CompressionTest { @Test fun testNoneEof() { read("none-eof.dat").use { compressed -> - assertThrows { + assertThrows { Js5Compression.uncompress(compressed).release() } } @@ -296,7 +296,7 @@ object Js5CompressionTest { @Test fun testBzip2Eof() { read("bzip2-eof.dat").use { compressed -> - assertThrows { + assertThrows { Js5Compression.uncompress(compressed).release() } } @@ -305,7 +305,7 @@ object Js5CompressionTest { @Test fun testGzipEof() { read("gzip-eof.dat").use { compressed -> - assertThrows { + assertThrows { Js5Compression.uncompress(compressed).release() } } @@ -314,7 +314,7 @@ object Js5CompressionTest { @Test fun testLzmaEof() { read("lzma-eof.dat").use { compressed -> - assertThrows { + assertThrows { Js5Compression.uncompress(compressed).release() } }