diff --git a/core/src/main/java/org/apache/struts2/interceptor/CoepInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/CoepInterceptor.java index 850556e32d..cafe000601 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/CoepInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/CoepInterceptor.java @@ -22,6 +22,7 @@ import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.interceptor.PreResultListener; import com.opensymphony.xwork2.util.TextParseUtil; +import org.apache.struts2.RequestUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -58,7 +59,7 @@ public String intercept(ActionInvocation invocation) throws Exception { @Override public void beforeResult(ActionInvocation invocation, String resultCode) { HttpServletRequest req = invocation.getInvocationContext().getServletRequest(); - final String path = req.getContextPath(); + final String path = RequestUtils.getUri(req); if (exemptedPaths.contains(path)) { // no need to add headers diff --git a/core/src/main/java/org/apache/struts2/interceptor/CoopInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/CoopInterceptor.java index 123170baff..209db33e2b 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/CoopInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/CoopInterceptor.java @@ -22,6 +22,7 @@ import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.interceptor.PreResultListener; import com.opensymphony.xwork2.util.TextParseUtil; +import org.apache.struts2.RequestUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -60,7 +61,7 @@ public String intercept(ActionInvocation invocation) throws Exception { @Override public void beforeResult(ActionInvocation invocation, String resultCode) { HttpServletRequest request = invocation.getInvocationContext().getServletRequest(); - String path = request.getContextPath(); + String path = RequestUtils.getUri(request); if (isExempted(path)) { // no need to add headers diff --git a/core/src/main/java/org/apache/struts2/interceptor/FetchMetadataInterceptor.java b/core/src/main/java/org/apache/struts2/interceptor/FetchMetadataInterceptor.java index 0f46206f2c..17d7cbbdaa 100644 --- a/core/src/main/java/org/apache/struts2/interceptor/FetchMetadataInterceptor.java +++ b/core/src/main/java/org/apache/struts2/interceptor/FetchMetadataInterceptor.java @@ -23,6 +23,7 @@ import com.opensymphony.xwork2.inject.Inject; import com.opensymphony.xwork2.interceptor.AbstractInterceptor; import com.opensymphony.xwork2.util.TextParseUtil; +import org.apache.struts2.RequestUtils; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -67,9 +68,9 @@ public String intercept(ActionInvocation invocation) throws Exception { addVaryHeaders(invocation); - String contextPath = request.getContextPath(); + String path = RequestUtils.getUri(request); // Apply exemptions: paths/endpoints meant to be served cross-origin - if (exemptedPaths.contains(contextPath)) { + if (exemptedPaths.contains(path)) { return invocation.invoke(); } @@ -78,7 +79,7 @@ public String intercept(ActionInvocation invocation) throws Exception { return invocation.invoke(); } - LOG.warn("Fetch metadata rejected cross-origin request to: {}", contextPath); + LOG.warn("Fetch metadata rejected cross-origin request to: {}", path); return SC_FORBIDDEN; } diff --git a/core/src/test/java/org/apache/struts2/interceptor/CoepInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/CoepInterceptorTest.java index 72ea1a024c..83392d6bf5 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/CoepInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/CoepInterceptorTest.java @@ -52,7 +52,19 @@ public void testEnforcingHeader() throws Exception { } public void testExemptedPath() throws Exception{ - request.setContextPath("/foo"); + request.setServletPath("/foo"); + interceptor.setEnforcingMode("true"); + + interceptor.intercept(mai); + + String header = response.getHeader(COEP_ENFORCING_HEADER); + assertTrue("COEP applied to exempted path", Strings.isEmpty(header)); + } + + public void testExemptedPathInfo() throws Exception { + interceptor.setExemptedPaths("/rest/orders"); + request.setServletPath("/rest"); + request.setPathInfo("/orders"); interceptor.setEnforcingMode("true"); interceptor.intercept(mai); @@ -61,6 +73,16 @@ public void testExemptedPath() throws Exception{ assertTrue("COEP applied to exempted path", Strings.isEmpty(header)); } + public void testHeaderSetWhenOnlyContextPathIsExempted() throws Exception { + request.setContextPath("/foo"); + request.setServletPath("/index.action"); + interceptor.setEnforcingMode("true"); + + interceptor.intercept(mai); + + assertEquals("COEP header value is incorrect", HEADER_CONTENT, response.getHeader(COEP_ENFORCING_HEADER)); + } + public void testReportingHeader() throws Exception { interceptor.setEnforcingMode("false"); diff --git a/core/src/test/java/org/apache/struts2/interceptor/CoopInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/CoopInterceptorTest.java index 544e7b5d88..b9e7aac0c8 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/CoopInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/CoopInterceptorTest.java @@ -38,7 +38,7 @@ public class CoopInterceptorTest extends StrutsInternalTestCase { String COOP_HEADER = "Cross-Origin-Opener-Policy"; public void testHeaderIsSetNonExemptedPath() throws Exception { - request.setContextPath("/some"); + request.setServletPath("/some.action"); interceptor.intercept(mai); String header = response.getHeader(COOP_HEADER); @@ -47,16 +47,34 @@ public void testHeaderIsSetNonExemptedPath() throws Exception { } public void testHeaderIsNotSetExemptedPath() throws Exception { - request.setContextPath("/foo"); + request.setServletPath("/foo"); + interceptor.intercept(mai); + + String header = response.getHeader(COOP_HEADER); + assertTrue("Coop header exists in exempted path", Strings.isEmpty(header)); + } + + public void testHeaderIsNotSetExemptedPathInfo() throws Exception { + interceptor.setExemptedPaths("/rest/orders"); + request.setServletPath("/rest"); + request.setPathInfo("/orders"); interceptor.intercept(mai); String header = response.getHeader(COOP_HEADER); assertTrue("Coop header exists in exempted path", Strings.isEmpty(header)); } + public void testHeaderIsSetWhenOnlyContextPathIsExempted() throws Exception { + request.setContextPath("/foo"); + request.setServletPath("/index.action"); + interceptor.intercept(mai); + + assertEquals("Coop header is not same-origin", SAME_ORIGIN, response.getHeader(COOP_HEADER)); + } + public void testChangeDefaultMode() throws Exception { interceptor.setMode("unsafe-none"); - request.setContextPath("/some"); + request.setServletPath("/some.action"); interceptor.intercept(mai); String header = response.getHeader(COOP_HEADER); @@ -65,7 +83,7 @@ public void testChangeDefaultMode() throws Exception { } public void testErrorNotRecognizedMode() { - request.setContextPath("/some"); + request.setServletPath("/some.action"); try{ interceptor.setMode("foobar"); diff --git a/core/src/test/java/org/apache/struts2/interceptor/FetchMetadataInterceptorTest.java b/core/src/test/java/org/apache/struts2/interceptor/FetchMetadataInterceptorTest.java index 9b52c94dfd..189472aa77 100644 --- a/core/src/test/java/org/apache/struts2/interceptor/FetchMetadataInterceptorTest.java +++ b/core/src/test/java/org/apache/struts2/interceptor/FetchMetadataInterceptorTest.java @@ -33,6 +33,7 @@ import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.mock.web.MockHttpServletResponse; +import javax.servlet.RequestDispatcher; import javax.servlet.http.HttpServletResponse; import java.util.Arrays; import java.util.Collection; @@ -150,21 +151,46 @@ public void testInvalidTopLevelNavigation() throws Exception { public void testPathInExemptedPaths() throws Exception { request.addHeader(SEC_FETCH_SITE_HEADER, "foo"); - request.setContextPath("/foo"); + request.setServletPath("/foo"); assertNotEquals("Expected interceptor to accept this request", SC_FORBIDDEN, interceptor.intercept(mai)); } public void testPathNotInExemptedPaths() throws Exception { request.addHeader(SEC_FETCH_SITE_HEADER, "foo"); - request.setContextPath("/foobar"); + request.setServletPath("/foobar"); + + assertEquals("Expected interceptor to NOT accept this request", SC_FORBIDDEN, interceptor.intercept(mai)); + } + + public void testPathInfoInExemptedPaths() throws Exception { + interceptor.setExemptedPaths("/rest/orders"); + request.addHeader(SEC_FETCH_SITE_HEADER, SITE_CROSS_SITE); + request.setServletPath("/rest"); + request.setPathInfo("/orders"); + + assertNotEquals("Expected interceptor to accept this request", SC_FORBIDDEN, interceptor.intercept(mai)); + } + + public void testContextPathInExemptedPathsDoesNotExempt() throws Exception { + request.addHeader(SEC_FETCH_SITE_HEADER, SITE_CROSS_SITE); + request.setContextPath("/foo"); + request.setServletPath("/index.action"); assertEquals("Expected interceptor to NOT accept this request", SC_FORBIDDEN, interceptor.intercept(mai)); } + public void testIncludedPathInExemptedPaths() throws Exception { + request.addHeader(SEC_FETCH_SITE_HEADER, SITE_CROSS_SITE); + request.setServletPath("/outer.action"); + request.setAttribute(RequestDispatcher.INCLUDE_SERVLET_PATH, "/foo"); + + assertNotEquals("Expected interceptor to accept this request", SC_FORBIDDEN, interceptor.intercept(mai)); + } + public void testVaryHeaderAcceptedReq() throws Exception { request.addHeader(SEC_FETCH_SITE_HEADER, "foo"); - request.setContextPath("/foo"); + request.setServletPath("/foo"); interceptor.intercept(mai); @@ -222,11 +248,11 @@ public void testSetExemptedPathsInjectionIndirectly() throws Exception { FetchMetadataInterceptor configuredFetchMetadataInterceptor = (FetchMetadataInterceptor) configuredFetchMetadataInterceptorMapping.getInterceptor(); request.removeHeader(SEC_FETCH_SITE_HEADER); request.addHeader(SEC_FETCH_SITE_HEADER, "foo"); - request.setContextPath("/foo"); + request.setServletPath("/foo"); assertEquals("Expected interceptor to NOT accept this request [/foo]", SC_FORBIDDEN, configuredFetchMetadataInterceptor.intercept(mai)); - request.setContextPath("/fetchMetadataExemptedGlobal"); + request.setServletPath("/fetchMetadataExemptedGlobal.action"); assertNotEquals("Expected interceptor to accept this request [/fetchMetadataExemptedGlobal]", SC_FORBIDDEN, configuredFetchMetadataInterceptor.intercept(mai)); - request.setContextPath("/someOtherPath"); + request.setServletPath("/someOtherPath.action"); assertNotEquals("Expected interceptor to accept this request [/someOtherPath]", SC_FORBIDDEN, configuredFetchMetadataInterceptor.intercept(mai)); // The test configuration in "struts-testing.xml" should also contain three actions configured differently for the "fetchMetadata" interceptor. @@ -257,8 +283,8 @@ public void testSetExemptedPathsInjectionIndirectly() throws Exception { configuredFetchMetadataInterceptor = (FetchMetadataInterceptor) configuredFetchMetadataInterceptorMapping.getInterceptor(); request.removeHeader(SEC_FETCH_SITE_HEADER); request.addHeader(SEC_FETCH_SITE_HEADER, fetchMetadataExemptedActionConfig.getName()); - request.setContextPath("/" + fetchMetadataExemptedActionConfig.getName()); - assertNotEquals("Expected interceptor to accept this request [" + "/" + fetchMetadataExemptedActionConfig.getName() + "]", SC_FORBIDDEN, configuredFetchMetadataInterceptor.intercept(mai)); + request.setServletPath("/" + fetchMetadataExemptedActionConfig.getName() + ".action"); + assertNotEquals("Expected interceptor to accept this request [" + "/" + fetchMetadataExemptedActionConfig.getName() + ".action" + "]", SC_FORBIDDEN, configuredFetchMetadataInterceptor.intercept(mai)); // Test fetchMetadata interceptor for the "fetchMetadataNotExempted" action. currentActionInterceptors = fetchMetadataNotExemptedActionConfig.getInterceptors(); @@ -277,8 +303,8 @@ public void testSetExemptedPathsInjectionIndirectly() throws Exception { configuredFetchMetadataInterceptor = (FetchMetadataInterceptor) configuredFetchMetadataInterceptorMapping.getInterceptor(); request.removeHeader(SEC_FETCH_SITE_HEADER); request.addHeader(SEC_FETCH_SITE_HEADER, fetchMetadataNotExemptedActionConfig.getName()); - request.setContextPath("/" + fetchMetadataNotExemptedActionConfig.getName()); - assertEquals("Expected interceptor to NOT accept this request [" + "/" + fetchMetadataNotExemptedActionConfig.getName() + "]", SC_FORBIDDEN, configuredFetchMetadataInterceptor.intercept(mai)); + request.setServletPath("/" + fetchMetadataNotExemptedActionConfig.getName() + ".action"); + assertEquals("Expected interceptor to NOT accept this request [" + "/" + fetchMetadataNotExemptedActionConfig.getName() + ".action" + "]", SC_FORBIDDEN, configuredFetchMetadataInterceptor.intercept(mai)); // Test fetchMetadata interceptor for the "fetchMetadataExemptedGlobal" action. currentActionInterceptors = fetchMetadataExemptedGlobalActionConfig.getInterceptors(); @@ -297,8 +323,8 @@ public void testSetExemptedPathsInjectionIndirectly() throws Exception { configuredFetchMetadataInterceptor = (FetchMetadataInterceptor) configuredFetchMetadataInterceptorMapping.getInterceptor(); request.removeHeader(SEC_FETCH_SITE_HEADER); request.addHeader(SEC_FETCH_SITE_HEADER, fetchMetadataExemptedGlobalActionConfig.getName()); - request.setContextPath("/" + fetchMetadataExemptedGlobalActionConfig.getName()); - assertNotEquals("Expected interceptor to accept this request [" + "/" + fetchMetadataExemptedGlobalActionConfig.getName() + "]", SC_FORBIDDEN, configuredFetchMetadataInterceptor.intercept(mai)); + request.setServletPath("/" + fetchMetadataExemptedGlobalActionConfig.getName() + ".action"); + assertNotEquals("Expected interceptor to accept this request [" + "/" + fetchMetadataExemptedGlobalActionConfig.getName() + ".action" + "]", SC_FORBIDDEN, configuredFetchMetadataInterceptor.intercept(mai)); } } diff --git a/core/src/test/resources/struts-testing.xml b/core/src/test/resources/struts-testing.xml index f1af997ce6..a73c2c5b64 100644 --- a/core/src/test/resources/struts-testing.xml +++ b/core/src/test/resources/struts-testing.xml @@ -28,21 +28,21 @@ - /fetchMetadataExemptedGlobal,/someOtherPath + /fetchMetadataExemptedGlobal.action,/someOtherPath.action - /fetchMetadataExempted + /fetchMetadataExempted.action hello.jsp - /nonMatchingActionPath + /nonMatchingActionPath.action hello.jsp