Skip to content

Commit 0d80f46

Browse files
committed
Remove node and recursive limits for YAML
Update `OriginTrackedYamlLoader` to remove node limits and recursive parsing restrictions. SnakeYAML 1.26 introduced these options in order to protect against the "billion laugh attacks" but since we consider `application.yml` files to be trusted, we don't need these restrictions. Fixes gh-23096
1 parent ee91462 commit 0d80f46

File tree

3 files changed

+49
-3
lines changed

3 files changed

+49
-3
lines changed

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

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,18 @@ class OriginTrackedYamlLoader extends YamlProcessor {
6262

6363
@Override
6464
protected Yaml createYaml() {
65-
BaseConstructor constructor = new OriginTrackingConstructor();
65+
LoaderOptions loaderOptions = new LoaderOptions();
66+
loaderOptions.setAllowDuplicateKeys(false);
67+
loaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE);
68+
loaderOptions.setAllowRecursiveKeys(true);
69+
return createYaml(loaderOptions);
70+
}
71+
72+
private Yaml createYaml(LoaderOptions loaderOptions) {
73+
BaseConstructor constructor = new OriginTrackingConstructor(loaderOptions);
6674
Representer representer = new Representer();
6775
DumperOptions dumperOptions = new DumperOptions();
6876
LimitedResolver resolver = new LimitedResolver();
69-
LoaderOptions loaderOptions = new LoaderOptions();
70-
loaderOptions.setAllowDuplicateKeys(false);
7177
return new Yaml(constructor, representer, dumperOptions, loaderOptions, resolver);
7278
}
7379

@@ -82,6 +88,10 @@ List<Map<String, Object>> load() {
8288
*/
8389
private class OriginTrackingConstructor extends SafeConstructor {
8490

91+
OriginTrackingConstructor(LoaderOptions loadingConfig) {
92+
super(loadingConfig);
93+
}
94+
8595
@Override
8696
protected Object constructObject(Node node) {
8797
if (node instanceof ScalarNode) {

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

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,35 @@ void unsupportedType() throws Exception {
128128
assertThatExceptionOfType(ConstructorException.class).isThrownBy(this.loader::load);
129129
}
130130

131+
@Test
132+
void loadWhenLargeNumberOfNodesLoadsYaml() {
133+
StringBuilder yaml = new StringBuilder();
134+
int size = 500;
135+
yaml.append("defs:\n");
136+
for (int i = 0; i < size; i++) {
137+
yaml.append(" - def" + i + ": &def" + i + "\n");
138+
yaml.append(" - value: " + i + "\n");
139+
}
140+
yaml.append("refs:\n");
141+
for (int i = 0; i < size; i++) {
142+
yaml.append(" ref" + i + ":\n");
143+
yaml.append(" - value: *def" + i + "\n");
144+
}
145+
Resource resource = new ByteArrayResource(yaml.toString().getBytes(StandardCharsets.UTF_8));
146+
this.loader = new OriginTrackedYamlLoader(resource);
147+
Map<String, Object> loaded = this.loader.load().get(0);
148+
assertThat(loaded).hasSize(size * 2);
149+
}
150+
151+
@Test
152+
void loadWhenRecursiveLoadsYaml() {
153+
Resource resource = new ClassPathResource("recursive.yml", getClass());
154+
this.loader = new OriginTrackedYamlLoader(resource);
155+
Map<String, Object> loaded = this.loader.load().get(0);
156+
assertThat(loaded.get("test.a.spring")).hasToString("a");
157+
assertThat(loaded.get("test.b.boot")).hasToString("b");
158+
}
159+
131160
private OriginTrackedValue getValue(String name) {
132161
if (this.result == null) {
133162
this.result = this.loader.load();
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
&def1
2+
*def1: a
3+
test:
4+
a:
5+
spring: 'a'
6+
b:
7+
boot: 'b'

0 commit comments

Comments
 (0)