No Content-Disposition if HTML in the request mapping

Issue: SPR-13629
This commit is contained in:
Rossen Stoyanchev 2015-11-02 13:07:48 -05:00
parent 1bc41bdf0f
commit bdb71e91ad
2 changed files with 133 additions and 7 deletions

View File

@ -359,14 +359,33 @@ public abstract class AbstractMessageConverterMethodProcessor extends AbstractMe
pathParams = DECODING_URL_PATH_HELPER.decodeRequestString(servletRequest, pathParams);
String extInPathParams = StringUtils.getFilenameExtension(pathParams);
if (!isSafeExtension(ext) || !isSafeExtension(extInPathParams)) {
if (!safeExtension(servletRequest, ext) || !safeExtension(servletRequest, extInPathParams)) {
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=f.txt");
}
}
private boolean isSafeExtension(String extension) {
return (!StringUtils.hasText(extension) ||
this.safeExtensions.contains(extension.toLowerCase(Locale.ENGLISH)));
@SuppressWarnings("unchecked")
private boolean safeExtension(HttpServletRequest request, String extension) {
if (!StringUtils.hasText(extension)) {
return true;
}
extension = extension.toLowerCase(Locale.ENGLISH);
if (this.safeExtensions.contains(extension)) {
return true;
}
if (extension.equals("html")) {
String name = HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE;
String pattern = (String) request.getAttribute(name);
if (pattern != null && pattern.endsWith(".html")) {
return true;
}
name = HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE;
Set<MediaType> mediaTypes = (Set<MediaType>) request.getAttribute(name);
if (!CollectionUtils.isEmpty(mediaTypes) && mediaTypes.contains(MediaType.TEXT_HTML)) {
return true;
}
}
return false;
}
}

View File

@ -16,8 +16,6 @@
package org.springframework.web.servlet.mvc.method.annotation;
import static org.junit.Assert.*;
import java.beans.PropertyEditorSupport;
import java.io.IOException;
import java.io.Serializable;
@ -30,6 +28,7 @@ import java.lang.annotation.Target;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.Charset;
import java.security.Principal;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@ -44,7 +43,6 @@ import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
@ -108,6 +106,8 @@ import org.springframework.validation.BindingResult;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.accept.ContentNegotiationManager;
import org.springframework.web.accept.ContentNegotiationManagerFactoryBean;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.CookieValue;
import org.springframework.web.bind.annotation.ExceptionHandler;
@ -141,6 +141,15 @@ import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import org.springframework.web.servlet.support.RequestContextUtils;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* The origin of this test class is {@link ServletAnnotationControllerHandlerMethodTests}.
*
@ -1624,6 +1633,84 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
assertEquals("Expected an empty content", 0, response.getContentLength());
}
@Test
public void responseBodyAsHtml() throws Exception {
initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
@Override
public void initialize(GenericWebApplicationContext wac) {
ContentNegotiationManagerFactoryBean factoryBean = new ContentNegotiationManagerFactoryBean();
factoryBean.afterPropertiesSet();
RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
adapterDef.getPropertyValues().add("contentNegotiationManager", factoryBean.getObject());
wac.registerBeanDefinition("handlerAdapter", adapterDef);
}
}, TextRestController.class);
byte[] content = "alert('boo')".getBytes(Charset.forName("ISO-8859-1"));
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/a1.html");
request.setContent(content);
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertEquals(200, response.getStatus());
assertEquals("text/html", response.getContentType());
assertEquals("attachment;filename=f.txt", response.getHeader("Content-Disposition"));
assertArrayEquals(content, response.getContentAsByteArray());
}
@Test
public void responseBodyAsHtmlWithSuffixPresent() throws Exception {
initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
@Override
public void initialize(GenericWebApplicationContext wac) {
ContentNegotiationManagerFactoryBean factoryBean = new ContentNegotiationManagerFactoryBean();
factoryBean.afterPropertiesSet();
RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
adapterDef.getPropertyValues().add("contentNegotiationManager", factoryBean.getObject());
wac.registerBeanDefinition("handlerAdapter", adapterDef);
}
}, TextRestController.class);
byte[] content = "alert('boo')".getBytes(Charset.forName("ISO-8859-1"));
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/a2.html");
request.setContent(content);
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertEquals(200, response.getStatus());
assertEquals("text/html", response.getContentType());
assertNull(response.getHeader("Content-Disposition"));
assertArrayEquals(content, response.getContentAsByteArray());
}
@Test
public void responseBodyAsHtmlWithProducesCondition() throws Exception {
initServlet(new ApplicationContextInitializer<GenericWebApplicationContext>() {
@Override
public void initialize(GenericWebApplicationContext wac) {
ContentNegotiationManagerFactoryBean factoryBean = new ContentNegotiationManagerFactoryBean();
factoryBean.afterPropertiesSet();
RootBeanDefinition adapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
adapterDef.getPropertyValues().add("contentNegotiationManager", factoryBean.getObject());
wac.registerBeanDefinition("handlerAdapter", adapterDef);
}
}, TextRestController.class);
byte[] content = "alert('boo')".getBytes(Charset.forName("ISO-8859-1"));
MockHttpServletRequest request = new MockHttpServletRequest("GET", "/a3.html");
request.setContent(content);
MockHttpServletResponse response = new MockHttpServletResponse();
getServlet().service(request, response);
assertEquals(200, response.getStatus());
assertEquals("text/html", response.getContentType());
assertNull(response.getHeader("Content-Disposition"));
assertArrayEquals(content, response.getContentAsByteArray());
}
/*
* Controllers
*/
@ -3083,6 +3170,26 @@ public class ServletAnnotationControllerHandlerMethodTests extends AbstractServl
}
@RestController
public static class TextRestController {
@RequestMapping(path = "/a1", method = RequestMethod.GET)
public String a1(@RequestBody String body) {
return body;
}
@RequestMapping(path = "/a2.html", method = RequestMethod.GET)
public String a2(@RequestBody String body) {
return body;
}
@RequestMapping(path = "/a3", method = RequestMethod.GET, produces = "text/html")
public String a3(@RequestBody String body) throws IOException {
return body;
}
}
// Test cases deleted from the original ServletAnnotationControllerTests: