From d5380e289537a9c1ff4e707c0a11de44748935d2 Mon Sep 17 00:00:00 2001 From: Glenn Renfro Date: Thu, 18 Jul 2024 11:46:50 -0400 Subject: [PATCH] Ensure PackageMetadata is created with SafeConstructor (#5871) Use root provided to determine type of conversion to use. * Added tests when root is specified * Added tests for builder Remove PackagMetadataBuilder, it is not needed Update default constructors to use the safe constructor. --- .../server/service/ReleaseReportService.java | 4 +- .../server/util/ArgumentSanitizer.java | 5 +- .../skipper/server/util/ManifestUtils.java | 2 +- .../controller/docs/BaseDocumentation.java | 5 +- .../server/service/ConfigValueUtilsTests.java | 5 +- .../shell/command/ManifestCommands.java | 5 +- .../skipper/io/DefaultPackageReader.java | 12 +- .../skipper/io/DefaultPackageWriter.java | 6 +- .../io/PackageMetadataSafeConstructor.java | 147 ++++++++++++++++++ .../support/yaml/DefaultYamlConverter.java | 4 +- .../PackageMetadataSafeConstructorTests.java | 101 ++++++++++++ .../cloud/skipper/io/PackageWriterTests.java | 5 +- 12 files changed, 286 insertions(+), 15 deletions(-) create mode 100644 spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/PackageMetadataSafeConstructor.java create mode 100644 spring-cloud-skipper/spring-cloud-skipper/src/test/java/org/springframework/cloud/skipper/io/PackageMetadataSafeConstructorTests.java diff --git a/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/service/ReleaseReportService.java b/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/service/ReleaseReportService.java index 45f70956ee..448351b970 100644 --- a/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/service/ReleaseReportService.java +++ b/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/service/ReleaseReportService.java @@ -21,6 +21,7 @@ import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.springframework.cloud.skipper.SkipperException; @@ -44,6 +45,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import org.springframework.util.StringUtils; +import org.yaml.snakeyaml.representer.Representer; /** * @author Mark Pollack @@ -131,7 +133,7 @@ private Release updateReplacingReleaseConfigValues(Release targetRelease, Releas DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); dumperOptions.setPrettyFlow(true); - Yaml yaml = new Yaml(dumperOptions); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()), new Representer(dumperOptions), dumperOptions); ConfigValues mergedConfigValues = new ConfigValues(); mergedConfigValues.setRaw(yaml.dump(targetConfigValueMap)); replacingRelease.setConfigValues(mergedConfigValues); diff --git a/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/util/ArgumentSanitizer.java b/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/util/ArgumentSanitizer.java index 53a82ac170..4ab0aec0fa 100644 --- a/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/util/ArgumentSanitizer.java +++ b/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/util/ArgumentSanitizer.java @@ -22,7 +22,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.representer.Representer; /** * Sanitizes potentially sensitive keys from manifest data. @@ -60,7 +63,7 @@ public static String sanitizeYml(String yml) { DumperOptions options = new DumperOptions(); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); options.setPrettyFlow(true); - Yaml yaml = new Yaml(options); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()), new Representer(options), options); Iterator iter = yaml.loadAll(yml).iterator(); while (iter.hasNext()) { Object o = iter.next(); diff --git a/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/util/ManifestUtils.java b/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/util/ManifestUtils.java index 2f16b62050..59d1d4f60f 100644 --- a/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/util/ManifestUtils.java +++ b/spring-cloud-skipper/spring-cloud-skipper-server-core/src/main/java/org/springframework/cloud/skipper/server/util/ManifestUtils.java @@ -152,7 +152,7 @@ private static Yaml createYaml() { dumperOptions.setDefaultScalarStyle(DumperOptions.ScalarStyle.DOUBLE_QUOTED); dumperOptions.setPrettyFlow(true); dumperOptions.setSplitLines(false); - return new Yaml(new ValueTypeRepresenter(), dumperOptions); + return new Yaml(new SafeConstructor(new LoaderOptions()), new ValueTypeRepresenter(), dumperOptions); } private static class ValueTypeRepresenter extends Representer { diff --git a/spring-cloud-skipper/spring-cloud-skipper-server-core/src/test/java/org/springframework/cloud/skipper/server/controller/docs/BaseDocumentation.java b/spring-cloud-skipper/spring-cloud-skipper-server-core/src/test/java/org/springframework/cloud/skipper/server/controller/docs/BaseDocumentation.java index 92e4466af2..52651aaa6c 100644 --- a/spring-cloud-skipper/spring-cloud-skipper-server-core/src/test/java/org/springframework/cloud/skipper/server/controller/docs/BaseDocumentation.java +++ b/spring-cloud-skipper/spring-cloud-skipper-server-core/src/test/java/org/springframework/cloud/skipper/server/controller/docs/BaseDocumentation.java @@ -26,6 +26,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.springframework.beans.factory.annotation.Autowired; @@ -66,6 +67,8 @@ import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import org.springframework.web.servlet.config.annotation.EnableWebMvc; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.representer.Representer; import static org.springframework.restdocs.hypermedia.HypermediaDocumentation.linkWithRel; import static org.springframework.restdocs.mockmvc.MockMvcRestDocumentation.document; @@ -206,7 +209,7 @@ private ConfigValues getSampleConfigValues() { DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); dumperOptions.setPrettyFlow(true); - Yaml yaml = new Yaml(dumperOptions); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()), new Representer(dumperOptions), dumperOptions); Map configMap = new HashMap<>(); configMap.put("config1", "value1"); configMap.put("config2", "value2"); diff --git a/spring-cloud-skipper/spring-cloud-skipper-server-core/src/test/java/org/springframework/cloud/skipper/server/service/ConfigValueUtilsTests.java b/spring-cloud-skipper/spring-cloud-skipper-server-core/src/test/java/org/springframework/cloud/skipper/server/service/ConfigValueUtilsTests.java index eb702c5d1e..348a05a959 100644 --- a/spring-cloud-skipper/spring-cloud-skipper-server-core/src/test/java/org/springframework/cloud/skipper/server/service/ConfigValueUtilsTests.java +++ b/spring-cloud-skipper/spring-cloud-skipper-server-core/src/test/java/org/springframework/cloud/skipper/server/service/ConfigValueUtilsTests.java @@ -23,6 +23,7 @@ import org.junit.jupiter.api.Test; import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.springframework.beans.factory.annotation.Autowired; @@ -46,6 +47,8 @@ import org.springframework.statemachine.boot.autoconfigure.StateMachineJpaRepositoriesAutoConfiguration; import org.springframework.test.annotation.DirtiesContext; import org.springframework.util.StreamUtils; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.representer.Representer; /** * @author Mark Pollack @@ -63,7 +66,7 @@ public void testYamlMerge() throws IOException { DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); dumperOptions.setPrettyFlow(true); - Yaml yaml = new Yaml(dumperOptions); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()), new Representer(dumperOptions), dumperOptions); Resource resource = new ClassPathResource("/org/springframework/cloud/skipper/server/service/ticktock-1.0.0"); diff --git a/spring-cloud-skipper/spring-cloud-skipper-shell-commands/src/main/java/org/springframework/cloud/skipper/shell/command/ManifestCommands.java b/spring-cloud-skipper/spring-cloud-skipper-shell-commands/src/main/java/org/springframework/cloud/skipper/shell/command/ManifestCommands.java index 1f4d28822d..4fae392c18 100644 --- a/spring-cloud-skipper/spring-cloud-skipper-shell-commands/src/main/java/org/springframework/cloud/skipper/shell/command/ManifestCommands.java +++ b/spring-cloud-skipper/spring-cloud-skipper-shell-commands/src/main/java/org/springframework/cloud/skipper/shell/command/ManifestCommands.java @@ -18,6 +18,7 @@ import javax.validation.constraints.NotNull; import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.springframework.beans.factory.annotation.Autowired; @@ -27,6 +28,8 @@ import org.springframework.shell.standard.ShellMethod; import org.springframework.shell.standard.ShellOption; import org.springframework.web.client.HttpStatusCodeException; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.representer.Representer; /** * Commands that operation on the manifest. @@ -43,7 +46,7 @@ public ManifestCommands(SkipperClient skipperClient) { DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); dumperOptions.setPrettyFlow(true); - this.yaml = new Yaml(dumperOptions); + this.yaml = new Yaml(new SafeConstructor(new LoaderOptions()), new Representer(dumperOptions), dumperOptions); } @ShellMethod(key = "manifest get", value = "Get the manifest for a release") diff --git a/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/DefaultPackageReader.java b/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/DefaultPackageReader.java index ab6f93be8a..814aa94246 100644 --- a/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/DefaultPackageReader.java +++ b/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/DefaultPackageReader.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 the original author or authors. + * Copyright 2017-2024 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. @@ -15,8 +15,10 @@ */ package org.springframework.cloud.skipper.io; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -28,7 +30,6 @@ import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; -import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.representer.Representer; import org.zeroturnaround.zip.commons.FileUtils; @@ -163,15 +164,14 @@ private PackageMetadata loadPackageMetadata(File file) { Representer representer = new Representer(options); representer.getPropertyUtils().setSkipMissingProperties(true); LoaderOptions loaderOptions = new LoaderOptions(); - Yaml yaml = new Yaml(new Constructor(PackageMetadata.class, loaderOptions), representer); - String fileContents = null; + Yaml yaml = new Yaml(new PackageMetadataSafeConstructor(loaderOptions), representer); + String fileContents; try { fileContents = FileUtils.readFileToString(file); } catch (IOException e) { throw new SkipperException("Error reading yaml file", e); } - PackageMetadata pkgMetadata = (PackageMetadata) yaml.load(fileContents); - return pkgMetadata; + return yaml.load(fileContents); } } diff --git a/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/DefaultPackageWriter.java b/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/DefaultPackageWriter.java index ea37ec9ee8..d4a4f926bd 100644 --- a/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/DefaultPackageWriter.java +++ b/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/DefaultPackageWriter.java @@ -22,7 +22,10 @@ import java.nio.charset.Charset; import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.representer.Representer; import org.zeroturnaround.zip.ZipUtil; import org.springframework.cloud.skipper.domain.Package; @@ -45,7 +48,8 @@ public DefaultPackageWriter() { DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); dumperOptions.setPrettyFlow(true); - this.yaml = new Yaml(dumperOptions); + this.yaml = new Yaml(new SafeConstructor(new LoaderOptions()), new Representer(dumperOptions), dumperOptions); + } @Override diff --git a/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/PackageMetadataSafeConstructor.java b/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/PackageMetadataSafeConstructor.java new file mode 100644 index 0000000000..d22d14c7a7 --- /dev/null +++ b/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/io/PackageMetadataSafeConstructor.java @@ -0,0 +1,147 @@ +/* + * Copyright 2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.skipper.io; + +import org.springframework.cloud.skipper.domain.PackageMetadata; + +import org.yaml.snakeyaml.LoaderOptions; +import org.yaml.snakeyaml.TypeDescription; +import org.yaml.snakeyaml.constructor.Construct; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.nodes.MappingNode; +import org.yaml.snakeyaml.nodes.Node; +import org.yaml.snakeyaml.nodes.NodeTuple; +import org.yaml.snakeyaml.nodes.ScalarNode; +import org.yaml.snakeyaml.nodes.Tag; + +/** + * Extends {@link SafeConstructor} so that we can construct an instance of {@link org.springframework.cloud.skipper.domain.PackageMetadata} + * When deserializing yaml for deploying apps in stream definitions. + * + * @author Glenn Renfro + */ +class PackageMetadataSafeConstructor extends SafeConstructor { + private static final String API_VERSION = "apiVersion"; + private static final String ORIGIN = "origin"; + private static final String REPOSITORY_ID = "repositoryId"; + private static final String REPOSITORY_NAME = "repositoryName"; + private static final String PACKAGE_KIND = "kind"; + private static final String NAME = "name"; + private static final String DISPLAY_NAME = "displayName"; + private static final String PACKAGE_VERSION = "version"; + private static final String PACKAGE_SOURCE_URL = "packageSourceUrl"; + private static final String PACKAGE_HOME_URL = "packageHomeUrl"; + private static final String TAGS = "tags"; + private static final String MAINTAINER = "maintainer"; + private static final String DESCRIPTION = "description"; + private static final String SHA256 = "sha256"; + private static final String ICON_URL = "iconUrl"; + + PackageMetadataSafeConstructor(LoaderOptions loadingConfig) { + super(loadingConfig); + this.yamlConstructors.put(new TypeDescription(PackageMetadata.class).getTag(), new ConstructYamlPackageMetadata()); + rootTag = new Tag(new TypeDescription(PackageMetadata.class).getType()); + } + + private class ConstructYamlPackageMetadata implements Construct { + @Override + public Object construct(Node node) { + MappingNode mappingNode = (MappingNode) node; + PackageMetadata packageMetadata = new PackageMetadata(); + try { + for (NodeTuple tuple : mappingNode.getValue()) { + ScalarNode keyNode = (ScalarNode) tuple.getKeyNode(); + ScalarNode valueNode = (ScalarNode) tuple.getValueNode(); + String key = keyNode.getValue(); + setKeyValue(packageMetadata, key, valueNode.getValue()); + } + } + catch (ClassCastException cce) { + throw new YAMLException("Unable to Parse yaml to PackageMetadata type", cce); + } + return packageMetadata; + } + + @Override + public void construct2ndStep(Node node, Object object) { + + } + + public PackageMetadata setKeyValue(PackageMetadata packageMetadata, String key, String value) { + switch (key) { + case API_VERSION: + packageMetadata.setApiVersion(value); + break; + case ORIGIN: + packageMetadata.setOrigin(value); + break; + case REPOSITORY_ID: + packageMetadata.setRepositoryId(isLong(value) ? Long.parseLong(value) : null); + break; + case REPOSITORY_NAME: + packageMetadata.setRepositoryName(value); + break; + case PACKAGE_KIND: + packageMetadata.setKind(value); + break; + case NAME: + packageMetadata.setName(value); + break; + case DISPLAY_NAME: + packageMetadata.setDisplayName(value); + break; + case PACKAGE_VERSION: + packageMetadata.setVersion(value); + break; + case PACKAGE_SOURCE_URL: + packageMetadata.setPackageSourceUrl(value); + break; + case PACKAGE_HOME_URL: + packageMetadata.setPackageHomeUrl(value); + break; + case TAGS: + packageMetadata.setTags(value); + break; + case MAINTAINER: + packageMetadata.setMaintainer(value); + break; + case DESCRIPTION: + packageMetadata.setDescription(value); + break; + case SHA256: + packageMetadata.setSha256(value); + break; + case ICON_URL: + packageMetadata.setIconUrl(value); + break; + } + return packageMetadata; + } + private boolean isLong(String str) { + if (str == null || str.isEmpty()) { + return false; + } + try { + Long.parseLong(str); + return true; + } catch (NumberFormatException e) { + return false; + } + } + } +} diff --git a/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/support/yaml/DefaultYamlConverter.java b/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/support/yaml/DefaultYamlConverter.java index b768bd5eff..45ab838211 100644 --- a/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/support/yaml/DefaultYamlConverter.java +++ b/spring-cloud-skipper/spring-cloud-skipper/src/main/java/org/springframework/cloud/skipper/support/yaml/DefaultYamlConverter.java @@ -33,6 +33,8 @@ import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.representer.Representer; /** * Default implementation of a {@link YamlConverter}. @@ -127,7 +129,7 @@ private YamlConversionResult convert(Map> properties) options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); options.setPrettyFlow(true); - Yaml yaml = new Yaml(options); + Yaml yaml = new Yaml(new SafeConstructor(), new Representer(options), options); String output = yaml.dump(object); return new YamlConversionResult(status, output); } diff --git a/spring-cloud-skipper/spring-cloud-skipper/src/test/java/org/springframework/cloud/skipper/io/PackageMetadataSafeConstructorTests.java b/spring-cloud-skipper/spring-cloud-skipper/src/test/java/org/springframework/cloud/skipper/io/PackageMetadataSafeConstructorTests.java new file mode 100644 index 0000000000..f7d40b6abb --- /dev/null +++ b/spring-cloud-skipper/spring-cloud-skipper/src/test/java/org/springframework/cloud/skipper/io/PackageMetadataSafeConstructorTests.java @@ -0,0 +1,101 @@ +/* + * Copyright 2024 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.cloud.skipper.io; + +import org.junit.jupiter.api.Test; +import org.springframework.cloud.skipper.domain.PackageMetadata; +import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; +import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.error.YAMLException; +import org.yaml.snakeyaml.representer.Representer; +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; + +public class PackageMetadataSafeConstructorTests { + private String testYaml = "!!org.springframework.cloud.skipper.domain.PackageMetadata\n" + + "apiVersion: skipper.spring.io/v1\n" + + "description: time --management.endpoints.web.exposure.include=health,info,bindings\n" + + " --management.metrics.tags.application.type=${spring.cloud.dataflow.stream.app.type:unknown}\n" + + " --management.metrics.tags.stream.name=${spring.cloud.dataflow.stream.name:unknown}\n" + + " --management.metrics.tags.application=${spring.cloud.dataflow.stream.name:unknown}-${spring.cloud.dataflow.stream.app.label:unknown}-${spring.cloud.dataflow.stream.app.type:unknown}\n" + + " --management.metrics.tags.instance.index=${vcap.application.instance_index:${spring.cloud.stream.instanceIndex:0}}\n" + + " --wavefront.application.service=${spring.cloud.dataflow.stream.app.label:unknown}-${spring.cloud.dataflow.stream.app.type:unknown}-${vcap.application.instance_index:${spring.cloud.stream.instanceIndex:0}}\n" + + " --management.metrics.tags.application.guid=${spring.cloud.application.guid:unknown}\n" + + " --management.metrics.tags.application.name=${vcap.application.application_name:${spring.cloud.dataflow.stream.app.label:unknown}}\n" + + " --wavefront.application.name=${spring.cloud.dataflow.stream.name:unknown} | log\n" + + " --management.endpoints.web.exposure.include=health,info,bindings --management.metrics.tags.application.type=${spring.cloud.dataflow.stream.app.type:unknown}\n" + + " --management.metrics.tags.stream.name=${spring.cloud.dataflow.stream.name:unknown}\n" + + " --management.metrics.tags.application=${spring.cloud.dataflow.stream.name:unknown}-${spring.cloud.dataflow.stream.app.label:unknown}-${spring.cloud.dataflow.stream.app.type:unknown}\n" + + " --management.metrics.tags.instance.index=${vcap.application.instance_index:${spring.cloud.stream.instanceIndex:0}}\n" + + " --wavefront.application.service=${spring.cloud.dataflow.stream.app.label:unknown}-${spring.cloud.dataflow.stream.app.type:unknown}-${vcap.application.instance_index:${spring.cloud.stream.instanceIndex:0}}\n" + + " --management.metrics.tags.application.guid=${spring.cloud.application.guid:unknown}\n" + + " --management.metrics.tags.application.name=${vcap.application.application_name:${spring.cloud.dataflow.stream.app.label:unknown}}\n" + + " --wavefront.application.name=${spring.cloud.dataflow.stream.name:unknown}\n" + + "displayName: null\n" + + "iconUrl: null\n" + + "kind: SpringCloudDeployerApplication\n" + + "maintainer: dataflow\n" + + "name: ticktock\n" + + "origin: null\n" + + "packageFile: null\n" + + "packageHomeUrl: null\n" + + "packageSourceUrl: null\n" + + "repositoryId: 11\n" + + "repositoryName: repositoryName\n" + + "sha256: null\n" + + "tags: null\n" + + "version: 1.0.0"; + + @Test + public void testSafeConstructor() { + DumperOptions options = new DumperOptions(); + Representer representer = new Representer(options); + representer.getPropertyUtils().setSkipMissingProperties(true); + LoaderOptions loaderOptions = new LoaderOptions(); + Yaml yaml = new Yaml(new PackageMetadataSafeConstructor(loaderOptions), representer); + PackageMetadata packageMetadata = yaml.load(testYaml); + assertThat(packageMetadata.getApiVersion()).isEqualTo("skipper.spring.io/v1"); + assertThat(packageMetadata.getOrigin()).isEqualTo("null"); + assertThat(packageMetadata.getRepositoryId()).isEqualTo(11L); + assertThat(packageMetadata.getRepositoryName()).isEqualTo("repositoryName"); + assertThat(packageMetadata.getKind()).isEqualTo("SpringCloudDeployerApplication"); + assertThat(packageMetadata.getName()).isEqualTo("ticktock"); + assertThat(packageMetadata.getDisplayName()).isEqualTo("null"); + assertThat(packageMetadata.getVersion()).isEqualTo("1.0.0"); + assertThat(packageMetadata.getPackageSourceUrl()).isEqualTo("null"); + assertThat(packageMetadata.getPackageHomeUrl()).isEqualTo("null"); + assertThat(packageMetadata.getTags()).isEqualTo("null"); + assertThat(packageMetadata.getMaintainer()).isEqualTo("dataflow"); + assertThat(packageMetadata.getDescription()).contains("--management.endpoints.web.exposure.include=health,info,bindings"); + assertThat(packageMetadata.getSha256()).isEqualTo("null"); + assertThat(packageMetadata.getIconUrl()).isEqualTo("null"); + } + + @Test + public void testBadYaml() { + DumperOptions options = new DumperOptions(); + Representer representer = new Representer(options); + representer.getPropertyUtils().setSkipMissingProperties(true); + LoaderOptions loaderOptions = new LoaderOptions(); + Yaml yaml = new Yaml(new PackageMetadataSafeConstructor(loaderOptions), representer); + assertThatThrownBy( () -> yaml.load("!!org.springframework.cloud.skipper.domain.PackageMetadata\n" + + "apiVersion: !!javax.script.ScriptEngineManager [!!java.lang.String [\"helloworld\"]]")) + .isInstanceOf(YAMLException.class); + } + +} diff --git a/spring-cloud-skipper/spring-cloud-skipper/src/test/java/org/springframework/cloud/skipper/io/PackageWriterTests.java b/spring-cloud-skipper/spring-cloud-skipper/src/test/java/org/springframework/cloud/skipper/io/PackageWriterTests.java index af459555e2..b582120e13 100644 --- a/spring-cloud-skipper/spring-cloud-skipper/src/test/java/org/springframework/cloud/skipper/io/PackageWriterTests.java +++ b/spring-cloud-skipper/spring-cloud-skipper/src/test/java/org/springframework/cloud/skipper/io/PackageWriterTests.java @@ -30,7 +30,10 @@ import org.junit.jupiter.api.Test; import org.yaml.snakeyaml.DumperOptions; +import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; +import org.yaml.snakeyaml.constructor.SafeConstructor; +import org.yaml.snakeyaml.representer.Representer; import org.zeroturnaround.zip.ZipUtil; import org.springframework.cloud.skipper.TestResourceUtils; @@ -105,7 +108,7 @@ private Package createSimplePackage() throws IOException { DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); dumperOptions.setPrettyFlow(true); - Yaml yaml = new Yaml(dumperOptions); + Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()), new Representer(dumperOptions), dumperOptions); ConfigValues configValues = new ConfigValues(); configValues.setRaw(yaml.dump(map)); pkg.setConfigValues(configValues);