From 58e9b187fedf03da21c4e2270bb5335b941ccf61 Mon Sep 17 00:00:00 2001 From: Brian Clozel Date: Tue, 9 Feb 2021 15:03:18 +0100 Subject: [PATCH] Enforce standard Java types in YamlProcessor `spring-beans` ships a `YamlProcessor` that's used as a base class by `YamlMapFactoryBean` and `YamlPropertiesFactoryBean`. These implementations have a clear use case: mapping application-internal Yaml documents for configuration or infrastructure purposes. Since this use case rarely requires extended types support from the underlying library, and since we're offering ways to list custom types (since #25152), we'll restrict to java standard types only by default. This simplifies the setup and focuses the abstract class on the core use cases. Closes gh-26530 --- .../beans/factory/config/YamlProcessor.java | 20 ++++----- .../factory/config/YamlProcessorTests.java | 42 ++++++++++++------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java index a7ea8c8d1f..e34d7c25ce 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/config/YamlProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -52,6 +52,7 @@ import org.springframework.util.StringUtils; * @author Dave Syer * @author Juergen Hoeller * @author Sam Brannen + * @author Brian Clozel * @since 4.1 */ public abstract class YamlProcessor { @@ -128,10 +129,11 @@ public abstract class YamlProcessor { /** * Set the supported types that can be loaded from YAML documents. - *

If no supported types are configured, all types encountered in YAML - * documents will be supported. If an unsupported type is encountered, an - * {@link IllegalStateException} will be thrown when the corresponding YAML - * node is processed. + *

If no supported types are configured, only Java standard classes + * (as defined in {@link org.yaml.snakeyaml.constructor.SafeConstructor}) + * encountered in YAML documents will be supported. + * If an unsupported type is encountered, an {@link IllegalStateException} + * will be thrown when the corresponding YAML node is processed. * @param supportedTypes the supported types, or an empty array to clear the * supported types * @since 5.1.16 @@ -182,12 +184,8 @@ public abstract class YamlProcessor { protected Yaml createYaml() { LoaderOptions loaderOptions = new LoaderOptions(); loaderOptions.setAllowDuplicateKeys(false); - - if (!this.supportedTypes.isEmpty()) { - return new Yaml(new FilteringConstructor(loaderOptions), new Representer(), - new DumperOptions(), loaderOptions); - } - return new Yaml(loaderOptions); + return new Yaml(new FilteringConstructor(loaderOptions), new Representer(), + new DumperOptions(), loaderOptions); } private boolean process(MatchCallback callback, Yaml yaml, Resource resource) { diff --git a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java index 60fbd272ce..50c7f903a4 100644 --- a/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java +++ b/spring-beans/src/test/java/org/springframework/beans/factory/config/YamlProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2020 the original author or authors. + * Copyright 2002-2021 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,9 +17,11 @@ package org.springframework.beans.factory.config; import java.net.URL; +import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Set; import org.junit.jupiter.api.Test; import org.yaml.snakeyaml.constructor.ConstructorException; @@ -28,7 +30,6 @@ import org.yaml.snakeyaml.scanner.ScannerException; import org.springframework.core.io.ByteArrayResource; -import static java.util.stream.Collectors.toList; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; import static org.assertj.core.api.Assertions.entry; @@ -39,10 +40,12 @@ import static org.assertj.core.api.Assertions.entry; * @author Dave Syer * @author Juergen Hoeller * @author Sam Brannen + * @author Brian Clozel */ class YamlProcessorTests { - private final YamlProcessor processor = new YamlProcessor() {}; + private final YamlProcessor processor = new YamlProcessor() { + }; @Test @@ -79,8 +82,8 @@ class YamlProcessorTests { void badResource() { setYaml("foo: bar\ncd\nspam:\n foo: baz"); assertThatExceptionOfType(ScannerException.class) - .isThrownBy(() -> this.processor.process((properties, map) -> {})) - .withMessageContaining("line 3, column 1"); + .isThrownBy(() -> this.processor.process((properties, map) -> {})) + .withMessageContaining("line 3, column 1"); } @Test @@ -127,8 +130,8 @@ class YamlProcessorTests { Map bar = (Map) map.get("bar"); assertThat(bar.get("spam")).isEqualTo("bucket"); - List keysFromProperties = properties.keySet().stream().collect(toList()); - List keysFromFlattenedMap = flattenedMap.keySet().stream().collect(toList()); + List keysFromProperties = new ArrayList<>(properties.keySet()); + List keysFromFlattenedMap = new ArrayList<>(flattenedMap.keySet()); assertThat(keysFromProperties).containsExactlyInAnyOrderElementsOf(keysFromFlattenedMap); // Keys in the Properties object are sorted. assertThat(keysFromProperties).containsExactly("bar.spam", "cat", "foo"); @@ -138,14 +141,23 @@ class YamlProcessorTests { } @Test - void customTypeSupportedByDefault() throws Exception { + void standardTypesSupportedByDefault() throws Exception { + setYaml("value: !!set\n ? first\n ? second"); + this.processor.process((properties, map) -> { + assertThat(properties).containsExactly(entry("value[0]", "first"), entry("value[1]", "second")); + assertThat(map.get("value")).isInstanceOf(Set.class); + Set set = (Set) map.get("value"); + assertThat(set).containsExactly("first", "second"); + }); + } + + @Test + void customTypeNotSupportedByDefault() throws Exception { URL url = new URL("https://localhost:9000/"); setYaml("value: !!java.net.URL [\"" + url + "\"]"); - - this.processor.process((properties, map) -> { - assertThat(properties).containsExactly(entry("value", url)); - assertThat(map).containsExactly(entry("value", url)); - }); + assertThatExceptionOfType(ConstructorException.class) + .isThrownBy(() -> this.processor.process((properties, map) -> {})) + .withMessageContaining("Unsupported type encountered in YAML document: java.net.URL"); } @Test @@ -168,8 +180,8 @@ class YamlProcessorTests { setYaml("value: !!java.net.URL [\"https://localhost:9000/\"]"); assertThatExceptionOfType(ConstructorException.class) - .isThrownBy(() -> this.processor.process((properties, map) -> {})) - .withMessageContaining("Unsupported type encountered in YAML document: java.net.URL"); + .isThrownBy(() -> this.processor.process((properties, map) -> {})) + .withMessageContaining("Unsupported type encountered in YAML document: java.net.URL"); } private void setYaml(String yaml) {