Description: Fixes CVE-2016-0714: The session-persistence implementation mishandles
 session attributes, which allows remote authenticated users to bypass intended
 SecurityManager restrictions and execute arbitrary code in a privileged context
 via a web application that places a crafted object in a session.
Author: Markus Koschany <apo@debian.org>
Origin: backport, https://svn.apache.org/r1726196
                  https://svn.apache.org/r1726203
--- a/java/org/apache/catalina/ha/session/ClusterManagerBase.java
+++ b/java/org/apache/catalina/ha/session/ClusterManagerBase.java
@@ -196,6 +196,9 @@
         copy.setProcessExpiresFrequency(getProcessExpiresFrequency());
         copy.setNotifyListenersOnReplication(isNotifyListenersOnReplication());
         copy.setSessionAttributeFilter(getSessionAttributeFilter());
+        copy.setSessionAttributeNameFilter(getSessionAttributeNameFilter());
+        copy.setSessionAttributeValueClassNameFilter(getSessionAttributeValueClassNameFilter());
+        copy.setWarnOnSessionAttributeFilterFailure(getWarnOnSessionAttributeFilterFailure());
         copy.setSecureRandomClass(getSecureRandomClass());
         copy.setSecureRandomProvider(getSecureRandomProvider());
         copy.setSecureRandomAlgorithm(getSecureRandomAlgorithm());
--- a/java/org/apache/catalina/ha/session/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/ha/session/mbeans-descriptors.xml
@@ -309,6 +309,18 @@
       is="true"
       description="All session messages before state transfer message creation are dropped."
       type="boolean"/>
+    <attribute
+         name="sessionAttributeNameFilter"
+         descritpion="The string pattern used for including session attributes in replication. Null means all attributes are included."
+         type="java.lang.String"/>
+    <attribute
+      name="sessionAttributeValueClassNameFilter"
+      description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name."
+      type="java.lang.String"/>
+    <attribute
+      name="warnOnSessionAttributeFilterFailure"
+      description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?"
+      type="boolean"/>
     <operation
       name="expireSession"
       description="Expired the given session"
@@ -530,6 +542,18 @@
       name="secureRandomProvider"
       description="The secure random number generator provider name"
       type="java.lang.String"/>
+    <attribute
+         name="sessionAttributeNameFilter"
+         descritpion="The string pattern used for including session attributes in replication. Null means all attributes are included."
+         type="java.lang.String"/>
+    <attribute
+      name="sessionAttributeValueClassNameFilter"
+      description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name."
+      type="java.lang.String"/>
+    <attribute
+      name="warnOnSessionAttributeFilterFailure"
+      description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?"
+      type="boolean"/>
     <operation
       name="expireSession"
       description="Expired the given session"
--- a/java/org/apache/catalina/session/LocalStrings.properties
+++ b/java/org/apache/catalina/session/LocalStrings.properties
@@ -32,6 +32,8 @@
 JDBCStore.commitSQLException=SQLException committing connection before closing
 managerBase.container.noop=Managers added to containers other than Contexts will never be used
 managerBase.createSession.ise=createSession: Too many active sessions
+managerBase.sessionAttributeNameFilter=Skipped session attribute named [{0}] because it did not match the name filter [{1}]
+managerBase.sessionAttributeValueClassNameFilter=Skipped session attribute named [{0}] because the value type [{1}] did not match the filter [{2}]
 managerBase.sessionTimeout=Invalid session timeout setting {0}
 standardManager.loading=Loading persisted sessions from {0}
 standardManager.loading.exception=Exception while loading persisted sessions
--- a/java/org/apache/catalina/session/ManagerBase.java
+++ b/java/org/apache/catalina/session/ManagerBase.java
@@ -32,10 +32,13 @@
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicLong;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
 
 import org.apache.catalina.Container;
 import org.apache.catalina.Context;
 import org.apache.catalina.Engine;
+import org.apache.catalina.Globals;
 import org.apache.catalina.LifecycleException;
 import org.apache.catalina.Manager;
 import org.apache.catalina.Session;
@@ -210,8 +213,57 @@
     protected final PropertyChangeSupport support =
             new PropertyChangeSupport(this);
 
+    private Pattern sessionAttributeNamePattern;
+    private Pattern sessionAttributeValueClassNamePattern;
+
+    private boolean warnOnSessionAttributeFilterFailure;
+
+    // ------------------------------------------------------------ Constructors
+
+    public ManagerBase() {
+        if (Globals.IS_SECURITY_ENABLED) {
+            // Minimum set required for default distribution/persistence to work
+            // plus String
+           setSessionAttributeValueClassNameFilter(
+                    "java\\.lang\\.(?:Boolean|Integer|Long|Number|String)");
+            setWarnOnSessionAttributeFilterFailure(true);
+        }
+    }
+
+
+    // -------------------------------------------------------------- Properties
+
+    /**
+     * Obtain the regular expression used to filter session attribute based on
+     * attribute name. The regular expression is anchored so it must match the
+     * entire name
+     *
+     * @return The regular expression currently used to filter attribute names.
+     *         {@code null} means no filter is applied. If an empty string is
+     *         specified then no names will match the filter and all attributes
+     *         will be blocked.
+     */
+   public String getSessionAttributeNameFilter() {
+        if (sessionAttributeNamePattern == null) {
+            return null;
+        }
+        return sessionAttributeNamePattern.toString();
+    }
+
+
+    public void setSessionAttributeNameFilter(String sessionAttributeNameFilter) {
+        if (sessionAttributeNameFilter == null || sessionAttributeNameFilter.length() == 0) {
+            sessionAttributeNamePattern = null;
+        }
+        sessionAttributeNamePattern = Pattern.compile(sessionAttributeNameFilter);
+    }
+
+
+    protected Pattern getSessionAttributeNamePattern() {
+        return sessionAttributeNamePattern;
+    }
+
 
-    // ------------------------------------------------------------- Properties
 
     @Override
     @Deprecated
@@ -220,6 +272,86 @@
     }
 
 
+    /**
+     * Obtain the regular expression used to filter session attribute based on
+     * the implementation class of the value. The regular expression is anchored
+     * and must match the fully qualified class name.
+     *
+     * @return The regular expression currently used to filter class names.
+     *         {@code null} means no filter is applied. If an empty string is
+     *         specified then no names will match the filter and all attributes
+     *         will be blocked.
+     */
+    public String getSessionAttributeValueClassNameFilter() {
+        if (sessionAttributeValueClassNamePattern == null) {
+            return null;
+        }
+        return sessionAttributeValueClassNamePattern.toString();
+    }
+
+
+    /**
+     * Provides {@link #getSessionAttributeValueClassNameFilter()} as a
+     * pre-compiled regular expression pattern.
+     *
+     * @return The pre-compiled pattern used to filter session attributes based
+     *         on the implementation class name of the value. {@code null} means
+     *         no filter is applied.
+     */
+    protected Pattern getSessionAttributeValueClassNamePattern() {
+        return sessionAttributeValueClassNamePattern;
+    }
+
+
+    /**
+     * Set the regular expression to use to filter classes used for session
+     * attributes. The regular expression is anchored and must match the fully
+     * qualified class name.
+     *
+     * @param sessionAttributeValueClassNameFilter The regular expression to use
+     *            to filter session attributes based on class name. Use {@code
+     *            null} if no filtering is required. If an empty string is
+     *           specified then no names will match the filter and all
+     *           attributes will be blocked.
+     *
+     * @throws PatternSyntaxException If the expression is not valid
+     */
+    public void setSessionAttributeValueClassNameFilter(String sessionAttributeValueClassNameFilter)
+            throws PatternSyntaxException {
+        if (sessionAttributeValueClassNameFilter == null ||
+                sessionAttributeValueClassNameFilter.length() == 0) {
+            sessionAttributeValueClassNamePattern = null;
+        }
+        sessionAttributeValueClassNamePattern =
+                Pattern.compile(sessionAttributeValueClassNameFilter);
+    }
+
+
+    /**
+     * Should a warn level log message be generated if a session attribute is
+     * not persisted / replicated / restored.
+     *
+     * @return {@code true} if a warn level log message should be generated
+     */
+    public boolean getWarnOnSessionAttributeFilterFailure() {
+        return warnOnSessionAttributeFilterFailure;
+    }
+
+
+    /**
+     * Configure whether or not a warn level log message should be generated if
+     * a session attribute is not persisted / replicated / restored.
+     *
+     * @param warnOnSessionAttributeFilterFailure {@code true} if the
+     *            warn level message should be generated
+     *
+     */
+    public void setWarnOnSessionAttributeFilterFailure(
+            boolean warnOnSessionAttributeFilterFailure) {
+        this.warnOnSessionAttributeFilterFailure = warnOnSessionAttributeFilterFailure;
+    }
+
+
     @Override
     @Deprecated
     public void setContainer(Container container) {
@@ -839,6 +971,44 @@
                 notifySessionListeners, notifyContainerListeners);
     }
 
+  public boolean willAttributeDistribute(String name, Object value) {
+      Pattern sessionAttributeNamePattern = getSessionAttributeNamePattern();
+      if (sessionAttributeNamePattern != null) {
+           if (!sessionAttributeNamePattern.matcher(name).matches()) {
+               if (getWarnOnSessionAttributeFilterFailure() || log.isDebugEnabled()) {
+                   String msg = sm.getString("managerBase.sessionAttributeNameFilter",
+                           name, sessionAttributeNamePattern);
+                    if (getWarnOnSessionAttributeFilterFailure()) {
+                        log.warn(msg);
+                    } else {
+                        log.debug(msg);
+                    }
+               }
+               return false;
+           }
+       }
+
+
+    Pattern sessionAttributeValueClassNamePattern = getSessionAttributeValueClassNamePattern();
+    if (value != null && sessionAttributeValueClassNamePattern != null) {
+        if (!sessionAttributeValueClassNamePattern.matcher(
+                    value.getClass().getName()).matches()) {
+            if (getWarnOnSessionAttributeFilterFailure() || log.isDebugEnabled()) {
+                String msg = sm.getString("managerBase.sessionAttributeValueClassNameFilter",
+                        name, value.getClass().getName(), sessionAttributeNamePattern);
+                if (getWarnOnSessionAttributeFilterFailure()) {
+                    log.warn(msg);
+                } else {
+                     log.debug(msg);
+                }
+            }
+            return false;
+        }
+    }
+
+    return true;
+
+  }
 
     // ------------------------------------------------------ Protected Methods
 
--- a/java/org/apache/catalina/session/StandardManager.java
+++ b/java/org/apache/catalina/session/StandardManager.java
@@ -208,19 +208,24 @@
         BufferedInputStream bis = null;
         ObjectInputStream ois = null;
         Loader loader = null;
+        Log logger = null;
         ClassLoader classLoader = null;
         try {
             fis = new FileInputStream(file.getAbsolutePath());
             bis = new BufferedInputStream(fis);
             Context c = getContext();
-            if (c != null)
+            if (c != null) {
                 loader = c.getLoader();
+                logger = c.getLogger();
+            }
             if (loader != null)
                 classLoader = loader.getClassLoader();
             if (classLoader != null) {
                 if (log.isDebugEnabled())
                     log.debug("Creating custom object input stream for class loader ");
-                ois = new CustomObjectInputStream(bis, classLoader);
+                ois = new CustomObjectInputStream(bis, classLoader, logger,
+                        getSessionAttributeValueClassNamePattern(),
+                        getWarnOnSessionAttributeFilterFailure());
             } else {
                 if (log.isDebugEnabled())
                     log.debug("Creating standard object input stream");
--- a/java/org/apache/catalina/session/mbeans-descriptors.xml
+++ b/java/org/apache/catalina/session/mbeans-descriptors.xml
@@ -132,6 +132,15 @@
           description="Number of sessions we rejected due to maxActive beeing reached"
                  type="int"
             writeable="false"/>
+    <attribute name="sessionAttributeNameFilter" description="The string pattern used for including session attributes in distribution. Null means all attributes are included."
+             type="java.lang.String"/>
+    <attribute   name="sessionAttributeValueClassNameFilter"
+          description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name."
+                 type="java.lang.String"/>
+
+    <attribute   name="warnOnSessionAttributeFilterFailure"
+          description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?"
+                 type="boolean"/>
 
     <operation   name="backgroundProcess"
           description="Invalidate all sessions that have expired."
@@ -321,6 +330,17 @@
                  type="int"
             writeable="false"/>
 
+    <attribute name="sessionAttributeNameFilter" description="The string pattern used for including session attributes in distribution. Null means all attributes are included."
+             type="java.lang.String"/>
+
+    <attribute   name="sessionAttributeValueClassNameFilter"
+          description="The regular expression used to filter session attributes based on the implementation class of the value. The regular expression is anchored and must match the fully qualified class name."
+                 type="java.lang.String"/>
+
+    <attribute   name="warnOnSessionAttributeFilterFailure"
+          description="Should a WARN level log message be generated if a session attribute fails to match sessionAttributeNameFilter or sessionAttributeClassNameFilter?"
+                 type="boolean"/>
+
     <operation   name="backgroundProcess"
           description="Invalidate all sessions that have expired."
                impact="ACTION"
--- a/java/org/apache/catalina/util/CustomObjectInputStream.java
+++ b/java/org/apache/catalina/util/CustomObjectInputStream.java
@@ -19,9 +19,18 @@
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.InvalidClassException;
 import java.io.ObjectInputStream;
 import java.io.ObjectStreamClass;
 import java.lang.reflect.Proxy;
+import java.util.Collections;
+import java.util.Set;
+import java.util.WeakHashMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+
+import org.apache.juli.logging.Log;
+import org.apache.tomcat.util.res.StringManager;
 
 /**
  * Custom subclass of <code>ObjectInputStream</code> that loads from the
@@ -35,14 +44,26 @@
     extends ObjectInputStream {
 
 
+    private static final StringManager sm = StringManager.getManager("org.apache.catalina.util");
+
+    private static final WeakHashMap<ClassLoader, Set<String>> reportedClassCache =
+            new WeakHashMap<>();
+
     /**
      * The class loader we will use to resolve classes.
      */
     private final ClassLoader classLoader;
+    private final Set<String> reportedClasses;
+    private final Log log;
+
+    private final Pattern allowedClassNamePattern;
+    private final String allowedClassNameFilter;
+    private final boolean warnOnFailure;
 
 
     /**
-     * Construct a new instance of CustomObjectInputStream
+     * Construct a new instance of CustomObjectInputStream without any filtering
+     * of deserialized classes.
      *
      * @param stream The input stream we will read from
      * @param classLoader The class loader used to instantiate objects
@@ -53,11 +74,57 @@
                                    ClassLoader classLoader)
         throws IOException {
 
+        this(stream, classLoader, null, null, false);
+    }
+
+
+
+    /**
+     * Construct a new instance of CustomObjectInputStream with filtering of
+     * deserialized classes.
+     *
+     * @param stream The input stream we will read from
+     * @param classLoader The class loader used to instantiate objects
+     * @param log The logger to use to report any issues. It may only be null if
+     *            the filterMode does not require logging
+     * @param allowedClassNamePattern The regular expression to use to filter
+     *                                deserialized classes. The fully qualified
+     *                                class name must match this pattern for
+     *                                deserialization to be allowed if filtering
+     *                                is enabled.
+     * @param warnOnFailure Should any failures be logged?
+     *
+     * @exception IOException if an input/output error occurs
+     */
+    public CustomObjectInputStream(InputStream stream, ClassLoader classLoader,
+            Log log, Pattern allowedClassNamePattern, boolean warnOnFailure)
+            throws IOException {
         super(stream);
+        if (log == null && allowedClassNamePattern != null && warnOnFailure) {
+            throw new IllegalArgumentException(
+                    sm.getString("customObjectInputStream.logRequired"));
+        }
         this.classLoader = classLoader;
+        this.log = log;
+        this.allowedClassNamePattern = allowedClassNamePattern;
+        if (allowedClassNamePattern == null) {
+            this.allowedClassNameFilter = null;
+        } else {
+            this.allowedClassNameFilter = allowedClassNamePattern.toString();
+        }
+        this.warnOnFailure = warnOnFailure;
+
+        Set<String> reportedClasses;
+        synchronized (reportedClassCache) {
+            reportedClasses = reportedClassCache.get(classLoader);
+            if (reportedClasses == null) {
+                reportedClasses = Collections.newSetFromMap(new ConcurrentHashMap<String,Boolean>());
+                reportedClassCache.put(classLoader, reportedClasses);
+            }
+        }
+        this.reportedClasses = reportedClasses;
     }
 
-
     /**
      * Load the local class equivalent of the specified stream class
      * description, by using the class loader assigned to this Context.
@@ -70,8 +137,24 @@
     @Override
     public Class<?> resolveClass(ObjectStreamClass classDesc)
         throws ClassNotFoundException, IOException {
+
+        String name = classDesc.getName();
+        if (allowedClassNamePattern != null) {
+            boolean allowed = allowedClassNamePattern.matcher(name).matches();
+            if (!allowed) {
+                boolean doLog = warnOnFailure && reportedClasses.add(name);
+                String msg = sm.getString("customObjectInputStream.nomatch", name, allowedClassNameFilter);
+                if (doLog) {
+                    log.warn(msg);
+                } else if (log.isDebugEnabled()) {
+                    log.debug(msg);
+                }
+                throw new InvalidClassException(msg);
+            }
+        }
+
         try {
-            return Class.forName(classDesc.getName(), false, classLoader);
+            return Class.forName(name, false, classLoader);
         } catch (ClassNotFoundException e) {
             try {
                 // Try also the superclass because of primitive types
--- a/java/org/apache/catalina/util/LocalStrings.properties
+++ b/java/org/apache/catalina/util/LocalStrings.properties
@@ -17,6 +17,8 @@
 resourceSet.locked=No modifications are allowed to a locked ResourceSet
 hexUtil.bad=Bad hexadecimal digit
 hexUtil.odd=Odd number of hexadecimal digits
+customObjectInputStream.logRequired=A valid logger is required for class name filtering with logging
+customObjectInputStream.nomatch=The class [{0}] did not match the regular expression [{1}] for classes allowed to be deserialized
 #Default Messages Utilized by the ExtensionValidator
 extensionValidator.web-application-manifest=Web Application Manifest
 extensionValidator.extension-not-found-error=ExtensionValidator[{0}][{1}]: Required extension [{2}] not found.
--- a/webapps/docs/changelog.xml
+++ b/webapps/docs/changelog.xml
@@ -323,6 +323,14 @@
         Add support for the EECDH alias when using the OpenSSL cipher syntax to
         define JSSE ciphers. (markt)
       </add>
+      <add>
+        Extend the session attribute filtering options to include filtering
+        based on the implementation class of the value and optional
+        <code>WARN</code> level logging if an attribute is filtered. These
+        options are avaialble for all of the Manager implementations that ship
+        with Tomcat. When a <code>SecurityManager</code> is used filtering will
+        be enabled by default. (markt)
+      </add>
     </changelog>
   </subsection>
   <subsection name="Jasper">
--- a/webapps/docs/config/cluster-manager.xml
+++ b/webapps/docs/config/cluster-manager.xml
@@ -182,6 +182,30 @@
         effective only when <code>sendAllSessions</code> is <code>false</code>.
         Default is <code>2000</code> milliseconds.
       </attribute>
+
+      <attribute name="sessionAttributeNameFilter" required="false">
+        <p>A regular expression used to filter which session attributes will be
+        replicated. An attribute will only be replicated if its name matches
+        this pattern. If the pattern is zero length or <code>null</code>, all
+        attributes are eligible for replication. The pattern is anchored so the
+        session attribute name must fully match the pattern. As an example, the
+        value <code>(userName|sessionHistory)</code> will only replicate the
+        two session attributes named <code>userName</code> and
+        <code>sessionHistory</code>. If not specified, the default value of
+        <code>null</code> will be used.</p>
+      </attribute>
+      <attribute name="sessionAttributeValueClassNameFilter" required="false">
+        <p>A regular expression used to filter which session attributes will be
+        replicated. An attribute will only be replicated if the implementation
+        class name of the value matches this pattern. If the pattern is zero
+        length or <code>null</code>, all attributes are eligible for
+        replication. The pattern is anchored so the fully qualified class name
+        must fully match the pattern. If not specified, the default value of
+        <code>null</code> will be used unless a <code>SecurityManager</code> is
+        enabled in which case the default will be
+        <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p>
+      </attribute>
+
       <attribute name="stateTimestampDrop" required="false">
         When this node sends a <code>GET_ALL_SESSIONS</code> message to other
         node, all session messages that are received as a response are queued.
@@ -193,6 +217,17 @@
         If set to <code>false</code>, all queued session messages are handled.
         Default is <code>true</code>.
       </attribute>
+
+      <attribute name="warnOnSessionAttributeFilterFailure" required="false">
+        <p>If <strong>sessionAttributeNameFilter</strong> or
+        <strong>sessionAttributeValueClassNameFilter</strong> blocks an
+        attribute, should this be logged at <code>WARN</code> level? If
+        <code>WARN</code> level logging is disabled then it will be logged at
+        <code>DEBUG</code>. The default value of this attribute is
+        <code>false</code> unless a <code>SecurityManager</code> is enabled in
+        which case the default will be <code>true</code>.</p>
+      </attribute>
+
     </attributes>
   </subsection>
   <subsection name="org.apache.catalina.ha.session.BackupManager Attributes">
@@ -216,6 +251,30 @@
         another map.
         Default value is <code>15000</code> milliseconds.
       </attribute>
+
+     <attribute name="sessionAttributeNameFilter" required="false">
+        <p>A regular expression used to filter which session attributes will be
+        replicated. An attribute will only be replicated if its name matches
+        this pattern. If the pattern is zero length or <code>null</code>, all
+        attributes are eligible for replication. The pattern is anchored so the
+        session attribute name must fully match the pattern. As an example, the
+        value <code>(userName|sessionHistory)</code> will only replicate the
+        two session attributes named <code>userName</code> and
+        <code>sessionHistory</code>. If not specified, the default value of
+        <code>null</code> will be used.</p>
+      </attribute>
+      <attribute name="sessionAttributeValueClassNameFilter" required="false">
+        <p>A regular expression used to filter which session attributes will be
+        replicated. An attribute will only be replicated if the implementation
+        class name of the value matches this pattern. If the pattern is zero
+        length or <code>null</code>, all attributes are eligible for
+        replication. The pattern is anchored so the fully qualified class name
+        must fully match the pattern. If not specified, the default value of
+        <code>null</code> will be used unless a <code>SecurityManager</code> is
+        enabled in which case the default will be
+        <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p>
+      </attribute>
+
       <attribute name="terminateOnStartFailure" required="false">
         Set to true if you wish to terminate replication map when replication
         map fails to start. If replication map is terminated, associated context
@@ -223,6 +282,17 @@
         does not end. It will try to join the map membership in the heartbeat.
         Default value is <code>false</code> .
       </attribute>
+
+      <attribute name="warnOnSessionAttributeFilterFailure" required="false">
+        <p>If <strong>sessionAttributeNameFilter</strong> or
+        <strong>sessionAttributeValueClassNameFilter</strong> blocks an
+        attribute, should this be logged at <code>WARN</code> level? If
+        <code>WARN</code> level logging is disabled then it will be logged at
+        <code>DEBUG</code>. The default value of this attribute is
+        <code>false</code> unless a <code>SecurityManager</code> is enabled in
+        which case the default will be <code>true</code>.</p>
+      </attribute>
+
     </attributes>
   </subsection>
 </section>
--- a/webapps/docs/config/manager.xml
+++ b/webapps/docs/config/manager.xml
@@ -175,6 +175,40 @@
         string.</p>
       </attribute>
 
+      <attribute name="sessionAttributeNameFilter" required="false">
+        <p>A regular expression used to filter which session attributes will be
+        distributed. An attribute will only be distributed if its name matches
+        this pattern. If the pattern is zero length or <code>null</code>, all
+        attributes are eligible for distribution. The pattern is anchored so the
+        session attribute name must fully match the pattern. As an example, the
+        value <code>(userName|sessionHistory)</code> will only distribute the
+        two session attributes named <code>userName</code> and
+        <code>sessionHistory</code>. If not specified, the default value of
+        <code>null</code> will be used.</p>
+      </attribute>
+
+      <attribute name="sessionAttributeValueClassNameFilter" required="false">
+        <p>A regular expression used to filter which session attributes will be
+        distributed. An attribute will only be distributed if the implementation
+        class name of the value matches this pattern. If the pattern is zero
+        length or <code>null</code>, all attributes are eligible for
+        distribution. The pattern is anchored so the fully qualified class name
+        must fully match the pattern. If not specified, the default value of
+        <code>null</code> will be used unless a <code>SecurityManager</code> is
+        enabled in which case the default will be
+        <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p>
+      </attribute>
+
+      <attribute name="warnOnSessionAttributeFilterFailure" required="false">
+        <p>If <strong>sessionAttributeNameFilter</strong> or
+        <strong>sessionAttributeValueClassNameFilter</strong> blocks an
+        attribute, should this be logged at <code>WARN</code> level? If
+        <code>WARN</code> level logging is disabled then it will be logged at
+        <code>DEBUG</code>. The default value of this attribute is
+        <code>false</code> unless a <code>SecurityManager</code> is enabled in
+        which case the default will be <code>true</code>.</p>
+      </attribute>
+
     </attributes>
 
     <h3>Persistent Manager Implementation</h3>
@@ -264,6 +298,41 @@
         <code>org.apache.catalina.session.StandardManager</code> class.
         </p>
       </attribute>
+
+      <attribute name="sessionAttributeNameFilter" required="false">
+        <p>A regular expression used to filter which session attributes will be
+        distributed. An attribute will only be distributed if its name matches
+        this pattern. If the pattern is zero length or <code>null</code>, all
+        attributes are eligible for distribution. The pattern is anchored so the
+        session attribute name must fully match the pattern. As an example, the
+        value <code>(userName|sessionHistory)</code> will only distribute the
+        two session attributes named <code>userName</code> and
+        <code>sessionHistory</code>. If not specified, the default value of
+        <code>null</code> will be used.</p>
+      </attribute>
+
+      <attribute name="sessionAttributeValueClassNameFilter" required="false">
+        <p>A regular expression used to filter which session attributes will be
+        distributed. An attribute will only be distributed if the implementation
+        class name of the value matches this pattern. If the pattern is zero
+        length or <code>null</code>, all attributes are eligible for
+        distribution. The pattern is anchored so the fully qualified class name
+        must fully match the pattern. If not specified, the default value of
+        <code>null</code> will be used unless a <code>SecurityManager</code> is
+        enabled in which case the default will be
+        <code>java\\.lang\\.(?:Boolean|Integer|Long|Number|String)</code>.</p>
+      </attribute>
+
+      <attribute name="warnOnSessionAttributeFilterFailure" required="false">
+        <p>If <strong>sessionAttributeNameFilter</strong> or
+        <strong>sessionAttributeValueClassNameFilter</strong> blocks an
+        attribute, should this be logged at <code>WARN</code> level? If
+        <code>WARN</code> level logging is disabled then it will be logged at
+        <code>DEBUG</code>. The default value of this attribute is
+        <code>false</code> unless a <code>SecurityManager</code> is enabled in
+        which case the default will be <code>true</code>.</p>
+      </attribute>
+
     </attributes>
 
     <p>In order to successfully use a PersistentManager, you must nest inside
