3939import org.springframework.util.StringUtils;
4040import org.springframework.web.HttpMediaTypeNotAcceptableException;
4141import org.springframework.web.accept.ContentNegotiationManager;
42+ import org.springframework.web.accept.ContentNegotiationStrategy;
43+ import org.springframework.web.accept.PathExtensionContentNegotiationStrategy;
4244import org.springframework.web.context.request.NativeWebRequest;
4345import org.springframework.web.context.request.ServletWebRequest;
4446import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
@@ -73,8 +75,14 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
7375 "json", "xml", "atom", "rss",
7476 "png", "jpe", "jpeg", "jpg", "gif", "wbmp", "bmp"));
7577
78+ private static final Set<String> WHITELISTED_MEDIA_BASE_TYPES = new HashSet<String>(
79+ Arrays.asList("audio", "image", "video"));
80+
81+
7682 private final ContentNegotiationManager contentNegotiationManager;
7783
84+ private final PathExtensionContentNegotiationStrategy pathStrategy;
85+
7886 private final ResponseBodyAdviceChain adviceChain;
7987
8088 private final Set<String> safeExtensions = new HashSet<String>();
@@ -94,11 +102,21 @@ protected AbstractMessageConverterMethodProcessor(List<HttpMessageConverter<?>>
94102
95103 super(messageConverters);
96104 this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
105+ this.pathStrategy = initPathStrategy(this.contentNegotiationManager);
97106 this.adviceChain = new ResponseBodyAdviceChain(responseBodyAdvice);
98107 this.safeExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
99108 this.safeExtensions.addAll(WHITELISTED_EXTENSIONS);
100109 }
101110
111+ private static PathExtensionContentNegotiationStrategy initPathStrategy(ContentNegotiationManager manager) {
112+ for (ContentNegotiationStrategy strategy : manager.getStrategies()) {
113+ if (strategy instanceof PathExtensionContentNegotiationStrategy) {
114+ return (PathExtensionContentNegotiationStrategy) strategy;
115+ }
116+ }
117+ return new PathExtensionContentNegotiationStrategy();
118+ }
119+
102120
103121 protected ResponseBodyAdviceChain getAdviceChain() {
104122 return this.adviceChain;
@@ -322,7 +340,31 @@ private boolean safeExtension(HttpServletRequest request, String extension) {
322340 return true;
323341 }
324342 }
325- return false;
343+ return safeMediaTypesForExtension(extension);
344+ }
345+
346+ private boolean safeMediaTypesForExtension(String extension) {
347+ List<MediaType> mediaTypes = null;
348+ try {
349+ mediaTypes = this.pathStrategy.resolveMediaTypeKey(null, extension);
350+ }
351+ catch (HttpMediaTypeNotAcceptableException e) {
352+ // Ignore
353+ }
354+ if (CollectionUtils.isEmpty(mediaTypes)) {
355+ return false;
356+ }
357+ for (MediaType mediaType : mediaTypes) {
358+ if (!safeMediaType(mediaType)) {
359+ return false;
360+ }
361+ }
362+ return true;
363+ }
364+
365+ private boolean safeMediaType(MediaType mediaType) {
366+ return (WHITELISTED_MEDIA_BASE_TYPES.contains(mediaType.getType()) ||
367+ mediaType.getSubtype().endsWith("+xml"));
326368 }
327369
328370}
0 commit comments