Merge pull request #262 from coheigea/jetty
Update Jetty to 9.4.33.v20201020 to fix CVE-2020-27216
diff --git a/samples/spring-mvc/src/main/java/org/apache/shiro/samples/spring/config/ApplicationConfig.java b/samples/spring-mvc/src/main/java/org/apache/shiro/samples/spring/config/ApplicationConfig.java
index 67a5cca..9c06eae 100644
--- a/samples/spring-mvc/src/main/java/org/apache/shiro/samples/spring/config/ApplicationConfig.java
+++ b/samples/spring-mvc/src/main/java/org/apache/shiro/samples/spring/config/ApplicationConfig.java
@@ -30,6 +30,7 @@
import org.apache.shiro.spring.remoting.SecureRemoteInvocationExecutor;
import org.apache.shiro.spring.web.config.DefaultShiroFilterChainDefinition;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
+import org.apache.shiro.spring.web.config.ShiroRequestMappingConfig;
import org.apache.shiro.spring.web.config.ShiroWebConfiguration;
import org.apache.shiro.spring.web.config.ShiroWebFilterConfiguration;
import org.springframework.context.annotation.Bean;
@@ -53,7 +54,8 @@
ShiroWebConfiguration.class,
ShiroWebFilterConfiguration.class,
JspViewsConfig.class,
- RemotingServletConfig.class})
+ RemotingServletConfig.class,
+ ShiroRequestMappingConfig.class})
@ComponentScan("org.apache.shiro.samples.spring")
public class ApplicationConfig {
diff --git a/samples/spring-mvc/src/main/java/org/apache/shiro/samples/spring/config/JspViewsConfig.java b/samples/spring-mvc/src/main/java/org/apache/shiro/samples/spring/config/JspViewsConfig.java
index 551089c..34f9912 100644
--- a/samples/spring-mvc/src/main/java/org/apache/shiro/samples/spring/config/JspViewsConfig.java
+++ b/samples/spring-mvc/src/main/java/org/apache/shiro/samples/spring/config/JspViewsConfig.java
@@ -25,7 +25,7 @@
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;
@@ -35,7 +35,7 @@
@Configuration
@ComponentScan("org.apache.shiro.samples.spring")
@EnableWebMvc
-public class JspViewsConfig extends WebMvcConfigurerAdapter {
+public class JspViewsConfig implements WebMvcConfigurer {
@Bean
@Order(1)
diff --git a/support/spring-boot/spring-boot-web-starter/src/main/java/org/apache/shiro/spring/config/web/autoconfigure/ShiroWebAutoConfiguration.java b/support/spring-boot/spring-boot-web-starter/src/main/java/org/apache/shiro/spring/config/web/autoconfigure/ShiroWebAutoConfiguration.java
index 4ab440c..3b89b63 100644
--- a/support/spring-boot/spring-boot-web-starter/src/main/java/org/apache/shiro/spring/config/web/autoconfigure/ShiroWebAutoConfiguration.java
+++ b/support/spring-boot/spring-boot-web-starter/src/main/java/org/apache/shiro/spring/config/web/autoconfigure/ShiroWebAutoConfiguration.java
@@ -33,9 +33,11 @@
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
import org.apache.shiro.spring.boot.autoconfigure.ShiroAutoConfiguration;
+import org.apache.shiro.spring.web.ShiroUrlPathHelper;
import org.apache.shiro.spring.web.config.AbstractShiroWebConfiguration;
import org.apache.shiro.spring.web.config.ShiroFilterChainDefinition;
import org.apache.shiro.web.servlet.Cookie;
+import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
@@ -47,6 +49,7 @@
*/
@Configuration
@AutoConfigureBefore(ShiroAutoConfiguration.class)
+@AutoConfigureAfter(ShiroWebMvcAutoConfiguration.class)
@ConditionalOnProperty(name = "shiro.web.enabled", matchIfMissing = true)
public class ShiroWebAutoConfiguration extends AbstractShiroWebConfiguration {
@@ -147,4 +150,11 @@
protected ShiroFilterChainDefinition shiroFilterChainDefinition() {
return super.shiroFilterChainDefinition();
}
+
+ @Bean
+ @ConditionalOnMissingBean
+ @Override
+ protected ShiroUrlPathHelper shiroUrlPathHelper() {
+ return super.shiroUrlPathHelper();
+ }
}
diff --git a/support/spring-boot/spring-boot-web-starter/src/main/java/org/apache/shiro/spring/config/web/autoconfigure/ShiroWebMvcAutoConfiguration.java b/support/spring-boot/spring-boot-web-starter/src/main/java/org/apache/shiro/spring/config/web/autoconfigure/ShiroWebMvcAutoConfiguration.java
new file mode 100644
index 0000000..26fdeb7
--- /dev/null
+++ b/support/spring-boot/spring-boot-web-starter/src/main/java/org/apache/shiro/spring/config/web/autoconfigure/ShiroWebMvcAutoConfiguration.java
@@ -0,0 +1,32 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.spring.config.web.autoconfigure;
+
+import org.apache.shiro.spring.web.config.ShiroRequestMappingConfig;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Import;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+@Configuration
+@ConditionalOnClass(RequestMappingHandlerMapping.class)
+@Import(ShiroRequestMappingConfig.class)
+@ConditionalOnProperty(name = "shiro.web.enabled", matchIfMissing = true)
+public class ShiroWebMvcAutoConfiguration { }
diff --git a/support/spring-boot/spring-boot-web-starter/src/main/resources/META-INF/spring.factories b/support/spring-boot/spring-boot-web-starter/src/main/resources/META-INF/spring.factories
index b69324a..328062d 100644
--- a/support/spring-boot/spring-boot-web-starter/src/main/resources/META-INF/spring.factories
+++ b/support/spring-boot/spring-boot-web-starter/src/main/resources/META-INF/spring.factories
@@ -1,3 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration = \
org.apache.shiro.spring.config.web.autoconfigure.ShiroWebAutoConfiguration,\
- org.apache.shiro.spring.config.web.autoconfigure.ShiroWebFilterConfiguration
+ org.apache.shiro.spring.config.web.autoconfigure.ShiroWebFilterConfiguration, \
+ org.apache.shiro.spring.config.web.autoconfigure.ShiroWebMvcAutoConfiguration
diff --git a/support/spring/pom.xml b/support/spring/pom.xml
index 254373f..78d8b65 100644
--- a/support/spring/pom.xml
+++ b/support/spring/pom.xml
@@ -50,6 +50,16 @@
<artifactId>spring-context</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-web</artifactId>
+ <optional>true</optional>
+ </dependency>
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-webmvc</artifactId>
+ <optional>true</optional>
+ </dependency>
<!-- Test dependencies -->
<dependency>
<groupId>org.slf4j</groupId>
@@ -72,11 +82,6 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-web</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-aspectj</artifactId>
<scope>test</scope>
diff --git a/support/spring/src/main/java/org/apache/shiro/spring/web/ShiroUrlPathHelper.java b/support/spring/src/main/java/org/apache/shiro/spring/web/ShiroUrlPathHelper.java
new file mode 100644
index 0000000..4b5ddbd
--- /dev/null
+++ b/support/spring/src/main/java/org/apache/shiro/spring/web/ShiroUrlPathHelper.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.spring.web;
+
+import org.apache.shiro.web.util.WebUtils;
+import org.springframework.web.util.UrlPathHelper;
+
+import javax.servlet.http.HttpServletRequest;
+
+/**
+ * A Spring UrlPathHelper that uses Shiro's path resolution logic.
+ * @since 1.7.0
+ */
+public class ShiroUrlPathHelper extends UrlPathHelper {
+
+ @Override
+ public String getPathWithinApplication(HttpServletRequest request) {
+ return WebUtils.getPathWithinApplication(request);
+ }
+
+ @Override
+ public String getPathWithinServletMapping(HttpServletRequest request) {
+ return WebUtils.getPathWithinApplication(request);
+ }
+}
diff --git a/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java b/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java
index ae2afb5..fab145f 100644
--- a/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java
+++ b/support/spring/src/main/java/org/apache/shiro/spring/web/config/AbstractShiroWebConfiguration.java
@@ -24,6 +24,7 @@
import org.apache.shiro.mgt.SubjectFactory;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.spring.config.AbstractShiroConfiguration;
+import org.apache.shiro.spring.web.ShiroUrlPathHelper;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSessionStorageEvaluator;
@@ -42,7 +43,7 @@
@Value("#{ @environment['shiro.sessionManager.sessionIdCookieEnabled'] ?: true }")
protected boolean sessionIdCookieEnabled;
- @Value("#{ @environment['shiro.sessionManager.sessionIdUrlRewritingEnabled'] ?: true }")
+ @Value("#{ @environment['shiro.sessionManager.sessionIdUrlRewritingEnabled'] ?: false }")
protected boolean sessionIdUrlRewritingEnabled;
@Value("#{ @environment['shiro.userNativeSessionManager'] ?: false }")
@@ -181,4 +182,8 @@
chainDefinition.addPathDefinition("/**", "authc");
return chainDefinition;
}
+
+ protected ShiroUrlPathHelper shiroUrlPathHelper() {
+ return new ShiroUrlPathHelper();
+ }
}
diff --git a/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroRequestMappingConfig.java b/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroRequestMappingConfig.java
new file mode 100644
index 0000000..317cb4d
--- /dev/null
+++ b/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroRequestMappingConfig.java
@@ -0,0 +1,31 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.spring.web.config;
+
+import org.apache.shiro.spring.web.ShiroUrlPathHelper;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
+
+@Configuration
+public class ShiroRequestMappingConfig {
+
+ public ShiroRequestMappingConfig(RequestMappingHandlerMapping requestMappingHandlerMapping) {
+ requestMappingHandlerMapping.setUrlPathHelper(new ShiroUrlPathHelper());
+ }
+}
diff --git a/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroWebConfiguration.java b/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroWebConfiguration.java
index dc57b22..952a26a 100644
--- a/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroWebConfiguration.java
+++ b/support/spring/src/main/java/org/apache/shiro/spring/web/config/ShiroWebConfiguration.java
@@ -26,6 +26,7 @@
import org.apache.shiro.session.mgt.SessionFactory;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.session.mgt.eis.SessionDAO;
+import org.apache.shiro.spring.web.ShiroUrlPathHelper;
import org.apache.shiro.web.servlet.Cookie;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@@ -122,4 +123,10 @@
protected ShiroFilterChainDefinition shiroFilterChainDefinition() {
return super.shiroFilterChainDefinition();
}
+
+ @Bean
+ @Override
+ protected ShiroUrlPathHelper shiroUrlPathHelper() {
+ return super.shiroUrlPathHelper();
+ }
}
diff --git a/support/spring/src/test/groovy/org/apache/shiro/spring/web/ShiroUrlPathHelperTest.groovy b/support/spring/src/test/groovy/org/apache/shiro/spring/web/ShiroUrlPathHelperTest.groovy
new file mode 100644
index 0000000..08eeb79
--- /dev/null
+++ b/support/spring/src/test/groovy/org/apache/shiro/spring/web/ShiroUrlPathHelperTest.groovy
@@ -0,0 +1,46 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.spring.web
+
+import org.junit.Test
+import org.springframework.mock.web.MockHttpServletRequest
+import org.springframework.web.util.UrlPathHelper
+
+import static org.hamcrest.MatcherAssert.assertThat
+import static org.hamcrest.Matchers.equalTo
+
+/**
+ * Tests a couple known differences between the stock and the ShiroUrlPathHelper
+ */
+class ShiroUrlPathHelperTest {
+
+ @Test
+ void testGetPathWithinApplication() {
+ MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/%2e%2e")
+ assertThat new UrlPathHelper().getPathWithinApplication(request), equalTo("/foo/..")
+ assertThat new ShiroUrlPathHelper().getPathWithinApplication(request), equalTo("/")
+ }
+
+ @Test
+ void testGetPathWithinServletMapping() {
+ MockHttpServletRequest request = new MockHttpServletRequest("GET", "/foo/%2e%2e")
+ assertThat new UrlPathHelper().getPathWithinServletMapping(request), equalTo("/foo/..")
+ assertThat new ShiroUrlPathHelper().getPathWithinServletMapping(request), equalTo("/")
+ }
+}
\ No newline at end of file
diff --git a/web/src/main/java/org/apache/shiro/web/filter/InvalidRequestFilter.java b/web/src/main/java/org/apache/shiro/web/filter/InvalidRequestFilter.java
index 3d229e9..63da6d4 100644
--- a/web/src/main/java/org/apache/shiro/web/filter/InvalidRequestFilter.java
+++ b/web/src/main/java/org/apache/shiro/web/filter/InvalidRequestFilter.java
@@ -19,10 +19,12 @@
package org.apache.shiro.web.filter;
+import org.apache.shiro.lang.util.StringUtils;
import org.apache.shiro.web.util.WebUtils;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
@@ -48,16 +50,24 @@
private boolean blockSemicolon = true;
- private boolean blockBackslash = true;
+ private boolean blockBackslash = !Boolean.getBoolean(WebUtils.ALLOW_BACKSLASH);
private boolean blockNonAscii = true;
@Override
- protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
- String uri = WebUtils.toHttp(request).getRequestURI();
- return !containsSemicolon(uri)
- && !containsBackslash(uri)
- && !containsNonAsciiCharacters(uri);
+ protected boolean isAccessAllowed(ServletRequest req, ServletResponse response, Object mappedValue) throws Exception {
+ HttpServletRequest request = WebUtils.toHttp(req);
+ // check the original and decoded values
+ return isValid(request.getRequestURI()) // user request string (not decoded)
+ && isValid(request.getServletPath()) // decoded servlet part
+ && isValid(request.getPathInfo()); // decoded path info (may be null)
+ }
+
+ private boolean isValid(String uri) {
+ return !StringUtils.hasText(uri)
+ || ( !containsSemicolon(uri)
+ && !containsBackslash(uri)
+ && !containsNonAsciiCharacters(uri));
}
@Override
diff --git a/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java b/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java
index eb7eda1..9aa275a 100644
--- a/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java
+++ b/web/src/main/java/org/apache/shiro/web/session/mgt/DefaultWebSessionManager.java
@@ -58,7 +58,7 @@
cookie.setHttpOnly(true); //more secure, protects against XSS attacks
this.sessionIdCookie = cookie;
this.sessionIdCookieEnabled = true;
- this.sessionIdUrlRewritingEnabled = true;
+ this.sessionIdUrlRewritingEnabled = false;
}
public Cookie getSessionIdCookie() {
diff --git a/web/src/main/java/org/apache/shiro/web/util/WebUtils.java b/web/src/main/java/org/apache/shiro/web/util/WebUtils.java
index abc27c4..992f7ad 100644
--- a/web/src/main/java/org/apache/shiro/web/util/WebUtils.java
+++ b/web/src/main/java/org/apache/shiro/web/util/WebUtils.java
@@ -57,6 +57,8 @@
public static final String SERVLET_REQUEST_KEY = ServletRequest.class.getName() + "_SHIRO_THREAD_CONTEXT_KEY";
public static final String SERVLET_RESPONSE_KEY = ServletResponse.class.getName() + "_SHIRO_THREAD_CONTEXT_KEY";
+ public static final String ALLOW_BACKSLASH = "org.apache.shiro.web.ALLOW_BACKSLASH";
+
/**
* {@link org.apache.shiro.session.Session Session} key used to save a request and later restore it, for example when redirecting to a
* requested page after login, equal to {@code shiroSavedRequest}.
@@ -163,7 +165,7 @@
* @return normalized path
*/
public static String normalize(String path) {
- return normalize(path, true);
+ return normalize(path, Boolean.getBoolean(ALLOW_BACKSLASH));
}
/**
diff --git a/web/src/test/groovy/org/apache/shiro/web/filter/InvalidRequestFilterTest.groovy b/web/src/test/groovy/org/apache/shiro/web/filter/InvalidRequestFilterTest.groovy
index 8d0b1c0..c7a0525 100644
--- a/web/src/test/groovy/org/apache/shiro/web/filter/InvalidRequestFilterTest.groovy
+++ b/web/src/test/groovy/org/apache/shiro/web/filter/InvalidRequestFilterTest.groovy
@@ -19,6 +19,7 @@
package org.apache.shiro.web.filter
+import org.apache.shiro.web.RestoreSystemProperties
import org.junit.Test
import javax.servlet.http.HttpServletRequest
@@ -39,6 +40,25 @@
}
@Test
+ void systemPropertyAllowBackslash() {
+ RestoreSystemProperties.withProperties(["org.apache.shiro.web.ALLOW_BACKSLASH": "true"]) {
+ InvalidRequestFilter filter = new InvalidRequestFilter()
+ assertThat "filter.blockBackslash expected to be false", !filter.isBlockBackslash()
+ }
+
+ RestoreSystemProperties.withProperties(["org.apache.shiro.web.ALLOW_BACKSLASH": ""]) {
+ InvalidRequestFilter filter = new InvalidRequestFilter()
+ assertThat "filter.blockBackslash expected to be false", filter.isBlockBackslash()
+ }
+
+ RestoreSystemProperties.withProperties(["org.apache.shiro.web.ALLOW_BACKSLASH": "false"]) {
+ InvalidRequestFilter filter = new InvalidRequestFilter()
+ assertThat "filter.blockBackslash expected to be false", filter.isBlockBackslash()
+ }
+ }
+
+
+ @Test
void testFilterBlocks() {
InvalidRequestFilter filter = new InvalidRequestFilter()
assertPathBlocked(filter, "/\\something")
@@ -48,6 +68,9 @@
assertPathBlocked(filter, "/%3bsomething")
assertPathBlocked(filter, "/%3Bsomething")
assertPathBlocked(filter, "/\u0019something")
+
+ assertPathBlocked(filter, "/something", "/;something")
+ assertPathBlocked(filter, "/something", "/something", "/;")
}
@Test
@@ -61,6 +84,9 @@
assertPathBlocked(filter, "/%3bsomething")
assertPathBlocked(filter, "/%3Bsomething")
assertPathBlocked(filter, "/\u0019something")
+
+ assertPathAllowed(filter, "/something", "/\\something")
+ assertPathAllowed(filter, "/something", "/something", "/\\")
}
@Test
@@ -74,6 +100,9 @@
assertPathBlocked(filter, "/%3bsomething")
assertPathBlocked(filter, "/%3Bsomething")
assertPathAllowed(filter, "/\u0019something")
+
+ assertPathAllowed(filter, "/something", "/\u0019something")
+ assertPathAllowed(filter, "/something", "/something", "/\u0019")
}
@Test
void testFilterAllowsSemicolon() {
@@ -86,20 +115,27 @@
assertPathAllowed(filter, "/%3bsomething")
assertPathAllowed(filter, "/%3Bsomething")
assertPathBlocked(filter, "/\u0019something")
+
+ assertPathAllowed(filter, "/something", "/;something")
+ assertPathAllowed(filter, "/something", "/something", "/;")
}
- static void assertPathBlocked(InvalidRequestFilter filter, String path) {
- assertThat "Expected path '${path}', to be blocked", !filter.isAccessAllowed(mockRequest(path), null, null)
+ static void assertPathBlocked(InvalidRequestFilter filter, String requestUri, String servletPath = requestUri, String pathInfo = null) {
+ assertThat "Expected path '${requestUri}', to be blocked", !filter.isAccessAllowed(mockRequest(requestUri, servletPath, pathInfo), null, null)
}
- static void assertPathAllowed(InvalidRequestFilter filter, String path) {
- assertThat "Expected path '${path}', to be allowed", filter.isAccessAllowed(mockRequest(path), null, null)
+ static void assertPathAllowed(InvalidRequestFilter filter, String requestUri, String servletPath = requestUri, String pathInfo = null) {
+ assertThat "Expected requestUri '${requestUri}', to be allowed", filter.isAccessAllowed(mockRequest(requestUri, servletPath, pathInfo), null, null)
}
- static HttpServletRequest mockRequest(String path) {
+ static HttpServletRequest mockRequest(String requestUri, String servletPath, String pathInfo) {
HttpServletRequest request = mock(HttpServletRequest)
- expect(request.getRequestURI()).andReturn(path)
+ expect(request.getRequestURI()).andReturn(requestUri)
+ expect(request.getServletPath()).andReturn(servletPath).anyTimes()
+ expect(request.getPathInfo()).andReturn(pathInfo).anyTimes()
+ expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn(servletPath)
+ expect(request.getAttribute("javax.servlet.include.path_info")).andReturn(pathInfo)
replay(request)
return request
}
diff --git a/web/src/test/groovy/org/apache/shiro/web/session/mgt/DefaultWebSessionManagerTest.groovy b/web/src/test/groovy/org/apache/shiro/web/session/mgt/DefaultWebSessionManagerTest.groovy
index 841569f..35b3120 100644
--- a/web/src/test/groovy/org/apache/shiro/web/session/mgt/DefaultWebSessionManagerTest.groovy
+++ b/web/src/test/groovy/org/apache/shiro/web/session/mgt/DefaultWebSessionManagerTest.groovy
@@ -127,7 +127,7 @@
ShiroHttpServletRequest.COOKIE_SESSION_ID_SOURCE);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID, id);
request.setAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_ID_IS_VALID, Boolean.TRUE);
- request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, Boolean.TRUE);
+ request.setAttribute(ShiroHttpServletRequest.SESSION_ID_URL_REWRITING_ENABLED, Boolean.FALSE);
replay(cookie);
replay(request);
@@ -147,6 +147,7 @@
Cookie cookie = createMock(Cookie.class);
mgr.setSessionIdCookie(cookie);
mgr.setSessionIdCookieEnabled(false);
+ mgr.setSessionIdUrlRewritingEnabled(true)
//we should not have any reads from the cookie fields - if we do, this test case will fail.
@@ -182,6 +183,7 @@
Cookie cookie = createMock(Cookie.class);
mgr.setSessionIdCookie(cookie);
mgr.setSessionIdCookieEnabled(false);
+ mgr.setSessionIdUrlRewritingEnabled(true)
//we should not have any reads from the cookie fields - if we do, this test case will fail.
@@ -218,6 +220,7 @@
public void testGetSessionIdFromRequestUriPathSegmentParam() {
mgr.setSessionIdCookieEnabled(false);
+ mgr.setSessionIdUrlRewritingEnabled(true)
HttpServletRequest request = createMock(HttpServletRequest.class);
HttpServletResponse response = createMock(HttpServletResponse.class);
diff --git a/web/src/test/groovy/org/apache/shiro/web/util/WebUtilsTest.groovy b/web/src/test/groovy/org/apache/shiro/web/util/WebUtilsTest.groovy
index 7956a10..b501605 100644
--- a/web/src/test/groovy/org/apache/shiro/web/util/WebUtilsTest.groovy
+++ b/web/src/test/groovy/org/apache/shiro/web/util/WebUtilsTest.groovy
@@ -18,12 +18,15 @@
*/
package org.apache.shiro.web.util
+import org.apache.shiro.web.RestoreSystemProperties
+import org.hamcrest.CoreMatchers
import org.junit.Test
import javax.servlet.http.HttpServletRequest
import static org.easymock.EasyMock.*
import static org.junit.Assert.*
+import static org.hamcrest.CoreMatchers.*
/**
* Tests for {@link WebUtils}.
@@ -193,6 +196,55 @@
doTestGetRequestURI("/context path/foobar", "/context path/foobar");
}
+ @Test
+ void testNormalize() {
+ doNormalizeTest"/foobar", "/foobar"
+ doNormalizeTest "/foobar/", "/foobar/"
+ doNormalizeTest"", "/"
+ doNormalizeTest"foobar", "/foobar"
+ doNormalizeTest"//foobar", "/foobar"
+ doNormalizeTest"//foobar///", "/foobar/"
+ doNormalizeTest"/context-path/foobar", "/context-path/foobar"
+ doNormalizeTest"/context-path/foobar/", "/context-path/foobar/"
+ doNormalizeTest"//context-path/foobar", "/context-path/foobar"
+ doNormalizeTest"//context-path//foobar" ,"/context-path/foobar"
+ doNormalizeTest"//context-path/remove-one/remove-two/../../././/foobar", "/context-path/foobar"
+ doNormalizeTest"//context-path//../../././/foobar", null
+ doNormalizeTest"/context path/foobar", "/context path/foobar"
+
+ doNormalizeTest"/context path/\\foobar", "/context path/\\foobar"
+ doNormalizeTest"//context-path\\..\\../.\\.\\foobar", "/context-path\\..\\../.\\.\\foobar"
+ doNormalizeTest"//context-path\\..\\..\\.\\.\\foobar", "/context-path\\..\\..\\.\\.\\foobar"
+ doNormalizeTest"\\context-path\\..\\foobar", "/\\context-path\\..\\foobar"
+ }
+
+ @Test
+ void testNormalize_allowBackslashes() {
+ RestoreSystemProperties.withProperties(["org.apache.shiro.web.ALLOW_BACKSLASH": "true"]) {
+ doNormalizeTest"/foobar", "/foobar"
+ doNormalizeTest "/foobar/", "/foobar/"
+ doNormalizeTest"", "/"
+ doNormalizeTest"foobar", "/foobar"
+ doNormalizeTest"//foobar", "/foobar"
+ doNormalizeTest"//foobar///", "/foobar/"
+ doNormalizeTest"/context-path/foobar", "/context-path/foobar"
+ doNormalizeTest"/context-path/foobar/", "/context-path/foobar/"
+ doNormalizeTest"//context-path/foobar", "/context-path/foobar"
+ doNormalizeTest"//context-path//foobar" ,"/context-path/foobar"
+ doNormalizeTest"//context-path/remove-one/remove-two/../../././/foobar", "/context-path/foobar"
+ doNormalizeTest"//context-path//../../././/foobar", null
+ doNormalizeTest"/context path/foobar", "/context path/foobar"
+ doNormalizeTest"/context path/\\foobar", "/context path/foobar"
+ doNormalizeTest"//context-path\\..\\..\\.\\.\\foobar", null
+ doNormalizeTest"\\context-path\\..\\foobar", "/foobar"
+
+ }
+ }
+
+ void doNormalizeTest(String path, String expected) {
+ assertThat WebUtils.normalize(path), equalTo(expected)
+ }
+
void doTestGetPathWithinApplication(String servletPath, String pathInfo, String expectedValue) {
def request = createMock(HttpServletRequest)
diff --git a/web/src/test/java/org/apache/shiro/web/RestoreSystemProperties.java b/web/src/test/java/org/apache/shiro/web/RestoreSystemProperties.java
new file mode 100644
index 0000000..882e2e9
--- /dev/null
+++ b/web/src/test/java/org/apache/shiro/web/RestoreSystemProperties.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.shiro.web;
+
+import groovy.lang.Closure;
+
+import java.io.Closeable;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * Wrapper that will restore System properties after test methods.
+ *
+ * Based on: https://github.com/stefanbirkner/system-rules/blob/master/src/main/java/org/junit/contrib/java/lang/system/RestoreSystemProperties.java
+ */
+public class RestoreSystemProperties implements Closeable {
+
+ private final Properties originalProperties;
+
+ public RestoreSystemProperties() {
+ originalProperties = System.getProperties();
+ System.setProperties(copyOf(originalProperties));
+ }
+
+ public void restore() {
+ System.setProperties(originalProperties);
+ }
+
+ private Properties copyOf(Properties source) {
+ Properties copy = new Properties();
+ copy.putAll(source);
+ return copy;
+ }
+
+ public static <T> T withProperties(Closure<T> closure) {
+ return withProperties(Collections.emptyMap(), closure);
+ }
+
+ public static <T> T withProperties(Map<String, String> properties, Closure<T> closure) {
+
+ try (RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties()) {
+ properties.forEach(System::setProperty);
+
+ return closure.call();
+ }
+ }
+
+ @Override
+ public void close() {
+ restore();
+ }
+}