diff --git a/SOURCES/tomcat-7.0.76-CVE-2018-1304.patch b/SOURCES/tomcat-7.0.76-CVE-2018-1304.patch
new file mode 100644
index 0000000..e9536cb
--- /dev/null
+++ b/SOURCES/tomcat-7.0.76-CVE-2018-1304.patch
@@ -0,0 +1,46 @@
+commit 2d69fde135302e8cff984bb2131ec69f2e396964
+Author: Mark Thomas
+Date: Tue Feb 6 11:41:16 2018 +0000
+
+ git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1823309 13f79535-47bb-0310-9956-ffa450edef68
+
+diff --git a/java/org/apache/catalina/realm/RealmBase.java b/java/org/apache/catalina/realm/RealmBase.java
+index 2098c2e8cc..9697440d35 100644
+--- java/org/apache/catalina/realm/RealmBase.java
++++ java/org/apache/catalina/realm/RealmBase.java
+@@ -688,9 +688,9 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
+
+ // Check each defined security constraint
+ String uri = request.getRequestPathMB().toString();
+- // Bug47080 - in rare cases this may be null
++ // Bug47080 - in rare cases this may be null or ""
+ // Mapper treats as '/' do the same to prevent NPE
+- if (uri == null) {
++ if (uri == null || uri.length() == 0) {
+ uri = "/";
+ }
+
+@@ -722,7 +722,8 @@ public abstract class RealmBase extends LifecycleMBeanBase implements Realm {
+ }
+
+ for(int k=0; k < patterns.length; k++) {
+- if(uri.equals(patterns[k])) {
++ // Exact match including special case for the context root.
++ if(uri.equals(patterns[k]) || patterns[k].length() == 0 && uri.equals("/")) {
+ found = true;
+ if(collection[j].findMethod(method)) {
+ if(results == null) {
+diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml
+--- webapps/docs/changelog.xml.orig 2019-02-28 15:11:59.735767416 -0500
++++ webapps/docs/changelog.xml 2019-02-28 15:12:23.805697236 -0500
+@@ -64,6 +64,10 @@
+ When generating a redirect to a directory in the Default Servlet, avoid
+ generating a protocol relative redirect. (markt)
+
++
++ 62067: Correctly apply security constraints mapped to the
++ context root using a URL pattern of ""
. (markt)
++
+
+
+
diff --git a/SOURCES/tomcat-7.0.76-CVE-2018-1305.patch b/SOURCES/tomcat-7.0.76-CVE-2018-1305.patch
new file mode 100644
index 0000000..1fe8bc8
--- /dev/null
+++ b/SOURCES/tomcat-7.0.76-CVE-2018-1305.patch
@@ -0,0 +1,974 @@
+commit 2aac69f694d42d9219eb27018b3da0ae1bdd73ab
+Author: Mark Thomas
+Date: Tue Feb 6 12:49:49 2018 +0000
+
+ Process all ServletSecurity annotations at web application start rather
+ than at servlet load time to ensure constraints are applied
+ consistently.
+
+ git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1823322 13f79535-47bb-0310-9956-ffa450edef68
+
+diff -up java/org/apache/catalina/authenticator/AuthenticatorBase.java.orig java/org/apache/catalina/authenticator/AuthenticatorBase.java
+--- java/org/apache/catalina/authenticator/AuthenticatorBase.java.orig 2019-02-28 15:41:52.561346049 -0500
++++ java/org/apache/catalina/authenticator/AuthenticatorBase.java 2019-02-28 15:44:09.129930251 -0500
+@@ -40,7 +40,6 @@ import org.apache.catalina.Manager;
+ import org.apache.catalina.Realm;
+ import org.apache.catalina.Session;
+ import org.apache.catalina.Valve;
+-import org.apache.catalina.Wrapper;
+ import org.apache.catalina.connector.Request;
+ import org.apache.catalina.connector.Response;
+ import org.apache.catalina.deploy.LoginConfig;
+@@ -488,13 +487,6 @@ public abstract class AuthenticatorBase
+ }
+ }
+
+- // The Servlet may specify security constraints through annotations.
+- // Ensure that they have been processed before constraints are checked
+- Wrapper wrapper = (Wrapper) request.getMappingData().wrapper;
+- if (wrapper != null) {
+- wrapper.servletSecurityAnnotationScan();
+- }
+-
+ Realm realm = this.context.getRealm();
+ // Is this request URI subject to a security constraint?
+ SecurityConstraint [] constraints
+diff -up java/org/apache/catalina/core/ApplicationContext.java.orig java/org/apache/catalina/core/ApplicationContext.java
+--- java/org/apache/catalina/core/ApplicationContext.java.orig 2017-03-09 08:51:39.000000000 -0500
++++ java/org/apache/catalina/core/ApplicationContext.java 2019-02-28 15:44:09.130930248 -0500
+@@ -53,8 +53,10 @@ import javax.servlet.ServletException;
+ import javax.servlet.ServletRegistration;
+ import javax.servlet.ServletRequestAttributeListener;
+ import javax.servlet.ServletRequestListener;
++import javax.servlet.ServletSecurityElement;
+ import javax.servlet.SessionCookieConfig;
+ import javax.servlet.SessionTrackingMode;
++import javax.servlet.annotation.ServletSecurity;
+ import javax.servlet.descriptor.JspConfigDescriptor;
+ import javax.servlet.http.HttpSessionAttributeListener;
+ import javax.servlet.http.HttpSessionListener;
+@@ -69,6 +71,7 @@ import org.apache.catalina.Wrapper;
+ import org.apache.catalina.connector.Connector;
+ import org.apache.catalina.deploy.FilterDef;
+ import org.apache.catalina.util.ResourceSet;
++import org.apache.catalina.util.Introspection;
+ import org.apache.catalina.util.ServerInfo;
+ import org.apache.catalina.util.URLEncoder;
+ import org.apache.naming.resources.DirContextURLStreamHandler;
+@@ -1186,14 +1189,27 @@ public class ApplicationContext implemen
+ }
+ }
+
++ ServletSecurity annotation = null;
+ if (servlet == null) {
+ wrapper.setServletClass(servletClass);
++ Class> clazz = Introspection.loadClass(context, servletClass);
++ if (clazz != null) {
++ annotation = clazz.getAnnotation(ServletSecurity.class);
++ }
+ } else {
+ wrapper.setServletClass(servlet.getClass().getName());
+ wrapper.setServlet(servlet);
++ if (context.wasCreatedDynamicServlet(servlet)) {
++ annotation = servlet.getClass().getAnnotation(ServletSecurity.class);
++ }
+ }
+
+- return context.dynamicServletAdded(wrapper);
++ ServletRegistration.Dynamic registration =
++ new ApplicationServletRegistration(wrapper, context);
++ if (annotation != null) {
++ registration.setServletSecurity(new ServletSecurityElement(annotation));
++ }
++ return registration;
+ }
+
+
+diff -up java/org/apache/catalina/core/ApplicationServletRegistration.java.orig java/org/apache/catalina/core/ApplicationServletRegistration.java
+--- java/org/apache/catalina/core/ApplicationServletRegistration.java.orig 2019-02-28 15:41:52.568346028 -0500
++++ java/org/apache/catalina/core/ApplicationServletRegistration.java 2019-02-28 15:44:09.130930248 -0500
+@@ -5,9 +5,9 @@
+ * 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.
+@@ -41,15 +41,16 @@ public class ApplicationServletRegistrat
+ */
+ private static final StringManager sm =
+ StringManager.getManager(Constants.Package);
+-
+- private Wrapper wrapper;
+- private Context context;
+-
++
++ private final Wrapper wrapper;
++ private final Context context;
++ private ServletSecurityElement constraint;
++
+ public ApplicationServletRegistration(Wrapper wrapper,
+ Context context) {
+ this.wrapper = wrapper;
+ this.context = context;
+-
++
+ }
+
+ @Override
+@@ -65,9 +66,9 @@ public class ApplicationServletRegistrat
+ @Override
+ public Map getInitParameters() {
+ ParameterMap result = new ParameterMap();
+-
++
+ String[] parameterNames = wrapper.findInitParameters();
+-
++
+ for (String parameterName : parameterNames) {
+ result.put(parameterName, wrapper.findInitParameter(parameterName));
+ }
+@@ -91,7 +92,7 @@ public class ApplicationServletRegistrat
+ if (getInitParameter(name) != null) {
+ return false;
+ }
+-
++
+ wrapper.addInitParameter(name, value);
+
+ return true;
+@@ -99,9 +100,9 @@ public class ApplicationServletRegistrat
+
+ @Override
+ public Set setInitParameters(Map initParameters) {
+-
++
+ Set conflicts = new HashSet();
+-
++
+ for (Map.Entry entry : initParameters.entrySet()) {
+ if (entry.getKey() == null || entry.getValue() == null) {
+ throw new IllegalArgumentException(sm.getString(
+@@ -151,13 +152,14 @@ public class ApplicationServletRegistrat
+ "applicationServletRegistration.setServletSecurity.iae",
+ getName(), context.getName()));
+ }
+-
++
+ if (!context.getState().equals(LifecycleState.STARTING_PREP)) {
+ throw new IllegalStateException(sm.getString(
+ "applicationServletRegistration.setServletSecurity.ise",
+ getName(), context.getName()));
+ }
+
++ this.constraint = constraint;
+ return context.addServletSecurity(this, constraint);
+ }
+
+@@ -167,9 +169,9 @@ public class ApplicationServletRegistrat
+ if (urlPatterns == null) {
+ return Collections.emptySet();
+ }
+-
++
+ Set conflicts = new HashSet();
+-
++
+ for (String urlPattern : urlPatterns) {
+ String wrapperName = context.findServletMapping(urlPattern);
+ if (wrapperName != null) {
+@@ -187,10 +189,15 @@ public class ApplicationServletRegistrat
+ if (!conflicts.isEmpty()) {
+ return conflicts;
+ }
+-
++
+ for (String urlPattern : urlPatterns) {
+ context.addServletMapping(urlPattern, wrapper.getName());
+ }
++
++ if (constraint != null) {
++ context.addServletSecurity(this, constraint);
++ }
++
+ return Collections.emptySet();
+ }
+
+@@ -199,7 +206,7 @@ public class ApplicationServletRegistrat
+
+ Set result = new HashSet();
+ String servletName = wrapper.getName();
+-
++
+ String[] urlPatterns = context.findServletMappings();
+ for (String urlPattern : urlPatterns) {
+ String name = context.findServletMapping(urlPattern);
+@@ -214,5 +221,5 @@ public class ApplicationServletRegistrat
+ public String getRunAsRole() {
+ return wrapper.getRunAs();
+ }
+-
++
+ }
+diff -up java/org/apache/catalina/core/StandardContext.java.orig java/org/apache/catalina/core/StandardContext.java
+--- java/org/apache/catalina/core/StandardContext.java.orig 2017-03-09 08:51:39.000000000 -0500
++++ java/org/apache/catalina/core/StandardContext.java 2019-02-28 15:44:09.132930242 -0500
+@@ -4813,27 +4813,36 @@ public class StandardContext extends Con
+ }
+
+ /**
+- * hook to register that we need to scan for security annotations.
+- * @param wrapper The wrapper for the Servlet that was added
++ * Create a servlet registration.
++ *
++ * @param wrapper The wrapper for which the registration should be created.
++ *
++ * @return An appropriate registration
++ *
++ * @deprecated This will be removed in Tomcat 9. The registration should be
++ * created directly.
+ */
++ @Deprecated
+ public ServletRegistration.Dynamic dynamicServletAdded(Wrapper wrapper) {
+- Servlet s = wrapper.getServlet();
+- if (s != null && createdServlets.contains(s)) {
+- // Mark the wrapper to indicate annotations need to be scanned
+- wrapper.setServletSecurityAnnotationScanRequired(true);
+- }
+ return new ApplicationServletRegistration(wrapper, this);
+ }
+
+ /**
+- * hook to track which registrations need annotation scanning
+- * @param servlet
++ * Hook to track which Servlets were created via
++ * {@link ServletContext#createServlet(Class)}.
++ *
++ * @param servlet the created Servlet
+ */
+ public void dynamicServletCreated(Servlet servlet) {
+ createdServlets.add(servlet);
+ }
+
+
++ public boolean wasCreatedDynamicServlet(Servlet servlet) {
++ return createdServlets.contains(servlet);
++ }
++
++
+ /**
+ * A helper class to manage the filter mappings in a Context.
+ */
+diff -up java/org/apache/catalina/core/StandardWrapper.java.orig java/org/apache/catalina/core/StandardWrapper.java
+--- java/org/apache/catalina/core/StandardWrapper.java.orig 2019-02-28 15:41:52.576346004 -0500
++++ java/org/apache/catalina/core/StandardWrapper.java 2019-02-28 15:44:09.133930239 -0500
+@@ -5,17 +5,15 @@
+ * 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.catalina.core;
+
+ import java.io.PrintStream;
+@@ -44,11 +42,9 @@ import javax.servlet.ServletContext;
+ import javax.servlet.ServletException;
+ import javax.servlet.ServletRequest;
+ import javax.servlet.ServletResponse;
+-import javax.servlet.ServletSecurityElement;
+ import javax.servlet.SingleThreadModel;
+ import javax.servlet.UnavailableException;
+ import javax.servlet.annotation.MultipartConfig;
+-import javax.servlet.annotation.ServletSecurity;
+
+ import org.apache.catalina.Container;
+ import org.apache.catalina.ContainerServlet;
+@@ -114,12 +110,12 @@ public class StandardWrapper extends Con
+ * servlet is considered permanent.
+ */
+ protected long available = 0L;
+-
++
+ /**
+- * The broadcaster that sends j2ee notifications.
++ * The broadcaster that sends j2ee notifications.
+ */
+ protected NotificationBroadcasterSupport broadcaster = null;
+-
++
+ /**
+ * The count of allocations that are currently active (even if they
+ * are for the same instance, as will be true on a non-STM servlet).
+@@ -231,12 +227,12 @@ public class StandardWrapper extends Con
+ */
+ protected Stack instancePool = null;
+
+-
++
+ /**
+ * Wait time for servlet unload in ms.
+ */
+ protected long unloadDelay = 2000;
+-
++
+
+ /**
+ * True if this StandardWrapper is for the JspServlet
+@@ -259,12 +255,12 @@ public class StandardWrapper extends Con
+ protected StandardWrapperValve swValve;
+ protected long loadTime=0;
+ protected int classLoadTime=0;
+-
++
+ /**
+ * Multipart config
+ */
+ protected MultipartConfigElement multipartConfigElement = null;
+-
++
+ /**
+ * Async support
+ */
+@@ -275,26 +271,24 @@ public class StandardWrapper extends Con
+ */
+ protected boolean enabled = true;
+
+- protected volatile boolean servletSecurityAnnotationScanRequired = false;
+-
+ private boolean overridable = false;
+-
++
+ /**
+- * Static class array used when the SecurityManager is turned on and
++ * Static class array used when the SecurityManager is turned on and
+ * Servlet.init
is invoked.
+ */
+ protected static Class>[] classType = new Class[]{ServletConfig.class};
+-
+-
++
++
+ /**
+- * Static class array used when the SecurityManager is turned on and
++ * Static class array used when the SecurityManager is turned on and
+ * Servlet.service
is invoked.
+- */
++ */
+ @Deprecated
+ protected static Class>[] classTypeUsedInService = new Class[]{
+ ServletRequest.class,
+ ServletResponse.class};
+-
++
+
+ private final ReentrantReadWriteLock parametersLock =
+ new ReentrantReadWriteLock();
+@@ -625,7 +619,7 @@ public class StandardWrapper extends Con
+ public String[] getServletMethods() throws ServletException {
+
+ instance = loadServlet();
+-
++
+ Class extends Servlet> servletClazz = instance.getClass();
+ if (!javax.servlet.http.HttpServlet.class.isAssignableFrom(
+ servletClazz)) {
+@@ -665,8 +659,8 @@ public class StandardWrapper extends Con
+ public Servlet getServlet() {
+ return instance;
+ }
+-
+-
++
++
+ /**
+ * Set the associated servlet instance.
+ */
+@@ -675,13 +669,13 @@ public class StandardWrapper extends Con
+ instance = servlet;
+ }
+
+-
++
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void setServletSecurityAnnotationScanRequired(boolean b) {
+- this.servletSecurityAnnotationScanRequired = b;
++ // NO-OP
+ }
+
+ // --------------------------------------------------------- Public Methods
+@@ -695,19 +689,19 @@ public class StandardWrapper extends Con
+ @Override
+ public void backgroundProcess() {
+ super.backgroundProcess();
+-
++
+ if (!getState().isAvailable())
+ return;
+-
++
+ if (getServlet() != null && (getServlet() instanceof PeriodicEventListener)) {
+ ((PeriodicEventListener) getServlet()).periodicEvent();
+ }
+ }
+-
+-
++
++
+ /**
+ * Extract the root cause from a servlet exception.
+- *
++ *
+ * @param e The servlet exception
+ */
+ public static Throwable getRootCause(ServletException e) {
+@@ -836,7 +830,7 @@ public class StandardWrapper extends Con
+ }
+
+ boolean newInstance = false;
+-
++
+ // If not SingleThreadedModel, return the same instance every time
+ if (!singleThreadModel) {
+ // Load and initialize our instance if necessary
+@@ -1070,7 +1064,7 @@ public class StandardWrapper extends Con
+ @Override
+ public synchronized void load() throws ServletException {
+ instance = loadServlet();
+-
++
+ if (!instanceInitialized) {
+ initServlet(instance);
+ }
+@@ -1078,12 +1072,12 @@ public class StandardWrapper extends Con
+ if (isJspServlet) {
+ StringBuilder oname =
+ new StringBuilder(MBeanUtils.getDomain(getParent()));
+-
++
+ oname.append(":type=JspMonitor,name=");
+ oname.append(getName());
+-
++
+ oname.append(getWebModuleKeyProperties());
+-
++
+ try {
+ jspMonitorON = new ObjectName(oname.toString());
+ Registry.getRegistry(null, null)
+@@ -1161,8 +1155,6 @@ public class StandardWrapper extends Con
+ }
+ }
+
+- processServletSecurityAnnotation(servlet.getClass());
+-
+ // Special handling for ContainerServlet instances
+ if ((servlet instanceof ContainerServlet) &&
+ (isContainerProvidedServlet(servletClass) ||
+@@ -1205,44 +1197,13 @@ public class StandardWrapper extends Con
+ */
+ @Override
+ public void servletSecurityAnnotationScan() throws ServletException {
+- if (getServlet() == null) {
+- Class> clazz = null;
+- try {
+- clazz = getParent().getLoader().getClassLoader().loadClass(
+- getServletClass());
+- processServletSecurityAnnotation(clazz);
+- } catch (ClassNotFoundException e) {
+- // Safe to ignore. No class means no annotations to process
+- }
+- } else {
+- if (servletSecurityAnnotationScanRequired) {
+- processServletSecurityAnnotation(getServlet().getClass());
+- }
+- }
++ // NO-OP
+ }
+
+- private void processServletSecurityAnnotation(Class> clazz) {
+- // Calling this twice isn't harmful so no syncs
+- servletSecurityAnnotationScanRequired = false;
+-
+- Context ctxt = (Context) getParent();
+-
+- if (ctxt.getIgnoreAnnotations()) {
+- return;
+- }
+-
+- ServletSecurity secAnnotation =
+- clazz.getAnnotation(ServletSecurity.class);
+- if (secAnnotation != null) {
+- ctxt.addServletSecurity(
+- new ApplicationServletRegistration(this, ctxt),
+- new ServletSecurityElement(secAnnotation));
+- }
+- }
+
+ private synchronized void initServlet(Servlet servlet)
+ throws ServletException {
+-
++
+ if (instanceInitialized && !singleThreadModel) return;
+
+ // Call the initialization method of this servlet
+@@ -1454,12 +1415,12 @@ public class StandardWrapper extends Con
+ if (swallowOutput) {
+ SystemLogHandler.startCapture();
+ }
+-
++
+ // Call the servlet destroy() method
+ try {
+ instanceSupport.fireInstanceEvent
+ (InstanceEvent.BEFORE_DESTROY_EVENT, instance);
+-
++
+ if( Globals.IS_SECURITY_ENABLED) {
+ try {
+ SecurityUtil.doAsPrivilege("destroy",
+@@ -1470,7 +1431,7 @@ public class StandardWrapper extends Con
+ } else {
+ instance.destroy();
+ }
+-
++
+ instanceSupport.fireInstanceEvent
+ (InstanceEvent.AFTER_DESTROY_EVENT, instance);
+
+@@ -1701,7 +1662,7 @@ public class StandardWrapper extends Con
+ public boolean isAsyncSupported() {
+ return asyncSupported;
+ }
+-
++
+ @Override
+ public void setAsyncSupported(boolean asyncSupported) {
+ this.asyncSupported = asyncSupported;
+@@ -1711,7 +1672,7 @@ public class StandardWrapper extends Con
+ public boolean isEnabled() {
+ return enabled;
+ }
+-
++
+ @Override
+ public void setEnabled(boolean enabled) {
+ this.enabled = enabled;
+@@ -1787,24 +1748,24 @@ public class StandardWrapper extends Con
+ */
+ @Override
+ protected synchronized void startInternal() throws LifecycleException {
+-
+- // Send j2ee.state.starting notification
++
++ // Send j2ee.state.starting notification
+ if (this.getObjectName() != null) {
+- Notification notification = new Notification("j2ee.state.starting",
+- this.getObjectName(),
++ Notification notification = new Notification("j2ee.state.starting",
++ this.getObjectName(),
+ sequenceNumber++);
+ broadcaster.sendNotification(notification);
+ }
+-
++
+ // Start up this component
+ super.startInternal();
+
+ setAvailable(0L);
+
+- // Send j2ee.state.running notification
++ // Send j2ee.state.running notification
+ if (this.getObjectName() != null) {
+- Notification notification =
+- new Notification("j2ee.state.running", this.getObjectName(),
++ Notification notification =
++ new Notification("j2ee.state.running", this.getObjectName(),
+ sequenceNumber++);
+ broadcaster.sendNotification(notification);
+ }
+@@ -1823,15 +1784,15 @@ public class StandardWrapper extends Con
+ protected synchronized void stopInternal() throws LifecycleException {
+
+ setAvailable(Long.MAX_VALUE);
+-
+- // Send j2ee.state.stopping notification
++
++ // Send j2ee.state.stopping notification
+ if (this.getObjectName() != null) {
+- Notification notification =
+- new Notification("j2ee.state.stopping", this.getObjectName(),
++ Notification notification =
++ new Notification("j2ee.state.stopping", this.getObjectName(),
+ sequenceNumber++);
+ broadcaster.sendNotification(notification);
+ }
+-
++
+ // Shut down our servlet instance (if it has been initialized)
+ try {
+ unload();
+@@ -1843,43 +1804,43 @@ public class StandardWrapper extends Con
+ // Shut down this component
+ super.stopInternal();
+
+- // Send j2ee.state.stopped notification
++ // Send j2ee.state.stopped notification
+ if (this.getObjectName() != null) {
+- Notification notification =
+- new Notification("j2ee.state.stopped", this.getObjectName(),
++ Notification notification =
++ new Notification("j2ee.state.stopped", this.getObjectName(),
+ sequenceNumber++);
+ broadcaster.sendNotification(notification);
+ }
+-
+- // Send j2ee.object.deleted notification
+- Notification notification =
+- new Notification("j2ee.object.deleted", this.getObjectName(),
++
++ // Send j2ee.object.deleted notification
++ Notification notification =
++ new Notification("j2ee.object.deleted", this.getObjectName(),
+ sequenceNumber++);
+ broadcaster.sendNotification(notification);
+
+ }
+
+-
++
+ @Override
+ protected String getObjectNameKeyProperties() {
+
+ StringBuilder keyProperties =
+ new StringBuilder("j2eeType=Servlet,name=");
+-
++
+ String name = getName();
+ if (Util.objectNameValueNeedsQuote(name)) {
+ name = ObjectName.quote(name);
+ }
+ keyProperties.append(name);
+-
++
+ keyProperties.append(getWebModuleKeyProperties());
+
+ return keyProperties.toString();
+ }
+-
++
+
+ private String getWebModuleKeyProperties() {
+-
++
+ StringBuilder keyProperties = new StringBuilder(",WebModule=//");
+ String hostName = getParent().getParent().getName();
+ if (hostName == null) {
+@@ -1887,7 +1848,7 @@ public class StandardWrapper extends Con
+ } else {
+ keyProperties.append(hostName);
+ }
+-
++
+ String contextName = ((Context) getParent()).getName();
+ if (!contextName.startsWith("/")) {
+ keyProperties.append('/');
+@@ -1898,7 +1859,7 @@ public class StandardWrapper extends Con
+ if (parent instanceof StandardContext) {
+ ctx = (StandardContext) getParent();
+ }
+-
++
+ keyProperties.append(",J2EEApplication=");
+ if (ctx == null) {
+ keyProperties.append("none");
+@@ -1911,10 +1872,10 @@ public class StandardWrapper extends Con
+ } else {
+ keyProperties.append(ctx.getJ2EEServer());
+ }
+-
++
+ return keyProperties.toString();
+ }
+-
++
+
+ /**
+ * JSR 77. Always return false.
+@@ -1922,19 +1883,19 @@ public class StandardWrapper extends Con
+ public boolean isStateManageable() {
+ return false;
+ }
+-
+
+- /* Remove a JMX notificationListener
++
++ /* Remove a JMX notificationListener
+ * @see javax.management.NotificationEmitter#removeNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
+ */
+ @Override
+- public void removeNotificationListener(NotificationListener listener,
++ public void removeNotificationListener(NotificationListener listener,
+ NotificationFilter filter, Object object) throws ListenerNotFoundException {
+ broadcaster.removeNotificationListener(listener,filter,object);
+ }
+-
++
+ protected MBeanNotificationInfo[] notificationInfo;
+-
++
+ /* Get JMX Broadcaster Info
+ * @TODO use StringManager for international support!
+ * @TODO This two events we not send j2ee.state.failed and j2ee.attribute.changed!
+@@ -1949,7 +1910,7 @@ public class StandardWrapper extends Con
+ "j2ee.object.created"},
+ Notification.class.getName(),
+ "servlet is created"
+- ),
++ ),
+ new MBeanNotificationInfo(new String[] {
+ "j2ee.state.starting"},
+ Notification.class.getName(),
+@@ -1980,37 +1941,37 @@ public class StandardWrapper extends Con
+
+ return notificationInfo;
+ }
+-
+-
++
++
+ /* Add a JMX-NotificationListener
+ * @see javax.management.NotificationBroadcaster#addNotificationListener(javax.management.NotificationListener, javax.management.NotificationFilter, java.lang.Object)
+ */
+ @Override
+- public void addNotificationListener(NotificationListener listener,
++ public void addNotificationListener(NotificationListener listener,
+ NotificationFilter filter, Object object) throws IllegalArgumentException {
+ broadcaster.addNotificationListener(listener,filter,object);
+ }
+-
+-
++
++
+ /**
+- * Remove a JMX-NotificationListener
++ * Remove a JMX-NotificationListener
+ * @see javax.management.NotificationBroadcaster#removeNotificationListener(javax.management.NotificationListener)
+ */
+ @Override
+- public void removeNotificationListener(NotificationListener listener)
++ public void removeNotificationListener(NotificationListener listener)
+ throws ListenerNotFoundException {
+ broadcaster.removeNotificationListener(listener);
+ }
+-
+-
++
++
+ // ------------------------------------------------------------- Attributes
+-
+-
++
++
+ @Deprecated
+ public boolean isEventProvider() {
+ return false;
+ }
+-
++
+ @Deprecated
+ public boolean isStatisticsProvider() {
+ return false;
+diff -up java/org/apache/catalina/startup/ContextConfig.java.orig java/org/apache/catalina/startup/ContextConfig.java
+--- java/org/apache/catalina/startup/ContextConfig.java.orig 2019-02-28 15:41:52.580345992 -0500
++++ java/org/apache/catalina/startup/ContextConfig.java 2019-02-28 15:44:09.134930236 -0500
+@@ -433,15 +433,14 @@ public class ContextConfig implements Li
+ LoginConfig loginConfig = context.getLoginConfig();
+
+ SecurityConstraint constraints[] = context.findConstraints();
+- if (context.getIgnoreAnnotations() &&
+- (constraints == null || constraints.length ==0) &&
++ if ((constraints == null || constraints.length ==0) &&
+ !context.getPreemptiveAuthentication()) {
++ // No need for an authenticator
+ return;
+ } else {
+ if (loginConfig == null) {
+- // Not metadata-complete or security constraints present, need
+- // an authenticator to support @ServletSecurity annotations
+- // and/or constraints
++ // Security constraints present. Need an authenticator to
++ // support them.
+ loginConfig = DUMMY_LOGIN_CONFIG;
+ context.setLoginConfig(loginConfig);
+ }
+diff -up java/org/apache/catalina/startup/Tomcat.java.orig java/org/apache/catalina/startup/Tomcat.java
+--- java/org/apache/catalina/startup/Tomcat.java.orig 2019-02-28 15:44:09.135930233 -0500
++++ java/org/apache/catalina/startup/Tomcat.java 2019-02-28 15:47:52.376250650 -0500
+@@ -928,6 +928,9 @@ public class Tomcat {
+ Context context = (Context) event.getLifecycle();
+ if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
+ context.setConfigured(true);
++
++ // Process annotations
++ WebAnnotationSet.loadApplicationAnnotations(context);
+ }
+ // LoginConfig is required to process @ServletSecurity
+ // annotations
+diff -up java/org/apache/catalina/startup/WebAnnotationSet.java.orig java/org/apache/catalina/startup/WebAnnotationSet.java
+--- java/org/apache/catalina/startup/WebAnnotationSet.java.orig 2017-03-09 08:51:40.000000000 -0500
++++ java/org/apache/catalina/startup/WebAnnotationSet.java 2019-02-28 15:47:07.249388035 -0500
+@@ -23,10 +23,13 @@ import javax.annotation.Resource;
+ import javax.annotation.Resources;
+ import javax.annotation.security.DeclareRoles;
+ import javax.annotation.security.RunAs;
++import javax.servlet.ServletSecurityElement;
++import javax.servlet.annotation.ServletSecurity;
+
+ import org.apache.catalina.Container;
+ import org.apache.catalina.Context;
+ import org.apache.catalina.Wrapper;
++import org.apache.catalina.core.ApplicationServletRegistration;
+ import org.apache.catalina.deploy.ContextEnvironment;
+ import org.apache.catalina.deploy.ContextResource;
+ import org.apache.catalina.deploy.ContextResourceEnvRef;
+@@ -143,9 +146,17 @@ public class WebAnnotationSet {
+ * Ref JSR 250, equivalent to the run-as element in
+ * the deployment descriptor
+ */
+- RunAs annotation = classClass.getAnnotation(RunAs.class);
+- if (annotation != null) {
+- wrapper.setRunAs(annotation.value());
++ RunAs runAs = classClass.getAnnotation(RunAs.class);
++ if (runAs != null) {
++ wrapper.setRunAs(runAs.value());
++ }
++
++ // Process ServletSecurity annotation
++ ServletSecurity servletSecurity = classClass.getAnnotation(ServletSecurity.class);
++ if (servletSecurity != null) {
++ context.addServletSecurity(
++ new ApplicationServletRegistration(wrapper, context),
++ new ServletSecurityElement(servletSecurity));
+ }
+ }
+ }
+diff -up java/org/apache/catalina/Wrapper.java.orig java/org/apache/catalina/Wrapper.java
+--- java/org/apache/catalina/Wrapper.java.orig 2019-02-28 15:41:52.554346071 -0500
++++ java/org/apache/catalina/Wrapper.java 2019-02-28 15:44:09.129930251 -0500
+@@ -370,19 +370,23 @@ public interface Wrapper extends Contain
+ public void setEnabled(boolean enabled);
+
+ /**
+- * Set the flag that indicates
+- * {@link javax.servlet.annotation.ServletSecurity} annotations must be
+- * scanned when the Servlet is first used.
++ * This method is no longer used. All implementations should be NO-OPs.
+ *
+- * @param b The new value of the flag
++ * @param b Unused.
++ *
++ * @deprecated This will be removed in Tomcat 9.
+ */
++ @Deprecated
+ public void setServletSecurityAnnotationScanRequired(boolean b);
+
+ /**
+- * Scan for (if necessary) and process (if found) the
+- * {@link javax.servlet.annotation.ServletSecurity} annotations for the
+- * Servlet associated with this wrapper.
++ * This method is no longer used. All implementations should be NO-OPs.
++ *
++ * @throws ServletException Never thrown
++ *
++ * @deprecated This will be removed in Tomcat 9.
+ */
++ @Deprecated
+ public void servletSecurityAnnotationScan() throws ServletException;
+
+ /**
+diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml
+--- webapps/docs/changelog.xml.orig 2019-02-28 15:41:52.591345958 -0500
++++ webapps/docs/changelog.xml 2019-02-28 15:44:09.142930212 -0500
+@@ -68,6 +68,11 @@
+ 62067: Correctly apply security constraints mapped to the
+ context root using a URL pattern of ""
. (markt)
+
++
++ Process all ServletSecurity
annotations at web application
++ start rather than at servlet load time to ensure constraints are applied
++ consistently. (markt)
++
+
+
+
+commit c63b96d72cd39287e17b2ba698f4eee0ba508073
+Author: Mark Thomas
+Date: Thu Feb 15 20:21:56 2018 +0000
+
+ Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=62104
+ Fix programmatic login regression as the NonLoginAuthenticator has to be set for it to work (if no login method is specified).
+
+ git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1824360 13f79535-47bb-0310-9956-ffa450edef68
+
+diff --git a/java/org/apache/catalina/startup/ContextConfig.java b/java/org/apache/catalina/startup/ContextConfig.java
+index d913b0e489..2d8aae0ff1 100644
+--- java/org/apache/catalina/startup/ContextConfig.java
++++ java/org/apache/catalina/startup/ContextConfig.java
+@@ -431,19 +431,10 @@ public class ContextConfig implements LifecycleListener {
+ protected void authenticatorConfig() {
+
+ LoginConfig loginConfig = context.getLoginConfig();
+-
+- SecurityConstraint constraints[] = context.findConstraints();
+- if ((constraints == null || constraints.length ==0) &&
+- !context.getPreemptiveAuthentication()) {
+- // No need for an authenticator
+- return;
+- } else {
+- if (loginConfig == null) {
+- // Security constraints present. Need an authenticator to
+- // support them.
+- loginConfig = DUMMY_LOGIN_CONFIG;
+- context.setLoginConfig(loginConfig);
+- }
++ if (loginConfig == null) {
++ // Need an authenticator to support HttpServletRequest.login()
++ loginConfig = DUMMY_LOGIN_CONFIG;
++ context.setLoginConfig(loginConfig);
+ }
+
+ // Has an authenticator been configured already?
+diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml
+--- webapps/docs/changelog.xml.orig 2019-02-28 16:12:24.743968359 -0500
++++ webapps/docs/changelog.xml 2019-02-28 16:12:39.412925943 -0500
+@@ -73,6 +73,11 @@
+ start rather than at servlet load time to ensure constraints are applied
+ consistently. (markt)
+
++
++ 62104: Fix programmatic login regression as the
++ NonLoginAuthenticator has to be set for it to work (if no login method
++ is specified). (remm)
++
+
+
+
diff --git a/SOURCES/tomcat-7.0.76-CVE-2018-8014.patch b/SOURCES/tomcat-7.0.76-CVE-2018-8014.patch
new file mode 100644
index 0000000..35f179d
--- /dev/null
+++ b/SOURCES/tomcat-7.0.76-CVE-2018-8014.patch
@@ -0,0 +1,280 @@
+commit 5877390a9605f56d9bd6859a54ccbfb16374a78b
+Author: Mark Thomas
+Date: Wed May 16 14:56:34 2018 +0000
+
+ Fix https://bz.apache.org/bugzilla/show_bug.cgi?id=62343
+ Make CORS filter defaults more secure.
+ This is the fix for CVE-2018-8014.
+
+ git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1831730 13f79535-47bb-0310-9956-ffa450edef68
+
+diff -up java/org/apache/catalina/filters/CorsFilter.java.orig java/org/apache/catalina/filters/CorsFilter.java
+--- java/org/apache/catalina/filters/CorsFilter.java.orig 2019-02-28 16:37:12.512591576 -0500
++++ java/org/apache/catalina/filters/CorsFilter.java 2019-02-28 16:39:43.449141647 -0500
+@@ -260,17 +260,14 @@ public class CorsFilter implements Filte
+
+ // Section 6.1.3
+ // Add a single Access-Control-Allow-Origin header.
+- if (anyOriginAllowed && !supportsCredentials) {
+- // If resource doesn't support credentials and if any origin is
+- // allowed
+- // to make CORS request, return header with '*'.
++ if (anyOriginAllowed) {
++ // If any origin is allowed, return header with '*'.
+ response.addHeader(
+ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
+ "*");
+ } else {
+- // If the resource supports credentials add a single
+- // Access-Control-Allow-Origin header, with the value of the Origin
+- // header as value.
++ // Add a single Access-Control-Allow-Origin header, with the value
++ // of the Origin header as value.
+ response.addHeader(
+ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN,
+ origin);
+@@ -799,6 +796,10 @@ public class CorsFilter implements Filte
+ .parseBoolean(supportsCredentials);
+ }
+
++ if (this.supportsCredentials && this.anyOriginAllowed) {
++ throw new ServletException(sm.getString("corsFilter.invalidSupportsCredentials"));
++ }
++
+ if (preflightMaxAge != null) {
+ try {
+ if (!preflightMaxAge.isEmpty()) {
+@@ -1156,7 +1156,7 @@ public class CorsFilter implements Filte
+ /**
+ * By default, all origins are allowed to make requests.
+ */
+- public static final String DEFAULT_ALLOWED_ORIGINS = "*";
++ public static final String DEFAULT_ALLOWED_ORIGINS = "";
+
+ /**
+ * By default, following methods are supported: GET, POST, HEAD and OPTIONS.
+@@ -1172,7 +1172,7 @@ public class CorsFilter implements Filte
+ /**
+ * By default, support credentials is turned on.
+ */
+- public static final String DEFAULT_SUPPORTS_CREDENTIALS = "true";
++ public static final String DEFAULT_SUPPORTS_CREDENTIALS = "false";
+
+ /**
+ * By default, following headers are supported:
+diff --git a/java/org/apache/catalina/filters/LocalStrings.properties b/java/org/apache/catalina/filters/LocalStrings.properties
+index 5c4c792f82..921f101456 100644
+--- java/org/apache/catalina/filters/LocalStrings.properties
++++ java/org/apache/catalina/filters/LocalStrings.properties
+@@ -14,6 +14,8 @@
+ # limitations under the License.
+
+ addDefaultCharset.unsupportedCharset=Specified character set [{0}] is not supported
++
++corsFilter.invalidSupportsCredentials=It is not allowed to configure supportsCredentials=[true] when allowedOrigins=[*]
+ corsFilter.invalidPreflightMaxAge=Unable to parse preflightMaxAge
+ corsFilter.nullRequest=HttpServletRequest object is null
+ corsFilter.nullRequestType=CORSRequestType object is null
+diff --git a/test/org/apache/catalina/filters/TestCorsFilter.java b/test/org/apache/catalina/filters/TestCorsFilter.java
+index 06634fbb57..47d520a08e 100644
+--- test/org/apache/catalina/filters/TestCorsFilter.java
++++ test/org/apache/catalina/filters/TestCorsFilter.java
+@@ -52,8 +52,7 @@ public class TestCorsFilter {
+ corsFilter.doFilter(request, response, filterChain);
+
+ Assert.assertTrue(response.getHeader(
+- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
+- "https://www.apache.org"));
++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
+ Assert.assertTrue(((Boolean) request.getAttribute(
+ CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
+ Assert.assertTrue(request.getAttribute(
+@@ -85,8 +84,7 @@ public class TestCorsFilter {
+ corsFilter.doFilter(request, response, filterChain);
+
+ Assert.assertTrue(response.getHeader(
+- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
+- "https://www.apache.org"));
++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
+ Assert.assertTrue(((Boolean) request.getAttribute(
+ CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
+ Assert.assertTrue(request.getAttribute(
+@@ -117,8 +115,7 @@ public class TestCorsFilter {
+ corsFilter.doFilter(request, response, filterChain);
+
+ Assert.assertTrue(response.getHeader(
+- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
+- "https://www.apache.org"));
++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
+ Assert.assertTrue(((Boolean) request.getAttribute(
+ CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
+ Assert.assertTrue(request.getAttribute(
+@@ -163,41 +160,15 @@ public class TestCorsFilter {
+ }
+
+ /**
+- * Tests the presence of the origin (and not '*') in the response, when
+- * supports credentials is enabled alongwith any origin, '*'.
++ * Tests the that supports credentials may not be enabled with any origin,
++ * '*'.
+ *
+- * @throws IOException
+ * @throws ServletException
+ */
+- @Test
+- public void testDoFilterSimpleAnyOriginAndSupportsCredentials()
+- throws IOException, ServletException {
+- TesterHttpServletRequest request = new TesterHttpServletRequest();
+- request.setHeader(CorsFilter.REQUEST_HEADER_ORIGIN,
+- TesterFilterConfigs.HTTPS_WWW_APACHE_ORG);
+- request.setMethod("GET");
+- TesterHttpServletResponse response = new TesterHttpServletResponse();
+-
++ @Test(expected=ServletException.class)
++ public void testDoFilterSimpleAnyOriginAndSupportsCredentials() throws ServletException {
+ CorsFilter corsFilter = new CorsFilter();
+- corsFilter.init(TesterFilterConfigs
+- .getFilterConfigAnyOriginAndSupportsCredentials());
+- corsFilter.doFilter(request, response, filterChain);
+-
+- Assert.assertTrue(response.getHeader(
+- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
+- TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
+- Assert.assertTrue(response.getHeader(
+- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_CREDENTIALS)
+- .equals(
+- "true"));
+- Assert.assertTrue(((Boolean) request.getAttribute(
+- CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
+- Assert.assertTrue(request.getAttribute(
+- CorsFilter.HTTP_REQUEST_ATTRIBUTE_ORIGIN).equals(
+- TesterFilterConfigs.HTTPS_WWW_APACHE_ORG));
+- Assert.assertTrue(request.getAttribute(
+- CorsFilter.HTTP_REQUEST_ATTRIBUTE_REQUEST_TYPE).equals(
+- CorsFilter.CORSRequestType.SIMPLE.name().toLowerCase(Locale.ENGLISH)));
++ corsFilter.init(TesterFilterConfigs.getFilterConfigAnyOriginAndSupportsCredentials());
+ }
+
+ /**
+@@ -258,8 +229,7 @@ public class TestCorsFilter {
+ corsFilter.doFilter(request, response, filterChain);
+
+ Assert.assertTrue(response.getHeader(
+- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
+- "https://www.apache.org"));
++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
+ Assert.assertTrue(response.getHeader(
+ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_EXPOSE_HEADERS)
+ .equals(TesterFilterConfigs.EXPOSED_HEADERS));
+@@ -707,9 +677,8 @@ public class TestCorsFilter {
+ corsFilter.init(null);
+ corsFilter.doFilter(request, response, filterChain);
+
+- Assert.assertTrue(response.getHeader(
+- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
+- "https://www.apache.org"));
++ Assert.assertNull(response.getHeader(
++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN));
+ Assert.assertTrue(((Boolean) request.getAttribute(
+ CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST)).booleanValue());
+ Assert.assertTrue(request.getAttribute(
+@@ -1401,7 +1370,7 @@ public class TestCorsFilter {
+ Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
+ Assert.assertTrue(corsFilter.isAnyOriginAllowed());
+ Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
+- Assert.assertTrue(corsFilter.isSupportsCredentials());
++ Assert.assertFalse(corsFilter.isSupportsCredentials());
+ Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
+ }
+
+@@ -1437,9 +1406,9 @@ public class TestCorsFilter {
+ Assert.assertTrue(corsFilter.getAllowedHttpHeaders().size() == 6);
+ Assert.assertTrue(corsFilter.getAllowedHttpMethods().size() == 4);
+ Assert.assertTrue(corsFilter.getAllowedOrigins().size() == 0);
+- Assert.assertTrue(corsFilter.isAnyOriginAllowed());
++ Assert.assertFalse(corsFilter.isAnyOriginAllowed());
+ Assert.assertTrue(corsFilter.getExposedHeaders().size() == 0);
+- Assert.assertTrue(corsFilter.isSupportsCredentials());
++ Assert.assertFalse(corsFilter.isSupportsCredentials());
+ Assert.assertTrue(corsFilter.getPreflightMaxAge() == 1800);
+ }
+
+@@ -1543,8 +1512,7 @@ public class TestCorsFilter {
+ corsFilter.doFilter(request, response, filterChain);
+
+ Assert.assertTrue(response.getHeader(
+- CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals(
+- "https://www.apache.org"));
++ CorsFilter.RESPONSE_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN).equals("*"));
+ Assert.assertNull(request
+ .getAttribute(CorsFilter.HTTP_REQUEST_ATTRIBUTE_IS_CORS_REQUEST));
+ Assert.assertNull(request
+diff --git a/test/org/apache/catalina/filters/TesterFilterConfigs.java b/test/org/apache/catalina/filters/TesterFilterConfigs.java
+index 941d8949d7..cb3d04f813 100644
+--- test/org/apache/catalina/filters/TesterFilterConfigs.java
++++ test/org/apache/catalina/filters/TesterFilterConfigs.java
+@@ -36,12 +36,13 @@ public class TesterFilterConfigs {
+ public static final TesterServletContext mockServletContext =
+ new TesterServletContext();
+
++ // Default config for the test is to allow any origin
+ public static FilterConfig getDefaultFilterConfig() {
+ final String allowedHttpHeaders =
+ CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
+ final String allowedHttpMethods =
+ CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
+- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
++ final String allowedOrigins = ANY_ORIGIN;
+ final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
+ final String supportCredentials =
+ CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
+@@ -59,7 +60,7 @@ public class TesterFilterConfigs {
+ CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
+ final String allowedHttpMethods =
+ CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
+- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
++ final String allowedOrigins = ANY_ORIGIN;
+ final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
+ final String supportCredentials = "true";
+ final String preflightMaxAge =
+@@ -77,7 +78,7 @@ public class TesterFilterConfigs {
+ CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
+ final String allowedHttpMethods =
+ CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS + ",PUT";
+- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
++ final String allowedOrigins = ANY_ORIGIN;
+ final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
+ final String supportCredentials = "false";
+ final String preflightMaxAge =
+@@ -131,7 +132,7 @@ public class TesterFilterConfigs {
+ CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
+ final String allowedHttpMethods =
+ CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
+- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
++ final String allowedOrigins = ANY_ORIGIN;
+ final String exposedHeaders = EXPOSED_HEADERS;
+ final String supportCredentials =
+ CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
+@@ -240,7 +241,7 @@ public class TesterFilterConfigs {
+ CorsFilter.DEFAULT_ALLOWED_HTTP_HEADERS;
+ final String allowedHttpMethods =
+ CorsFilter.DEFAULT_ALLOWED_HTTP_METHODS;
+- final String allowedOrigins = CorsFilter.DEFAULT_ALLOWED_ORIGINS;
++ final String allowedOrigins = ANY_ORIGIN;
+ final String exposedHeaders = CorsFilter.DEFAULT_EXPOSED_HEADERS;
+ final String supportCredentials =
+ CorsFilter.DEFAULT_SUPPORTS_CREDENTIALS;
+diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml
+--- webapps/docs/changelog.xml.orig 2019-02-28 16:33:00.254343407 -0500
++++ webapps/docs/changelog.xml 2019-02-28 16:33:24.135272232 -0500
+@@ -78,6 +78,10 @@
+ NonLoginAuthenticator has to be set for it to work (if no login method
+ is specified). (remm)
+
++
++ 62343: Make CORS filter defaults more secure. This is the fix
++ for CVE-2018-8014. (markt)
++
+
+
+
diff --git a/SOURCES/tomcat-7.0.76-CVE-2018-8034.patch b/SOURCES/tomcat-7.0.76-CVE-2018-8034.patch
new file mode 100644
index 0000000..68035e1
--- /dev/null
+++ b/SOURCES/tomcat-7.0.76-CVE-2018-8034.patch
@@ -0,0 +1,109 @@
+commit 2c522795166c930741a9cecca76797bf48cb1634
+Author: Mark Thomas
+Date: Mon Jun 18 19:45:13 2018 +0000
+
+ Enable host name verification for secure WebSocket client connections by default.
+
+ git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1833760 13f79535-47bb-0310-9956-ffa450edef68
+
+diff --git a/java/org/apache/tomcat/websocket/WsWebSocketContainer.java b/java/org/apache/tomcat/websocket/WsWebSocketContainer.java
+index ac5122a89c..b526022958 100644
+--- java/org/apache/tomcat/websocket/WsWebSocketContainer.java
++++ java/org/apache/tomcat/websocket/WsWebSocketContainer.java
+@@ -53,6 +53,7 @@ import java.util.concurrent.TimeoutException;
+ import javax.net.ssl.SSLContext;
+ import javax.net.ssl.SSLEngine;
+ import javax.net.ssl.SSLException;
++import javax.net.ssl.SSLParameters;
+ import javax.net.ssl.TrustManagerFactory;
+ import javax.websocket.ClientEndpoint;
+ import javax.websocket.ClientEndpointConfig;
+@@ -363,7 +364,7 @@ public class WsWebSocketContainer
+ // proxy CONNECT, need to use TLS from this point on so wrap the
+ // original AsynchronousSocketChannel
+ SSLEngine sslEngine = createSSLEngine(
+- clientEndpointConfiguration.getUserProperties());
++ clientEndpointConfiguration.getUserProperties(), host, port);
+ channel = new AsyncChannelWrapperSecure(socketChannel, sslEngine);
+ } else if (channel == null) {
+ // Only need to wrap as this point if it wasn't wrapped to process a
+@@ -931,7 +932,7 @@ public class WsWebSocketContainer
+ }
+
+
+- private SSLEngine createSSLEngine(Map userProperties)
++ private SSLEngine createSSLEngine(Map userProperties, String host, int port)
+ throws DeploymentException {
+
+ try {
+@@ -979,7 +980,7 @@ public class WsWebSocketContainer
+ }
+ }
+
+- SSLEngine engine = sslContext.createSSLEngine();
++ SSLEngine engine = sslContext.createSSLEngine(host, port);
+
+ String sslProtocolsValue =
+ (String) userProperties.get(SSL_PROTOCOLS_PROPERTY);
+@@ -989,6 +990,14 @@ public class WsWebSocketContainer
+
+ engine.setUseClientMode(true);
+
++ // Enable host verification
++ // Start with current settings (returns a copy)
++ SSLParameters sslParams = engine.getSSLParameters();
++ // Use HTTPS since WebSocket starts over HTTP(S)
++ sslParams.setEndpointIdentificationAlgorithm("HTTPS");
++ // Write the parameters back
++ engine.setSSLParameters(sslParams);
++
+ return engine;
+ } catch (Exception e) {
+ throw new DeploymentException(sm.getString(
+diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml
+--- webapps/docs/changelog.xml.orig 2019-03-01 09:38:54.202817893 -0500
++++ webapps/docs/changelog.xml 2019-03-01 09:39:29.413704569 -0500
+@@ -84,6 +84,14 @@
+
+
+
++
++
++
++ Enable host name verification when using TLS with the WebSocket client.
++ (markt)
++
++
++
+
+
+
+diff -up webapps/docs/web-socket-howto.xml.orig webapps/docs/web-socket-howto.xml
+--- webapps/docs/web-socket-howto.xml.orig 2019-03-01 12:46:34.515904379 -0500
++++ webapps/docs/web-socket-howto.xml 2019-03-01 12:51:00.900175575 -0500
+@@ -148,10 +148,21 @@ implement its own timeout mechanism to h
+ org.apache.tomcat.websocket.SSL_TRUSTSTORE_PWD
+
+ The default truststore password is changeit
.
+- If the org.apache.tomcat.websocket.SSL_CONTEXT
property is
+- set then the org.apache.tomcat.websocket.SSL_TRUSTSTORE
and
+- org.apache.tomcat.websocket.SSL_TRUSTSTORE_PWD
properties
+- will be ignored.
++
++If the org.apache.tomcat.websocket.SSL_CONTEXT
property is
++ set then the org.apache.tomcat.websocket.SSL_TRUSTSTORE
and
++ org.apache.tomcat.websocket.SSL_TRUSTSTORE_PWD
properties
++ will be ignored.
++
++For secure server end points, host name verification is enabled by default.
++ To bypass this verification (not recommended), it is necessary to provide a
++ custom SSLContext
via the
++ org.apache.tomcat.websocket.SSL_CONTEXT
user property. The
++ custom SSLContext
must be configured with a custom
++ TrustManager
that extends
++ javax.net.ssl.X509ExtendedTrustManager
. The desired verification
++ (or lack of verification) can then be controlled by appropriate
++ implementations of the individual abstract methods.
+
+
+
diff --git a/SOURCES/tomcat-7.0.76-rhbz-1455483.patch b/SOURCES/tomcat-7.0.76-rhbz-1455483.patch
new file mode 100644
index 0000000..cb1c256
--- /dev/null
+++ b/SOURCES/tomcat-7.0.76-rhbz-1455483.patch
@@ -0,0 +1,828 @@
+diff -up java/org/apache/coyote/http11/AbstractHttp11Processor.java.orig java/org/apache/coyote/http11/AbstractHttp11Processor.java
+--- java/org/apache/coyote/http11/AbstractHttp11Processor.java.orig 2017-03-09 08:51:40.000000000 -0500
++++ java/org/apache/coyote/http11/AbstractHttp11Processor.java 2019-03-05 14:58:20.285295932 -0500
+@@ -48,6 +48,7 @@ import org.apache.tomcat.util.buf.HexUti
+ import org.apache.tomcat.util.buf.MessageBytes;
+ import org.apache.tomcat.util.http.FastHttpDateFormat;
+ import org.apache.tomcat.util.http.MimeHeaders;
++import org.apache.tomcat.util.http.parser.HttpParser;
+ import org.apache.tomcat.util.log.UserDataHelper;
+ import org.apache.tomcat.util.net.AbstractEndpoint;
+ import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+@@ -262,6 +263,9 @@ public abstract class AbstractHttp11Proc
+ protected org.apache.coyote.http11.upgrade.UpgradeInbound upgradeInbound = null;
+
+
++ protected HttpParser httpParser;
++
++
+ /**
+ * Instance of the new protocol to use after the HTTP connection has been
+ * upgraded using the Servlet 3.1 based upgrade process.
+@@ -1301,33 +1305,62 @@ public abstract class AbstractHttp11Proc
+ }
+ }
+
+- // Check for a full URI (including protocol://host:port/)
++ // Check for an absolute-URI less the query string which has already
++ // been removed during the parsing of the request line
+ ByteChunk uriBC = request.requestURI().getByteChunk();
++ byte[] uriB = uriBC.getBytes();
+ if (uriBC.startsWithIgnoreCase("http", 0)) {
+
+- int pos = uriBC.indexOf("://", 0, 3, 4);
+- int uriBCStart = uriBC.getStart();
+- int slashPos = -1;
+- if (pos != -1) {
+- byte[] uriB = uriBC.getBytes();
+- slashPos = uriBC.indexOf('/', pos + 3);
++ int pos = 4;
++ // Check for https
++ if (uriBC.startsWithIgnoreCase("s", pos)) {
++ pos++;
++ }
++ // Next 3 characters must be "://"
++ if (uriBC.startsWith("://", pos)) {
++ int uriBCStart = uriBC.getStart();
++
++ // '/' does not appear in the authority so use the first
++ // instance to split the authority and the path segments
++ int slashPos = uriBC.indexOf('/', pos);
++ // '@' in the authority delimits the userinfo
++
+ if (slashPos == -1) {
+ slashPos = uriBC.getLength();
+- // Set URI as "/"
+- request.requestURI().setBytes
+- (uriB, uriBCStart + pos + 1, 1);
++ // Set URI as "/". Use 6 as it will always be a '/'.
++ // 01234567
++ // http://
++ // https://
++ request.requestURI().setBytes(uriB, uriBCStart + 6, 1);
+ } else {
+- request.requestURI().setBytes
+- (uriB, uriBCStart + slashPos,
+- uriBC.getLength() - slashPos);
++ request.requestURI().setBytes(uriB, uriBCStart + slashPos, uriBC.getLength() - slashPos);
+ }
+ MessageBytes hostMB = headers.setValue("host");
+ hostMB.setBytes(uriB, uriBCStart + pos + 3,
+ slashPos - pos - 3);
++ } else {
++ response.setStatus(400);
++ setErrorState(ErrorState.CLOSE_CLEAN, null);
++ if (getLog().isDebugEnabled()) {
++ getLog().debug(sm.getString("http11processor.request.invalidScheme"));
++ }
+ }
+
+ }
+
++ // Validate the characters in the URI. %nn decoding will be checked at
++ // the point of decoding.
++ for (int i = uriBC.getStart(); i < uriBC.getEnd(); i++) {
++ if (!httpParser.isAbsolutePathRelaxed(uriB[i])) {
++ response.setStatus(400);
++ setErrorState(ErrorState.CLOSE_CLEAN, null);
++ if (getLog().isDebugEnabled()) {
++ getLog().debug(sm.getString("http11processor.request.invalidUri"));
++ }
++ break;
++ }
++ }
++
+ // Input filter setup
+ InputFilter[] inputFilters = getInputBuffer().getFilters();
+
+@@ -1364,8 +1397,7 @@ public abstract class AbstractHttp11Proc
+ headers.removeHeader("content-length");
+ request.setContentLength(-1);
+ } else {
+- getInputBuffer().addActiveFilter
+- (inputFilters[Constants.IDENTITY_FILTER]);
++ getInputBuffer().addActiveFilter(inputFilters[Constants.IDENTITY_FILTER]);
+ contentDelimitation = true;
+ }
+ }
+@@ -1383,14 +1415,14 @@ public abstract class AbstractHttp11Proc
+ }
+ }
+
++ // Validate host name and extract port if present
+ parseHost(valueMB);
+
+ if (!contentDelimitation) {
+ // If there's no content length
+ // (broken HTTP/1.0 or HTTP/1.1), assume
+ // the client is not broken and didn't send a body
+- getInputBuffer().addActiveFilter
+- (inputFilters[Constants.VOID_FILTER]);
++ getInputBuffer().addActiveFilter(inputFilters[Constants.VOID_FILTER]);
+ contentDelimitation = true;
+ }
+
+diff -up java/org/apache/coyote/http11/AbstractHttp11Protocol.java.orig java/org/apache/coyote/http11/AbstractHttp11Protocol.java
+--- java/org/apache/coyote/http11/AbstractHttp11Protocol.java.orig 2019-03-05 12:14:08.096279991 -0500
++++ java/org/apache/coyote/http11/AbstractHttp11Protocol.java 2019-03-05 14:03:32.186921274 -0500
+@@ -45,6 +45,23 @@ public abstract class AbstractHttp11Prot
+ // ------------------------------------------------ HTTP specific properties
+ // ------------------------------------------ managed in the ProtocolHandler
+
++ private String relaxedPathChars = null;
++ public String getRelaxedPathChars() {
++ return relaxedPathChars;
++ }
++ public void setRelaxedPathChars(String relaxedPathChars) {
++ this.relaxedPathChars = relaxedPathChars;
++ }
++
++
++ private String relaxedQueryChars = null;
++ public String getRelaxedQueryChars() {
++ return relaxedQueryChars;
++ }
++ public void setRelaxedQueryChars(String relaxedQueryChars) {
++ this.relaxedQueryChars = relaxedQueryChars;
++ }
++
+ private int socketBuffer = 9000;
+ public int getSocketBuffer() { return socketBuffer; }
+ public void setSocketBuffer(int socketBuffer) {
+diff -up java/org/apache/coyote/http11/AbstractInputBuffer.java.orig java/org/apache/coyote/http11/AbstractInputBuffer.java
+--- java/org/apache/coyote/http11/AbstractInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500
++++ java/org/apache/coyote/http11/AbstractInputBuffer.java 2019-03-05 12:14:08.096279991 -0500
+@@ -22,6 +22,7 @@ import org.apache.coyote.InputBuffer;
+ import org.apache.coyote.Request;
+ import org.apache.tomcat.util.buf.ByteChunk;
+ import org.apache.tomcat.util.http.MimeHeaders;
++import org.apache.tomcat.util.http.parser.HttpParser;
+ import org.apache.tomcat.util.net.AbstractEndpoint;
+ import org.apache.tomcat.util.net.SocketWrapper;
+ import org.apache.tomcat.util.res.StringManager;
+@@ -108,6 +109,9 @@ public abstract class AbstractInputBuffe
+ protected int lastActiveFilter;
+
+
++ protected HttpParser httpParser;
++
++
+ // ------------------------------------------------------------- Properties
+
+
+diff -up java/org/apache/coyote/http11/Http11AprProcessor.java.orig java/org/apache/coyote/http11/Http11AprProcessor.java
+--- java/org/apache/coyote/http11/Http11AprProcessor.java.orig 2019-03-05 12:13:47.032344988 -0500
++++ java/org/apache/coyote/http11/Http11AprProcessor.java 2019-03-05 14:58:20.298295897 -0500
+@@ -35,6 +35,7 @@ import org.apache.tomcat.jni.SSLSocket;
+ import org.apache.tomcat.jni.Sockaddr;
+ import org.apache.tomcat.jni.Socket;
+ import org.apache.tomcat.util.ExceptionUtils;
++import org.apache.tomcat.util.http.parser.HttpParser;
+ import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+ import org.apache.tomcat.util.net.AprEndpoint;
+ import org.apache.tomcat.util.net.SSLSupport;
+@@ -61,11 +62,15 @@ public class Http11AprProcessor extends
+
+
+ public Http11AprProcessor(int headerBufferSize, AprEndpoint endpoint, int maxTrailerSize,
+- Set allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {
++ Set allowedTrailerHeaders,
++ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars,
++ String relaxedQueryChars) {
+
+ super(endpoint);
+
+- inputBuffer = new InternalAprInputBuffer(request, headerBufferSize);
++ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars);
++
++ inputBuffer = new InternalAprInputBuffer(request, headerBufferSize, httpParser);
+ request.setInputBuffer(inputBuffer);
+
+ outputBuffer = new InternalAprOutputBuffer(response, headerBufferSize);
+diff -up java/org/apache/coyote/http11/Http11AprProtocol.java.orig java/org/apache/coyote/http11/Http11AprProtocol.java
+--- java/org/apache/coyote/http11/Http11AprProtocol.java.orig 2019-03-05 12:14:08.097279988 -0500
++++ java/org/apache/coyote/http11/Http11AprProtocol.java 2019-03-05 13:59:45.131631454 -0500
+@@ -301,7 +301,9 @@ public class Http11AprProtocol extends A
+ Http11AprProcessor processor = new Http11AprProcessor(
+ proto.getMaxHttpHeaderSize(), (AprEndpoint)proto.endpoint,
+ proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
+- proto.getMaxExtensionSize(), proto.getMaxSwallowSize());
++ proto.getMaxExtensionSize(),
++ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(),
++ proto.getRelaxedQueryChars());
+ processor.setAdapter(proto.adapter);
+ processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
+ processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
+diff -up java/org/apache/coyote/http11/Http11NioProcessor.java.orig java/org/apache/coyote/http11/Http11NioProcessor.java
+--- java/org/apache/coyote/http11/Http11NioProcessor.java.orig 2019-03-05 12:13:47.033344985 -0500
++++ java/org/apache/coyote/http11/Http11NioProcessor.java 2019-03-05 13:04:00.335042387 -0500
+@@ -31,6 +31,7 @@ import org.apache.coyote.http11.filters.
+ import org.apache.juli.logging.Log;
+ import org.apache.juli.logging.LogFactory;
+ import org.apache.tomcat.util.ExceptionUtils;
++import org.apache.tomcat.util.http.parser.HttpParser;
+ import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+ import org.apache.tomcat.util.net.NioChannel;
+ import org.apache.tomcat.util.net.NioEndpoint;
+@@ -66,11 +67,15 @@ public class Http11NioProcessor extends
+
+
+ public Http11NioProcessor(int maxHttpHeaderSize, NioEndpoint endpoint, int maxTrailerSize,
+- Set allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {
++ Set allowedTrailerHeaders,
++ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars,
++ String relaxedQueryChars) {
+
+ super(endpoint);
+
+- inputBuffer = new InternalNioInputBuffer(request, maxHttpHeaderSize);
++ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars);
++
++ inputBuffer = new InternalNioInputBuffer(request, maxHttpHeaderSize, httpParser);
+ request.setInputBuffer(inputBuffer);
+
+ outputBuffer = new InternalNioOutputBuffer(response, maxHttpHeaderSize);
+diff -up java/org/apache/coyote/http11/Http11NioProtocol.java.orig java/org/apache/coyote/http11/Http11NioProtocol.java
+--- java/org/apache/coyote/http11/Http11NioProtocol.java.orig 2019-03-05 12:14:08.098279985 -0500
++++ java/org/apache/coyote/http11/Http11NioProtocol.java 2019-03-05 14:00:15.034537932 -0500
+@@ -266,7 +266,9 @@ public class Http11NioProtocol extends A
+ Http11NioProcessor processor = new Http11NioProcessor(
+ proto.getMaxHttpHeaderSize(), (NioEndpoint)proto.endpoint,
+ proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
+- proto.getMaxExtensionSize(), proto.getMaxSwallowSize());
++ proto.getMaxExtensionSize(),
++ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(),
++ proto.getRelaxedQueryChars());
+ processor.setAdapter(proto.adapter);
+ processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
+ processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
+diff -up java/org/apache/coyote/http11/Http11Processor.java.orig java/org/apache/coyote/http11/Http11Processor.java
+--- java/org/apache/coyote/http11/Http11Processor.java.orig 2017-03-09 08:51:40.000000000 -0500
++++ java/org/apache/coyote/http11/Http11Processor.java 2019-03-05 14:58:20.306295875 -0500
+@@ -26,6 +26,7 @@ import org.apache.coyote.ActionCode;
+ import org.apache.coyote.http11.filters.BufferedInputFilter;
+ import org.apache.juli.logging.Log;
+ import org.apache.juli.logging.LogFactory;
++import org.apache.tomcat.util.http.parser.HttpParser;
+ import org.apache.tomcat.util.net.AbstractEndpoint.Handler.SocketState;
+ import org.apache.tomcat.util.net.JIoEndpoint;
+ import org.apache.tomcat.util.net.SSLSupport;
+@@ -51,11 +52,16 @@ public class Http11Processor extends Abs
+
+
+ public Http11Processor(int headerBufferSize, JIoEndpoint endpoint, int maxTrailerSize,
+- Set allowedTrailerHeaders, int maxExtensionSize, int maxSwallowSize) {
++ Set allowedTrailerHeaders,
++ int maxExtensionSize, int maxSwallowSize, String relaxedPathChars,
++ String relaxedQueryChars) {
+
+ super(endpoint);
++
++ httpParser = new HttpParser(relaxedPathChars, relaxedQueryChars);
++
++ inputBuffer = new InternalInputBuffer(request, headerBufferSize, httpParser);
+
+- inputBuffer = new InternalInputBuffer(request, headerBufferSize);
+ request.setInputBuffer(inputBuffer);
+
+ outputBuffer = new InternalOutputBuffer(response, headerBufferSize);
+diff -up java/org/apache/coyote/http11/Http11Protocol.java.orig java/org/apache/coyote/http11/Http11Protocol.java
+--- java/org/apache/coyote/http11/Http11Protocol.java.orig 2019-03-05 12:14:08.099279982 -0500
++++ java/org/apache/coyote/http11/Http11Protocol.java 2019-03-05 13:02:36.769301263 -0500
+@@ -165,7 +165,9 @@ public class Http11Protocol extends Abst
+ Http11Processor processor = new Http11Processor(
+ proto.getMaxHttpHeaderSize(), (JIoEndpoint)proto.endpoint,
+ proto.getMaxTrailerSize(), proto.getAllowedTrailerHeadersAsSet(),
+- proto.getMaxExtensionSize(), proto.getMaxSwallowSize());
++ proto.getMaxExtensionSize(),
++ proto.getMaxSwallowSize(), proto.getRelaxedPathChars(),
++ proto.getRelaxedQueryChars());
+ processor.setAdapter(proto.adapter);
+ processor.setMaxKeepAliveRequests(proto.getMaxKeepAliveRequests());
+ processor.setKeepAliveTimeout(proto.getKeepAliveTimeout());
+diff -up java/org/apache/coyote/http11/InternalAprInputBuffer.java.orig java/org/apache/coyote/http11/InternalAprInputBuffer.java
+--- java/org/apache/coyote/http11/InternalAprInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500
++++ java/org/apache/coyote/http11/InternalAprInputBuffer.java 2019-03-05 14:58:20.312295859 -0500
+@@ -51,7 +51,8 @@ public class InternalAprInputBuffer exte
+ /**
+ * Alternate constructor.
+ */
+- public InternalAprInputBuffer(Request request, int headerBufferSize) {
++ public InternalAprInputBuffer(Request request, int headerBufferSize,
++ HttpParser httpParser) {
+
+ this.request = request;
+ headers = request.getMimeHeaders();
+@@ -63,6 +64,8 @@ public class InternalAprInputBuffer exte
+ bbuf = ByteBuffer.allocateDirect((headerBufferSize / 1500 + 1) * 1500);
+ }
+
++ this.httpParser = httpParser;
++
+ inputStreamInputBuffer = new SocketInputBuffer();
+
+ filterLibrary = new InputFilter[0];
+@@ -231,7 +234,13 @@ public class InternalAprInputBuffer exte
+ end = pos;
+ } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) {
+ questionPos = pos;
+- } else if (HttpParser.isNotRequestTarget(buf[pos])) {
++ } else if (questionPos != -1 && !httpParser.isQueryRelaxed(buf[pos])) {
++ // %nn decoding will be checked at the point of decoding
++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
++ } else if (httpParser.isNotRequestTargetRelaxed(buf[pos])) {
++ // This is a general check that aims to catch problems early
++ // Detailed checking of each part of the request target will
++ // happen in AbstractHttp11Processor#prepareRequest()
+ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
+ }
+
+diff -up java/org/apache/coyote/http11/InternalInputBuffer.java.orig java/org/apache/coyote/http11/InternalInputBuffer.java
+--- java/org/apache/coyote/http11/InternalInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500
++++ java/org/apache/coyote/http11/InternalInputBuffer.java 2019-03-05 14:58:21.215293426 -0500
+@@ -52,13 +52,16 @@ public class InternalInputBuffer extends
+ /**
+ * Default constructor.
+ */
+- public InternalInputBuffer(Request request, int headerBufferSize) {
++ public InternalInputBuffer(Request request, int headerBufferSize,
++ HttpParser httpParser) {
+
+ this.request = request;
+ headers = request.getMimeHeaders();
+
+ buf = new byte[headerBufferSize];
+
++ this.httpParser = httpParser;
++
+ inputStreamInputBuffer = new InputStreamInputBuffer();
+
+ filterLibrary = new InputFilter[0];
+@@ -185,7 +188,13 @@ public class InternalInputBuffer extends
+ end = pos;
+ } else if ((buf[pos] == Constants.QUESTION) && (questionPos == -1)) {
+ questionPos = pos;
+- } else if (HttpParser.isNotRequestTarget(buf[pos])) {
++ } else if (questionPos != -1 && !httpParser.isQueryRelaxed(buf[pos])) {
++ // %nn decoding will be checked at the point of decoding
++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
++ } else if (httpParser.isNotRequestTargetRelaxed(buf[pos])) {
++ // This is a general check that aims to catch problems early
++ // Detailed checking of each part of the request target will
++ // happen in AbstractHttp11Processor#prepareRequest()
+ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
+ }
+
+diff -up java/org/apache/coyote/http11/InternalNioInputBuffer.java.orig java/org/apache/coyote/http11/InternalNioInputBuffer.java
+--- java/org/apache/coyote/http11/InternalNioInputBuffer.java.orig 2017-03-09 08:51:40.000000000 -0500
++++ java/org/apache/coyote/http11/InternalNioInputBuffer.java 2019-03-05 14:58:20.272295967 -0500
+@@ -98,12 +98,14 @@ public class InternalNioInputBuffer exte
+ /**
+ * Alternate constructor.
+ */
+- public InternalNioInputBuffer(Request request, int headerBufferSize) {
++ public InternalNioInputBuffer(Request request, int headerBufferSize,
++ HttpParser httpParser) {
+
+ this.request = request;
+ headers = request.getMimeHeaders();
+
+ this.headerBufferSize = headerBufferSize;
++ this.httpParser = httpParser;
+
+ inputStreamInputBuffer = new SocketInputBuffer();
+
+@@ -313,7 +315,13 @@ public class InternalNioInputBuffer exte
+ end = pos;
+ } else if ((buf[pos] == Constants.QUESTION) && (parsingRequestLineQPos == -1)) {
+ parsingRequestLineQPos = pos;
+- } else if (HttpParser.isNotRequestTarget(buf[pos])) {
++ } else if (parsingRequestLineQPos != -1 && !httpParser.isQueryRelaxed(buf[pos])) {
++ // %nn decoding will be checked at the point of decoding
++ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
++ } else if (httpParser.isNotRequestTargetRelaxed(buf[pos])) {
++ // This is a general check that aims to catch problems early
++ // Detailed checking of each part of the request target will
++ // happen in AbstractHttp11Processor#prepareRequest()
+ throw new IllegalArgumentException(sm.getString("iib.invalidRequestTarget"));
+ }
+ pos++;
+diff -up java/org/apache/coyote/http11/LocalStrings.properties.orig java/org/apache/coyote/http11/LocalStrings.properties
+--- java/org/apache/coyote/http11/LocalStrings.properties.orig 2019-03-05 12:14:08.092280004 -0500
++++ java/org/apache/coyote/http11/LocalStrings.properties 2019-03-05 12:23:45.474498387 -0500
+@@ -27,6 +27,9 @@ http11processor.filter.unknown=Unknown f
+ http11processor.filter.error=Error intializing filter {0}
+ http11processor.header.parse=Error parsing HTTP request header
+ http11processor.neverused=This method should never be used
++http11processor.request.invalidScheme=The HTTP request contained an absolute URI with an invalid scheme
++http11processor.request.invalidUri==The HTTP request contained an invalid URI
++http11processor.request.invalidUserInfo=The HTTP request contained an absolute URI with an invalid userinfo
+ http11processor.request.prepare=Error preparing request
+ http11processor.request.process=Error processing request
+ http11processor.request.finish=Error finishing request
+diff -up java/org/apache/tomcat/util/buf/ByteChunk.java.orig java/org/apache/tomcat/util/buf/ByteChunk.java
+--- java/org/apache/tomcat/util/buf/ByteChunk.java.orig 2017-03-09 08:51:41.000000000 -0500
++++ java/org/apache/tomcat/util/buf/ByteChunk.java 2019-03-05 12:16:53.404769901 -0500
+@@ -668,7 +668,8 @@ public final class ByteChunk implements
+ }
+
+ /**
+- * Returns true if the message bytes starts with the specified string.
++ * Returns true if the buffer starts with the specified string when tested
++ * in a case sensitive manner.
+ * @param s the string
+ * @deprecated Unused. Will be removed in Tomcat 8.0.x onwards.
+ */
+@@ -717,6 +718,31 @@ public final class ByteChunk implements
+ * @param s the string
+ * @param pos The position
+ */
++ public boolean startsWith(String s, int pos) {
++ byte[] b = buff;
++ int len = s.length();
++ if (b == null || len + pos > end - start) {
++ return false;
++ }
++ int off = start + pos;
++ for (int i = 0; i < len; i++) {
++ if (b[off++] != s.charAt(i)) {
++ return false;
++ }
++ }
++ return true;
++ }
++
++
++ /**
++ * Returns true if the buffer starts with the specified string when tested
++ * in a case insensitive manner.
++ *
++ * @param s the string
++ * @param pos The position
++ *
++ * @return true
if the start matches
++ */
+ public boolean startsWithIgnoreCase(String s, int pos) {
+ byte[] b = buff;
+ int len = s.length();
+diff -up java/org/apache/tomcat/util/http/parser/HttpParser.java.orig java/org/apache/tomcat/util/http/parser/HttpParser.java
+--- java/org/apache/tomcat/util/http/parser/HttpParser.java.orig 2017-03-09 08:51:41.000000000 -0500
++++ java/org/apache/tomcat/util/http/parser/HttpParser.java 2019-03-05 14:58:20.291295916 -0500
+@@ -67,9 +67,17 @@ public class HttpParser {
+ private static final boolean[] IS_SEPARATOR = new boolean[ARRAY_SIZE];
+ private static final boolean[] IS_TOKEN = new boolean[ARRAY_SIZE];
+ private static final boolean[] IS_HEX = new boolean[ARRAY_SIZE];
+- private static final boolean[] IS_NOT_REQUEST_TARGET = new boolean[ARRAY_SIZE];
+ private static final boolean[] IS_HTTP_PROTOCOL = new boolean[ARRAY_SIZE];
++ private static final boolean[] IS_ALPHA = new boolean[ARRAY_SIZE];
++ private static final boolean[] IS_NUMERIC = new boolean[ARRAY_SIZE];
+ private static final boolean[] REQUEST_TARGET_ALLOW = new boolean[ARRAY_SIZE];
++ private static final boolean[] IS_UNRESERVED = new boolean[ARRAY_SIZE];
++ private static final boolean[] IS_SUBDELIM = new boolean[ARRAY_SIZE];
++ private static final boolean[] IS_USERINFO = new boolean[ARRAY_SIZE];
++ private static final boolean[] IS_RELAXABLE = new boolean[ARRAY_SIZE];
++
++ private static final HttpParser DEFAULT;
++
+
+ static {
+ // Digest field types.
+@@ -91,19 +99,6 @@ public class HttpParser {
+ // RFC2617 says nc is 8LHEX. <">8LHEX<"> will also be accepted
+ fieldTypes.put("nc", FIELD_TYPE_LHEX);
+
+- String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow");
+- if (prop != null) {
+- for (int i = 0; i < prop.length(); i++) {
+- char c = prop.charAt(i);
+- if (c == '{' || c == '}' || c == '|') {
+- REQUEST_TARGET_ALLOW[c] = true;
+- } else {
+- log.warn(sm.getString("httpparser.invalidRequestTargetCharacter",
+- Character.valueOf(c)));
+- }
+- }
+- }
+-
+ for (int i = 0; i < ARRAY_SIZE; i++) {
+ // Control> 0-31, 127
+ if (i < 32 || i == 127) {
+@@ -128,6 +123,67 @@ public class HttpParser {
+ IS_HEX[i] = true;
+ }
+
++ // Not valid for HTTP protocol
++ // "HTTP/" DIGIT "." DIGIT
++ if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) {
++ IS_HTTP_PROTOCOL[i] = true;
++ }
++
++ if (i >= '0' && i <= '9') {
++ IS_NUMERIC[i] = true;
++ }
++
++ if (i >= 'a' && i <= 'z' || i >= 'A' && i <= 'Z') {
++ IS_ALPHA[i] = true;
++ }
++
++ if (IS_ALPHA[i] || IS_NUMERIC[i] || i == '-' || i == '.' || i == '_' || i == '~') {
++ IS_UNRESERVED[i] = true;
++ }
++
++ if (i == '!' || i == '$' || i == '&' || i == '\'' || i == '(' || i == ')' || i == '*' ||
++ i == '+' || i == ',' || i == ';' || i == '=') {
++ IS_SUBDELIM[i] = true;
++ }
++
++ // userinfo = *( unreserved / pct-encoded / sub-delims / ":" )
++ if (IS_UNRESERVED[i] || i == '%' || IS_SUBDELIM[i] || i == ':') {
++ IS_USERINFO[i] = true;
++ }
++
++ // The characters that are normally not permitted for which the
++ // restrictions may be relaxed when used in the path and/or query
++ // string
++ if (i == '\"' || i == '<' || i == '>' || i == '[' || i == '\\' || i == ']' ||
++ i == '^' || i == '`' || i == '{' || i == '|' || i == '}') {
++ IS_RELAXABLE[i] = true;
++ }
++ }
++
++ String prop = System.getProperty("tomcat.util.http.parser.HttpParser.requestTargetAllow");
++ if (prop != null) {
++ for (int i = 0; i < prop.length(); i++) {
++ char c = prop.charAt(i);
++ if (c == '{' || c == '}' || c == '|') {
++ REQUEST_TARGET_ALLOW[c] = true;
++ } else {
++ log.warn(sm.getString("http.invalidRequestTargetCharacter",
++ Character.valueOf(c)));
++ }
++ }
++ }
++
++ DEFAULT = new HttpParser(null, null);
++ }
++
++
++ private final boolean[] IS_NOT_REQUEST_TARGET = new boolean[ARRAY_SIZE];
++ private final boolean[] IS_ABSOLUTEPATH_RELAXED = new boolean[ARRAY_SIZE];
++ private final boolean[] IS_QUERY_RELAXED = new boolean[ARRAY_SIZE];
++
++
++ public HttpParser(String relaxedPathChars, String relaxedQueryChars) {
++ for (int i = 0; i < ARRAY_SIZE; i++) {
+ // Not valid for request target.
+ // Combination of multiple rules from RFC7230 and RFC 3986. Must be
+ // ASCII, no controls plus a few additional characters excluded
+@@ -139,12 +195,29 @@ public class HttpParser {
+ }
+ }
+
+- // Not valid for HTTP protocol
+- // "HTTP/" DIGIT "." DIGIT
+- if (i == 'H' || i == 'T' || i == 'P' || i == '/' || i == '.' || (i >= '0' && i <= '9')) {
+- IS_HTTP_PROTOCOL[i] = true;
++ /*
++ * absolute-path = 1*( "/" segment )
++ * segment = *pchar
++ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
++ *
++ * Note pchar allows everything userinfo allows plus "@"
++ */
++ if (IS_USERINFO[i] || i == '@' || i == '/' || REQUEST_TARGET_ALLOW[i]) {
++ IS_ABSOLUTEPATH_RELAXED[i] = true;
++ }
++
++ /*
++ * query = *( pchar / "/" / "?" )
++ *
++ * Note query allows everything absolute-path allows plus "?"
++ */
++ if (IS_ABSOLUTEPATH_RELAXED[i] || i == '?' || REQUEST_TARGET_ALLOW[i]) {
++ IS_QUERY_RELAXED[i] = true;
+ }
+ }
++
++ relax(IS_ABSOLUTEPATH_RELAXED, relaxedPathChars);
++ relax(IS_QUERY_RELAXED, relaxedQueryChars);
+ }
+
+ /**
+@@ -277,6 +350,39 @@ public class HttpParser {
+ }
+
+
++ public boolean isNotRequestTargetRelaxed(int c) {
++ // Fast for valid request target characters, slower for some incorrect
++ // ones
++ try {
++ return IS_NOT_REQUEST_TARGET[c];
++ } catch (ArrayIndexOutOfBoundsException ex) {
++ return true;
++ }
++ }
++
++
++ public boolean isAbsolutePathRelaxed(int c) {
++ // Fast for valid user info characters, slower for some incorrect
++ // ones
++ try {
++ return IS_ABSOLUTEPATH_RELAXED[c];
++ } catch (ArrayIndexOutOfBoundsException ex) {
++ return false;
++ }
++ }
++
++
++ public boolean isQueryRelaxed(int c) {
++ // Fast for valid user info characters, slower for some incorrect
++ // ones
++ try {
++ return IS_QUERY_RELAXED[c];
++ } catch (ArrayIndexOutOfBoundsException ex) {
++ return false;
++ }
++ }
++
++
+ public static String unquote(String input) {
+ if (input == null || input.length() < 2) {
+ return input;
+@@ -329,27 +435,53 @@ public class HttpParser {
+
+
+ public static boolean isNotRequestTarget(int c) {
+- // Fast for valid request target characters, slower for some incorrect
++ return DEFAULT.isNotRequestTargetRelaxed(c);
++ }
++
++
++ public static boolean isHttpProtocol(int c) {
++ // Fast for valid HTTP protocol characters, slower for some incorrect
+ // ones
+ try {
+- return IS_NOT_REQUEST_TARGET[c];
++ return IS_HTTP_PROTOCOL[c];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+- return true;
++ return false;
+ }
+ }
+
+
+- public static boolean isHttpProtocol(int c) {
+- // Fast for valid HTTP protocol characters, slower for some incorrect
++ public static boolean isUserInfo(int c) {
++ // Fast for valid user info characters, slower for some incorrect
+ // ones
+ try {
+- return IS_HTTP_PROTOCOL[c];
++ return IS_USERINFO[c];
++ } catch (ArrayIndexOutOfBoundsException ex) {
++ return false;
++ }
++ }
++
++
++ private static boolean isRelaxable(int c) {
++ // Fast for valid user info characters, slower for some incorrect
++ // ones
++ try {
++ return IS_RELAXABLE[c];
+ } catch (ArrayIndexOutOfBoundsException ex) {
+ return false;
+ }
+ }
+
+
++ public static boolean isAbsolutePath(int c) {
++ return DEFAULT.isAbsolutePathRelaxed(c);
++ }
++
++
++ public static boolean isQuery(int c) {
++ return DEFAULT.isQueryRelaxed(c);
++ }
++
++
+ // Skip any LWS and return the next char
+ private static int skipLws(StringReader input, boolean withReset)
+ throws IOException {
+@@ -579,6 +711,18 @@ public class HttpParser {
+ }
+ }
+
++ private void relax(boolean[] flags, String relaxedChars) {
++ if (relaxedChars != null && relaxedChars.length() > 0) {
++ char[] chars = relaxedChars.toCharArray();
++ for (char c : chars) {
++ if (isRelaxable(c)) {
++ flags[c] = true;
++ IS_NOT_REQUEST_TARGET[c] = false;
++ }
++ }
++ }
++ }
++
+ private static enum SkipConstantResult {
+ FOUND,
+ NOT_FOUND,
+diff -up conf/catalina.properties.orig conf/catalina.properties
+--- conf/catalina.properties.orig 2019-03-05 12:13:51.934329862 -0500
++++ conf/catalina.properties 2019-03-05 12:14:08.094279997 -0500
+@@ -132,6 +132,9 @@ tomcat.util.buf.StringCache.byte.enabled
+ #tomcat.util.buf.StringCache.trainThreshold=500000
+ #tomcat.util.buf.StringCache.cacheSize=5000
+
++# This system property is deprecated. Use the relaxedPathChars relaxedQueryChars
++# attributes of the Connector instead. These attributes permit a wider range of
++# characters to be configured as valid.
+ # Allow for changes to HTTP request validation
+-# WARNING: Using this option will expose the server to CVE-2016-6816
++# WARNING: Using this option may expose the server to CVE-2016-6816
+ #tomcat.util.http.parser.HttpParser.requestTargetAllow=|
+diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml
+--- webapps/docs/changelog.xml.orig 2019-03-05 12:13:47.068344877 -0500
++++ webapps/docs/changelog.xml 2019-03-05 12:14:08.103279970 -0500
+@@ -108,6 +108,12 @@
+ Improve handing of overflow in the UTF-8 decoder with supplementary
+ characters. (markt)
+
++
++ 62273: Implement configuration options to work-around
++ specification non-compliant user agents (including all the major
++ browsers) that do not correctly %nn encode URI paths and query strings
++ as required by RFC 7230 and RFC 3986. (markt)
++
+
+
+
+diff -up webapps/docs/config/http.xml.orig webapps/docs/config/http.xml
+--- webapps/docs/config/http.xml.orig 2017-03-09 08:51:43.000000000 -0500
++++ webapps/docs/config/http.xml 2019-03-05 12:14:08.103279970 -0500
+@@ -516,6 +516,32 @@
+ expected concurrent requests (synchronous and asynchronous).
+
+
++
++ The HTTP/1.1
++ specification requires that certain characters are %nn encoded when
++ used in URI paths. Unfortunately, many user agents including all the major
++ browsers are not compliant with this specification and use these
++ characters in unencoded form. To prevent Tomcat rejecting such requests,
++ this attribute may be used to specify the additional characters to allow.
++ If not specified, no addtional characters will be allowed. The value may
++ be any combination of the following characters:
++ " < > [ \ ] ^ ` { | }
. Any other characters
++ present in the value will be ignored.
++
++
++
++ The HTTP/1.1
++ specification requires that certain characters are %nn encoded when
++ used in URI query strings. Unfortunately, many user agents including all
++ the major browsers are not compliant with this specification and use these
++ characters in unencoded form. To prevent Tomcat rejecting such requests,
++ this attribute may be used to specify the additional characters to allow.
++ If not specified, no addtional characters will be allowed. The value may
++ be any combination of the following characters:
++ " < > [ \ ] ^ ` { | }
. Any other characters
++ present in the value will be ignored.
++
++
+
+ The value is a regular expression (using java.util.regex
)
+ matching the user-agent
header of HTTP clients for which
+diff -up webapps/docs/config/systemprops.xml.orig webapps/docs/config/systemprops.xml
+--- webapps/docs/config/systemprops.xml.orig 2019-03-05 12:14:08.104279967 -0500
++++ webapps/docs/config/systemprops.xml 2019-03-05 12:16:02.075928285 -0500
+@@ -709,11 +709,15 @@
+
+
+
++ This system property is deprecated. Use the
++ relaxedPathChars
and relaxedQueryChars
++ attributes of the Connector instead. These attributes permit a wider range
++ of characters to be configured as valid.
+ A string comprised of characters the server should allow even when they are not encoded.
+ These characters would normally result in a 400 status.
+ The acceptable characters for this property are: |
, {
+ , and }
+- WARNING: Use of this option will expose the server to CVE-2016-6816.
++
WARNING: Use of this option may expose the server to CVE-2016-6816.
+
+ If not specified, the default value of null
will be used.
+
+diff -up test/org/apache/catalina/core/TestApplicationContext.java.orig test/org/apache/catalina/core/TestApplicationContext.java
+--- test/org/apache/catalina/core/TestApplicationContext.java.orig 2019-03-05 12:13:51.981329717 -0500
++++ test/org/apache/catalina/core/TestApplicationContext.java 2019-03-05 12:14:08.094279997 -0500
+@@ -77,7 +77,7 @@ public class TestApplicationContext exte
+
+ ByteChunk res = new ByteChunk();
+ int rc = getUrl("http://localhost:" + getPort() +
+- "/test/bug5nnnn/bug53467].jsp", res, null);
++ "/test/bug5nnnn/bug53467%5D.jsp", res, null);
+
+ Assert.assertEquals(HttpServletResponse.SC_OK, rc);
+ Assert.assertTrue(res.toString().contains("
OK
"));
diff --git a/SOURCES/tomcat-7.0.76-rhbz-1588703.patch b/SOURCES/tomcat-7.0.76-rhbz-1588703.patch
new file mode 100644
index 0000000..3834dd2
--- /dev/null
+++ b/SOURCES/tomcat-7.0.76-rhbz-1588703.patch
@@ -0,0 +1,39 @@
+commit 7f474ba097347fbe4c3db776b46eef59a587c0aa
+Author: Coty Sutherland
+Date: Tue Sep 19 14:22:06 2017 +0000
+
+ Update fix for bug 59904 so that values less than zero are accepted instead of throwing a NegativeArraySizeException.
+
+ git-svn-id: https://svn.apache.org/repos/asf/tomcat/tc7.0.x/trunk@1808887 13f79535-47bb-0310-9956-ffa450edef68
+
+diff --git a/java/org/apache/tomcat/util/http/Cookies.java b/java/org/apache/tomcat/util/http/Cookies.java
+index 5ae71ec97c..511bbf3243 100644
+--- java/org/apache/tomcat/util/http/Cookies.java
++++ java/org/apache/tomcat/util/http/Cookies.java
+@@ -134,7 +134,7 @@ public final class Cookies {
+ }
+
+ if (cookieCount >= scookies.length) {
+- int newSize = Math.min(2*cookieCount, limit);
++ int newSize = limit > -1 ? Math.min(2*cookieCount, limit) : 2*cookieCount;
+ ServerCookie scookiesTmp[] = new ServerCookie[newSize];
+ System.arraycopy( scookies, 0, scookiesTmp, 0, cookieCount);
+ scookies=scookiesTmp;
+diff -up webapps/docs/changelog.xml.orig webapps/docs/changelog.xml
+--- webapps/docs/changelog.xml.orig 2019-03-01 13:04:17.093997197 -0500
++++ webapps/docs/changelog.xml 2019-03-01 13:05:04.168868394 -0500
+@@ -92,6 +92,14 @@
+
+
+
++
++
++
++ Update fix for 59904 so that values less than zero are accepted
++ instead of throwing a NegativeArraySizeException. (remm)
++
++
++
+
+
+
diff --git a/SOURCES/tomcat-7.0.conf b/SOURCES/tomcat-7.0.conf
index 21e1506..07a1cf2 100644
--- a/SOURCES/tomcat-7.0.conf
+++ b/SOURCES/tomcat-7.0.conf
@@ -40,9 +40,8 @@ CATALINA_TMPDIR="/var/cache/tomcat/temp"
# Run tomcat under the Java Security Manager
SECURITY_MANAGER="false"
-# Time to wait in seconds, before killing process
-# TODO(stingray): does nothing, fix.
-# SHUTDOWN_WAIT="30"
+# SHUTDOWN_WAIT has been deprecated. To change the shutdown wait time, set
+# TimeoutStopSec in tomcat.service.
# If you wish to further customize your tomcat environment,
# put your own definitions here
diff --git a/SOURCES/tomcat-7.0.sysconfig b/SOURCES/tomcat-7.0.sysconfig
index fd2274a..9167dd7 100644
--- a/SOURCES/tomcat-7.0.sysconfig
+++ b/SOURCES/tomcat-7.0.sysconfig
@@ -29,23 +29,14 @@
# Use JAVA_OPTS to set java.library.path for libtcnative.so
#JAVA_OPTS="-Djava.library.path=@@@LIBDIR@@@"
-# What user should run tomcat
-#TOMCAT_USER="tomcat"
-
# You can change your tomcat locale here
#LANG="en_US"
# Run tomcat under the Java Security Manager
#SECURITY_MANAGER="false"
-# Time to wait in seconds, before killing process
-#SHUTDOWN_WAIT="30"
-
-# Whether to annoy the user with "attempting to shut down" messages or not
-#SHUTDOWN_VERBOSE="false"
-
-# Connector port is 8080 for this tomcat instance
-#CONNECTOR_PORT="8080"
+# SHUTDOWN_WAIT has been deprecated. To change the shutdown wait time, set
+# TimeoutStopSec in tomcat.service.
# If you wish to further customize your tomcat environment,
# put your own definitions here
diff --git a/SPECS/tomcat.spec b/SPECS/tomcat.spec
index 2f01f9e..a83984e 100644
--- a/SPECS/tomcat.spec
+++ b/SPECS/tomcat.spec
@@ -95,6 +95,12 @@ Patch7: %{name}-7.0.76-CVE-2017-12617.patch
Patch8: patch.rhbz1602060
Patch9: %{name}-7.0.76-CVE-2018-1336.patch
Patch10: %{name}-7.0.76-CVE-2018-11784.patch
+Patch11: %{name}-7.0.76-CVE-2018-1304.patch
+Patch12: %{name}-7.0.76-CVE-2018-1305.patch
+Patch13: %{name}-7.0.76-CVE-2018-8014.patch
+Patch14: %{name}-7.0.76-CVE-2018-8034.patch
+Patch15: %{name}-7.0.76-rhbz-1588703.patch
+Patch16: %{name}-7.0.76-rhbz-1455483.patch
BuildArch: noarch
@@ -250,6 +256,12 @@ find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name "
%patch8 -p0
%patch9 -p0
%patch10 -p0
+%patch11 -p0
+%patch12 -p0
+%patch13 -p0
+%patch14 -p0
+%patch15 -p0
+%patch16 -p0
%{__ln_s} $(build-classpath jakarta-taglibs-core) webapps/examples/WEB-INF/lib/jstl.jar
%{__ln_s} $(build-classpath jakarta-taglibs-standard) webapps/examples/WEB-INF/lib/standard.jar
@@ -696,6 +708,13 @@ fi
%changelog
* Tue Feb 12 2019 Coty Sutherland 0:7.0.76-9
- Resolves: rhbz#1641873 CVE-2018-11784 tomcat: Open redirect in default servlet
+- Resolves: rhbz#1552375 CVE-2018-1304 tomcat: Incorrect handling of empty string URL in security constraints can lead to unintended exposure of resources
+- Resolves: rhbz#1552374 CVE-2018-1305 tomcat: Late application of security constraints can lead to resource exposure for unauthorised users
+- Resolves: rhbz#1590182 CVE-2018-8014 tomcat: Insecure defaults in CORS filter enable 'supportsCredentials' for all origins
+- Resolves: rhbz#1608609 CVE-2018-8034 tomcat: host name verification missing in WebSocket client
+- Resolves: rhbz#1588703 Backport of Negative maxCookieCount value causes exception for Tomcat
+- Resolves: rhbz#1472950 shutdown_wait option is not working for Tomcat
+- Resolves: rhbz#1455483 Add support for characters "<" and ">" to the possible whitelist values
* Fri Oct 12 2018 Coty Sutherland 0:7.0.76-8
- Resolves: rhbz#1608607 CVE-2018-1336 tomcat: A bug in the UTF 8 decoder can lead to DoS