Skip to content

Commit 9c6b1b6

Browse files
committed
Prevent kotlinx.serialization usage on collection of interfaces
Closes gh-26371
1 parent 0fc8bf6 commit 9c6b1b6

File tree

6 files changed

+67
-12
lines changed

6 files changed

+67
-12
lines changed

spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonDecoder.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,11 +17,14 @@
1717
package org.springframework.http.codec.json;
1818

1919
import java.lang.reflect.Type;
20+
import java.util.HashSet;
2021
import java.util.Map;
22+
import java.util.Set;
2123

2224
import kotlinx.serialization.KSerializer;
2325
import kotlinx.serialization.SerializersKt;
2426
import kotlinx.serialization.descriptors.PolymorphicKind;
27+
import kotlinx.serialization.descriptors.SerialDescriptor;
2528
import kotlinx.serialization.json.Json;
2629
import org.reactivestreams.Publisher;
2730
import reactor.core.publisher.Flux;
@@ -135,12 +138,26 @@ private KSerializer<Object> serializer(Type type) {
135138
KSerializer<Object> serializer = serializerCache.get(type);
136139
if (serializer == null) {
137140
serializer = SerializersKt.serializer(type);
138-
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
141+
if (hasPolymorphism(serializer.getDescriptor(), new HashSet<>())) {
139142
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
140143
}
141144
serializerCache.put(type, serializer);
142145
}
143146
return serializer;
144147
}
145148

149+
private boolean hasPolymorphism(SerialDescriptor descriptor, Set<String> alreadyProcessed) {
150+
alreadyProcessed.add(descriptor.getSerialName());
151+
if (descriptor.getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
152+
return true;
153+
}
154+
for (int i = 0 ; i < descriptor.getElementsCount() ; i++) {
155+
SerialDescriptor elementDescriptor = descriptor.getElementDescriptor(i);
156+
if (!alreadyProcessed.contains(elementDescriptor.getSerialName()) && hasPolymorphism(elementDescriptor, alreadyProcessed)) {
157+
return true;
158+
}
159+
}
160+
return false;
161+
}
162+
146163
}

spring-web/src/main/java/org/springframework/http/codec/json/KotlinSerializationJsonEncoder.java

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -17,12 +17,15 @@
1717
package org.springframework.http.codec.json;
1818

1919
import java.lang.reflect.Type;
20+
import java.util.HashSet;
2021
import java.util.List;
2122
import java.util.Map;
23+
import java.util.Set;
2224

2325
import kotlinx.serialization.KSerializer;
2426
import kotlinx.serialization.SerializersKt;
2527
import kotlinx.serialization.descriptors.PolymorphicKind;
28+
import kotlinx.serialization.descriptors.SerialDescriptor;
2629
import kotlinx.serialization.json.Json;
2730
import org.reactivestreams.Publisher;
2831
import reactor.core.publisher.Flux;
@@ -123,12 +126,26 @@ private KSerializer<Object> serializer(Type type) {
123126
KSerializer<Object> serializer = serializerCache.get(type);
124127
if (serializer == null) {
125128
serializer = SerializersKt.serializer(type);
126-
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
129+
if (hasPolymorphism(serializer.getDescriptor(), new HashSet<>())) {
127130
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
128131
}
129132
serializerCache.put(type, serializer);
130133
}
131134
return serializer;
132135
}
133136

137+
private boolean hasPolymorphism(SerialDescriptor descriptor, Set<String> alreadyProcessed) {
138+
alreadyProcessed.add(descriptor.getSerialName());
139+
if (descriptor.getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
140+
return true;
141+
}
142+
for (int i = 0 ; i < descriptor.getElementsCount() ; i++) {
143+
SerialDescriptor elementDescriptor = descriptor.getElementDescriptor(i);
144+
if (!alreadyProcessed.contains(elementDescriptor.getSerialName()) && hasPolymorphism(elementDescriptor, alreadyProcessed)) {
145+
return true;
146+
}
147+
}
148+
return false;
149+
}
150+
134151
}

spring-web/src/main/java/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverter.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,15 @@
2020
import java.lang.reflect.Type;
2121
import java.nio.charset.Charset;
2222
import java.nio.charset.StandardCharsets;
23+
import java.util.HashSet;
2324
import java.util.Map;
25+
import java.util.Set;
2426

2527
import kotlinx.serialization.KSerializer;
2628
import kotlinx.serialization.SerializationException;
2729
import kotlinx.serialization.SerializersKt;
2830
import kotlinx.serialization.descriptors.PolymorphicKind;
31+
import kotlinx.serialization.descriptors.SerialDescriptor;
2932
import kotlinx.serialization.json.Json;
3033

3134
import org.springframework.core.GenericTypeResolver;
@@ -185,12 +188,26 @@ private KSerializer<Object> serializer(Type type) {
185188
KSerializer<Object> serializer = serializerCache.get(type);
186189
if (serializer == null) {
187190
serializer = SerializersKt.serializer(type);
188-
if (serializer.getDescriptor().getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
191+
if (hasPolymorphism(serializer.getDescriptor(), new HashSet<>())) {
189192
throw new UnsupportedOperationException("Open polymorphic serialization is not supported yet");
190193
}
191194
serializerCache.put(type, serializer);
192195
}
193196
return serializer;
194197
}
195198

199+
private boolean hasPolymorphism(SerialDescriptor descriptor, Set<String> alreadyProcessed) {
200+
alreadyProcessed.add(descriptor.getSerialName());
201+
if (descriptor.getKind().equals(PolymorphicKind.OPEN.INSTANCE)) {
202+
return true;
203+
}
204+
for (int i = 0 ; i < descriptor.getElementsCount() ; i++) {
205+
SerialDescriptor elementDescriptor = descriptor.getElementDescriptor(i);
206+
if (!alreadyProcessed.contains(elementDescriptor.getSerialName()) && hasPolymorphism(elementDescriptor, alreadyProcessed)) {
207+
return true;
208+
}
209+
}
210+
return false;
211+
}
212+
196213
}

spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonDecoderTests.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -56,6 +56,7 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests<KotlinSerializa
5656
MediaType("application", "json", StandardCharsets.ISO_8859_1))).isTrue()
5757

5858
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(List::class.java, Int::class.java), MediaType.APPLICATION_JSON)).isTrue()
59+
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(List::class.java, Ordered::class.java), MediaType.APPLICATION_JSON)).isFalse()
5960
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(List::class.java, Pojo::class.java), MediaType.APPLICATION_JSON)).isTrue()
6061
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(ArrayList::class.java, Int::class.java), MediaType.APPLICATION_JSON)).isTrue()
6162
Assertions.assertThat(decoder.canDecode(ResolvableType.forClassWithGenerics(ArrayList::class.java, Int::class.java), MediaType.APPLICATION_PDF)).isFalse()
@@ -101,6 +102,6 @@ class KotlinSerializationJsonDecoderTests : AbstractDecoderTests<KotlinSerializa
101102

102103

103104
@Serializable
104-
data class Pojo(val foo: String, val bar: String)
105+
data class Pojo(val foo: String, val bar: String, val pojo: Pojo? = null)
105106

106107
}

spring-web/src/test/kotlin/org/springframework/http/codec/json/KotlinSerializationJsonEncoderTests.kt

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -52,7 +52,8 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests<KotlinSerializa
5252
MediaType("application", "json", StandardCharsets.US_ASCII))).isTrue()
5353

5454
Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(List::class.java, Int::class.java), MediaType.APPLICATION_JSON)).isTrue()
55-
Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(List::class.java, KotlinSerializationJsonDecoderTests.Pojo::class.java), MediaType.APPLICATION_JSON)).isTrue()
55+
Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(List::class.java, Ordered::class.java), MediaType.APPLICATION_JSON)).isFalse()
56+
Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(List::class.java, Pojo::class.java), MediaType.APPLICATION_JSON)).isTrue()
5657
Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(ArrayList::class.java, Int::class.java), MediaType.APPLICATION_JSON)).isTrue()
5758
Assertions.assertThat(encoder.canEncode(ResolvableType.forClassWithGenerics(ArrayList::class.java, Int::class.java), MediaType.APPLICATION_PDF)).isFalse()
5859
}
@@ -97,6 +98,6 @@ class KotlinSerializationJsonEncoderTests : AbstractEncoderTests<KotlinSerializa
9798

9899

99100
@Serializable
100-
data class Pojo(val foo: String, val bar: String)
101+
data class Pojo(val foo: String, val bar: String, val pojo: Pojo? = null)
101102

102103
}

spring-web/src/test/kotlin/org/springframework/http/converter/json/KotlinSerializationJsonHttpMessageConverterTests.kt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -61,6 +61,7 @@ class KotlinSerializationJsonHttpMessageConverterTests {
6161
assertThat(converter.canRead(typeTokenOf<List<Int>>(), List::class.java, MediaType.APPLICATION_PDF)).isFalse()
6262

6363
assertThat(converter.canRead(typeTokenOf<Ordered>(), Ordered::class.java, MediaType.APPLICATION_JSON)).isFalse()
64+
assertThat(converter.canRead(typeTokenOf<List<Ordered>>(), List::class.java, MediaType.APPLICATION_JSON)).isFalse()
6465
}
6566

6667
@Test
@@ -315,7 +316,8 @@ class KotlinSerializationJsonHttpMessageConverterTests {
315316
val number: Int,
316317
val string: String?,
317318
val bool: Boolean,
318-
val fraction: Float
319+
val fraction: Float,
320+
val serializableBean: SerializableBean? = null
319321
)
320322

321323
data class NotSerializableBean(val string: String)

0 commit comments

Comments
 (0)