[Git][java-team/shiro][master] 6 commits: Import Debian changes 1.3.2-5

Emmanuel Bourg (@ebourg) gitlab at salsa.debian.org
Tue Dec 17 14:40:11 GMT 2024



Emmanuel Bourg pushed to branch master at Debian Java Maintainers / shiro


Commits:
f83bf205 by Roberto C. Sánchez at 2021-08-27T13:10:19-04:00
Import Debian changes 1.3.2-5

shiro (1.3.2-5) unstable; urgency=medium
.
  * Team upload.
  * Update patch for Spring Framework 4.3.x build failure.
  * Cherry-pick upstream patch with Guice improvements.
  * CVE-2020-1957: Fix a path-traversal issue where a specially-crafted request
    could cause an authentication bypass. (Closes: #955018)
  * CVE-2020-11989: Fix an encoding issue introduced in the handling of the
    previous CVE-2020-1957 path-traversal issue which could have also caused an
    authentication bypass.
  * CVE-2020-13933: Fix an authentication bypass resulting from a specially
    crafted HTTP request. (Closes: #968753)
  * CVE-2020-17510: Fix an authentication bypass resulting from a specially
    crafted HTTP request.

- - - - -
217a26d8 by Emmanuel Bourg at 2024-12-17T15:28:28+01:00
Standards-Version updated to 4.7.0

- - - - -
e4e54ef5 by Emmanuel Bourg at 2024-12-17T15:28:32+01:00
Switch to debhelper level 13

- - - - -
c540df96 by Emmanuel Bourg at 2024-12-17T15:35:00+01:00
Depend on libservlet-api-java instead of libservlet3.1-java

- - - - -
f26685ee by Emmanuel Bourg at 2024-12-17T15:36:31+01:00
Ignore the deprecated guice-multibindings dependency

- - - - -
058ca477 by Emmanuel Bourg at 2024-12-17T15:36:56+01:00
Upload to unstable

- - - - -


12 changed files:

- debian/changelog
- − debian/compat
- debian/control
- debian/maven.ignoreRules
- debian/maven.rules
- debian/patches/03-spring-compatibility.patch
- + debian/patches/05-guice-improvements.patch
- + debian/patches/CVE-2020-13933.patch
- + debian/patches/CVE-2020-17510_1_of_2.patch
- + debian/patches/CVE-2020-17510_2_of_2.patch
- + debian/patches/CVE-2020-1957.patch
- debian/patches/series


Changes:

=====================================
debian/changelog
=====================================
@@ -1,3 +1,29 @@
+shiro (1.3.2-6) unstable; urgency=medium
+
+  * Depend on libservlet-api-java instead of libservlet3.1-java
+  * Ignore the deprecated guice-multibindings dependency
+  * Standards-Version updated to 4.7.0
+  * Switch to debhelper level 13
+
+ -- Emmanuel Bourg <ebourg at apache.org>  Tue, 17 Dec 2024 15:36:48 +0100
+
+shiro (1.3.2-5) unstable; urgency=medium
+
+  * Team upload.
+  * Update patch for Spring Framework 4.3.x build failure.
+  * Cherry-pick upstream patch with Guice improvements.
+  * CVE-2020-1957: Fix a path-traversal issue where a specially-crafted request
+    could cause an authentication bypass. (Closes: #955018)
+  * CVE-2020-11989: Fix an encoding issue introduced in the handling of the
+    previous CVE-2020-1957 path-traversal issue which could have also caused an
+    authentication bypass.
+  * CVE-2020-13933: Fix an authentication bypass resulting from a specially
+    crafted HTTP request. (Closes: #968753)
+  * CVE-2020-17510: Fix an authentication bypass resulting from a specially
+    crafted HTTP request.
+
+ -- Roberto C. Sánchez <roberto at debian.org>  Fri, 27 Aug 2021 13:10:19 -0400
+
 shiro (1.3.2-4) unstable; urgency=medium
 
   * Team upload.


=====================================
debian/compat deleted
=====================================
@@ -1 +0,0 @@
-11


=====================================
debian/control
=====================================
@@ -4,7 +4,7 @@ Priority: optional
 Maintainer: Debian Java Maintainers <pkg-java-maintainers at lists.alioth.debian.org>
 Uploaders: Emmanuel Bourg <ebourg at apache.org>
 Build-Depends:
- debhelper (>= 11),
+ debhelper-compat (= 13),
  default-jdk,
  junit4,
  libaspectj-java,
@@ -13,17 +13,18 @@ Build-Depends:
  libcommons-cli-java,
  libehcache-java,
  libgeronimo-annotation-1.3-spec-java,
- libguice-java (>= 3.0-6~),
+ libguice-java (>= 4.0),
  libhsqldb-java,
+ libjsp-api-java,
  liblog4j1.2-java,
  libmaven-bundle-plugin-java,
  libquartz-java,
- libservlet3.1-java,
+ libservlet-api-java,
  libslf4j-java,
  libspring-context-java,
  libtaglibs-standard-spec-java,
  maven-debian-helper
-Standards-Version: 4.3.0
+Standards-Version: 4.7.0
 Vcs-Git: https://salsa.debian.org/java-team/shiro.git
 Vcs-Browser: https://salsa.debian.org/java-team/shiro
 Homepage: http://shiro.apache.org


=====================================
debian/maven.ignoreRules
=====================================
@@ -1,4 +1,5 @@
 
+com.google.inject.extensions guice-multibindings * * * *
 org.apache.shiro shiro-all jar * * *
 org.apache.shiro shiro-cas bundle * * *
 org.apache.shiro shiro-core test-jar * * *


=====================================
debian/maven.rules
=====================================
@@ -4,8 +4,8 @@ s/aspectj/org.aspectj/ aspectjtools jar s/.*/debian/ * *
 s/aspectj/org.aspectj/ aspectjweaver jar s/.*/debian/ * *
 commons-beanutils commons-beanutils jar s/.*/debian/ * *
 s/javax.servlet/org.apache.taglibs/ s/jstl/taglibs-standard-spec/ jar s/.*/debian/ * *
-javax.servlet s/servlet-api/javax.servlet-api/ * s/.*/3.1/ * *
-javax.servlet.jsp s/jsp-api/javax.servlet.jsp-api/ * s/.*/2.3/ * *
+javax.servlet s/servlet-api/javax.servlet-api/ * s/.*/debian/ * *
+javax.servlet.jsp s/jsp-api/javax.servlet.jsp-api/ * s/.*/debian/ * *
 junit junit jar s/4\..*/4.x/ * *
 org.apache apache pom s/.*/debian/ * *
 s/hsqldb/org.hsqldb/ hsqldb * s/.*/debian/ * *


=====================================
debian/patches/03-spring-compatibility.patch
=====================================
@@ -1,17 +1,33 @@
-Description: Fixes the compatibility with the version of Spring Framework in Debian
-Author: Emmanuel Bourg <ebourg at apache.org>
-Forwarded: no
+From aa2cd4fca10416623ecc29008cd851a4f1ed3d98 Mon Sep 17 00:00:00 2001
+From: Brian Demers <bdemers at apache.org>
+Date: Fri, 23 Sep 2016 16:43:48 -0400
+Subject: [PATCH] SHIRO-590 - Added Spring Boot starters and programatic Spring
+ support.
+
+---
+ .../shiro/spring/LifecycleBeanPostProcessor.java      | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+diff --git a/support/spring/src/main/java/org/apache/shiro/spring/LifecycleBeanPostProcessor.java b/support/spring/src/main/java/org/apache/shiro/spring/LifecycleBeanPostProcessor.java
+index a526769e..a318d20b 100644
 --- a/support/spring/src/main/java/org/apache/shiro/spring/LifecycleBeanPostProcessor.java
 +++ b/support/spring/src/main/java/org/apache/shiro/spring/LifecycleBeanPostProcessor.java
-@@ -125,6 +125,11 @@
-         }
+@@ -134,4 +134,15 @@ public int getOrder() {
+         // LifecycleBeanPostProcessor needs Order. See https://issues.apache.org/jira/browse/SHIRO-222
+         return order;
      }
- 
-+    @Override
-+    public boolean requiresDestruction(Object object) {
-+        return true;
-+    }
 +
-     /**
-      * Order value of this BeanPostProcessor.
-      *
++    /**
++     * Return true only if <code>bean</code> implements Destroyable.
++     * @param bean bean to check if requires destruction.
++     * @return true only if <code>bean</code> implements Destroyable.
++     * @since 1.4
++     */
++    @SuppressWarnings("unused")
++    public boolean requiresDestruction(Object bean) {
++        return (bean instanceof Destroyable);
++    }
+ }
+-- 
+2.20.1
+


=====================================
debian/patches/05-guice-improvements.patch
=====================================
@@ -0,0 +1,751 @@
+commit f2dfa7ff39c9870e7b9856ceca8690c5398080fa
+Author: Brian Demers <bdemers at apache.org>
+Date:   Thu Jul 14 09:51:45 2016 -0400
+
+    SHIRO-493 - Adding new methods and deprecating old to ShiroWebModule to support Guice 4
+
+--- a/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java
++++ b/support/guice/src/main/java/org/apache/shiro/guice/ShiroModule.java
+@@ -18,20 +18,33 @@
+  */
+ package org.apache.shiro.guice;
+ 
++import java.lang.reflect.Method;
+ import java.util.Collection;
+ import java.util.Collections;
++import java.util.List;
+ import java.util.Set;
+ import java.util.WeakHashMap;
+ 
+ import javax.annotation.PreDestroy;
+ 
++import com.google.inject.Provider;
++import com.google.inject.matcher.Matchers;
++import com.google.inject.name.Names;
++import com.google.inject.spi.InjectionListener;
++import com.google.inject.spi.TypeEncounter;
++import com.google.inject.spi.TypeListener;
+ import org.apache.shiro.config.ConfigurationException;
+ import org.apache.shiro.env.Environment;
++import org.apache.shiro.event.EventBus;
++import org.apache.shiro.event.EventBusAware;
++import org.apache.shiro.event.Subscribe;
++import org.apache.shiro.event.support.DefaultEventBus;
+ import org.apache.shiro.mgt.DefaultSecurityManager;
+ import org.apache.shiro.mgt.SecurityManager;
+ import org.apache.shiro.realm.Realm;
+ import org.apache.shiro.session.mgt.DefaultSessionManager;
+ import org.apache.shiro.session.mgt.SessionManager;
++import org.apache.shiro.util.ClassUtils;
+ import org.apache.shiro.util.Destroyable;
+ 
+ import com.google.inject.Key;
+@@ -57,6 +70,9 @@
+         bindSessionManager(bind(SessionManager.class));
+         bindEnvironment(bind(Environment.class));
+         bindListener(BeanTypeListener.MATCHER, new BeanTypeListener());
++        bindEventBus(bind(EventBus.class));
++        bindListener(Matchers.any(), new SubscribedEventTypeListener());
++        bindListener(Matchers.any(), new EventBusAwareTypeListener());
+         final DestroyableInjectionListener.DestroyableRegistry registry = new DestroyableInjectionListener.DestroyableRegistry() {
+             public void add(Destroyable destroyable) {
+                 ShiroModule.this.add(destroyable);
+@@ -70,6 +86,7 @@
+         bindListener(LifecycleTypeListener.MATCHER, new LifecycleTypeListener(registry));
+ 
+         expose(SecurityManager.class);
++        expose(EventBus.class);
+ 
+         configureShiro();
+         bind(realmCollectionKey())
+@@ -153,6 +170,15 @@
+     }
+ 
+     /**
++     * Binds the EventBus.  Override this method in order to provide your own {@link EventBus} binding.
++     * @param bind
++     * @since 1.4
++     */
++    protected void bindEventBus(AnnotatedBindingBuilder<EventBus> bind) {
++        bind.to(DefaultEventBus.class).asEagerSingleton();
++    }
++
++    /**
+      * Destroys all beans created within this module that implement {@link org.apache.shiro.util.Destroyable}.  Should be called when this
+      * module will no longer be used.
+      *
+@@ -167,4 +193,39 @@
+     public void add(Destroyable destroyable) {
+         this.destroyables.add(destroyable);
+     }
++
++    private class SubscribedEventTypeListener implements TypeListener {
++        @Override
++        public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
++
++            final Provider<EventBus> eventBusProvider = typeEncounter.getProvider(EventBus.class);
++
++            List<Method> methods = ClassUtils.getAnnotatedMethods(typeLiteral.getRawType(), Subscribe.class);
++            if (methods != null && !methods.isEmpty()) {
++                typeEncounter.register( new InjectionListener<I>() {
++                    @Override
++                    public void afterInjection(Object o) {
++                        eventBusProvider.get().register(o);
++                    }
++                });
++            }
++        }
++    }
++
++    private class EventBusAwareTypeListener implements TypeListener {
++        @Override
++        public <I> void hear(TypeLiteral<I> typeLiteral, TypeEncounter<I> typeEncounter) {
++
++            final Provider<EventBus> eventBusProvider = typeEncounter.getProvider(EventBus.class);
++
++            if (EventBusAware.class.isAssignableFrom(typeLiteral.getRawType())) {
++                typeEncounter.register( new InjectionListener<I>() {
++                    @Override
++                    public void afterInjection(Object o) {
++                        ((EventBusAware)o).setEventBus(eventBusProvider.get());
++                    }
++                });
++            }
++        }
++    }
+ }
+--- a/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java
++++ b/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java
+@@ -18,10 +18,7 @@
+  */
+ package org.apache.shiro.guice.web;
+ 
+-import java.util.Collection;
+-import java.util.HashMap;
+-import java.util.LinkedHashMap;
+-import java.util.Map;
++import java.util.*;
+ 
+ import javax.servlet.Filter;
+ import javax.servlet.ServletContext;
+@@ -31,6 +28,7 @@
+ import org.apache.shiro.guice.ShiroModule;
+ import org.apache.shiro.mgt.SecurityManager;
+ import org.apache.shiro.session.mgt.SessionManager;
++import org.apache.shiro.util.StringUtils;
+ import org.apache.shiro.web.env.WebEnvironment;
+ import org.apache.shiro.web.filter.PathMatchingFilter;
+ import org.apache.shiro.web.filter.authc.AnonymousFilter;
+@@ -94,7 +92,7 @@
+      * We use a LinkedHashMap here to ensure that iterator order is the same as add order.  This is important, as the
+      * FilterChainResolver uses iterator order when searching for a matching chain.
+      */
+-    private final Map<String, Key<? extends Filter>[]> filterChains = new LinkedHashMap<String, Key<? extends Filter>[]>();
++    private final Map<String, FilterConfig<? extends Filter>[]> filterChains = new LinkedHashMap<String, FilterConfig<? extends Filter>[]>();
+     private final ServletContext servletContext;
+ 
+     public ShiroWebModule(ServletContext servletContext) {
+@@ -134,37 +132,65 @@
+ 
+         this.configureShiroWeb();
+ 
+-        setupFilterChainConfigs();
+-
+-        bind(FilterChainResolver.class).toProvider(new FilterChainResolverProvider(filterChains));
++        bind(FilterChainResolver.class).toProvider(new FilterChainResolverProvider(setupFilterChainConfigs()));
+     }
+ 
+-    private void setupFilterChainConfigs() {
+-        Map<Key<? extends PathMatchingFilter>, Map<String, String>> configs = new HashMap<Key<? extends PathMatchingFilter>, Map<String, String>>();
++    private Map<String, Key<? extends Filter>[]> setupFilterChainConfigs() {
++
++        // loop through and build a map of Filter Key -> Map<Path, Config>
++        Map<Key<? extends Filter>, Map<String, String>> filterToPathToConfig = new HashMap<Key<? extends Filter>, Map<String, String>>();
++
++        // At the same time build a map to return with Path -> Key[]
++        Map<String, Key<? extends Filter>[]> resultConfigMap = new HashMap<String, Key<? extends Filter>[]>();
++
++        for (Map.Entry<String, FilterConfig<? extends Filter>[]> filterChain : filterChains.entrySet()) {
++
++            String path = filterChain.getKey();
++
++            // collect the keys used for this path
++            List<Key<? extends Filter>> keysForPath = new ArrayList<Key<? extends Filter>>();
+ 
+-        for (Map.Entry<String, Key<? extends Filter>[]> filterChain : filterChains.entrySet()) {
+             for (int i = 0; i < filterChain.getValue().length; i++) {
+-                Key<? extends Filter> key = filterChain.getValue()[i];
+-                if (key instanceof FilterConfigKey) {
+-                    FilterConfigKey<? extends PathMatchingFilter> configKey = (FilterConfigKey<? extends PathMatchingFilter>) key;
+-                    key = configKey.getKey();
+-                    filterChain.getValue()[i] = key;
+-                    if (!PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) {
+-                        throw new ConfigurationException("Config information requires a PathMatchingFilter - can't apply to " + key.getTypeLiteral().getRawType());
+-                    }
+-                    if (configs.get(castToPathMatching(key)) == null) configs.put(castToPathMatching(key), new HashMap<String, String>());
+-                    configs.get(castToPathMatching(key)).put(filterChain.getKey(), configKey.getConfigValue());
+-                } else if (PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) {
+-	                  if (configs.get(castToPathMatching(key)) == null) configs.put(castToPathMatching(key), new HashMap<String, String>());
+-                    configs.get(castToPathMatching(key)).put(filterChain.getKey(), "");
++                FilterConfig<? extends Filter> filterConfig = filterChain.getValue()[i];
++
++                Key<? extends Filter> key = filterConfig.getKey();
++                String config = filterConfig.getConfigValue();
++
++                // initialize key in filterToPathToConfig, if it doesn't exist
++                if (filterToPathToConfig.get(key) == null) {
++                    filterToPathToConfig.put((key), new HashMap<String, String>());
++                }
++                // now set the value
++                filterToPathToConfig.get(key).put(path, config);
++
++                // Config error if someone configured a non PathMatchingFilter with a config value
++                if (StringUtils.hasText(config) && !PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) {
++                    throw new ConfigurationException("Config information requires a PathMatchingFilter - can't apply to " + key.getTypeLiteral().getRawType());
+                 }
++
++                // store the key in keysForPath
++                keysForPath.add(key);
+             }
++
++            // map the current path to all of its Keys
++            resultConfigMap.put(path, keysForPath.toArray(new Key[keysForPath.size()]));
+         }
+-        for (Key<? extends PathMatchingFilter> filterKey : configs.keySet()) {
+-            bindPathMatchingFilter(filterKey, configs.get(filterKey));
++
++        // now we find only the PathMatchingFilter and configure bindings
++        // non PathMatchingFilter, can be loaded with the default provider via the class name
++        for (Key<? extends Filter> key : filterToPathToConfig.keySet()) {
++            if (PathMatchingFilter.class.isAssignableFrom(key.getTypeLiteral().getRawType())) {
++                bindPathMatchingFilter(castToPathMatching(key), filterToPathToConfig.get(key));
++            }
++            else {
++                bind(key);
++            }
+         }
++
++        return resultConfigMap;
+     }
+ 
++
+     private <T extends PathMatchingFilter> void bindPathMatchingFilter(Key<T> filterKey, Map<String, String> configs) {
+         bind(filterKey).toProvider(new PathMatchingFilterProvider<T>(filterKey, configs)).asEagerSingleton();
+     }
+@@ -218,6 +244,126 @@
+         bind.to(WebGuiceEnvironment.class).asEagerSingleton();
+     }
+ 
++    protected final void addFilterChain(String pattern, Key<? extends Filter> key) {
++        // check for legacy API
++        if (key instanceof FilterConfigKey) {
++            addLegacyFilterChain(pattern, (FilterConfigKey) key);
++        }
++        else {
++            addFilterChain(pattern, new FilterConfig<Filter>((Key<Filter>) key, ""));
++        }
++    }
++
++    /**
++     * Maps 'n' number of <code>filterConfig</code>s to a specific path pattern.<BR/>
++     * For example, a path of '/my_private_resource/**' to 'filterConfig(AUTHC)' would require
++     * any resource under the path '/my_private_resource' would be processed through the {@link FormAuthenticationFilter}.
++     *
++     * @param pattern URL patter to be mapped to a FilterConfig, e.g. '/my_private-path/**'
++     * @param filterConfigs FilterConfiguration representing the Filter and config to be used when processing resources on <code>pattern</code>.
++     * @since 1.4
++     */
++    protected final void addFilterChain(String pattern, FilterConfig<? extends Filter>... filterConfigs) {
++        filterChains.put(pattern, filterConfigs);
++    }
++
++    /**
++     * Builds a FilterConfig from a Filer and configuration String
++     * @param baseKey The Key of the Filter class to be used.
++     * @param <T> A Servlet Filter class.
++     * @return A FilterConfig used to map a String path to this configuration.
++     * @since 1.4
++     */
++    protected static <T extends Filter> FilterConfig<T> filterConfig(Key<T> baseKey, String configValue) {
++        return new FilterConfig<T>(baseKey, configValue);
++    }
++
++    /**
++     * Builds a FilterConfig from a Filer and configuration String
++     * @param baseKey The Key of the Filter class to be used.
++     * @param <T> A Servlet Filter class.
++     * @return A FilterConfig used to map a String path to this configuration.
++     * @since 1.4
++     */
++    protected static <T extends Filter> FilterConfig<T> filterConfig(Key<T> baseKey) {
++        return filterConfig(baseKey, "");
++    }
++
++    /**
++     * Builds a FilterConfig from a Filer and configuration String
++     * @param typeLiteral The TyleLiteral of the filter key to be used.
++     * @param configValue the configuration used.
++     * @param <T> A Servlet Filter class.
++     * @return A FilterConfig used to map a String path to this configuration.
++     * @since 1.4
++     */
++    @SuppressWarnings({"UnusedDeclaration"})
++    protected static <T extends Filter> FilterConfig<T> filterConfig(TypeLiteral<T> typeLiteral, String configValue) {
++        return filterConfig(Key.get(typeLiteral), configValue);
++    }
++
++    /**
++     * Builds a FilterConfig from a Filer and configuration String
++     * @param type The filter to be used.
++     * @param configValue the configuration used.
++     * @param <T> A Servlet Filter class.
++     * @return A FilterConfig used to map a String path to this configuration.
++     * @since 1.4
++     */
++    @SuppressWarnings({"UnusedDeclaration"})
++    protected static <T extends Filter> FilterConfig<T> filterConfig(Class<T> type, String configValue) {
++        return filterConfig(Key.get(type), configValue);
++    }
++
++
++    /**
++     * Filter configuration which pairs a Filter class with its configuration used on a path.
++     * @param <T> The Servlet Filter class.
++     * @since 1.4
++     */
++    public static class FilterConfig<T extends Filter> {
++        private Key<T> key;
++        private String configValue;
++
++        private FilterConfig(Key<T> key, String configValue) {
++            super();
++            this.key = key;
++            this.configValue = configValue;
++        }
++
++        public Key<T> getKey() {
++            return key;
++        }
++
++        public String getConfigValue() {
++            return configValue;
++        }
++    }
++
++
++
++
++
++
++
++    // legacy methods
++
++
++    static boolean isGuiceVersion3() {
++        try {
++            Class.forName("com.google.inject.multibindings.MapKey");
++            return false;
++        } catch (ClassNotFoundException e) {
++            return true;
++        }
++    }
++
++    private void addLegacyFilterChain(String pattern, FilterConfigKey filterConfigKey) {
++
++        FilterConfig<Filter> filterConfig = new FilterConfig<Filter>(filterConfigKey.getKey(), filterConfigKey.getConfigValue());
++        addFilterChain(pattern, filterConfig);
++    }
++
+     /**
+      * Adds a filter chain to the shiro configuration.
+      * <p/>
+@@ -228,24 +374,52 @@
+      * @param keys
+      */
+     @SuppressWarnings({"UnusedDeclaration"})
++    @Deprecated
+     protected final void addFilterChain(String pattern, Key<? extends Filter>... keys) {
+-        filterChains.put(pattern, keys);
++
++        // We need to extract the keys and FilterConfigKey and convert to the new format.
++
++        FilterConfig[] filterConfigs = new FilterConfig[keys.length];
++        for (int ii = 0; ii < keys.length; ii++) {
++            Key<? extends Filter> key = keys[ii];
++            // If this is a path matching filter, we need to remember the config
++            if (key instanceof FilterConfigKey) {
++                // legacy config
++                FilterConfigKey legacyKey = (FilterConfigKey) key;
++                filterConfigs[ii] = new FilterConfig(legacyKey.getKey(), legacyKey.getConfigValue());
++            }
++            else {
++                // Some other type of Filter key, no config
++                filterConfigs[ii] = new FilterConfig(key, "");
++            }
++        }
++
++        filterChains.put(pattern, filterConfigs);
+     }
+ 
++    @Deprecated
+     protected static <T extends PathMatchingFilter> Key<T> config(Key<T> baseKey, String configValue) {
++
++        if( !isGuiceVersion3()) {
++            throw new ConfigurationException("Method ShiroWebModule.config(Key<? extends PathMatchingFilter>, String configValue), is not supported when using Guice 4+");
++        }
++
+         return new FilterConfigKey<T>(baseKey, configValue);
+     }
+ 
+     @SuppressWarnings({"UnusedDeclaration"})
++    @Deprecated
+     protected static <T extends PathMatchingFilter> Key<T> config(TypeLiteral<T> typeLiteral, String configValue) {
+         return config(Key.get(typeLiteral), configValue);
+     }
+ 
+     @SuppressWarnings({"UnusedDeclaration"})
++    @Deprecated
+     protected static <T extends PathMatchingFilter> Key<T> config(Class<T> type, String configValue) {
+         return config(Key.get(type), configValue);
+     }
+ 
++    @Deprecated
+     private static class FilterConfigKey<T extends PathMatchingFilter> extends Key<T> {
+         private Key<T> key;
+         private String configValue;
+@@ -264,4 +438,5 @@
+             return configValue;
+         }
+     }
++
+ }
+--- a/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java
++++ b/support/guice/src/main/java/org/apache/shiro/guice/web/SimpleFilterChain.java
+@@ -44,4 +44,13 @@
+             originalChain.doFilter(request, response);
+         }
+     }
++
++    /**
++     * Exposed for testing, not part of public API.
++     * @return an Iterater of filters.
++     */
++    Iterator<? extends Filter> getFilters() {
++        return chain;
++    }
++
+ }
+--- a/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java
++++ b/support/guice/src/test/java/org/apache/shiro/guice/ShiroModuleTest.java
+@@ -28,6 +28,10 @@
+ import org.apache.shiro.authc.AuthenticationToken;
+ import org.apache.shiro.authc.SimpleAuthenticationInfo;
+ import org.apache.shiro.env.Environment;
++import org.apache.shiro.event.EventBus;
++import org.apache.shiro.event.EventBusAware;
++import org.apache.shiro.event.Subscribe;
++import org.apache.shiro.event.support.DefaultEventBus;
+ import org.apache.shiro.mgt.DefaultSecurityManager;
+ import org.apache.shiro.mgt.SecurityManager;
+ import org.apache.shiro.realm.Realm;
+@@ -37,11 +41,13 @@
+ import org.apache.shiro.util.Destroyable;
+ import org.junit.Test;
+ 
++import java.lang.reflect.Field;
+ import java.util.Collection;
++import java.util.Map;
+ 
+ import static org.easymock.EasyMock.*;
+-import static org.junit.Assert.assertNotNull;
+-import static org.junit.Assert.assertTrue;
++import static org.junit.Assert.*;
++import static org.hamcrest.CoreMatchers.*;
+ 
+ public class ShiroModuleTest {
+ 
+@@ -204,6 +210,82 @@
+         verify(myDestroyable);
+     }
+ 
++    /**
++     * @since 1.4
++     * @throws Exception
++     */
++    @Test
++    public void testEventListener() throws Exception {
++
++        final MockRealm mockRealm = createMock(MockRealm.class);
++        final EventBus eventBus = createMock(EventBus.class);
++
++        // expect both objects to be registered
++        eventBus.register(anyObject(MockEventListener1.class));
++        eventBus.register(anyObject(MockEventListener2.class));
++        replay(eventBus);
++
++        final ShiroModule shiroModule = new ShiroModule() {
++            @Override
++            protected void configureShiro() {
++                bindRealm().to(MockRealm.class);
++
++                // bind our event listeners
++                binder().bind(MockEventListener1.class).asEagerSingleton();
++                binder().bind(MockEventListener2.class).asEagerSingleton();
++            }
++
++            @Override
++            protected void bindEventBus(AnnotatedBindingBuilder<EventBus> bind) {
++                bind.toInstance(eventBus);
++            }
++
++            @Provides
++            public MockRealm createRealm() {
++                return mockRealm;
++            }
++
++        };
++        Guice.createInjector(shiroModule);
++
++        verify(eventBus);
++
++    }
++
++    /**
++     * @since 1.4
++     * @throws Exception
++     */
++    @Test
++    public void testEventBusAware() throws Exception {
++
++        final MockRealm mockRealm = createMock(MockRealm.class);
++
++        final ShiroModule shiroModule = new ShiroModule() {
++            @Override
++            protected void configureShiro() {
++                bindRealm().to(MockRealm.class);
++
++                binder().bind(MockEventBusAware.class).asEagerSingleton();
++                expose(MockEventBusAware.class);
++            }
++
++            @Provides
++            public MockRealm createRealm() {
++                return mockRealm;
++            }
++
++        };
++        Injector injector = Guice.createInjector(shiroModule);
++        EventBus eventBus = injector.getInstance(EventBus.class);
++        SecurityManager securityManager = injector.getInstance(SecurityManager.class);
++
++        MockEventBusAware eventBusAware = injector.getInstance(MockEventBusAware.class);
++
++        assertSame(eventBus, eventBusAware.eventBus);
++        assertSame(eventBus, ((DefaultSecurityManager)securityManager).getEventBus());
++    }
++
+     public static interface MockRealm extends Realm {
+ 
+     }
+@@ -227,4 +309,27 @@
+ 
+     public static interface MyDestroyable extends Destroyable {
+     }
++
++    public static class MockEventListener1 {
++        @Subscribe
++        public void listenToAllAndDoNothing(Object o) {}
++    }
++
++    public static class MockEventListener2 {
++        @Subscribe
++        public void listenToAllAndDoNothing(Object o) {}
++    }
++
++    public static class MockEventBusAware implements EventBusAware {
++        private EventBus eventBus;
++
++        public EventBus getEventBus() {
++            return eventBus;
++        }
++
++        @Override
++        public void setEventBus(EventBus eventBus) {
++            this.eventBus = eventBus;
++        }
++    }
+ }
+--- a/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java
++++ b/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java
+@@ -45,7 +45,8 @@
+                 bindRealm().to(ShiroModuleTest.MockRealm.class);
+ 
+                 addFilterChain("/index.html", AUTHC_BASIC);
+-                addFilterChain("/index2.html", config(PERMS, "permission"));
++//                addFilterChain("/index2.html", config(PERMS, "permission"));
++                addFilterChain("/index2.html", filterConfig(PERMS, "permission"));
+             }
+ 
+             @Provides
+--- a/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
++++ b/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
+@@ -21,6 +21,7 @@
+ import com.google.inject.Guice;
+ import com.google.inject.Inject;
+ import com.google.inject.Injector;
++import com.google.inject.Key;
+ import com.google.inject.Provides;
+ import com.google.inject.binder.AnnotatedBindingBuilder;
+ import org.apache.shiro.guice.ShiroModuleTest;
+@@ -28,21 +29,38 @@
+ import org.apache.shiro.mgt.SecurityManager;
+ import org.apache.shiro.realm.Realm;
+ import org.apache.shiro.session.mgt.SessionManager;
++import org.apache.shiro.web.env.EnvironmentLoader;
+ import org.apache.shiro.web.env.WebEnvironment;
++import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
++import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
++import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
++import org.apache.shiro.web.filter.authz.RolesAuthorizationFilter;
+ import org.apache.shiro.web.filter.mgt.FilterChainResolver;
+ import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
+ import org.apache.shiro.web.mgt.WebSecurityManager;
+ import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
+ import org.apache.shiro.web.session.mgt.ServletContainerSessionManager;
++import org.easymock.EasyMock;
++import org.junit.Assume;
+ import org.junit.Test;
+ 
+ import javax.inject.Named;
++import javax.servlet.Filter;
++import javax.servlet.FilterChain;
++import javax.servlet.FilterConfig;
+ import javax.servlet.ServletContext;
++import javax.servlet.ServletException;
++import javax.servlet.ServletRequest;
++import javax.servlet.ServletResponse;
++import javax.servlet.http.HttpServletRequest;
++import java.io.IOException;
+ import java.util.Collection;
++import java.util.Iterator;
++
++import static org.easymock.EasyMock.*;
++import static org.junit.Assert.*;
++import static org.hamcrest.CoreMatchers.*;
+ 
+-import static org.easymock.EasyMock.createMock;
+-import static org.junit.Assert.assertNotNull;
+-import static org.junit.Assert.assertTrue;
+ 
+ public class ShiroWebModuleTest {
+ 
+@@ -146,6 +164,92 @@
+         assertTrue( environment == webEnvironment );
+     }
+ 
++    /**
++     * @since 1.4
++     */
++    @Test
++    public void testAddFilterChainGuice3and4() {
++
++        final ShiroModuleTest.MockRealm mockRealm = createMock(ShiroModuleTest.MockRealm.class);
++        ServletContext servletContext = createMock(ServletContext.class);
++        HttpServletRequest request = createMock(HttpServletRequest.class);
++
++        servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), EasyMock.anyObject());
++        expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes();
++        expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes();
++        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_authc");
++        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_custom_filter");
++        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_authc_basic");
++        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_perms");
++        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/multiple_configs");
++        replay(servletContext, request);
++
++        Injector injector = Guice.createInjector(new ShiroWebModule(servletContext) {
++            @Override
++            protected void configureShiroWeb() {
++                bindRealm().to(ShiroModuleTest.MockRealm.class);
++                expose(FilterChainResolver.class);
++                this.addFilterChain("/test_authc/**", filterConfig(AUTHC));
++                this.addFilterChain("/test_custom_filter/**", Key.get(CustomFilter.class));
++                this.addFilterChain("/test_authc_basic/**", AUTHC_BASIC);
++                this.addFilterChain("/test_perms/**", filterConfig(PERMS, "remote:invoke:lan,wan"));
++                this.addFilterChain("/multiple_configs/**", filterConfig(AUTHC), filterConfig(ROLES, "b2bClient"), filterConfig(PERMS, "remote:invoke:lan,wan"));
++            }
++
++            @Provides
++            public ShiroModuleTest.MockRealm createRealm() {
++                return mockRealm;
++            }
++        });
++
++        FilterChainResolver resolver = injector.getInstance(FilterChainResolver.class);
++        assertThat(resolver, instanceOf(SimpleFilterChainResolver.class));
++        SimpleFilterChainResolver simpleFilterChainResolver = (SimpleFilterChainResolver) resolver;
++
++        // test the /test_authc resource
++        FilterChain filterChain = simpleFilterChainResolver.getChain(request, null, null);
++        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
++        Filter nextFilter = getNextFilter((SimpleFilterChain) filterChain);
++        assertThat(nextFilter, instanceOf(FormAuthenticationFilter.class));
++
++        // test the /test_custom_filter resource
++        filterChain = simpleFilterChainResolver.getChain(request, null, null);
++        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
++        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
++        assertThat(nextFilter, instanceOf(CustomFilter.class));
++
++        // test the /test_authc_basic resource
++        filterChain = simpleFilterChainResolver.getChain(request, null, null);
++        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
++        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
++        assertThat(nextFilter, instanceOf(BasicHttpAuthenticationFilter.class));
++
++        // test the /test_perms resource
++        filterChain = simpleFilterChainResolver.getChain(request, null, null);
++        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
++        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
++        assertThat(nextFilter, instanceOf(PermissionsAuthorizationFilter.class));
++
++        // test the /multiple_configs resource
++        filterChain = simpleFilterChainResolver.getChain(request, null, null);
++        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
++        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(FormAuthenticationFilter.class));
++        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(RolesAuthorizationFilter.class));
++        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(PermissionsAuthorizationFilter.class));
++
++        verify(servletContext, request);
++    }
++
++    private Filter getNextFilter(SimpleFilterChain filterChain) {
++
++        Iterator<? extends Filter> filters = filterChain.getFilters();
++        if (filters.hasNext()) {
++            return filters.next();
++        }
++
++        return null;
++    }
++
+     public static class MyDefaultWebSecurityManager extends DefaultWebSecurityManager {
+         @Inject
+         public MyDefaultWebSecurityManager(Collection<Realm> realms) {
+@@ -162,4 +266,16 @@
+             super(filterChainResolver, servletContext, securityManager);
+         }
+     }
++
++    public static class CustomFilter implements Filter {
++
++        @Override
++        public void init(FilterConfig filterConfig) throws ServletException {}
++
++        @Override
++        public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {}
++
++        @Override
++        public void destroy() {}
++    }
+ }


=====================================
debian/patches/CVE-2020-13933.patch
=====================================
@@ -0,0 +1,1009 @@
+From dc194fc977ab6cfbf3c1ecb085e2bac5db14af6d Mon Sep 17 00:00:00 2001
+From: Brian Demers <bdemers at apache.org>
+Date: Tue, 7 Jul 2020 21:06:35 -0400
+Subject: [PATCH] Add a feature to allow for global filters
+
+Adds new filter to block invalid requests
+---
+ .../shiro/guice/web/ShiroWebModule.java       |  25 ++-
+ .../shiro/guice/web/ShiroWebModuleTest.java   | 153 ++++++++++++++++++
+ .../ShiroWebFilterConfiguration.java          |   8 +
+ .../web/ConfiguredGlobalFiltersTest.groovy    | 104 ++++++++++++
+ .../web/DisabledGlobalFiltersTest.groovy      |  64 ++++++++
+ ...ShiroWebSpringAutoConfigurationTest.groovy |  30 +++-
+ ...roWebAutoConfigurationTestApplication.java |   4 +-
+ .../spring/web/ShiroFilterFactoryBean.java    |  23 +++
+ .../config/AbstractShiroWebConfiguration.java |   3 -
+ .../AbstractShiroWebFilterConfiguration.java  |   9 +-
+ .../config/ShiroWebFilterConfiguration.java   |   6 +
+ .../ShiroWebFilterConfigurationTest.groovy    |   3 +-
+ .../web/ShiroFilterFactoryBeanTest.java       |   8 +-
+ .../config/IniFilterChainResolverFactory.java |  18 +++
+ .../web/filter/InvalidRequestFilter.java      | 124 ++++++++++++++
+ .../shiro/web/filter/mgt/DefaultFilter.java   |   4 +-
+ .../filter/mgt/DefaultFilterChainManager.java |  37 ++++-
+ .../web/filter/mgt/FilterChainManager.java    |  22 +++
+ .../web/servlet/AbstractShiroFilter.java      |   1 +
+ .../IniFilterChainResolverFactoryTest.groovy  |  26 +++
+ .../web/env/IniWebEnvironmentTest.groovy      |  69 ++++++++
+ .../filter/InvalidRequestFilterTest.groovy    | 106 ++++++++++++
+ .../mgt/DefaultFilterChainManagerTest.groovy  |  52 ++++++
+ .../org/apache/shiro/web/env/FilterStub.java  |  45 ++++++
+ 24 files changed, 925 insertions(+), 19 deletions(-)
+ create mode 100644 support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/ConfiguredGlobalFiltersTest.groovy
+ create mode 100644 support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/DisabledGlobalFiltersTest.groovy
+ create mode 100644 web/src/main/java/org/apache/shiro/web/filter/InvalidRequestFilter.java
+ create mode 100644 web/src/test/groovy/org/apache/shiro/web/filter/InvalidRequestFilterTest.groovy
+ create mode 100644 web/src/test/java/org/apache/shiro/web/env/FilterStub.java
+
+--- a/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java
++++ b/support/guice/src/main/java/org/apache/shiro/guice/web/ShiroWebModule.java
+@@ -30,6 +30,7 @@
+ import org.apache.shiro.session.mgt.SessionManager;
+ import org.apache.shiro.util.StringUtils;
+ import org.apache.shiro.web.env.WebEnvironment;
++import org.apache.shiro.web.filter.InvalidRequestFilter;
+ import org.apache.shiro.web.filter.PathMatchingFilter;
+ import org.apache.shiro.web.filter.authc.AnonymousFilter;
+ import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
+@@ -84,7 +85,8 @@
+     public static final Key<SslFilter> SSL = Key.get(SslFilter.class);
+     @SuppressWarnings({"UnusedDeclaration"})
+     public static final Key<UserFilter> USER = Key.get(UserFilter.class);
+-
++    @SuppressWarnings({"UnusedDeclaration"})
++    public static final Key<InvalidRequestFilter> INVALID_REQUEST = Key.get(InvalidRequestFilter.class);
+ 
+     static final String NAME = "SHIRO";
+ 
+@@ -121,6 +123,12 @@
+         };
+     }
+ 
++    public List<FilterConfig<? extends Filter>> globalFilters() {
++        ArrayList<FilterConfig<? extends Filter>> filters = new ArrayList<FilterConfig<? extends Filter>>();
++        filters.add(filterConfig(INVALID_REQUEST));
++        return Collections.unmodifiableList(filters);
++    }
++
+     @Override
+     protected final void configureShiro() {
+         bindBeanType(TypeLiteral.get(ServletContext.class), Key.get(ServletContext.class, Names.named(NAME)));
+@@ -132,6 +140,12 @@
+ 
+         this.configureShiroWeb();
+ 
++        // add default matching route if not already set
++        if (!filterChains.containsKey("/**")) {
++            // no config, this will add only the global filters
++            this.addFilterChain("/**", new FilterConfig[0]);
++        }
++
+         bind(FilterChainResolver.class).toProvider(new FilterChainResolverProvider(setupFilterChainConfigs()));
+     }
+ 
+@@ -150,8 +164,15 @@
+             // collect the keys used for this path
+             List<Key<? extends Filter>> keysForPath = new ArrayList<Key<? extends Filter>>();
+ 
+-            for (int i = 0; i < filterChain.getValue().length; i++) {
+-                FilterConfig<? extends Filter> filterConfig = filterChain.getValue()[i];
++            List<FilterConfig<? extends Filter>> globalFilters = this.globalFilters();
++            FilterConfig<? extends Filter>[] pathFilters = filterChain.getValue();
++
++            // merge the global filters and the path specific filters
++            List<FilterConfig<? extends Filter>> filterConfigs = new ArrayList<FilterConfig<? extends Filter>>(globalFilters.size() + pathFilters.length);
++            filterConfigs.addAll(globalFilters);
++            filterConfigs.addAll(Arrays.asList(pathFilters));
++
++            for (FilterConfig<? extends Filter> filterConfig : filterConfigs) {
+ 
+                 Key<? extends Filter> key = filterConfig.getKey();
+                 String config = filterConfig.getConfigValue();
+--- a/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
++++ b/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
+@@ -24,6 +24,7 @@
+ import com.google.inject.Key;
+ import com.google.inject.Provides;
+ import com.google.inject.binder.AnnotatedBindingBuilder;
++import com.google.inject.name.Names;
+ import org.apache.shiro.guice.ShiroModuleTest;
+ import org.apache.shiro.env.Environment;
+ import org.apache.shiro.mgt.SecurityManager;
+@@ -31,6 +32,7 @@
+ import org.apache.shiro.session.mgt.SessionManager;
+ import org.apache.shiro.web.env.EnvironmentLoader;
+ import org.apache.shiro.web.env.WebEnvironment;
++import org.apache.shiro.web.filter.InvalidRequestFilter;
+ import org.apache.shiro.web.filter.authc.BasicHttpAuthenticationFilter;
+ import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
+ import org.apache.shiro.web.filter.authz.PermissionsAuthorizationFilter;
+@@ -55,7 +57,9 @@
+ import javax.servlet.http.HttpServletRequest;
+ import java.io.IOException;
+ import java.util.Collection;
++import java.util.Collections;
+ import java.util.Iterator;
++import java.util.List;
+ 
+ import static org.easymock.EasyMock.*;
+ import static org.junit.Assert.*;
+@@ -212,35 +216,184 @@
+         FilterChain filterChain = simpleFilterChainResolver.getChain(request, null, null);
+         assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+         Filter nextFilter = getNextFilter((SimpleFilterChain) filterChain);
++        assertThat(nextFilter, instanceOf(InvalidRequestFilter.class));
++        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+         assertThat(nextFilter, instanceOf(FormAuthenticationFilter.class));
+ 
+         // test the /test_custom_filter resource
+         filterChain = simpleFilterChainResolver.getChain(request, null, null);
+         assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+         nextFilter = getNextFilter((SimpleFilterChain) filterChain);
++        assertThat(nextFilter, instanceOf(InvalidRequestFilter.class));
++        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+         assertThat(nextFilter, instanceOf(CustomFilter.class));
+ 
+         // test the /test_authc_basic resource
+         filterChain = simpleFilterChainResolver.getChain(request, null, null);
+         assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+         nextFilter = getNextFilter((SimpleFilterChain) filterChain);
++        assertThat(nextFilter, instanceOf(InvalidRequestFilter.class));
++        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+         assertThat(nextFilter, instanceOf(BasicHttpAuthenticationFilter.class));
+ 
+         // test the /test_perms resource
+         filterChain = simpleFilterChainResolver.getChain(request, null, null);
+         assertThat(filterChain, instanceOf(SimpleFilterChain.class));
+         nextFilter = getNextFilter((SimpleFilterChain) filterChain);
++        assertThat(nextFilter, instanceOf(InvalidRequestFilter.class));
++        nextFilter = getNextFilter((SimpleFilterChain) filterChain);
+         assertThat(nextFilter, instanceOf(PermissionsAuthorizationFilter.class));
+ 
+         // test the /multiple_configs resource
+         filterChain = simpleFilterChainResolver.getChain(request, null, null);
+         assertThat(filterChain, instanceOf(SimpleFilterChain.class));
++        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(InvalidRequestFilter.class));
+         assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(FormAuthenticationFilter.class));
+         assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(RolesAuthorizationFilter.class));
+         assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(PermissionsAuthorizationFilter.class));
+ 
+         verify(servletContext, request);
+     }
++
++    @Test
++    public void testDefaultPath() {
++
++        final ShiroModuleTest.MockRealm mockRealm = createMock(ShiroModuleTest.MockRealm.class);
++        ServletContext servletContext = createMock(ServletContext.class);
++        HttpServletRequest request = createMock(HttpServletRequest.class);
++
++        servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), EasyMock.anyObject());
++        expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes();
++        expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes();
++        expect(request.getAttribute("javax.servlet.include.path_info")).andReturn(null).anyTimes();
++        expect(request.getPathInfo()).andReturn(null).anyTimes();
++        expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn("/test/foobar");
++        replay(servletContext, request);
++
++        Injector injector = Guice.createInjector(new ShiroWebModule(servletContext) {
++            @Override
++            protected void configureShiroWeb() {
++                bindRealm().to(ShiroModuleTest.MockRealm.class);
++                expose(FilterChainResolver.class);
++                // no paths configured
++            }
++
++            @Provides
++            public ShiroModuleTest.MockRealm createRealm() {
++                return mockRealm;
++            }
++        });
++
++        FilterChainResolver resolver = injector.getInstance(FilterChainResolver.class);
++        assertThat(resolver, instanceOf(SimpleFilterChainResolver.class));
++        SimpleFilterChainResolver simpleFilterChainResolver = (SimpleFilterChainResolver) resolver;
++
++        // test the /test_authc resource
++        FilterChain filterChain = simpleFilterChainResolver.getChain(request, null, null);
++        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
++
++        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(InvalidRequestFilter.class));
++        assertThat(getNextFilter((SimpleFilterChain) filterChain), nullValue());
++
++        verify(servletContext, request);
++    }
++
++    @Test
++    public void testDisableGlobalFilters() {
++
++        final ShiroModuleTest.MockRealm mockRealm = createMock(ShiroModuleTest.MockRealm.class);
++        ServletContext servletContext = createMock(ServletContext.class);
++        HttpServletRequest request = createMock(HttpServletRequest.class);
++
++        servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), EasyMock.anyObject());
++        expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes();
++        expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes();
++        expect(request.getAttribute("javax.servlet.include.path_info")).andReturn(null).anyTimes();
++        expect(request.getPathInfo()).andReturn(null).anyTimes();
++        expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn("/test/foobar");
++        replay(servletContext, request);
++
++        Injector injector = Guice.createInjector(new ShiroWebModule(servletContext) {
++            @Override
++            protected void configureShiroWeb() {
++                bindRealm().to(ShiroModuleTest.MockRealm.class);
++                expose(FilterChainResolver.class);
++                this.addFilterChain("/**", filterConfig(AUTHC));
++            }
++
++            @Override
++            public List<FilterConfig<? extends Filter>> globalFilters() {
++                return Collections.emptyList();
++            }
++
++            @Provides
++            public ShiroModuleTest.MockRealm createRealm() {
++                return mockRealm;
++            }
++        });
++
++        FilterChainResolver resolver = injector.getInstance(FilterChainResolver.class);
++        assertThat(resolver, instanceOf(SimpleFilterChainResolver.class));
++        SimpleFilterChainResolver simpleFilterChainResolver = (SimpleFilterChainResolver) resolver;
++
++        // test the /test_authc resource
++        FilterChain filterChain = simpleFilterChainResolver.getChain(request, null, null);
++        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
++
++        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(FormAuthenticationFilter.class));
++        assertThat(getNextFilter((SimpleFilterChain) filterChain), nullValue());
++
++        verify(servletContext, request);
++    }
++
++    @Test
++    public void testChangeInvalidFilterConfig() {
++
++        final ShiroModuleTest.MockRealm mockRealm = createMock(ShiroModuleTest.MockRealm.class);
++        ServletContext servletContext = createMock(ServletContext.class);
++        HttpServletRequest request = createMock(HttpServletRequest.class);
++
++        servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), EasyMock.anyObject());
++        expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes();
++        expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes();
++        expect(request.getAttribute("javax.servlet.include.path_info")).andReturn(null).anyTimes();
++        expect(request.getPathInfo()).andReturn(null).anyTimes();
++        expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn("/test/foobar");
++        replay(servletContext, request);
++
++        Injector injector = Guice.createInjector(new ShiroWebModule(servletContext) {
++            @Override
++            protected void configureShiroWeb() {
++
++                bindConstant().annotatedWith(Names.named("shiro.blockBackslash")).to(false);
++
++                bindRealm().to(ShiroModuleTest.MockRealm.class);
++                expose(FilterChainResolver.class);
++                this.addFilterChain("/**", filterConfig(AUTHC));
++            }
++
++            @Provides
++            public ShiroModuleTest.MockRealm createRealm() {
++                return mockRealm;
++            }
++        });
++
++        FilterChainResolver resolver = injector.getInstance(FilterChainResolver.class);
++        assertThat(resolver, instanceOf(SimpleFilterChainResolver.class));
++        SimpleFilterChainResolver simpleFilterChainResolver = (SimpleFilterChainResolver) resolver;
++
++        // test the /test_authc resource
++        FilterChain filterChain = simpleFilterChainResolver.getChain(request, null, null);
++        assertThat(filterChain, instanceOf(SimpleFilterChain.class));
++
++        Filter invalidRequestFilter = getNextFilter((SimpleFilterChain) filterChain);
++        assertThat(invalidRequestFilter, instanceOf(InvalidRequestFilter.class));
++        assertFalse("Expected 'blockBackslash' to be false", ((InvalidRequestFilter) invalidRequestFilter).isBlockBackslash());
++        assertThat(getNextFilter((SimpleFilterChain) filterChain), instanceOf(FormAuthenticationFilter.class));
++        assertThat(getNextFilter((SimpleFilterChain) filterChain), nullValue());
++
++        verify(servletContext, request);
++    }
+ 
+     private Filter getNextFilter(SimpleFilterChain filterChain) {
+ 
+--- /dev/null
++++ b/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/ConfiguredGlobalFiltersTest.groovy
+@@ -0,0 +1,104 @@
++/*
++ * 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.boot.autoconfigure.web
++
++import org.apache.shiro.spring.boot.autoconfigure.web.application.ShiroWebAutoConfigurationTestApplication
++import org.apache.shiro.spring.web.ShiroFilterFactoryBean
++import org.apache.shiro.spring.web.config.AbstractShiroWebFilterConfiguration
++import org.apache.shiro.web.filter.InvalidRequestFilter
++import org.apache.shiro.web.filter.authz.PortFilter
++import org.apache.shiro.web.filter.mgt.DefaultFilter
++import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager
++import org.apache.shiro.web.filter.mgt.NamedFilterList
++import org.apache.shiro.web.servlet.AbstractShiroFilter
++import org.junit.Test
++import org.junit.runner.RunWith
++import org.springframework.beans.factory.annotation.Autowired
++import org.springframework.boot.test.context.SpringBootTest
++import org.springframework.context.annotation.Bean
++import org.springframework.context.annotation.Configuration
++import org.springframework.test.context.junit4.SpringRunner
++
++import static org.hamcrest.MatcherAssert.assertThat
++import static org.hamcrest.Matchers.*
++
++ at RunWith(SpringRunner.class)
++ at SpringBootTest(classes = [ShiroWebAutoConfigurationTestApplication, Config])
++
++class ConfiguredGlobalFiltersTest {
++
++    @Configuration
++    static class Config extends AbstractShiroWebFilterConfiguration {
++
++        @Bean
++        List<String> globalFilters() {
++            return [DefaultFilter.invalidRequest.name(), DefaultFilter.port.name()]
++        }
++
++        @Bean
++        @Override
++        ShiroFilterFactoryBean shiroFilterFactoryBean() {
++            ShiroFilterFactoryBean bean = super.shiroFilterFactoryBean()
++            InvalidRequestFilter invalidRequestFilter = new InvalidRequestFilter()
++            invalidRequestFilter.setBlockBackslash(false)
++            PortFilter portFilter = new PortFilter()
++            portFilter.setPort(9999)
++            bean.getFilters().put("invalidRequest", invalidRequestFilter)
++            bean.getFilters().put("port", portFilter)
++            return bean
++        }
++    }
++
++    @Autowired
++    private AbstractShiroFilter shiroFilter
++
++    @Test
++    void testGlobalFiltersConfigured() {
++        // make sure global chains are configured
++        assertThat shiroFilter.filterChainResolver.filterChainManager, instanceOf(DefaultFilterChainManager)
++        DefaultFilterChainManager filterChainManager = shiroFilter.filterChainResolver.filterChainManager
++
++        // default config set
++        assertThat filterChainManager.globalFilterNames, contains(DefaultFilter.invalidRequest.name(),
++                                                                  DefaultFilter.port.name())
++        // default route configured
++        NamedFilterList allChain = filterChainManager.getChain("/**")
++        assertThat allChain, contains(
++                instanceOf(DefaultFilter.invalidRequest.filterClass),
++                instanceOf(DefaultFilter.port.filterClass))
++
++        InvalidRequestFilter invalidRequest = allChain.get(0)
++        assertThat "Expected invalidRequest.blockBackslash to be false", !invalidRequest.isBlockBackslash()
++        PortFilter portFilter = allChain.get(1) // an ugly line, but we want to make sure that we can override the filters
++        // defined in Shiro's DefaultFilter
++        assertThat portFilter.port, equalTo(9999)
++
++        // configured routes also contain global filters
++        NamedFilterList loginChain = filterChainManager.getChain("/login.html")
++        assertThat loginChain, contains(
++                instanceOf(DefaultFilter.invalidRequest.filterClass),
++                instanceOf(DefaultFilter.port.filterClass),
++                instanceOf(DefaultFilter.authc.filterClass)) // configured in ShiroWebAutoConfigurationTestApplication
++
++        assertThat loginChain.get(0), sameInstance(invalidRequest)
++        assertThat loginChain.get(1), sameInstance(portFilter)
++
++
++    }
++}
+--- /dev/null
++++ b/support/spring-boot/spring-boot-web-starter/src/test/groovy/org/apache/shiro/spring/boot/autoconfigure/web/DisabledGlobalFiltersTest.groovy
+@@ -0,0 +1,64 @@
++/*
++ * 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.boot.autoconfigure.web;
++
++import org.apache.shiro.spring.boot.autoconfigure.web.application.ShiroWebAutoConfigurationTestApplication
++import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager
++import org.apache.shiro.web.servlet.AbstractShiroFilter
++import org.junit.Test
++import org.junit.runner.RunWith
++import org.springframework.beans.factory.annotation.Autowired
++import org.springframework.boot.test.context.SpringBootTest
++import org.springframework.context.annotation.Bean
++import org.springframework.context.annotation.Configuration
++import org.springframework.test.context.junit4.SpringRunner
++
++import static org.hamcrest.MatcherAssert.assertThat
++import static org.hamcrest.Matchers.equalTo
++import static org.hamcrest.Matchers.instanceOf
++import static org.hamcrest.Matchers.nullValue
++
++ at RunWith(SpringRunner.class)
++ at SpringBootTest(classes = [ShiroWebAutoConfigurationTestApplication, Config])
++class DisabledGlobalFiltersTest {
++
++    @Configuration
++    static class Config {
++
++        @Bean
++        List<String> globalFilters() {
++            return []
++        }
++    }
++
++    @Autowired
++    private AbstractShiroFilter shiroFilter
++
++    @Test
++    void testGlobalFiltersDisabled() {
++        // make sure global chains are configured
++        assertThat shiroFilter.filterChainResolver.filterChainManager, instanceOf(DefaultFilterChainManager)
++        DefaultFilterChainManager filterChainManager = shiroFilter.filterChainResolver.filterChainManager
++
++        // default config set
++        assertThat filterChainManager.globalFilterNames, equalTo([])
++        // default route configured
++        assertThat filterChainManager.getChain("/**"), nullValue()
++    }
++}
+--- a/support/spring/src/main/java/org/apache/shiro/spring/web/ShiroFilterFactoryBean.java
++++ b/support/spring/src/main/java/org/apache/shiro/spring/web/ShiroFilterFactoryBean.java
+@@ -25,8 +25,10 @@
+ import org.apache.shiro.util.StringUtils;
+ import org.apache.shiro.web.config.IniFilterChainResolverFactory;
+ import org.apache.shiro.web.filter.AccessControlFilter;
++import org.apache.shiro.web.filter.InvalidRequestFilter;
+ import org.apache.shiro.web.filter.authc.AuthenticationFilter;
+ import org.apache.shiro.web.filter.authz.AuthorizationFilter;
++import org.apache.shiro.web.filter.mgt.DefaultFilter;
+ import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
+ import org.apache.shiro.web.filter.mgt.FilterChainManager;
+ import org.apache.shiro.web.filter.mgt.FilterChainResolver;
+@@ -41,7 +43,9 @@
+ import org.springframework.beans.factory.config.BeanPostProcessor;
+ 
+ import javax.servlet.Filter;
++import java.util.ArrayList;
+ import java.util.LinkedHashMap;
++import java.util.List;
+ import java.util.Map;
+ 
+ /**
+@@ -121,6 +125,8 @@
+ 
+     private Map<String, Filter> filters;
+ 
++    private List<String> globalFilters;
++
+     private Map<String, String> filterChainDefinitionMap; //urlPathExpression_to_comma-delimited-filter-chain-definition
+ 
+     private String loginUrl;
+@@ -131,6 +137,8 @@
+ 
+     public ShiroFilterFactoryBean() {
+         this.filters = new LinkedHashMap<String, Filter>();
++        this.globalFilters = new ArrayList<String>();
++        this.globalFilters.add(DefaultFilter.invalidRequest.name());
+         this.filterChainDefinitionMap = new LinkedHashMap<String, String>(); //order matters!
+     }
+ 
+@@ -332,6 +340,14 @@
+     }
+ 
+     /**
++     * Sets the list of filters that will be executed against every request.  Defaults to the {@link InvalidRequestFilter} which will block known invalid request attacks.
++     * @param globalFilters the list of filters to execute before specific path filters.
++     */
++    public void setGlobalFilters(List<String> globalFilters) {
++        this.globalFilters = globalFilters;
++    }
++
++    /**
+      * Lazily creates and returns a {@link AbstractShiroFilter} concrete instance via the
+      * {@link #createInstance} method.
+      *
+@@ -388,6 +404,9 @@
+             }
+         }
+ 
++        // set the global filters
++        manager.setGlobalFilters(this.globalFilters);
++
+         //build up the chains:
+         Map<String, String> chains = getFilterChainDefinitionMap();
+         if (!CollectionUtils.isEmpty(chains)) {
+@@ -398,6 +417,9 @@
+             }
+         }
+ 
++        // create the default chain, to match anything the path matching would have missed
++        manager.createDefaultChain("/**"); // TODO this assumes ANT path matching, which might be OK here
++
+         return manager;
+     }
+ 
+@@ -533,6 +555,7 @@
+                 throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
+             }
+             setSecurityManager(webSecurityManager);
++
+             if (resolver != null) {
+                 setFilterChainResolver(resolver);
+             }
+--- a/support/spring/src/test/java/org/apache/shiro/spring/web/ShiroFilterFactoryBeanTest.java
++++ b/support/spring/src/test/java/org/apache/shiro/spring/web/ShiroFilterFactoryBeanTest.java
+@@ -18,6 +18,7 @@
+  */
+ package org.apache.shiro.spring.web;
+ 
++import org.apache.shiro.web.filter.InvalidRequestFilter;
+ import org.apache.shiro.web.filter.authc.FormAuthenticationFilter;
+ import org.apache.shiro.web.filter.mgt.DefaultFilterChainManager;
+ import org.apache.shiro.web.filter.mgt.NamedFilterList;
+@@ -55,11 +56,12 @@
+         DefaultFilterChainManager fcManager = (DefaultFilterChainManager) resolver.getFilterChainManager();
+         NamedFilterList chain = fcManager.getChain("/test");
+         assertNotNull(chain);
+-        assertEquals(chain.size(), 2);
++        assertEquals(chain.size(), 3);
+         Filter[] filters = new Filter[chain.size()];
+         filters = chain.toArray(filters);
+-        assertTrue(filters[0] instanceof DummyFilter);
+-        assertTrue(filters[1] instanceof FormAuthenticationFilter);
++        assertTrue(filters[0] instanceof InvalidRequestFilter); // global filter
++        assertTrue(filters[1] instanceof DummyFilter);
++        assertTrue(filters[2] instanceof FormAuthenticationFilter);
+     }
+ 
+     /**
+--- a/web/src/main/java/org/apache/shiro/web/config/IniFilterChainResolverFactory.java
++++ b/web/src/main/java/org/apache/shiro/web/config/IniFilterChainResolverFactory.java
+@@ -24,6 +24,7 @@
+ import org.apache.shiro.config.ReflectionBuilder;
+ import org.apache.shiro.util.CollectionUtils;
+ import org.apache.shiro.util.Factory;
++import org.apache.shiro.web.filter.mgt.DefaultFilter;
+ import org.apache.shiro.web.filter.mgt.FilterChainManager;
+ import org.apache.shiro.web.filter.mgt.FilterChainResolver;
+ import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
+@@ -32,7 +33,9 @@
+ 
+ import javax.servlet.Filter;
+ import javax.servlet.FilterConfig;
++import java.util.Collections;
+ import java.util.LinkedHashMap;
++import java.util.List;
+ import java.util.Map;
+ 
+ /**
+@@ -51,6 +54,8 @@
+ 
+     private Map<String, ?> defaultBeans;
+ 
++    private List<String> globalFilters = Collections.singletonList(DefaultFilter.invalidRequest.name());
++
+     public IniFilterChainResolverFactory() {
+         super();
+     }
+@@ -72,6 +77,14 @@
+         this.filterConfig = filterConfig;
+     }
+ 
++    public List<String> getGlobalFilters() {
++        return globalFilters;
++    }
++
++    public void setGlobalFilters(List<String> globalFilters) {
++        this.globalFilters = globalFilters;
++    }
++
+     protected FilterChainResolver createInstance(Ini ini) {
+         FilterChainResolver filterChainResolver = createDefaultInstance();
+         if (filterChainResolver instanceof PathMatchingFilterChainResolver) {
+@@ -122,9 +135,14 @@
+         //add the filters to the manager:
+         registerFilters(filters, manager);
+ 
++        manager.setGlobalFilters(getGlobalFilters());
++
+         //urls section:
+         section = ini.getSection(URLS);
+         createChains(section, manager);
++
++        // create the default chain, to match anything the path matching would have missed
++        manager.createDefaultChain("/**"); // TODO this assumes ANT path matching
+     }
+ 
+     protected void registerFilters(Map<String, Filter> filters, FilterChainManager manager) {
+--- /dev/null
++++ b/web/src/main/java/org/apache/shiro/web/filter/InvalidRequestFilter.java
+@@ -0,0 +1,136 @@
++/*
++ * 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.filter;
++
++import org.apache.shiro.web.util.WebUtils;
++
++import javax.servlet.ServletRequest;
++import javax.servlet.ServletResponse;
++import java.util.Arrays;
++import java.util.Collections;
++import java.util.List;
++
++/**
++ * A request filter that blocks malicious requests. Invalid request will respond with a 400 response code.
++ * <p>
++ * This filter checks and blocks the request if the following characters are found in the request URI:
++ * <ul>
++ *     <li>Semicolon - can be disabled by setting {@code blockSemicolon = false}</li>
++ *     <li>Backslash - can be disabled by setting {@code blockBackslash = false}</li>
++ *     <li>Non-ASCII characters - can be disabled by setting {@code blockNonAscii = false}, the ability to disable this check will be removed in future version.</li>
++ * </ul>
++ *
++ * @see <a href="https://docs.spring.io/spring-security/site/docs/current/api/org/springframework/security/web/firewall/StrictHttpFirewall.html">This class was inspired by Spring Security StrictHttpFirewall</a>
++ * @since 1.6
++ */
++public class InvalidRequestFilter extends AccessControlFilter {
++
++    private static final List<String> SEMICOLON = Collections.unmodifiableList(Arrays.asList(";", "%3b", "%3B"));
++
++    private static final List<String> BACKSLASH = Collections.unmodifiableList(Arrays.asList("\\", "%5c", "%5C"));
++
++    private boolean blockSemicolon = true;
++
++    private boolean blockBackslash = true;
++
++    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);
++    }
++
++    @Override
++    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
++        WebUtils.toHttp(response).sendError(400, "Invalid request");
++        return false;
++    }
++
++    private boolean containsSemicolon(String uri) {
++        if (isBlockSemicolon()) {
++            int length = uri.length();
++            for (int i = 0; i < length; i++) {
++                char c = uri.charAt(i);
++                if (c == ';') {
++                    return true;
++                }
++            }
++        }
++        return false;
++    }
++
++    private boolean containsBackslash(String uri) {
++        if (isBlockBackslash()) {
++            int length = uri.length();
++            for (int i = 0; i < length; i++) {
++                char c = uri.charAt(i);
++                if (c == '\\') {
++                    return true;
++                }
++            }
++        }
++        return false;
++    }
++
++    private boolean containsNonAsciiCharacters(String uri) {
++        if (isBlockNonAscii()) {
++            return !containsOnlyPrintableAsciiCharacters(uri);
++        }
++        return false;
++    }
++
++    private static boolean containsOnlyPrintableAsciiCharacters(String uri) {
++        int length = uri.length();
++        for (int i = 0; i < length; i++) {
++            char c = uri.charAt(i);
++            if (c < '\u0020' || c > '\u007e') {
++                return false;
++            }
++        }
++        return true;
++    }
++
++    public boolean isBlockSemicolon() {
++        return blockSemicolon;
++    }
++
++    public void setBlockSemicolon(boolean blockSemicolon) {
++        this.blockSemicolon = blockSemicolon;
++    }
++
++    public boolean isBlockBackslash() {
++        return blockBackslash;
++    }
++
++    public void setBlockBackslash(boolean blockBackslash) {
++        this.blockBackslash = blockBackslash;
++    }
++
++    public boolean isBlockNonAscii() {
++        return blockNonAscii;
++    }
++
++    public void setBlockNonAscii(boolean blockNonAscii) {
++        this.blockNonAscii = blockNonAscii;
++    }
++}
+--- a/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilter.java
++++ b/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilter.java
+@@ -19,6 +19,7 @@
+ package org.apache.shiro.web.filter.mgt;
+ 
+ import org.apache.shiro.util.ClassUtils;
++import org.apache.shiro.web.filter.InvalidRequestFilter;
+ import org.apache.shiro.web.filter.authc.*;
+ import org.apache.shiro.web.filter.authz.*;
+ import org.apache.shiro.web.filter.session.NoSessionCreationFilter;
+@@ -47,7 +48,8 @@
+     rest(HttpMethodPermissionFilter.class),
+     roles(RolesAuthorizationFilter.class),
+     ssl(SslFilter.class),
+-    user(UserFilter.class);
++    user(UserFilter.class),
++    invalidRequest(InvalidRequestFilter.class);
+ 
+     private final Class<? extends Filter> filterClass;
+ 
+--- a/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilterChainManager.java
++++ b/web/src/main/java/org/apache/shiro/web/filter/mgt/DefaultFilterChainManager.java
+@@ -30,8 +30,10 @@
+ import javax.servlet.FilterChain;
+ import javax.servlet.FilterConfig;
+ import javax.servlet.ServletException;
++import java.util.ArrayList;
+ import java.util.Collections;
+ import java.util.LinkedHashMap;
++import java.util.List;
+ import java.util.Map;
+ import java.util.Set;
+ 
+@@ -52,17 +54,21 @@
+ 
+     private Map<String, Filter> filters; //pool of filters available for creating chains
+ 
++    private List<String> globalFilterNames; // list of filters to prepend to every chain
++
+     private Map<String, NamedFilterList> filterChains; //key: chain name, value: chain
+ 
+     public DefaultFilterChainManager() {
+         this.filters = new LinkedHashMap<String, Filter>();
+         this.filterChains = new LinkedHashMap<String, NamedFilterList>();
++        this.globalFilterNames = new ArrayList<String>();
+         addDefaultFilters(false);
+     }
+ 
+     public DefaultFilterChainManager(FilterConfig filterConfig) {
+         this.filters = new LinkedHashMap<String, Filter>();
+         this.filterChains = new LinkedHashMap<String, NamedFilterList>();
++        this.globalFilterNames = new ArrayList<String>();
+         setFilterConfig(filterConfig);
+         addDefaultFilters(true);
+     }
+@@ -115,6 +121,17 @@
+         addFilter(name, filter, init, true);
+     }
+ 
++    public void createDefaultChain(String chainName) {
++        // only create the defaultChain if we don't have a chain with this name already
++        // (the global filters will already be in that chain)
++        if (!getChainNames().contains(chainName) && !CollectionUtils.isEmpty(globalFilterNames)) {
++            // add each of global filters
++            for (String filterName : globalFilterNames) {
++                addToChain(chainName, filterName);
++            }
++        }
++    }
++
+     public void createChain(String chainName, String chainDefinition) {
+         if (!StringUtils.hasText(chainName)) {
+             throw new NullPointerException("chainName cannot be null or empty.");
+@@ -124,7 +141,14 @@
+         }
+ 
+         if (log.isDebugEnabled()) {
+-            log.debug("Creating chain [" + chainName + "] from String definition [" + chainDefinition + "]");
++            log.debug("Creating chain [" + chainName + "] with global filters " + globalFilterNames + " and from String definition [" + chainDefinition + "]");
++        }
++
++        // first add each of global filters
++        if (!CollectionUtils.isEmpty(globalFilterNames)) {
++            for (String filterName : globalFilterNames) {
++                addToChain(chainName, filterName);
++            }
+         }
+ 
+         //parse the value by tokenizing it to get the resulting filter-specific config entries
+@@ -273,6 +297,21 @@
+         chain.add(filter);
+     }
+ 
++    public void setGlobalFilters(List<String> globalFilterNames) throws ConfigurationException {
++        // validate each filter name
++        if (!CollectionUtils.isEmpty(globalFilterNames)) {
++            for (String filterName : globalFilterNames) {
++                Filter filter = filters.get(filterName);
++                if (filter == null) {
++                    throw new ConfigurationException("There is no filter with name '" + filterName +
++                                                     "' to apply to the global filters in the pool of available Filters.  Ensure a " +
++                                                     "filter with that name/path has first been registered with the addFilter method(s).");
++                }
++                this.globalFilterNames.add(filterName);
++            }
++        }
++    }
++
+     protected void applyChainConfig(String chainName, Filter filter, String chainSpecificFilterConfig) {
+         if (log.isDebugEnabled()) {
+             log.debug("Attempting to apply path [" + chainName + "] to filter [" + filter + "] " +
+--- a/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java
++++ b/web/src/main/java/org/apache/shiro/web/filter/mgt/FilterChainManager.java
+@@ -22,6 +22,7 @@
+ 
+ import javax.servlet.Filter;
+ import javax.servlet.FilterChain;
++import java.util.List;
+ import java.util.Map;
+ import java.util.Set;
+ 
+@@ -165,6 +166,14 @@
+     void createChain(String chainName, String chainDefinition);
+ 
+     /**
++     * Creates a chain that should match any non-matched request paths, typically {@code /**} assuming an {@link AntPathMatcher} I used.
++     * @param chainName The name of the chain to create, likely {@code /**}.
++     * @since 1.6
++     * @see org.apache.shiro.lang.util.AntPathMatcher AntPathMatcher
++     */
++    void createDefaultChain(String chainName);
++
++    /**
+      * Adds (appends) a filter to the filter chain identified by the given {@code chainName}.  If there is no chain
+      * with the given name, a new one is created and the filter will be the first in the chain.
+      *
+@@ -195,4 +204,17 @@
+      *                                  interface).
+      */
+     void addToChain(String chainName, String filterName, String chainSpecificFilterConfig) throws ConfigurationException;
++
++    /**
++     * Configures the set of named filters that will match all paths.  These filters will match BEFORE explicitly
++     * configured filter chains i.e. by calling {@link #createChain(String, String)}, {@link #addToChain(String, String)}, etc.
++     * <br>
++     * <strong>Filters configured in this list wll apply to ALL requests.</strong>
++     *
++     * @param globalFilterNames         the list of filter names to match ALL paths.
++     * @throws ConfigurationException   if one of the filter names is invalid and cannot be loaded from the set of
++     *                                  configured filters {@link #getFilters()}}.
++     * @since 1.6
++     */
++    void setGlobalFilters(List<String> globalFilterNames) throws ConfigurationException;
+ }
+--- a/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java
++++ b/web/src/main/java/org/apache/shiro/web/servlet/AbstractShiroFilter.java
+@@ -404,6 +404,7 @@
+      * @since 1.0
+      */
+     protected FilterChain getExecutionChain(ServletRequest request, ServletResponse response, FilterChain origChain) {
++
+         FilterChain chain = origChain;
+ 
+         FilterChainResolver resolver = getFilterChainResolver();
+--- /dev/null
++++ b/web/src/test/java/org/apache/shiro/web/env/FilterStub.java
+@@ -0,0 +1,45 @@
++/*
++ * 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.env;
++
++import javax.servlet.Filter;
++import javax.servlet.FilterChain;
++import javax.servlet.FilterConfig;
++import javax.servlet.ServletException;
++import javax.servlet.ServletRequest;
++import javax.servlet.ServletResponse;
++import java.io.IOException;
++
++public class FilterStub implements Filter {
++
++    @Override
++    public void init(FilterConfig filterConfig) throws ServletException {
++
++    }
++
++    @Override
++    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
++
++    }
++
++    @Override
++    public void destroy() {
++
++    }
++}


=====================================
debian/patches/CVE-2020-17510_1_of_2.patch
=====================================
@@ -0,0 +1,236 @@
+From a28300448ae6c4bb78a8ba626b0cacb00f82d5f8 Mon Sep 17 00:00:00 2001
+From: Brian Demers <bdemers at apache.org>
+Date: Thu, 3 Sep 2020 14:58:45 -0400
+Subject: [PATCH] Adds configuration to toggle the normalization of backslashes
+
+This is normally handled by the container
+Update the InvalidRequestFilter to use WebUtils.ALLOW_BACKSLASH
+(new system property: org.apache.shiro.web.ALLOW_BACKSLASH)
+
+Fixes: SHIRO-794
+---
+ .../web/filter/InvalidRequestFilter.java      | 22 ++++--
+ .../org/apache/shiro/web/util/WebUtils.java   |  4 +-
+ .../filter/InvalidRequestFilterTest.groovy    | 48 +++++++++++--
+ .../apache/shiro/web/util/WebUtilsTest.groovy | 52 ++++++++++++++
+ .../shiro/web/RestoreSystemProperties.java    | 69 +++++++++++++++++++
+ 5 files changed, 182 insertions(+), 13 deletions(-)
+ create mode 100644 web/src/test/java/org/apache/shiro/web/RestoreSystemProperties.java
+
+--- 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.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
+--- a/web/src/main/java/org/apache/shiro/web/util/WebUtils.java
++++ b/web/src/main/java/org/apache/shiro/web/util/WebUtils.java
+@@ -56,6 +56,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}.
+@@ -162,7 +164,7 @@
+      * @return normalized path
+      */
+     public static String normalize(String path) {
+-        return normalize(path, true);
++        return normalize(path, Boolean.getBoolean(ALLOW_BACKSLASH));
+     }
+ 
+     /**
+--- 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)
+--- /dev/null
++++ b/web/src/test/java/org/apache/shiro/web/RestoreSystemProperties.java
+@@ -0,0 +1,74 @@
++/*
++ * 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.<String, String>emptyMap(), closure);
++    }
++
++    public static <T> T withProperties(Map<String, String> properties, Closure<T> closure) {
++
++        RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties();
++        try {
++            for (Map.Entry<String, String> property : properties.entrySet()) {
++                System.setProperty(property.getKey(), property.getValue());
++            }
++
++            return closure.call();
++        } finally {
++            restoreSystemProperties.close();
++        }
++    }
++
++    @Override
++    public void close() {
++        restore();
++    }
++}


=====================================
debian/patches/CVE-2020-17510_2_of_2.patch
=====================================
@@ -0,0 +1,67 @@
+From 74d4cb6aee9aa1af4b098edc526a1e5630743f9b Mon Sep 17 00:00:00 2001
+From: Brian Demers <bdemers at apache.org>
+Date: Tue, 29 Sep 2020 17:59:29 -0400
+Subject: [PATCH] Disable jsessionid URL rewriting by default
+
+This matches the default of the InvalidRequestFilter
+
+Fixes: SHIRO-795
+---
+ .../spring/web/config/AbstractShiroWebConfiguration.java     | 2 +-
+ .../shiro/web/session/mgt/DefaultWebSessionManager.java      | 2 +-
+ .../web/session/mgt/DefaultWebSessionManagerTest.groovy      | 5 ++++-
+ 3 files changed, 6 insertions(+), 3 deletions(-)
+
+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 eb7eda1f..9aa275a9 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 @@ public DefaultWebSessionManager() {
+         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/test/groovy/org/apache/shiro/web/session/mgt/DefaultWebSessionManagerTest.groovy b/web/src/test/groovy/org/apache/shiro/web/session/mgt/DefaultWebSessionManagerTest.groovy
+index 841569fc..35b31204 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 @@ public class DefaultWebSessionManagerTest {
+                 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 @@ public class DefaultWebSessionManagerTest {
+         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 @@ public class DefaultWebSessionManagerTest {
+         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 class DefaultWebSessionManagerTest {
+     public void testGetSessionIdFromRequestUriPathSegmentParam() {
+ 
+         mgr.setSessionIdCookieEnabled(false);
++        mgr.setSessionIdUrlRewritingEnabled(true)
+ 
+         HttpServletRequest request = createMock(HttpServletRequest.class);
+         HttpServletResponse response = createMock(HttpServletResponse.class);
+-- 
+2.20.1
+


=====================================
debian/patches/CVE-2020-1957.patch
=====================================
@@ -0,0 +1,297 @@
+--- a/web/src/main/java/org/apache/shiro/web/util/WebUtils.java
++++ b/web/src/main/java/org/apache/shiro/web/util/WebUtils.java
+@@ -108,16 +108,7 @@
+      * @return the path within the web application
+      */
+     public static String getPathWithinApplication(HttpServletRequest request) {
+-        String contextPath = getContextPath(request);
+-        String requestUri = getRequestUri(request);
+-        if (StringUtils.startsWithIgnoreCase(requestUri, contextPath)) {
+-            // Normal case: URI contains context path.
+-            String path = requestUri.substring(contextPath.length());
+-            return (StringUtils.hasText(path) ? path : "/");
+-        } else {
+-            // Special case: rather unusual.
+-            return requestUri;
+-        }
++        return normalize(removeSemicolon(getServletPath(request) + getPathInfo(request)));
+     }
+ 
+     /**
+@@ -131,7 +122,9 @@
+      *
+      * @param request current HTTP request
+      * @return the request URI
++     * @deprecated use getPathWithinApplication() to get the path minus the context path, or call HttpServletRequest.getRequestURI() directly from your code.
+      */
++    @Deprecated
+     public static String getRequestUri(HttpServletRequest request) {
+         String uri = (String) request.getAttribute(INCLUDE_REQUEST_URI_ATTRIBUTE);
+         if (uri == null) {
+@@ -140,6 +133,23 @@
+         return normalize(decodeAndCleanUriString(request, uri));
+     }
+ 
++    private static String getServletPath(HttpServletRequest request) {
++        String servletPath = (String) request.getAttribute(INCLUDE_SERVLET_PATH_ATTRIBUTE);
++        return servletPath != null ? servletPath : valueOrEmpty(request.getServletPath());
++    }
++
++    private static String getPathInfo(HttpServletRequest request) {
++        String pathInfo = (String) request.getAttribute(INCLUDE_PATH_INFO_ATTRIBUTE);
++        return pathInfo != null ? pathInfo : valueOrEmpty(request.getPathInfo());
++    }
++
++    private static String valueOrEmpty(String input) {
++        if (input == null) {
++            return "";
++        }
++        return input;
++    }
++
+     /**
+      * Normalize a relative URI path that may have relative values ("/./",
+      * "/../", and so on ) it it.  <strong>WARNING</strong> - This method is
+@@ -230,6 +240,10 @@
+      */
+     private static String decodeAndCleanUriString(HttpServletRequest request, String uri) {
+         uri = decodeRequestString(request, uri);
++        return removeSemicolon(uri);
++    }
++
++    private static String removeSemicolon(String uri) {
+         int semicolonIndex = uri.indexOf(';');
+         return (semicolonIndex != -1 ? uri.substring(0, semicolonIndex) : uri);
+     }
+--- a/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java
++++ b/support/guice/src/test/java/org/apache/shiro/guice/web/FilterConfigTest.java
+@@ -85,9 +85,8 @@
+     private HttpServletRequest createMockRequest(String path) {
+         HttpServletRequest request = createNiceMock(HttpServletRequest.class);
+ 
+-        expect(request.getAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE)).andReturn(null).anyTimes();
+-        expect(request.getContextPath()).andReturn("");
+-        expect(request.getRequestURI()).andReturn(path);
++        expect(request.getServletPath()).andReturn("");
++        expect(request.getPathInfo()).andReturn(path);
+         replay(request);
+         return request;
+     }
+--- a/web/src/test/groovy/org/apache/shiro/web/util/WebUtilsTest.groovy
++++ b/web/src/test/groovy/org/apache/shiro/web/util/WebUtilsTest.groovy
+@@ -140,34 +140,90 @@
+ 
+     }
+ 
++    @Test
++    void testGetRequestUriWithServlet() {
++
++        dotTestGetPathWithinApplicationFromRequest("/servlet", "/foobar", "/servlet/foobar")
++        dotTestGetPathWithinApplicationFromRequest("/servlet", "/foobar", "/servlet/foobar")
++        dotTestGetPathWithinApplicationFromRequest("servlet", "/foobar", "/servlet/foobar")
++        dotTestGetPathWithinApplicationFromRequest("servlet", "/foobar", "/servlet/foobar")
++        dotTestGetPathWithinApplicationFromRequest("servlet", "/foobar", "/servlet/foobar")
++        dotTestGetPathWithinApplicationFromRequest("//servlet", "//foobar", "/servlet/foobar")
++        dotTestGetPathWithinApplicationFromRequest("/servlet", "/foobar", "/servlet/foobar")
++        dotTestGetPathWithinApplicationFromRequest("//servlet", "//foobar", "/servlet/foobar")
++        dotTestGetPathWithinApplicationFromRequest("/servlet", "/../servlet/other", "/servlet/other")
++        dotTestGetPathWithinApplicationFromRequest("/asdf", "/../servlet/other", "/servlet/other")
++        dotTestGetPathWithinApplicationFromRequest("/asdf/foo", ";/../servlet/other", "/asdf/foo")
++        dotTestGetPathWithinApplicationFromRequest("/servlet", "/foobar", "/servlet/foobar")
++        dotTestGetPathWithinApplicationFromRequest("/servlet", "/foobar", "/servlet/foobar")
++        dotTestGetPathWithinApplicationFromRequest("/servlet", "/foobar", "/servlet/foobar")
++        dotTestGetPathWithinApplicationFromRequest(null, null, "/")
++        dotTestGetPathWithinApplicationFromRequest("index.jsp", null, "/index.jsp")
++    }
+ 
+     @Test
+     void testGetPathWithinApplication() {
+ 
+-        doTestGetPathWithinApplication("/", "/foobar", "/foobar");
+-        doTestGetPathWithinApplication("", "/foobar", "/foobar");
+-        doTestGetPathWithinApplication("", "foobar", "/foobar");
+-        doTestGetPathWithinApplication("/", "foobar", "/foobar");
+-        doTestGetPathWithinApplication("//", "foobar", "/foobar");
+-        doTestGetPathWithinApplication("//", "//foobar", "/foobar");
+-        doTestGetPathWithinApplication("/context-path", "/context-path/foobar", "/foobar");
+-        doTestGetPathWithinApplication("/context-path", "/context-path/foobar/", "/foobar/");
+-        doTestGetPathWithinApplication("//context-path", "//context-path/foobar", "/foobar");
+-        doTestGetPathWithinApplication("//context-path", "//context-path//foobar", "/foobar");
+-        doTestGetPathWithinApplication("//context-path", "//context-path/remove-one/remove-two/../../././/foobar", "/foobar");
+-        doTestGetPathWithinApplication("//context-path", "//context-path//../../././/foobar", null);
+-        doTestGetPathWithinApplication("/context%2525path", "/context%2525path/foobar", "/foobar");
+-        doTestGetPathWithinApplication("/c%6Fntext%20path", "/c%6Fntext%20path/foobar", "/foobar");
+-        doTestGetPathWithinApplication("/context path", "/context path/foobar", "/foobar");
++        doTestGetPathWithinApplication("/foobar", null, "/foobar");
++        doTestGetPathWithinApplication("/foobar", "", "/foobar");
++        doTestGetPathWithinApplication("", "/", "/");
++        doTestGetPathWithinApplication("", null, "/");
++        doTestGetPathWithinApplication("/foobar", "//", "/foobar/");
++        doTestGetPathWithinApplication("/foobar", "//extra", "/foobar/extra");
++        doTestGetPathWithinApplication("/foobar", "//extra///", "/foobar/extra/");
++        doTestGetPathWithinApplication("/foo bar", "/path info" ,"/foo bar/path info");
++    }
+ 
++    @Test
++    void testGetRequestURI() {
++        doTestGetRequestURI("/foobar", "/foobar")
++        doTestGetRequestURI( "/foobar/", "/foobar/")
++        doTestGetRequestURI("",  "/");
++        doTestGetRequestURI("foobar", "/foobar");
++        doTestGetRequestURI("//foobar", "/foobar");
++        doTestGetRequestURI("//foobar///", "/foobar/");
++        doTestGetRequestURI("/context-path/foobar", "/context-path/foobar");
++        doTestGetRequestURI("/context-path/foobar/", "/context-path/foobar/");
++        doTestGetRequestURI("//context-path/foobar", "/context-path/foobar");
++        doTestGetRequestURI("//context-path//foobar", "/context-path/foobar");
++        doTestGetRequestURI("//context-path/remove-one/remove-two/../../././/foobar", "/context-path/foobar");
++        doTestGetRequestURI("//context-path//../../././/foobar", null);
++        doTestGetRequestURI("/context%2525path/foobar", "/context%25path/foobar");
++        doTestGetRequestURI("/c%6Fntext%20path/foobar", "/context path/foobar");
++        doTestGetRequestURI("/context path/foobar", "/context path/foobar");
+     }
+ 
+-    void doTestGetPathWithinApplication(String contextPath, String requestUri, String expectedValue) {
++    void doTestGetPathWithinApplication(String servletPath, String pathInfo, String expectedValue) {
+ 
+         def request = createMock(HttpServletRequest)
+-        expect(request.getAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE)).andReturn(contextPath)
+-        expect(request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE)).andReturn(requestUri)
+-        expect(request.getCharacterEncoding()).andReturn("UTF-8").times(2)
++        expect(request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE)).andReturn(servletPath)
++        expect(request.getAttribute(WebUtils.INCLUDE_PATH_INFO_ATTRIBUTE)).andReturn(pathInfo)
++        if (pathInfo == null) {
++            expect(request.getPathInfo()).andReturn(null) // path info can be null
++        }
++        replay request
++        assertEquals expectedValue, WebUtils.getPathWithinApplication(request)
++        verify request
++    }
++
++    void doTestGetRequestURI(String rawRequestUri, String expectedValue) {
++
++        def request = createMock(HttpServletRequest)
++        expect(request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE)).andReturn(rawRequestUri)
++        expect(request.getCharacterEncoding()).andReturn("UTF-8").times(1)
++        replay request
++        assertEquals expectedValue, WebUtils.getRequestUri(request)
++        verify request
++    }
++
++    void dotTestGetPathWithinApplicationFromRequest(String servletPath, String pathInfo, String expectedValue) {
++
++        HttpServletRequest request = createMock(HttpServletRequest)
++        expect(request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE)).andReturn(null)
++        expect(request.getAttribute(WebUtils.INCLUDE_PATH_INFO_ATTRIBUTE)).andReturn(null)
++        expect(request.getServletPath()).andReturn(servletPath)
++        expect(request.getPathInfo()).andReturn(pathInfo)
++        expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes()
+         replay request
+         assertEquals expectedValue, WebUtils.getPathWithinApplication(request)
+         verify request
+--- a/web/src/test/java/org/apache/shiro/web/filter/PathMatchingFilterTest.java
++++ b/web/src/test/java/org/apache/shiro/web/filter/PathMatchingFilterTest.java
+@@ -112,6 +112,8 @@
+ 
+         expect(request.getContextPath()).andReturn(CONTEXT_PATH).anyTimes();
+         expect(request.getRequestURI()).andReturn(ENABLED_PATH).anyTimes();
++        expect(request.getServletPath()).andReturn("").anyTimes();
++        expect(request.getPathInfo()).andReturn(ENABLED_PATH).anyTimes();
+         replay(request);
+ 
+         boolean continueFilterChain = filter.preHandle(request, response);
+--- a/web/src/test/java/org/apache/shiro/web/filter/mgt/PathMatchingFilterChainResolverTest.java
++++ b/web/src/test/java/org/apache/shiro/web/filter/mgt/PathMatchingFilterChainResolverTest.java
+@@ -97,9 +97,8 @@
+         //ensure at least one chain is defined:
+         resolver.getFilterChainManager().addToChain("/index.html", "authcBasic");
+ 
+-        expect(request.getAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE)).andReturn(null).anyTimes();
+-        expect(request.getContextPath()).andReturn("");
+-        expect(request.getRequestURI()).andReturn("/index.html");
++        expect(request.getServletPath()).andReturn("");
++        expect(request.getPathInfo()).andReturn("/index.html");
+         replay(request);
+ 
+         FilterChain resolved = resolver.getChain(request, response, chain);
+@@ -116,9 +115,8 @@
+         //ensure at least one chain is defined:
+         resolver.getFilterChainManager().addToChain("/index.html", "authcBasic");
+ 
+-        expect(request.getAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE)).andReturn(null).anyTimes();
+-        expect(request.getContextPath()).andReturn("");
+-        expect(request.getRequestURI()).andReturn("/./index.html");
++        expect(request.getServletPath()).andReturn("/");
++        expect(request.getPathInfo()).andReturn("./index.html");
+         replay(request);
+ 
+         FilterChain resolved = resolver.getChain(request, response, chain);
+@@ -135,9 +133,8 @@
+         //ensure at least one chain is defined:
+         resolver.getFilterChainManager().addToChain("/index.html", "authcBasic");
+ 
+-        expect(request.getAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE)).andReturn(null).anyTimes();
+-        expect(request.getContextPath()).andReturn("");
+-        expect(request.getRequestURI()).andReturn("/public/../index.html");
++        expect(request.getServletPath()).andReturn("/public/");
++        expect(request.getPathInfo()).andReturn("../index.html");
+         replay(request);
+ 
+         FilterChain resolved = resolver.getChain(request, response, chain);
+@@ -154,9 +151,8 @@
+         //ensure at least one chain is defined:
+         resolver.getFilterChainManager().addToChain("/index.html", "authcBasic");
+ 
+-        expect(request.getAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE)).andReturn(null).anyTimes();
+-        expect(request.getContextPath()).andReturn("");
+-        expect(request.getRequestURI()).andReturn("/");
++        expect(request.getServletPath()).andReturn("/");
++        expect(request.getPathInfo()).andReturn(null);
+         replay(request);
+ 
+         FilterChain resolved = resolver.getChain(request, response, chain);
+--- a/support/guice/src/test/java/org/apache/shiro/guice/web/SimpleFilterChainResolverTest.java
++++ b/support/guice/src/test/java/org/apache/shiro/guice/web/SimpleFilterChainResolverTest.java
+@@ -82,8 +82,8 @@
+         ServletResponse response = ctrl.createMock(HttpServletResponse.class);
+         FilterChain originalChain = ctrl.createMock(FilterChain.class);
+ 
+-        expect(request.getAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE)).andReturn("/context");
+-        expect(request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE)).andReturn("/mychain");
++        expect(request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE)).andReturn("/mychain");
++        expect(request.getAttribute(WebUtils.INCLUDE_PATH_INFO_ATTRIBUTE)).andReturn("");
+ 
+         expect(request.getCharacterEncoding()).andStubReturn(null);
+ 
+@@ -113,8 +113,8 @@
+ 
+         ctrl.reset();
+ 
+-        expect(request.getAttribute(WebUtils.INCLUDE_CONTEXT_PATH_ATTRIBUTE)).andReturn("/context");
+-        expect(request.getAttribute(WebUtils.INCLUDE_REQUEST_URI_ATTRIBUTE)).andReturn("/nochain");
++        expect(request.getAttribute(WebUtils.INCLUDE_SERVLET_PATH_ATTRIBUTE)).andReturn("/nochain");
++        expect(request.getAttribute(WebUtils.INCLUDE_PATH_INFO_ATTRIBUTE)).andReturn("");
+ 
+         expect(request.getCharacterEncoding()).andStubReturn(null);
+ 
+--- a/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
++++ b/support/guice/src/test/java/org/apache/shiro/guice/web/ShiroWebModuleTest.java
+@@ -177,11 +177,13 @@
+         servletContext.setAttribute(eq(EnvironmentLoader.ENVIRONMENT_ATTRIBUTE_KEY), EasyMock.anyObject());
+         expect(request.getAttribute("javax.servlet.include.context_path")).andReturn("").anyTimes();
+         expect(request.getCharacterEncoding()).andReturn("UTF-8").anyTimes();
+-        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_authc");
+-        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_custom_filter");
+-        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_authc_basic");
+-        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/test_perms");
+-        expect(request.getAttribute("javax.servlet.include.request_uri")).andReturn("/multiple_configs");
++        expect(request.getAttribute("javax.servlet.include.path_info")).andReturn(null).anyTimes();
++        expect(request.getPathInfo()).andReturn(null).anyTimes();
++        expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn("/test_authc");
++        expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn("/test_custom_filter");
++        expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn("/test_authc_basic");
++        expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn("/test_perms");
++        expect(request.getAttribute("javax.servlet.include.servlet_path")).andReturn("/multiple_configs");
+         replay(servletContext, request);
+ 
+         Injector injector = Guice.createInjector(new ShiroWebModule(servletContext) {


=====================================
debian/patches/series
=====================================
@@ -2,3 +2,8 @@
 02-reproducible-build.patch
 03-spring-compatibility.patch
 04-java11-compatibility.patch
+05-guice-improvements.patch
+CVE-2020-1957.patch
+CVE-2020-13933.patch
+CVE-2020-17510_1_of_2.patch
+CVE-2020-17510_2_of_2.patch



View it on GitLab: https://salsa.debian.org/java-team/shiro/-/compare/eb6d684042c00a1b935bd8157e236427db5f70f4...058ca4777ea4a5223cfcfafbecf2278929c46d8f

-- 
View it on GitLab: https://salsa.debian.org/java-team/shiro/-/compare/eb6d684042c00a1b935bd8157e236427db5f70f4...058ca4777ea4a5223cfcfafbecf2278929c46d8f
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20241217/e5e6aef0/attachment.htm>


More information about the pkg-java-commits mailing list