4343import org .springframework .util .StringUtils ;
4444import org .springframework .web .HttpMediaTypeNotAcceptableException ;
4545import org .springframework .web .accept .ContentNegotiationManager ;
46+ import org .springframework .web .accept .ContentNegotiationStrategy ;
47+ import org .springframework .web .accept .PathExtensionContentNegotiationStrategy ;
4648import org .springframework .web .context .request .NativeWebRequest ;
4749import org .springframework .web .context .request .ServletWebRequest ;
4850import 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