From 1a3a58b8f0bbcde71d05e3d2c636770b7963e6e8 Mon Sep 17 00:00:00 2001 From: cottongin Date: Tue, 10 Mar 2026 01:22:31 -0400 Subject: [PATCH] fix: add metaint validation and mid-metadata truncation test Made-with: Cursor --- .../xyz/cottongin/radio247/audio/IcyParser.kt | 1 + .../cottongin/radio247/audio/IcyParserTest.kt | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/app/src/main/java/xyz/cottongin/radio247/audio/IcyParser.kt b/app/src/main/java/xyz/cottongin/radio247/audio/IcyParser.kt index 573af05..c9e4427 100644 --- a/app/src/main/java/xyz/cottongin/radio247/audio/IcyParser.kt +++ b/app/src/main/java/xyz/cottongin/radio247/audio/IcyParser.kt @@ -27,6 +27,7 @@ class IcyParser( } private fun parseWithMetadata(metaint: Int) { + require(metaint > 0) { "metaint must be positive, got $metaint" } val audioBuf = ByteArray(metaint) while (true) { val audioRead = readFully(audioBuf, 0, metaint) diff --git a/app/src/test/java/xyz/cottongin/radio247/audio/IcyParserTest.kt b/app/src/test/java/xyz/cottongin/radio247/audio/IcyParserTest.kt index a4fb7cb..905959d 100644 --- a/app/src/test/java/xyz/cottongin/radio247/audio/IcyParserTest.kt +++ b/app/src/test/java/xyz/cottongin/radio247/audio/IcyParserTest.kt @@ -267,4 +267,28 @@ class IcyParserTest { assertTrue(audioCollected.all { it == 0x42.toByte() }) assertEquals(0, metadataEvents.size) } + + @Test + fun handlesTruncationMidMetadata_noCrash_noIncompleteMetadataEmitted() { + val metaint = 8 + val audioChunk = ByteArray(8) { 0x42 } + val fullStream = buildIcyStream(metaint, IcyBlock(audioChunk, "StreamTitle='Test';")) + // Full audio (8) + length byte (2 = 32 bytes) + only 16 bytes of metadata (truncated) + val truncated = + fullStream.copyOf(8 + 1 + 16) + + val audioCollected = mutableListOf() + val metadataEvents = mutableListOf() + + IcyParser( + input = ByteArrayInputStream(truncated), + metaint = metaint, + onAudioData = { buf, off, len -> (0 until len).forEach { audioCollected.add(buf[off + it]) } }, + onMetadata = { metadataEvents.add(it) } + ).readAll() + + assertEquals(8, audioCollected.size) + assertTrue(audioCollected.all { it == 0x42.toByte() }) + assertEquals(0, metadataEvents.size) + } }