mirror of
https://github.com/apache/struts.git
synced 2026-08-31 11:24:28 +00:00
WW-5021 Adds validation of the provided static content path
This commit is contained in:
@@ -30,6 +30,7 @@ import org.apache.struts2.components.template.Template;
|
||||
import org.apache.struts2.components.template.TemplateEngine;
|
||||
import org.apache.struts2.components.template.TemplateEngineManager;
|
||||
import org.apache.struts2.components.template.TemplateRenderingContext;
|
||||
import org.apache.struts2.dispatcher.StaticContentLoader;
|
||||
import org.apache.struts2.util.TextProviderHelper;
|
||||
import org.apache.struts2.views.annotations.StrutsTagAttribute;
|
||||
import org.apache.struts2.views.util.ContextUtil;
|
||||
@@ -526,8 +527,8 @@ public abstract class UIBean extends Component {
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH)
|
||||
public void setUiStaticContentPath(String uiStaticContentPath) {
|
||||
this.uiStaticContentPath = uiStaticContentPath;
|
||||
public void setStaticContentPath(String uiStaticContentPath) {
|
||||
this.uiStaticContentPath = StaticContentLoader.Validator.validateStaticContentPath(uiStaticContentPath);
|
||||
}
|
||||
|
||||
@Inject
|
||||
|
||||
@@ -29,6 +29,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.dispatcher.StaticContentLoader;
|
||||
|
||||
public class ConstantConfig {
|
||||
private Boolean devMode;
|
||||
@@ -274,7 +275,7 @@ public class ConstantConfig {
|
||||
map.put(StrutsConstants.STRUTS_LOCALIZED_TEXT_PROVIDER, beanConfToString(localizedTextProvider));
|
||||
map.put(StrutsConstants.STRUTS_DISALLOW_PROXY_MEMBER_ACCESS, Objects.toString(disallowProxyMemberAccess, null));
|
||||
map.put(StrutsConstants.STRUTS_OGNL_AUTO_GROWTH_COLLECTION_LIMIT, Objects.toString(ognlAutoGrowthCollectionLimit, null));
|
||||
map.put(StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH, Objects.toString(staticContentPath, null));
|
||||
map.put(StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH, Objects.toString(staticContentPath, StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH));
|
||||
|
||||
return map;
|
||||
}
|
||||
@@ -1352,6 +1353,6 @@ public class ConstantConfig {
|
||||
}
|
||||
|
||||
public void setStaticContentPath(String staticContentPath) {
|
||||
this.staticContentPath = staticContentPath;
|
||||
this.staticContentPath = StaticContentLoader.Validator.validateStaticContentPath(staticContentPath);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ import java.util.StringTokenizer;
|
||||
*
|
||||
* <p>
|
||||
* This class is used to serve common static content needed when using various parts of Struts, such as JavaScript
|
||||
* files, CSS files, etc. It works by looking for requests to {@link #staticContentPath}/* and then mapping the value
|
||||
* files, CSS files, etc. It works by looking for requests to {@link #uiStaticContentPath}/* and then mapping the value
|
||||
* after to common packages in Struts and, optionally, in your class path. By default, the following packages are
|
||||
* automatically searched:
|
||||
* </p>
|
||||
@@ -60,7 +60,7 @@ import java.util.StringTokenizer;
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* This means that you can simply request {@link #staticContentPath}/xhtml/styles.css and the XHTML UI theme's default stylesheet
|
||||
* This means that you can simply request {@link #uiStaticContentPath}/xhtml/styles.css and the XHTML UI theme's default stylesheet
|
||||
* will be returned. Likewise, many of the AJAX UI components require various JavaScript files, which are found in the
|
||||
* org.apache.struts2.static package. If you wish to add additional packages to be searched, you can add a comma
|
||||
* separated (space, tab and new line will do as well) list in the filter init parameter named "packages". <b>Be
|
||||
@@ -86,9 +86,9 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
|
||||
protected boolean serveStatic;
|
||||
|
||||
/**
|
||||
* Store state of StrutsConstants.STRUTS_STATIC_CONTENT_PATH setting.
|
||||
* Store state of {@link StrutsConstants#STRUTS_UI_STATIC_CONTENT_PATH} setting.
|
||||
*/
|
||||
protected String staticContentPath;
|
||||
protected String uiStaticContentPath;
|
||||
|
||||
/**
|
||||
* Store state of StrutsConstants.STRUTS_SERVE_STATIC_BROWSER_CACHE setting.
|
||||
@@ -118,8 +118,8 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
|
||||
}
|
||||
|
||||
@Inject(StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH)
|
||||
public void setStaticContentPath(String staticContentPath) {
|
||||
this.staticContentPath = staticContentPath;
|
||||
public void setStaticContentPath(String uiStaticContentPath) {
|
||||
this.uiStaticContentPath = StaticContentLoader.Validator.validateStaticContentPath(uiStaticContentPath);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -238,7 +238,7 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
|
||||
LOG.warn("Unable to send error response, code: {};", HttpServletResponse.SC_NOT_FOUND, e1);
|
||||
} catch (IllegalStateException ise) {
|
||||
// Log illegalstate instead of passing unrecoverable exception to calling thread
|
||||
LOG.warn("Unable to send error response, code: {}; isCommited: {};", HttpServletResponse.SC_NOT_FOUND, response.isCommitted(), ise);
|
||||
LOG.warn("Unable to send error response, code: {}; isCommitted: {};", HttpServletResponse.SC_NOT_FOUND, response.isCommitted(), ise);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
|
||||
* Look for a static resource in the classpath.
|
||||
*
|
||||
* @param path The resource path
|
||||
* @return The inputstream of the resource
|
||||
* @return The URL of the resource
|
||||
* @throws IOException If there is a problem locating the resource
|
||||
*/
|
||||
protected URL findResource(String path) throws IOException {
|
||||
@@ -368,16 +368,16 @@ public class DefaultStaticContentLoader implements StaticContentLoader {
|
||||
}
|
||||
|
||||
public boolean canHandle(String resourcePath) {
|
||||
return serveStatic && resourcePath.startsWith(staticContentPath + "/");
|
||||
return serveStatic && resourcePath.startsWith(uiStaticContentPath + "/");
|
||||
}
|
||||
|
||||
/**
|
||||
* @param path requested path
|
||||
* @return path without leading {@link #staticContentPath}
|
||||
* @return path without leading {@link #uiStaticContentPath}
|
||||
*/
|
||||
protected String cleanupPath(String path) {
|
||||
if (path.startsWith(staticContentPath)) {
|
||||
return path.substring(staticContentPath.length());
|
||||
if (path.startsWith(uiStaticContentPath)) {
|
||||
return path.substring(uiStaticContentPath.length());
|
||||
} else {
|
||||
return path;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,10 @@
|
||||
*/
|
||||
package org.apache.struts2.dispatcher;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.config.StrutsBeanSelectionProvider;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
@@ -35,6 +39,12 @@ import java.io.IOException;
|
||||
*/
|
||||
public interface StaticContentLoader {
|
||||
|
||||
/**
|
||||
* Default path at which static content is served, can be changed
|
||||
* by using {@link org.apache.struts2.StrutsConstants#STRUTS_UI_STATIC_CONTENT_PATH}
|
||||
*/
|
||||
String DEFAULT_STATIC_CONTENT_PATH = "/static";
|
||||
|
||||
/**
|
||||
* @param path Requested resource path
|
||||
* @return true if this loader is able to load this type of resource, false otherwise
|
||||
@@ -57,4 +67,32 @@ public interface StaticContentLoader {
|
||||
*/
|
||||
void findStaticResource(String path, HttpServletRequest request, HttpServletResponse response) throws IOException;
|
||||
|
||||
class Validator {
|
||||
|
||||
private static final Logger LOG = LogManager.getLogger(DefaultStaticContentLoader.class);
|
||||
|
||||
public static String validateStaticContentPath(String uiStaticContentPath) {
|
||||
if (StringUtils.isBlank(uiStaticContentPath)) {
|
||||
LOG.warn("\"{}\" has been set to \"{}\", falling back into default value \"{}\"",
|
||||
StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH,
|
||||
uiStaticContentPath,
|
||||
StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH);
|
||||
return StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH;
|
||||
} else if ("/".equals(uiStaticContentPath)) {
|
||||
LOG.warn("\"{}\" cannot be set to \"{}\", falling back into default value \"{}\"",
|
||||
StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH,
|
||||
uiStaticContentPath,
|
||||
StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH);
|
||||
return StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH;
|
||||
} else if(!uiStaticContentPath.startsWith("/")) {
|
||||
LOG.warn("\"{}\" must start with \"/\", but has been set to \"{}\", prepending the missing \"/\"!",
|
||||
StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH,
|
||||
uiStaticContentPath);
|
||||
return "/" + uiStaticContentPath;
|
||||
} else {
|
||||
LOG.debug("\"{}\" has been set to \"{}\"", StrutsConstants.STRUTS_UI_STATIC_CONTENT_PATH, uiStaticContentPath);
|
||||
return uiStaticContentPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ import org.apache.struts2.StrutsInternalTestCase;
|
||||
import org.apache.struts2.components.template.Template;
|
||||
import org.apache.struts2.components.template.TemplateEngine;
|
||||
import org.apache.struts2.components.template.TemplateEngineManager;
|
||||
import org.apache.struts2.dispatcher.DefaultStaticContentLoader;
|
||||
import org.apache.struts2.dispatcher.StaticContentLoader;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
@@ -343,4 +345,34 @@ public class UIBeanTest extends StrutsInternalTestCase {
|
||||
|
||||
assertEquals(nonceVal, dblSelect.getParameters().get("nonce"));
|
||||
}
|
||||
|
||||
public void testSetNullUiStaticContentPath() {
|
||||
// given
|
||||
ValueStack stack = ActionContext.getContext().getValueStack();
|
||||
MockHttpServletRequest req = new MockHttpServletRequest();
|
||||
MockHttpServletResponse res = new MockHttpServletResponse();
|
||||
|
||||
TextField field = new TextField(stack, req, res);
|
||||
|
||||
// when
|
||||
field.setStaticContentPath(null);
|
||||
// then
|
||||
assertEquals(StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH, field.uiStaticContentPath);
|
||||
|
||||
// when
|
||||
field.setStaticContentPath(" ");
|
||||
// then
|
||||
assertEquals(StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH, field.uiStaticContentPath);
|
||||
|
||||
// when
|
||||
field.setStaticContentPath("content");
|
||||
// then
|
||||
assertEquals("/content", field.uiStaticContentPath);
|
||||
|
||||
// when
|
||||
field.setStaticContentPath("/content");
|
||||
// then
|
||||
assertEquals("/content", field.uiStaticContentPath);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,11 +18,17 @@
|
||||
*/
|
||||
package org.apache.struts2.config.entities;
|
||||
|
||||
import com.opensymphony.xwork2.ActionContext;
|
||||
import com.opensymphony.xwork2.TestBean;
|
||||
import com.opensymphony.xwork2.inject.Container;
|
||||
import com.opensymphony.xwork2.util.ValueStack;
|
||||
import org.apache.struts2.StrutsConstants;
|
||||
import org.apache.struts2.components.TextField;
|
||||
import org.apache.struts2.dispatcher.StaticContentLoader;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.springframework.mock.web.MockHttpServletRequest;
|
||||
import org.springframework.mock.web.MockHttpServletResponse;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -109,4 +115,30 @@ public class ConstantConfigTest {
|
||||
map.get(StrutsConstants.STRUTS_DEV_MODE_EXCLUDED_CLASSES));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSettingStaticContentPath() {
|
||||
// given
|
||||
ConstantConfig config = new ConstantConfig();
|
||||
|
||||
// when
|
||||
config.setStaticContentPath(null);
|
||||
// then
|
||||
Assert.assertEquals(StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH, config.getStaticContentPath());
|
||||
|
||||
// when
|
||||
config.setStaticContentPath(" ");
|
||||
// then
|
||||
Assert.assertEquals(StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH, config.getStaticContentPath());
|
||||
|
||||
// when
|
||||
config.setStaticContentPath("content");
|
||||
// then
|
||||
Assert.assertEquals("/content", config.getStaticContentPath());
|
||||
|
||||
// when
|
||||
config.setStaticContentPath("/content");
|
||||
// then
|
||||
Assert.assertEquals("/content", config.getStaticContentPath());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+49
-5
@@ -30,13 +30,12 @@ import static org.easymock.EasyMock.expectLastCall;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
|
||||
public class DefaultStaticContentLoaderTest extends StrutsInternalTestCase {
|
||||
|
||||
private HttpServletRequest requestMock;
|
||||
private HttpServletResponse responseMock;
|
||||
private HostConfig hostConfigMock;
|
||||
private DefaultStaticContentLoader defaultStaticContentLoader;
|
||||
|
||||
public void testParsePackages() throws Exception {
|
||||
|
||||
public void testParsePackages() {
|
||||
DefaultStaticContentLoader filterDispatcher = new DefaultStaticContentLoader();
|
||||
List<String> result1 = filterDispatcher.parse("foo.bar.package1 foo.bar.package2 foo.bar.package3");
|
||||
List<String> result2 = filterDispatcher.parse("foo.bar.package1\tfoo.bar.package2\tfoo.bar.package3");
|
||||
@@ -102,10 +101,55 @@ public class DefaultStaticContentLoaderTest extends StrutsInternalTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
protected void setUp() {
|
||||
public void testSetNullUiStaticContentPath() {
|
||||
// given
|
||||
DefaultStaticContentLoader loader = new DefaultStaticContentLoader();
|
||||
|
||||
// when
|
||||
loader.setStaticContentPath(null);
|
||||
|
||||
// then
|
||||
assertEquals(StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH, loader.uiStaticContentPath);
|
||||
}
|
||||
|
||||
public void testSetEmptyUiStaticContentPath() {
|
||||
// given
|
||||
DefaultStaticContentLoader loader = new DefaultStaticContentLoader();
|
||||
|
||||
// when
|
||||
loader.setStaticContentPath(" ");
|
||||
|
||||
// then
|
||||
assertEquals(StaticContentLoader.DEFAULT_STATIC_CONTENT_PATH, loader.uiStaticContentPath);
|
||||
}
|
||||
|
||||
public void testSetUiStaticContentPathWithoutLeadingSlash() {
|
||||
// given
|
||||
DefaultStaticContentLoader loader = new DefaultStaticContentLoader();
|
||||
|
||||
// when
|
||||
loader.setStaticContentPath("content");
|
||||
|
||||
// then
|
||||
assertEquals("/content", loader.uiStaticContentPath);
|
||||
}
|
||||
|
||||
public void testSetUiStaticContentPath() {
|
||||
// given
|
||||
DefaultStaticContentLoader loader = new DefaultStaticContentLoader();
|
||||
|
||||
// when
|
||||
loader.setStaticContentPath("/content");
|
||||
|
||||
// then
|
||||
assertEquals("/content", loader.uiStaticContentPath);
|
||||
}
|
||||
|
||||
protected void setUp() throws Exception {
|
||||
super.setUp();
|
||||
requestMock = createMock(HttpServletRequest.class);
|
||||
responseMock = createMock(HttpServletResponse.class);
|
||||
hostConfigMock = createMock(HostConfig.class);
|
||||
HostConfig hostConfigMock = createMock(HostConfig.class);
|
||||
expect(hostConfigMock.getInitParameter("packages")).andStubReturn(null);
|
||||
expect(hostConfigMock.getInitParameter("loggerFactory")).andStubReturn(null);
|
||||
defaultStaticContentLoader = new DefaultStaticContentLoader();
|
||||
|
||||
Reference in New Issue
Block a user