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
This commit is contained in:
Rossen Stoyanchev
2015-11-05 19:31:03 -05:00
parent 237439ef97
commit a3168fde18
3 changed files with 62 additions and 2 deletions

View File

@@ -60,7 +60,17 @@ public abstract class AbstractMappingContentNegotiationStrategy
public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) public List<MediaType> resolveMediaTypes(NativeWebRequest webRequest)
throws HttpMediaTypeNotAcceptableException { throws HttpMediaTypeNotAcceptableException {
String key = getMediaTypeKey(webRequest); return resolveMediaTypeKey(webRequest, getMediaTypeKey(webRequest));
}
/**
* An alternative to {@link #resolveMediaTypes(NativeWebRequest)} that accepts
* an already extracted key.
* @since 3.2.16
*/
public List<MediaType> resolveMediaTypeKey(NativeWebRequest webRequest, String key)
throws HttpMediaTypeNotAcceptableException {
if (StringUtils.hasText(key)) { if (StringUtils.hasText(key)) {
MediaType mediaType = lookupMediaType(key); MediaType mediaType = lookupMediaType(key);
if (mediaType != null) { if (mediaType != null) {

View File

@@ -88,6 +88,14 @@ public class ContentNegotiationManager implements ContentNegotiationStrategy,
} }
/**
* Return the configured content negotiation strategies.
* @since 3.2.16
*/
public List<ContentNegotiationStrategy> getStrategies() {
return this.strategies;
}
/** /**
* Register more {@code MediaTypeFileExtensionResolver} instances in addition * Register more {@code MediaTypeFileExtensionResolver} instances in addition
* to those detected at construction. * to those detected at construction.

View File

@@ -43,6 +43,8 @@ import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
import org.springframework.web.HttpMediaTypeNotAcceptableException; import org.springframework.web.HttpMediaTypeNotAcceptableException;
import org.springframework.web.accept.ContentNegotiationManager; import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.accept.ContentNegotiationStrategy;
import org.springframework.web.accept.PathExtensionContentNegotiationStrategy;
import org.springframework.web.context.request.NativeWebRequest; import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.method.support.HandlerMethodReturnValueHandler; import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
@@ -77,12 +79,18 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
"json", "xml", "atom", "rss", "json", "xml", "atom", "rss",
"png", "jpe", "jpeg", "jpg", "gif", "wbmp", "bmp")); "png", "jpe", "jpeg", "jpg", "gif", "wbmp", "bmp"));
private static final Set<String> WHITELISTED_MEDIA_BASE_TYPES = new HashSet<String>(
Arrays.asList("audio", "image", "video"));
private final ContentNegotiationManager contentNegotiationManager; private final ContentNegotiationManager contentNegotiationManager;
private final PathExtensionContentNegotiationStrategy pathStrategy;
private final Set<String> safeExtensions = new HashSet<String>(); private final Set<String> safeExtensions = new HashSet<String>();
/** /**
* Constructor with list of converters only. * Constructor with list of converters only.
*/ */
@@ -108,10 +116,20 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
super(converters, requestResponseBodyAdvice); super(converters, requestResponseBodyAdvice);
this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager()); this.contentNegotiationManager = (manager != null ? manager : new ContentNegotiationManager());
this.pathStrategy = initPathStrategy(this.contentNegotiationManager);
this.safeExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions()); this.safeExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
this.safeExtensions.addAll(WHITELISTED_EXTENSIONS); this.safeExtensions.addAll(WHITELISTED_EXTENSIONS);
} }
private static PathExtensionContentNegotiationStrategy initPathStrategy(ContentNegotiationManager manager) {
for (ContentNegotiationStrategy strategy : manager.getStrategies()) {
if (strategy instanceof PathExtensionContentNegotiationStrategy) {
return (PathExtensionContentNegotiationStrategy) strategy;
}
}
return new PathExtensionContentNegotiationStrategy();
}
/** /**
* Creates a new {@link HttpOutputMessage} from the given {@link NativeWebRequest}. * Creates a new {@link HttpOutputMessage} from the given {@link NativeWebRequest}.
@@ -386,7 +404,31 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
return true; return true;
} }
} }
return false; return safeMediaTypesForExtension(extension);
}
private boolean safeMediaTypesForExtension(String extension) {
List<MediaType> mediaTypes = null;
try {
mediaTypes = this.pathStrategy.resolveMediaTypeKey(null, extension);
}
catch (HttpMediaTypeNotAcceptableException e) {
// Ignore
}
if (CollectionUtils.isEmpty(mediaTypes)) {
return false;
}
for (MediaType mediaType : mediaTypes) {
if (!safeMediaType(mediaType)) {
return false;
}
}
return true;
}
private boolean safeMediaType(MediaType mediaType) {
return (WHITELISTED_MEDIA_BASE_TYPES.contains(mediaType.getType()) ||
mediaType.getSubtype().endsWith("+xml"));
} }
} }