Skip to content

Commit d8ddd99

Browse files
committed
fixup! Catch duplicate keys in class deserialization (including unknown)
1 parent 47cfbc1 commit d8ddd99

File tree

2 files changed

+41
-14
lines changed

2 files changed

+41
-14
lines changed

Diff for: formats/cbor/commonMain/src/kotlinx/serialization/cbor/internal/Encoding.kt

+16-14
Original file line numberDiff line numberDiff line change
@@ -197,21 +197,8 @@ internal class CborEncoder(private val output: ByteArrayOutput) {
197197
}
198198
}
199199

200-
private class CborMapReader(val cbor: Cbor, decoder: CborDecoder) : CborListReader(cbor, decoder) {
201-
/**
202-
* Keys that have been seen so far while reading this map.
203-
*
204-
* Only used if [Cbor.forbidDuplicateKeys] is in effect.
205-
*/
206-
private val seenKeys = mutableSetOf<Any?>()
207-
200+
private class CborMapReader(cbor: Cbor, decoder: CborDecoder) : CborListReader(cbor, decoder) {
208201
override fun skipBeginToken() = setSize(decoder.startMap() * 2)
209-
210-
override fun visitKey(key: Any?) {
211-
if (cbor.forbidDuplicateKeys) {
212-
seenKeys.add(key) || throw DuplicateKeyException(key)
213-
}
214-
}
215202
}
216203

217204
private open class CborListReader(cbor: Cbor, decoder: CborDecoder) : CborReader(cbor, decoder) {
@@ -232,6 +219,13 @@ internal open class CborReader(private val cbor: Cbor, protected val decoder: Cb
232219

233220
private var decodeByteArrayAsByteString = false
234221

222+
/**
223+
* Keys that have been seen so far while reading this map.
224+
*
225+
* Only used if [Cbor.forbidDuplicateKeys] is in effect.
226+
*/
227+
private val seenKeys = mutableSetOf<Any?>()
228+
235229
protected fun setSize(size: Int) {
236230
if (size >= 0) {
237231
finiteMode = true
@@ -259,12 +253,19 @@ internal open class CborReader(private val cbor: Cbor, protected val decoder: Cb
259253
if (!finiteMode) decoder.end()
260254
}
261255

256+
override fun visitKey(key: Any?) {
257+
if (cbor.forbidDuplicateKeys) {
258+
seenKeys.add(key) || throw DuplicateKeyException(key)
259+
}
260+
}
261+
262262
override fun decodeElementIndex(descriptor: SerialDescriptor): Int {
263263
val index = if (cbor.ignoreUnknownKeys) {
264264
val knownIndex: Int
265265
while (true) {
266266
if (isDone()) return CompositeDecoder.DECODE_DONE
267267
val elemName = decoder.nextString()
268+
visitKey(elemName)
268269
readProperties++
269270

270271
val index = descriptor.getElementIndex(elemName)
@@ -279,6 +280,7 @@ internal open class CborReader(private val cbor: Cbor, protected val decoder: Cb
279280
} else {
280281
if (isDone()) return CompositeDecoder.DECODE_DONE
281282
val elemName = decoder.nextString()
283+
visitKey(elemName)
282284
readProperties++
283285
descriptor.getElementIndexOrThrow(elemName)
284286
}

Diff for: formats/cbor/commonTest/src/kotlinx/serialization/cbor/CborStrictModeTest.kt

+25
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import kotlinx.serialization.assertFailsWithMessage
44
import kotlinx.serialization.decodeFromByteArray
55
import kotlinx.serialization.HexConverter
66
import kotlinx.serialization.DuplicateKeyException
7+
import kotlinx.serialization.Serializable
78
import kotlin.test.Test
89

910
class CborStrictModeTest {
@@ -17,4 +18,28 @@ class CborStrictModeTest {
1718
strict.decodeFromByteArray<Map<String, Long>>(duplicateKeys)
1819
}
1920
}
21+
22+
@Serializable
23+
data class ExampleClass(val x: Long)
24+
25+
/** Duplicate keys are rejected in classes. */
26+
@Test
27+
fun testDuplicateKeysInDataClass() {
28+
// {"x": 5, "x", 6}
29+
val duplicateKeys = HexConverter.parseHexBinary("A2617805617806")
30+
assertFailsWithMessage<DuplicateKeyException>("Duplicate keys not allowed. Key appeared twice: x") {
31+
strict.decodeFromByteArray<ExampleClass>(duplicateKeys)
32+
}
33+
}
34+
35+
/** Duplicate unknown keys are rejected as well. */
36+
@Test
37+
fun testDuplicateUnknownKeys() {
38+
// {"a": 1, "a", 2, "x", 6}
39+
val duplicateKeys = HexConverter.parseHexBinary("A3616101616102617806")
40+
val cbor = Cbor(strict) { ignoreUnknownKeys = true }
41+
assertFailsWithMessage<DuplicateKeyException>("Duplicate keys not allowed. Key appeared twice: a") {
42+
cbor.decodeFromByteArray<ExampleClass>(duplicateKeys)
43+
}
44+
}
2045
}

0 commit comments

Comments
 (0)