Skip to content

Commit a3168fd

Browse files
committed
Expand range of whitelisted extensions by media type
This commit expands the range of whitelisted extensions by checking if an extension can be resolved to image/*, audo/*, video/*, as well as any content type that ends with +xml. Issue: SPR-13643
1 parent 237439e commit a3168fd

File tree

3 files changed

+62
-2
lines changed

3 files changed

+62
-2
lines changed

spring-web/src/main/java/org/springframework/web/accept/AbstractMappingContentNegotiationStrategy.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,17 @@ public AbstractMappingContentNegotiationStrategy(Map<String, MediaType> mediaTyp
6060
public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest)
6161
throws HttpMediaTypeNotAcceptableException {
6262

63-
String key = getMediaTypeKey(webRequest);
63+
return resolveMediaTypeKey(webRequest, getMediaTypeKey(webRequest));
64+
}
65+
66+
/**
67+
* An alternative to {@link #resolveMediaTypes(NativeWebRequest)} that accepts
68+
* an already extracted key.
69+
* @since 3.2.16
70+
*/
71+
public List<MediaType> resolveMediaTypeKey(NativeWebRequest webRequest, String key)
72+
throws HttpMediaTypeNotAcceptableException {
73+
6474
if (StringUtils.hasText(key)) {
6575
MediaType mediaType = lookupMediaType(key);
6676
if (mediaType != null) {

spring-web/src/main/java/org/springframework/web/accept/ContentNegotiationManager.java

+8
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,14 @@ public ContentNegotiationManager() {
8888
}
8989

9090

91+
/**
92+
* Return the configured content negotiation strategies.
93+
* @since 3.2.16
94+
*/
95+
public List<ContentNegotiationStrategy> getStrategies() {
96+
return this.strategies;
97+
}
98+
9199
/**
92100
* Register more {@code MediaTypeFileExtensionResolver} instances in addition
93101
* to those detected at construction.

spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/AbstractMessageConverterMethodProcessor.java

+43-1
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@
4343
import org.springframework.util.StringUtils;
4444
import org.springframework.web.HttpMediaTypeNotAcceptableException;
4545
import org.springframework.web.accept.ContentNegotiationManager;
46+
import org.springframework.web.accept.ContentNegotiationStrategy;
47+
import org.springframework.web.accept.PathExtensionContentNegotiationStrategy;
4648
import org.springframework.web.context.request.NativeWebRequest;
4749
import org.springframework.web.context.request.ServletWebRequest;
4850
import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
@@ -77,12 +79,18 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
7779
"json", "xml", "atom", "rss",
7880
"png", "jpe", "jpeg", "jpg", "gif", "wbmp", "bmp"));
7981

82+
private static final Set<String> WHITELISTED_MEDIA_BASE_TYPES = new HashSet<String>(
83+
Arrays.asList("audio", "image", "video"));
84+
8085

8186
private final ContentNegotiationManager contentNegotiationManager;
8287

88+
private final PathExtensionContentNegotiationStrategy pathStrategy;
89+
8390
private final Set<String> safeExtensions = new HashSet<String>();
8491

8592

93+
8694
/**
8795
* Constructor with list of converters only.
8896
*/
@@ -108,10 +116,20 @@ protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>>
108116

109117
super(converters, requestResponseBodyAdvice);
110118
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
119+
this.pathStrategy = initPathStrategy(this.contentNegotiationManager);
111120
this.safeExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
112121
this.safeExtensions.addAll(WHITELISTED_EXTENSIONS);
113122
}
114123

124+
private static PathExtensionContentNegotiationStrategy initPathStrategy(ContentNegotiationManager manager) {
125+
for (ContentNegotiationStrategy strategy : manager.getStrategies()) {
126+
if (strategy instanceof PathExtensionContentNegotiationStrategy) {
127+
return (PathExtensionContentNegotiationStrategy) strategy;
128+
}
129+
}
130+
return new PathExtensionContentNegotiationStrategy();
131+
}
132+
115133

116134
/**
117135
* Creates a new {@link HttpOutputMessage} from the given {@link NativeWebRequest}.
@@ -386,7 +404,31 @@ private boolean safeExtension(HttpServletRequest request, String extension) {
386404
return true;
387405
}
388406
}
389-
return false;
407+
return safeMediaTypesForExtension(extension);
408+
}
409+
410+
private boolean safeMediaTypesForExtension(String extension) {
411+
List<MediaType> mediaTypes = null;
412+
try {
413+
mediaTypes = this.pathStrategy.resolveMediaTypeKey(null, extension);
414+
}
415+
catch (HttpMediaTypeNotAcceptableException e) {
416+
// Ignore
417+
}
418+
if (CollectionUtils.isEmpty(mediaTypes)) {
419+
return false;
420+
}
421+
for (MediaType mediaType : mediaTypes) {
422+
if (!safeMediaType(mediaType)) {
423+
return false;
424+
}
425+
}
426+
return true;
427+
}
428+
429+
private boolean safeMediaType(MediaType mediaType) {
430+
return (WHITELISTED_MEDIA_BASE_TYPES.contains(mediaType.getType()) ||
431+
mediaType.getSubtype().endsWith("+xml"));
390432
}
391433

392434
}

0 commit comments

Comments
 (0)