Preserve ambiguous raw deflate compatibility

This commit is contained in:
a1denvalu3 2026-07-25 08:26:28 +02:00
parent 0d04653bb8
commit 6e4bb8daca
2 changed files with 21 additions and 4 deletions

View File

@ -93,12 +93,17 @@ object CompressionUtil {
return synchronized(decompressionLock) {
if (looksLikeZlib(compressedData)) {
// A structurally valid zlib header selects the legacy wrapped format. Do not
// retry size/completion failures as raw deflate: that only doubles attacker work.
try {
// A raw stream can coincidentally begin with a valid-looking zlib header. The
// header therefore only determines which format to try first; any non-exact zlib
// result must still fall back to raw under the same size/completion bounds.
val zlibResult = try {
inflateExact(compressedData, originalSize, nowrap = false)
} catch (zlibException: DataFormatException) {
// A raw stream can coincidentally begin with a valid-looking zlib header.
null
}
if (zlibResult != null) {
zlibResult
} else {
try {
inflateExact(compressedData, originalSize, nowrap = true)
} catch (rawException: DataFormatException) {

View File

@ -1139,6 +1139,18 @@ class BinaryProtocolTest {
assertArrayEquals(payload, decoded!!.payload)
}
@Test
fun `raw deflate with zlib-looking prefix falls back after non-exact zlib parse`() {
val payload = ByteArray(29) { index -> (index + 1).toByte() }
val compressed = byteArrayOf(
0x08, // non-final raw stored block; also zlib CMF
0x1d, 0x00, // LEN = 29; 0x08 0x1d passes the RFC 1950 header check
0xe2.toByte(), 0xff.toByte() // one's complement of LEN
) + payload + byteArrayOf(0x03, 0x00) // final empty fixed-Huffman block
assertArrayEquals(payload, CompressionUtil.decompress(compressed, payload.size))
}
@Test
fun `under-declared zlib expansion is rejected by fallback`() {
val payload = ByteArray(4_096) { 0x51 }