Skip to content

Commit 062d9e0

Browse files
authored
Merge pull request #13207 from grails/4.1.x-snakeyaml
Upgrade snakeyaml
2 parents b733fdf + 0cef99e commit 062d9e0

File tree

10 files changed

+221
-12
lines changed

10 files changed

+221
-12
lines changed

grails-bootstrap/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import org.apache.tools.ant.filters.ReplaceTokens
33
dependencies {
44
compile ( "org.codehaus.groovy:groovy-xml:$groovyVersion" )
55
compile ( "org.codehaus.groovy:groovy-templates:$groovyVersion" )
6-
compile "org.yaml:snakeyaml:1.33"
6+
compile "org.yaml:snakeyaml:2.2"
77

88
compileOnly("io.methvin:directory-watcher:0.16.1")
99
compileOnly("org.fusesource.jansi:jansi:$jansiVersion")

grails-bootstrap/src/main/groovy/grails/util/Metadata.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.grails.config.NavigableMap
2222
import org.grails.io.support.FileSystemResource
2323
import org.grails.io.support.Resource
2424
import org.grails.io.support.UrlResource
25+
import org.yaml.snakeyaml.LoaderOptions
2526
import org.yaml.snakeyaml.Yaml
2627
import org.yaml.snakeyaml.constructor.SafeConstructor
2728

@@ -168,7 +169,7 @@ class Metadata extends NavigableMap implements ConfigMap {
168169
}
169170

170171
private Object loadYml(InputStream input) {
171-
Yaml yaml = new Yaml(new SafeConstructor())
172+
Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()))
172173
def loadedYaml = yaml.loadAll(input)
173174
List result = []
174175
for(Object yamlObject : loadedYaml) {

grails-bootstrap/src/main/groovy/org/grails/config/CodeGenConfig.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import groovy.transform.CompileDynamic
2222
import groovy.transform.CompileStatic
2323
import org.codehaus.groovy.runtime.DefaultGroovyMethods
2424
import org.codehaus.groovy.runtime.typehandling.GroovyCastException
25+
import org.yaml.snakeyaml.LoaderOptions
2526
import org.yaml.snakeyaml.Yaml
2627
import org.yaml.snakeyaml.constructor.SafeConstructor
2728

@@ -153,7 +154,7 @@ class CodeGenConfig implements Cloneable, ConfigMap {
153154

154155
@CompileDynamic // fails with CompileStatic!
155156
void loadYml(InputStream input) {
156-
Yaml yaml = new Yaml(new SafeConstructor())
157+
Yaml yaml = new Yaml(new SafeConstructor(new LoaderOptions()))
157158
for(Object yamlObject : yaml.loadAll(input)) {
158159
if(yamlObject instanceof Map) { // problem here with CompileStatic
159160
mergeMap((Map)yamlObject)
Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
/*
2+
* Copyright 2012-2023 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.boot.env;
18+
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
import java.util.Map;
22+
import java.util.NoSuchElementException;
23+
import java.util.regex.Pattern;
24+
import java.util.stream.Collectors;
25+
26+
import org.yaml.snakeyaml.DumperOptions;
27+
import org.yaml.snakeyaml.LoaderOptions;
28+
import org.yaml.snakeyaml.Yaml;
29+
import org.yaml.snakeyaml.constructor.BaseConstructor;
30+
import org.yaml.snakeyaml.constructor.Constructor;
31+
import org.yaml.snakeyaml.constructor.SafeConstructor;
32+
import org.yaml.snakeyaml.error.Mark;
33+
import org.yaml.snakeyaml.nodes.CollectionNode;
34+
import org.yaml.snakeyaml.nodes.MappingNode;
35+
import org.yaml.snakeyaml.nodes.Node;
36+
import org.yaml.snakeyaml.nodes.NodeTuple;
37+
import org.yaml.snakeyaml.nodes.ScalarNode;
38+
import org.yaml.snakeyaml.nodes.Tag;
39+
import org.yaml.snakeyaml.representer.Representer;
40+
import org.yaml.snakeyaml.resolver.Resolver;
41+
42+
import org.springframework.beans.factory.config.YamlProcessor;
43+
import org.springframework.boot.origin.Origin;
44+
import org.springframework.boot.origin.OriginTrackedValue;
45+
import org.springframework.boot.origin.TextResourceOrigin;
46+
import org.springframework.boot.origin.TextResourceOrigin.Location;
47+
import org.springframework.core.io.Resource;
48+
import org.springframework.util.ReflectionUtils;
49+
50+
/**
51+
* Class to load {@code .yml} files into a map of {@code String} to
52+
* {@link OriginTrackedValue}.
53+
*
54+
* @author Madhura Bhave
55+
* @author Phillip Webb
56+
*/
57+
class OriginTrackedYamlLoader extends YamlProcessor {
58+
59+
private static final boolean HAS_RESOLVER_LIMIT = ReflectionUtils.findMethod(Resolver.class, "addImplicitResolver",
60+
Tag.class, Pattern.class, String.class, int.class) != null;
61+
62+
private final Resource resource;
63+
64+
OriginTrackedYamlLoader(Resource resource) {
65+
this.resource = resource;
66+
setResources(resource);
67+
}
68+
69+
@Override
70+
protected Yaml createYaml() {
71+
LoaderOptions loaderOptions = new LoaderOptions();
72+
loaderOptions.setAllowDuplicateKeys(false);
73+
loaderOptions.setMaxAliasesForCollections(Integer.MAX_VALUE);
74+
loaderOptions.setAllowRecursiveKeys(true);
75+
return createYaml(loaderOptions);
76+
}
77+
78+
private Yaml createYaml(LoaderOptions loaderOptions) {
79+
BaseConstructor constructor = new OriginTrackingConstructor(loaderOptions);
80+
DumperOptions dumperOptions = new DumperOptions();
81+
Representer representer = new Representer(dumperOptions);
82+
Resolver resolver = HAS_RESOLVER_LIMIT ? new NoTimestampResolverWithLimit() : new NoTimestampResolver();
83+
return new Yaml(constructor, representer, dumperOptions, loaderOptions, resolver);
84+
}
85+
86+
List<Map<String, Object>> load() {
87+
final List<Map<String, Object>> result = new ArrayList<>();
88+
process((properties, map) -> result.add(getFlattenedMap(map)));
89+
return result;
90+
}
91+
92+
/**
93+
* {@link Constructor} that tracks property origins.
94+
*/
95+
private class OriginTrackingConstructor extends SafeConstructor {
96+
97+
OriginTrackingConstructor(LoaderOptions loadingConfig) {
98+
super(loadingConfig);
99+
}
100+
101+
@Override
102+
public Object getData() throws NoSuchElementException {
103+
Object data = super.getData();
104+
if (data instanceof CharSequence && ((CharSequence) data).length() == 0) {
105+
return null;
106+
}
107+
return data;
108+
}
109+
110+
@Override
111+
protected Object constructObject(Node node) {
112+
if (node instanceof CollectionNode && ((CollectionNode<?>) node).getValue().isEmpty()) {
113+
return constructTrackedObject(node, super.constructObject(node));
114+
}
115+
if (node instanceof ScalarNode) {
116+
if (!(node instanceof KeyScalarNode)) {
117+
return constructTrackedObject(node, super.constructObject(node));
118+
}
119+
}
120+
if (node instanceof MappingNode) {
121+
replaceMappingNodeKeys((MappingNode) node);
122+
}
123+
return super.constructObject(node);
124+
}
125+
126+
private void replaceMappingNodeKeys(MappingNode node) {
127+
node.setValue(node.getValue().stream().map(KeyScalarNode::get).collect(Collectors.toList()));
128+
}
129+
130+
private Object constructTrackedObject(Node node, Object value) {
131+
Origin origin = getOrigin(node);
132+
return OriginTrackedValue.of(getValue(value), origin);
133+
}
134+
135+
private Object getValue(Object value) {
136+
return (value != null) ? value : "";
137+
}
138+
139+
private Origin getOrigin(Node node) {
140+
Mark mark = node.getStartMark();
141+
Location location = new Location(mark.getLine(), mark.getColumn());
142+
return new TextResourceOrigin(OriginTrackedYamlLoader.this.resource, location);
143+
}
144+
145+
}
146+
147+
/**
148+
* {@link ScalarNode} that replaces the key node in a {@link NodeTuple}.
149+
*/
150+
private static class KeyScalarNode extends ScalarNode {
151+
152+
KeyScalarNode(ScalarNode node) {
153+
super(node.getTag(), node.getValue(), node.getStartMark(), node.getEndMark(), node.getScalarStyle());
154+
}
155+
156+
static NodeTuple get(NodeTuple nodeTuple) {
157+
Node keyNode = nodeTuple.getKeyNode();
158+
Node valueNode = nodeTuple.getValueNode();
159+
return new NodeTuple(KeyScalarNode.get(keyNode), valueNode);
160+
}
161+
162+
private static Node get(Node node) {
163+
if (node instanceof ScalarNode) {
164+
return new KeyScalarNode((ScalarNode) node);
165+
}
166+
return node;
167+
}
168+
169+
}
170+
171+
/**
172+
* {@link Resolver} that limits {@link Tag#TIMESTAMP} tags.
173+
*/
174+
private static class NoTimestampResolver extends Resolver {
175+
176+
@Override
177+
public void addImplicitResolver(Tag tag, Pattern regexp, String first) {
178+
if (tag == Tag.TIMESTAMP) {
179+
return;
180+
}
181+
super.addImplicitResolver(tag, regexp, first);
182+
}
183+
184+
}
185+
186+
/**
187+
* {@link Resolver} that limits {@link Tag#TIMESTAMP} tags.
188+
*/
189+
private static class NoTimestampResolverWithLimit extends Resolver {
190+
191+
@Override
192+
public void addImplicitResolver(Tag tag, Pattern regexp, String first, int limit) {
193+
if (tag == Tag.TIMESTAMP) {
194+
return;
195+
}
196+
super.addImplicitResolver(tag, regexp, first, limit);
197+
}
198+
199+
}
200+
201+
}

grails-docs/src/main/groovy/grails/doc/DocPublisher.groovy

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import org.apache.commons.logging.LogFactory
2424
import org.radeox.api.engine.WikiRenderEngine
2525
import org.radeox.engine.context.BaseInitialRenderContext
2626
import org.radeox.engine.context.BaseRenderContext
27+
import org.yaml.snakeyaml.LoaderOptions
2728
import org.yaml.snakeyaml.Yaml
2829
import org.yaml.snakeyaml.constructor.SafeConstructor
2930

@@ -265,7 +266,7 @@ class DocPublisher {
265266
def legacyLinks = [:]
266267
if (legacyLinksFile.exists()) {
267268
legacyLinksFile.withInputStream { input ->
268-
legacyLinks = new Yaml(new SafeConstructor()).load(input)
269+
legacyLinks = new Yaml(new SafeConstructor(new LoaderOptions())).load(input)
269270
}
270271
}
271272

@@ -537,7 +538,7 @@ class DocPublisher {
537538
}
538539
else if(propertiesFile.name.endsWith('.yml')) {
539540
propertiesFile.withInputStream { input ->
540-
def ymls = new Yaml(new SafeConstructor()).loadAll(input)
541+
def ymls = new Yaml(new SafeConstructor(new LoaderOptions())).loadAll(input)
541542
for(yml in ymls) {
542543
if(yml instanceof Map) {
543544
def config = yml.grails?.doc

grails-docs/src/main/groovy/grails/doc/internal/YamlTocStrategy.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
package grails.doc.internal
22

3+
import org.yaml.snakeyaml.LoaderOptions
34
import org.yaml.snakeyaml.Yaml
45
import org.yaml.snakeyaml.constructor.SafeConstructor
56

67
/**
78
* Class representing a Grails user guide table of contents defined in YAML.
89
*/
910
class YamlTocStrategy {
10-
private final parser = new Yaml(new SafeConstructor())
11+
private final parser = new Yaml(new SafeConstructor(new LoaderOptions()))
1112
private resourceChecker
1213
private String ext = ".gdoc"
1314

grails-shell/src/main/groovy/org/grails/cli/gradle/cache/MapReadingCachedGradleOperation.groovy

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import groovy.transform.CompileStatic
2020
import groovy.transform.InheritConstructors
2121
import org.gradle.tooling.ProjectConnection
2222
import org.yaml.snakeyaml.DumperOptions
23+
import org.yaml.snakeyaml.LoaderOptions
2324
import org.yaml.snakeyaml.Yaml
2425
import org.yaml.snakeyaml.constructor.SafeConstructor
2526
import org.yaml.snakeyaml.representer.Representer
@@ -37,7 +38,7 @@ abstract class MapReadingCachedGradleOperation <V> extends CachedGradleOperation
3738
@Override
3839
Map<String, V> readFromCached(File f) {
3940
def map = (Map<String, Object>) f.withReader { BufferedReader r ->
40-
new Yaml(new SafeConstructor()).load(r)
41+
new Yaml(new SafeConstructor(new LoaderOptions())).load(r)
4142
}
4243
Map<String, V> newMap = [:]
4344

@@ -61,7 +62,7 @@ abstract class MapReadingCachedGradleOperation <V> extends CachedGradleOperation
6162
return [(key):val.toString()]
6263
}
6364
}
64-
new Yaml(new SafeConstructor(), new Representer(), options).dump(toWrite, writer)
65+
new Yaml(new SafeConstructor(new LoaderOptions()), new Representer(options), options).dump(toWrite, writer)
6566
}
6667

6768
}

grails-shell/src/main/groovy/org/grails/cli/profile/AbstractProfile.groovy

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import org.grails.cli.profile.commands.DefaultMultiStepCommand
3333
import org.grails.cli.profile.commands.script.GroovyScriptCommand
3434
import org.grails.config.NavigableMap
3535
import org.grails.io.support.Resource
36+
import org.yaml.snakeyaml.LoaderOptions
3637
import org.yaml.snakeyaml.Yaml
3738
import org.yaml.snakeyaml.constructor.SafeConstructor
3839

@@ -108,7 +109,7 @@ abstract class AbstractProfile implements Profile {
108109

109110
protected void initialize() {
110111
def profileYml = profileDir.createRelative("profile.yml")
111-
Map<String, Object> profileConfig = new Yaml(new SafeConstructor()).<Map<String, Object>> load(profileYml.getInputStream())
112+
Map<String, Object> profileConfig = new Yaml(new SafeConstructor(new LoaderOptions())).<Map<String, Object>> load(profileYml.getInputStream())
112113

113114
name = profileConfig.get("name")?.toString()
114115
description = profileConfig.get("description")?.toString() ?: ''
@@ -138,7 +139,7 @@ abstract class AbstractProfile implements Profile {
138139
else if(fileName.endsWith('.yml')) {
139140
def yamlCommand = profileDir.createRelative("commands/$fileName")
140141
if(yamlCommand.exists()) {
141-
Map<String, Object> data = new Yaml(new SafeConstructor()).<Map>load(yamlCommand.getInputStream())
142+
Map<String, Object> data = new Yaml(new SafeConstructor(new LoaderOptions())).<Map>load(yamlCommand.getInputStream())
142143
Command cmd = new DefaultMultiStepCommand(clsName.toString(), this, data)
143144
Object minArguments = data?.minArguments
144145
cmd.minArguments = minArguments instanceof Integer ? (Integer)minArguments : 1

grails-shell/src/main/groovy/org/grails/cli/profile/DefaultFeature.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import org.eclipse.aether.artifact.DefaultArtifact
2323
import org.eclipse.aether.graph.Dependency
2424
import org.grails.config.NavigableMap
2525
import org.grails.io.support.Resource
26+
import org.yaml.snakeyaml.LoaderOptions
2627
import org.yaml.snakeyaml.Yaml
2728
import org.yaml.snakeyaml.constructor.SafeConstructor
2829

@@ -49,7 +50,7 @@ class DefaultFeature implements Feature {
4950
this.name = name
5051
this.location = location
5152
def featureYml = location.createRelative("feature.yml")
52-
Map<String, Object> featureConfig = new Yaml(new SafeConstructor()).<Map<String, Object>>load(featureYml.getInputStream())
53+
Map<String, Object> featureConfig = new Yaml(new SafeConstructor(new LoaderOptions())).<Map<String, Object>>load(featureYml.getInputStream())
5354
configuration.merge(featureConfig)
5455
def dependencyMap = configuration.get("dependencies")
5556

grails-shell/src/main/groovy/org/grails/cli/profile/commands/factory/YamlCommandFactory.groovy

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import org.grails.cli.profile.Command
2222
import org.grails.cli.profile.Profile
2323
import org.grails.cli.profile.commands.DefaultMultiStepCommand
2424
import org.grails.io.support.Resource
25+
import org.yaml.snakeyaml.LoaderOptions
2526
import org.yaml.snakeyaml.Yaml
2627
import org.yaml.snakeyaml.constructor.SafeConstructor
2728

@@ -36,7 +37,7 @@ import java.util.regex.Pattern
3637
*/
3738
@CompileStatic
3839
class YamlCommandFactory extends ResourceResolvingCommandFactory<Map> {
39-
protected Yaml yamlParser=new Yaml(new SafeConstructor())
40+
protected Yaml yamlParser=new Yaml(new SafeConstructor(new LoaderOptions()))
4041
// LAX parser for JSON: http://mrhaki.blogspot.ie/2014/08/groovy-goodness-relax-groovy-will-parse.html
4142
protected JsonSlurper jsonSlurper = new JsonSlurper().setType(JsonParserType.LAX)
4243

0 commit comments

Comments
 (0)