Skip to content

Commit 587b840

Browse files
committed
Restrict use of custom YAML types
Update `YamlJsonParser` and `OriginTrackedYamlLoader` to ensure that custom types cannot be loaded. Closes spring-projectsgh-21596
1 parent bce48ea commit 587b840

File tree

4 files changed

+52
-3
lines changed

4 files changed

+52
-3
lines changed

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/OriginTrackedYamlLoader.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.yaml.snakeyaml.Yaml;
2828
import org.yaml.snakeyaml.constructor.BaseConstructor;
2929
import org.yaml.snakeyaml.constructor.Constructor;
30+
import org.yaml.snakeyaml.constructor.SafeConstructor;
3031
import org.yaml.snakeyaml.error.Mark;
3132
import org.yaml.snakeyaml.nodes.MappingNode;
3233
import org.yaml.snakeyaml.nodes.Node;
@@ -79,7 +80,7 @@ public List<Map<String, Object>> load() {
7980
/**
8081
* {@link Constructor} that tracks property origins.
8182
*/
82-
private class OriginTrackingConstructor extends Constructor {
83+
private class OriginTrackingConstructor extends SafeConstructor {
8384

8485
@Override
8586
protected Object constructObject(Node node) {

spring-boot-project/spring-boot/src/main/java/org/springframework/boot/json/YamlJsonParser.java

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,15 @@
1616

1717
package org.springframework.boot.json;
1818

19+
import java.util.Collections;
20+
import java.util.LinkedHashSet;
1921
import java.util.List;
2022
import java.util.Map;
23+
import java.util.Set;
24+
import java.util.stream.Collectors;
2125

2226
import org.yaml.snakeyaml.Yaml;
27+
import org.yaml.snakeyaml.constructor.Constructor;
2328

2429
/**
2530
* Thin wrapper to adapt Snake {@link Yaml} to {@link JsonParser}.
@@ -31,16 +36,36 @@
3136
*/
3237
public class YamlJsonParser extends AbstractJsonParser {
3338

39+
private final Yaml yaml = new Yaml(new TypeLimitedConstructor());
40+
3441
@Override
3542
@SuppressWarnings("unchecked")
3643
public Map<String, Object> parseMap(String json) {
37-
return parseMap(json, (trimmed) -> new Yaml().loadAs(trimmed, Map.class));
44+
return parseMap(json, (trimmed) -> this.yaml.loadAs(trimmed, Map.class));
3845
}
3946

4047
@Override
4148
@SuppressWarnings("unchecked")
4249
public List<Object> parseList(String json) {
43-
return parseList(json, (trimmed) -> new Yaml().loadAs(trimmed, List.class));
50+
return parseList(json, (trimmed) -> this.yaml.loadAs(trimmed, List.class));
51+
}
52+
53+
private static class TypeLimitedConstructor extends Constructor {
54+
55+
private static final Set<String> SUPPORTED_TYPES;
56+
static {
57+
Set<Class<?>> supportedTypes = new LinkedHashSet<>();
58+
supportedTypes.add(List.class);
59+
supportedTypes.add(Map.class);
60+
SUPPORTED_TYPES = supportedTypes.stream().map(Class::getName)
61+
.collect(Collectors.collectingAndThen(Collectors.toSet(), Collections::unmodifiableSet));
62+
}
63+
64+
@Override
65+
protected Class<?> getClassForName(String name) throws ClassNotFoundException {
66+
return (SUPPORTED_TYPES.contains(name)) ? super.getClassForName(name) : null;
67+
}
68+
4469
}
4570

4671
}

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/env/OriginTrackedYamlLoaderTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,22 @@
1616

1717
package org.springframework.boot.env;
1818

19+
import java.nio.charset.StandardCharsets;
1920
import java.util.List;
2021
import java.util.Map;
2122

2223
import org.junit.Before;
2324
import org.junit.Test;
25+
import org.yaml.snakeyaml.constructor.ConstructorException;
2426

2527
import org.springframework.boot.origin.OriginTrackedValue;
2628
import org.springframework.boot.origin.TextResourceOrigin;
29+
import org.springframework.core.io.ByteArrayResource;
2730
import org.springframework.core.io.ClassPathResource;
2831
import org.springframework.core.io.Resource;
2932

3033
import static org.assertj.core.api.Assertions.assertThat;
34+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
3135

3236
/**
3337
* Tests for {@link OriginTrackedYamlLoader}.
@@ -116,6 +120,14 @@ public void processEmptyAndNullValues() {
116120
assertThat(getLocation(nullValue)).isEqualTo("28:13");
117121
}
118122

123+
@Test
124+
public void unsupportedType() throws Exception {
125+
String yaml = "value: !!java.net.URL [!!java.lang.String [!!java.lang.StringBuilder [\"http://localhost:9000/\"]]]";
126+
Resource resource = new ByteArrayResource(yaml.getBytes(StandardCharsets.UTF_8));
127+
this.loader = new OriginTrackedYamlLoader(resource);
128+
assertThatExceptionOfType(ConstructorException.class).isThrownBy(this.loader::load);
129+
}
130+
119131
private OriginTrackedValue getValue(String name) {
120132
if (this.result == null) {
121133
this.result = this.loader.load();

spring-boot-project/spring-boot/src/test/java/org/springframework/boot/json/YamlJsonParserTests.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616

1717
package org.springframework.boot.json;
1818

19+
import org.junit.Test;
20+
import org.yaml.snakeyaml.constructor.ConstructorException;
21+
22+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
23+
1924
/**
2025
* Tests for {@link YamlJsonParser}.
2126
*
@@ -28,4 +33,10 @@ protected JsonParser getParser() {
2833
return new YamlJsonParser();
2934
}
3035

36+
@Test
37+
public void customTypesAreNotLoaded() throws Exception {
38+
assertThatExceptionOfType(ConstructorException.class)
39+
.isThrownBy(() -> getParser().parseMap("{value: !!java.net.URL [\"http://localhost:9000/\"]}"));
40+
}
41+
3142
}

0 commit comments

Comments
 (0)