From 6bf27bfddb487053e17a62ac60cba24588ee079e Mon Sep 17 00:00:00 2001
From: AlmaLinux RelEng Bot
Date: Tue, 23 Jun 2026 04:02:07 -0400
Subject: [PATCH] import Oracle_OSS pki-servlet-engine-9.0.50-1.el9_2.3
---
SOURCES/message-byte-conversion.patch | 483 ++++++++++++++++++++
SOURCES/rhbz-2362782.patch | 582 +++++++++++++++++++++++++
SOURCES/rhbz-2406591.patch | 258 +++++++++++
SOURCES/tomcat-9.0-digest.script | 0
SOURCES/tomcat-9.0-tool-wrapper.script | 0
SOURCES/tomcat-9.0.wrapper | 0
SOURCES/tomcat-functions | 0
SOURCES/tomcat-preamble | 0
SOURCES/tomcat-server | 0
SPECS/pki-servlet-engine.spec | 14 +-
10 files changed, 1336 insertions(+), 1 deletion(-)
create mode 100644 SOURCES/message-byte-conversion.patch
create mode 100644 SOURCES/rhbz-2362782.patch
create mode 100644 SOURCES/rhbz-2406591.patch
mode change 100644 => 100755 SOURCES/tomcat-9.0-digest.script
mode change 100644 => 100755 SOURCES/tomcat-9.0-tool-wrapper.script
mode change 100644 => 100755 SOURCES/tomcat-9.0.wrapper
mode change 100644 => 100755 SOURCES/tomcat-functions
mode change 100644 => 100755 SOURCES/tomcat-preamble
mode change 100644 => 100755 SOURCES/tomcat-server
diff --git a/SOURCES/message-byte-conversion.patch b/SOURCES/message-byte-conversion.patch
new file mode 100644
index 0000000..3e32c21
--- /dev/null
+++ b/SOURCES/message-byte-conversion.patch
@@ -0,0 +1,483 @@
+--- java/org/apache/catalina/core/ApplicationContext.java
++++ java/org/apache/catalina/core/ApplicationContext.java
+@@ -465,6 +465,7 @@
+
+ try {
+ // Map the URI
++ uriMB.setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
+ CharChunk uriCC = uriMB.getCharChunk();
+ try {
+ uriCC.append(context.getPath());
+--- java/org/apache/catalina/mapper/Mapper.java
++++ java/org/apache/catalina/mapper/Mapper.java
+@@ -695,6 +695,7 @@
+ if (defaultHostName == null) {
+ return;
+ }
++ host.setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
+ host.getCharChunk().append(defaultHostName);
+ }
+ host.toChars();
+--- java/org/apache/catalina/valves/rewrite/RewriteValve.java
++++ java/org/apache/catalina/valves/rewrite/RewriteValve.java
+@@ -492,49 +492,39 @@
+ contextPath = request.getContextPath();
+ }
+ // Populated the encoded (i.e. undecoded) requestURI
+- request.getCoyoteRequest().requestURI().setString(null);
++ request.getCoyoteRequest().requestURI().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
+ CharChunk chunk = request.getCoyoteRequest().requestURI().getCharChunk();
+- chunk.recycle();
+ if (context) {
+ // This is neither decoded nor normalized
+ chunk.append(contextPath);
+ }
+ chunk.append(URLEncoder.DEFAULT.encode(urlStringDecoded, uriCharset));
+- request.getCoyoteRequest().requestURI().toChars();
+ // Decoded and normalized URI
+ // Rewriting may have denormalized the URL
+ urlStringDecoded = RequestUtil.normalize(urlStringDecoded);
+- request.getCoyoteRequest().decodedURI().setString(null);
++ request.getCoyoteRequest().decodedURI().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
+ chunk = request.getCoyoteRequest().decodedURI().getCharChunk();
+- chunk.recycle();
+ if (context) {
+ // This is decoded and normalized
+ chunk.append(request.getServletContext().getContextPath());
+ }
+ chunk.append(urlStringDecoded);
+- request.getCoyoteRequest().decodedURI().toChars();
+ // Set the new Query if there is one
+ if (queryStringDecoded != null) {
+- request.getCoyoteRequest().queryString().setString(null);
++ request.getCoyoteRequest().queryString().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
+ chunk = request.getCoyoteRequest().queryString().getCharChunk();
+- chunk.recycle();
+ chunk.append(URLEncoder.QUERY.encode(queryStringDecoded, uriCharset));
+ if (qsa && originalQueryStringEncoded != null &&
+ originalQueryStringEncoded.length() > 0) {
+ chunk.append('&');
+ chunk.append(originalQueryStringEncoded);
+ }
+- if (!chunk.isNull()) {
+- request.getCoyoteRequest().queryString().toChars();
+- }
+ }
+ // Set the new host if it changed
+ if (!host.equals(request.getServerName())) {
+- request.getCoyoteRequest().serverName().setString(null);
++ request.getCoyoteRequest().serverName().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
+ chunk = request.getCoyoteRequest().serverName().getCharChunk();
+- chunk.recycle();
+ chunk.append(host.toString());
+- request.getCoyoteRequest().serverName().toChars();
+ }
+ request.getMappingData().recycle();
+ // Reinvoke the whole request recursively
+--- java/org/apache/tomcat/util/buf/MessageBytes.java
++++ java/org/apache/tomcat/util/buf/MessageBytes.java
+@@ -51,6 +51,8 @@
+ was a char[] */
+ public static final int T_CHARS = 3;
+
++ public static final char[] EMPTY_CHAR_ARRAY = new char[0];
++
+ private int hashCode=0;
+ // did we compute the hashcode ?
+ private boolean hasHashCode=false;
+@@ -61,9 +63,6 @@
+
+ // String
+ private String strValue;
+- // true if a String value was computed. Probably not needed,
+- // strValue!=null is the same
+- private boolean hasStrValue=false;
+
+ /**
+ * Creates a new, uninitialized MessageBytes object.
+@@ -87,7 +86,7 @@
+ }
+
+ public boolean isNull() {
+- return byteC.isNull() && charC.isNull() && !hasStrValue;
++ return type == T_NULL;
+ }
+
+ /**
+@@ -100,7 +99,6 @@
+
+ strValue=null;
+
+- hasStrValue=false;
+ hasHashCode=false;
+ hasLongValue=false;
+ }
+@@ -116,7 +114,6 @@
+ public void setBytes(byte[] b, int off, int len) {
+ byteC.setBytes( b, off, len );
+ type=T_BYTES;
+- hasStrValue=false;
+ hasHashCode=false;
+ hasLongValue=false;
+ }
+@@ -131,7 +128,6 @@
+ public void setChars( char[] c, int off, int len ) {
+ charC.setChars( c, off, len );
+ type=T_CHARS;
+- hasStrValue=false;
+ hasHashCode=false;
+ hasLongValue=false;
+ }
+@@ -141,15 +137,13 @@
+ * @param s The string
+ */
+ public void setString( String s ) {
+- strValue=s;
+- hasHashCode=false;
+- hasLongValue=false;
++ strValue = s;
++ hasHashCode = false;
++ hasLongValue = false;
+ if (s == null) {
+- hasStrValue=false;
+- type=T_NULL;
++ type = T_NULL;
+ } else {
+- hasStrValue=true;
+- type=T_STR;
++ type = T_STR;
+ }
+ }
+
+@@ -161,21 +155,22 @@
+ */
+ @Override
+ public String toString() {
+- if (hasStrValue) {
+- return strValue;
+- }
+-
+- switch (type) {
+- case T_CHARS:
+- strValue = charC.toString();
+- hasStrValue = true;
+- return strValue;
+- case T_BYTES:
+- strValue = byteC.toString();
+- hasStrValue = true;
+- return strValue;
+- }
+- return null;
++ switch (type) {
++ case T_NULL:
++ case T_STR:
++ // No conversion required
++ break;
++ case T_BYTES:
++ type = T_STR;
++ strValue = byteC.toString();
++ break;
++ case T_CHARS:
++ type = T_STR;
++ strValue = charC.toString();
++ break;
++ }
++
++ return strValue;
+ }
+
+ //----------------------------------------
+@@ -232,21 +227,26 @@
+
+
+ /**
+- * Do a char->byte conversion.
++ * Convert to bytes and fill the ByteChunk with the converted value.
+ */
+ public void toBytes() {
+- if (isNull()) {
+- return;
+- }
+- if (!byteC.isNull()) {
+- type = T_BYTES;
+- return;
+- }
+- toString();
+- type = T_BYTES;
+- Charset charset = byteC.getCharset();
+- ByteBuffer result = charset.encode(strValue);
+- byteC.setBytes(result.array(), result.arrayOffset(), result.limit());
++ switch (type) {
++ case T_NULL:
++ byteC.recycle();
++ //$FALL-THROUGH$
++ case T_BYTES:
++ // No conversion required
++ return;
++ case T_CHARS:
++ toString();
++ //$FALL-THROUGH$
++ case T_STR: {
++ type = T_BYTES;
++ Charset charset = byteC.getCharset();
++ ByteBuffer result = charset.encode(strValue);
++ byteC.setBytes(result.array(), result.arrayOffset(), result.limit());
++ }
++ }
+ }
+
+
+@@ -255,18 +255,22 @@
+ * XXX Not optimized - it converts to String first.
+ */
+ public void toChars() {
+- if (isNull()) {
+- return;
+- }
+- if (!charC.isNull()) {
+- type = T_CHARS;
+- return;
+- }
+- // inefficient
+- toString();
+- type = T_CHARS;
+- char cc[] = strValue.toCharArray();
+- charC.setChars(cc, 0, cc.length);
++ switch (type) {
++ case T_NULL:
++ charC.recycle();
++ //$FALL-THROUGH$
++ case T_CHARS:
++ // No conversion required
++ return;
++ case T_BYTES:
++ toString();
++ //$FALL-THROUGH$
++ case T_STR: {
++ type = T_CHARS;
++ char cc[] = strValue.toCharArray();
++ charC.setChars(cc, 0, cc.length);
++ }
++ }
+ }
+
+
+@@ -535,7 +539,6 @@
+ end--;
+ }
+ longValue=l;
+- hasStrValue=false;
+ hasHashCode=false;
+ hasLongValue=true;
+ type=T_BYTES;
+---
++++ test/org/apache/tomcat/util/buf/TestMessageBytesConversion.java
+@@ -0,0 +1,207 @@
++/*
++ * Licensed to the Apache Software Foundation (ASF) under one or more
++ * contributor license agreements. See the NOTICE file distributed with
++ * this work for additional information regarding copyright ownership.
++ * The ASF licenses this file to You under the Apache License, Version 2.0
++ * (the "License"); you may not use this file except in compliance with
++ * the License. You may obtain a copy of the License at
++ *
++ * http://www.apache.org/licenses/LICENSE-2.0
++ *
++ * Unless required by applicable law or agreed to in writing, software
++ * distributed under the License is distributed on an "AS IS" BASIS,
++ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
++ * See the License for the specific language governing permissions and
++ * limitations under the License.
++ */
++package org.apache.tomcat.util.buf;
++
++import java.nio.charset.StandardCharsets;
++import java.util.ArrayList;
++import java.util.Collection;
++import java.util.List;
++import java.util.function.Consumer;
++
++import org.junit.Assert;
++import org.junit.Test;
++import org.junit.runner.RunWith;
++import org.junit.runners.Parameterized;
++import org.junit.runners.Parameterized.Parameter;
++import org.junit.runners.Parameterized.Parameters;
++
++/**
++ * Checks that all MessageBytes getters are consistent with most recently used
++ * setter.
++ */
++@RunWith(Parameterized.class)
++public class TestMessageBytesConversion {
++
++ private static final String PREVIOUS_STRING = "previous-string";
++ private static final byte[] PREVIOUS_BYTES = "previous-bytes".getBytes(StandardCharsets.ISO_8859_1);
++ private static final char[] PREVIOUS_CHARS = "previous-chars".toCharArray();
++
++ private static final String EXPECTED_STRING = "expected";
++ private static final byte[] EXPECTED_BYTES = "expected".getBytes(StandardCharsets.ISO_8859_1);
++ private static final char[] EXPECTED_CHARS = "expected".toCharArray();
++
++ @Parameters(name = "{index}: previous({0}, {1}, {2}, {3}), set {4}, check({5}, {6}, {7}")
++ public static Collection
+
+
diff --git a/SOURCES/rhbz-2406591.patch b/SOURCES/rhbz-2406591.patch
new file mode 100644
index 0000000..854c931
--- /dev/null
+++ b/SOURCES/rhbz-2406591.patch
@@ -0,0 +1,258 @@
+--- java/org/apache/catalina/valves/rewrite/RewriteValve.java
++++ java/org/apache/catalina/valves/rewrite/RewriteValve.java
+@@ -326,9 +326,8 @@
+
+ // As long as MB isn't a char sequence or affiliated, this has to be converted to a string
+ Charset uriCharset = request.getConnector().getURICharset();
+- String originalQueryStringEncoded = request.getQueryString();
+- MessageBytes urlMB =
+- context ? request.getRequestPathMB() : request.getDecodedRequestURIMB();
++ String queryStringOriginalEncoded = request.getQueryString();
++ MessageBytes urlMB = context ? request.getRequestPathMB() : request.getDecodedRequestURIMB();
+ urlMB.toChars();
+ CharSequence urlDecoded = urlMB.getCharChunk();
+
+@@ -425,10 +424,10 @@
+ StringBuilder urlStringEncoded =
+ new StringBuilder(REWRITE_DEFAULT_ENCODER.encode(urlStringRewriteEncoded, uriCharset));
+
+- if (!qsd && originalQueryStringEncoded != null && !originalQueryStringEncoded.isEmpty()) {
++ if (!qsd && queryStringOriginalEncoded != null && !queryStringOriginalEncoded.isEmpty()) {
+ if (rewrittenQueryStringRewriteEncoded == null) {
+ urlStringEncoded.append('?');
+- urlStringEncoded.append(originalQueryStringEncoded);
++ urlStringEncoded.append(queryStringOriginalEncoded);
+ } else {
+ if (qsa) {
+ // if qsa is specified append the query
+@@ -436,7 +435,7 @@
+ urlStringEncoded.append(
+ REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset));
+ urlStringEncoded.append('&');
+- urlStringEncoded.append(originalQueryStringEncoded);
++ urlStringEncoded.append(queryStringOriginalEncoded);
+ } else if (index == urlStringEncoded.length() - 1) {
+ // if the ? is the last character delete it, its only purpose was to
+ // prevent the rewrite module from appending the query string
+@@ -550,24 +549,31 @@
+
+ // Step 3. Complete the 2nd stage to encoding.
+ chunk.append(REWRITE_DEFAULT_ENCODER.encode(urlStringRewriteEncoded, uriCharset));
+- // Decoded and normalized URI
+- // Rewriting may have denormalized the URL
+- urlStringRewriteEncoded = RequestUtil.normalize(urlStringRewriteEncoded);
++ // Rewriting may have denormalized the URL and added encoded characters
++ // Decode then normalize
++ String urlStringRewriteDecoded = URLDecoder.decode(urlStringRewriteEncoded, String.valueOf(uriCharset));
++ urlStringRewriteDecoded = RequestUtil.normalize(urlStringRewriteDecoded);
+ request.getCoyoteRequest().decodedURI().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
+ chunk = request.getCoyoteRequest().decodedURI().getCharChunk();
+ if (context) {
+ // This is decoded and normalized
+ chunk.append(request.getServletContext().getContextPath());
+ }
+- chunk.append(URLDecoder.decode(urlStringRewriteEncoded, String.valueOf(uriCharset)));
+- // Set the new Query if there is one
+- if (queryStringRewriteEncoded != null) {
++ chunk.append(urlStringRewriteDecoded);
++ // Set the new Query String
++ if (queryStringRewriteEncoded == null) {
++ // No new query string. Therefore the original is retained unless QSD is defined.
++ if (qsd) {
++ request.getCoyoteRequest().queryString().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
++ }
++ } else {
++ // New query string. Therefore the original is dropped unless QSA is defined (and QSD is not).
+ request.getCoyoteRequest().queryString().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
+ chunk = request.getCoyoteRequest().queryString().getCharChunk();
+ chunk.append(REWRITE_QUERY_ENCODER.encode(queryStringRewriteEncoded, uriCharset));
+- if (qsa && originalQueryStringEncoded != null && !originalQueryStringEncoded.isEmpty()) {
++ if (qsa && queryStringOriginalEncoded != null && !queryStringOriginalEncoded.isEmpty()) {
+ chunk.append('&');
+- chunk.append(originalQueryStringEncoded);
++ chunk.append(queryStringOriginalEncoded);
+ }
+ }
+ // Set the new host if it changed
+@@ -652,6 +658,10 @@
+ StringTokenizer flagsTokenizer = new StringTokenizer(flags, ",");
+ while (flagsTokenizer.hasMoreElements()) {
+ parseRuleFlag(line, rule, flagsTokenizer.nextToken());
++ }
++ // If QSD and QSA are present, QSD always takes precedence
++ if (rule.isQsdiscard()) {
++ rule.setQsappend(false);
+ }
+ }
+ return rule;
+--- test/org/apache/catalina/startup/TomcatBaseTest.java
++++ test/org/apache/catalina/startup/TomcatBaseTest.java
+@@ -551,7 +551,7 @@
+ value.append(";");
+ }
+ }
+- out.println("PARAM/" + name + ": " + value);
++ out.println("PARAM:" + name + ": " + value);
+ }
+
+ out.println("SESSION-REQUESTED-ID: " +
+--- test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
++++ test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
+@@ -180,17 +180,112 @@
+ }
+
+ @Test
+- public void testQueryString() throws Exception {
++ public void testQueryStringTargetOnly() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2", "/b/id=1", "/c/id=1", "je=2");
++ }
++
++ @Test
++ public void testQueryStringTargetOnlyQSA() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2 [QSA]", "/b/id=1", "/c/id=1", "je=2");
++ }
++
++ @Test
++ public void testQueryStringTargetOnlyQSD() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2 [QSD]", "/b/id=1", "/c/id=1", "je=2");
++ }
++
++ @Test
++ public void testQueryStringTargetOnlyQSAQSD() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?je=2 [QSA,QSD]", "/b/id=1", "/c/id=1", "je=2");
++ }
++
++ @Test
++ public void testQueryStringTargetOnlyQS() throws Exception {
+ doTestRewrite("RewriteRule ^/b/(.*) /c?$1", "/b/id=1", "/c", "id=1");
+ }
+
+ @Test
++ public void testQueryStringTargetOnlyQSAQS() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c?$1 [QSA]", "/b/id=1", "/c", "id=1");
++ }
++
++ @Test
++ public void testQueryStringTargetOnlyQSDQS() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c?$1 [QSD]", "/b/id=1", "/c", "id=1");
++ }
++
++ @Test
++ public void testQueryStringTargetOnlyQSAQSDQS() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c?$1 [QSA,QSD]", "/b/id=1", "/c", "id=1");
++ }
++
++ @Test
++ public void testQueryStringSourceOnly() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1", "/b/d?id=1", "/c/d", "id=1");
++ }
++
++ @Test
++ public void testQueryStringSourceOnlyQSA() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSA]", "/b/d?id=1", "/c/d", "id=1");
++ }
++
++ @Test
++ public void testQueryStringSourceOnlyQSD() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSD]", "/b/d?id=1", "/c/d", null);
++ }
++
++ @Test
++ public void testQueryStringSourceOnlyQSAQSD() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSA,QSD]", "/b/d?id=1", "/c/d", null);
++ }
++
++ @Test
++ public void testQueryStringSourceAndTarget() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1", "/b/d?je=2", "/c/d", "id=1");
++ }
++
++ @Test
++ public void testQueryStringSourceAndTargetQSA() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1 [QSA]", "/b/d?je=2", "/c/d", "id=1&je=2");
++ }
++
++ @Test
++ public void testQueryStringSourceAndTargetQSD() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1 [QSD]", "/b/d?je=2", "/c/d", "id=1");
++ }
++
++ @Test
++ public void testQueryStringSourceAndTargetQSAQSD() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?id=1 [QSA,QSD]", "/b/d?je=2", "/c/d", "id=1");
++ }
++
++ @Test
++ public void testQueryStringEncoded01() throws Exception {
++ doTestRewrite("RewriteCond %{QUERY_STRING} a=(.*)\nRewriteRule ^/b.*$ /%1 [QSD]", "/b?a=c", "/c", null);
++ }
++
++ @Test
++ public void testQueryStringEncoded02() throws Exception {
++ doTestRewrite("RewriteCond %{QUERY_STRING} a=(.*)\nRewriteRule ^/b.*$ /z/%1 [QSD]", "/b?a=%2e%2e%2fc%2faAbB", "/z/%2e%2e%2fc%2faAbB", null);
++ }
++
++ @Test
+ public void testQueryStringRemove() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?", "/b/d?id=1", "/c/d", null);
++ }
++
++ @Test
++ public void testQueryStringRemove02() throws Exception {
++ doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSD]", "/b/d?id=1", "/c/d", null);
++ }
++
++ @Test
++ public void testQueryStringRemoveInvalid() throws Exception {
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1?", "/b/d?=1", "/c/d", null);
+ }
+
+ @Test
+- public void testQueryStringRemove02() throws Exception {
++ public void testQueryStringRemoveInvalid02() throws Exception {
+ doTestRewrite("RewriteRule ^/b/(.*) /c/$1 [QSD]", "/b/d?=1", "/c/d", null);
+ }
+
+@@ -511,9 +606,8 @@
+ @Test
+ public void testFlagsNC() throws Exception {
+ // https://bz.apache.org/bugzilla/show_bug.cgi?id=60116
+- doTestRewrite("RewriteCond %{QUERY_STRING} a=([a-z]*) [NC]\n"
+- + "RewriteRule .* - [E=X-Test:%1]",
+- "/c?a=aAa", "/c", null, "aAa");
++ doTestRewrite("RewriteCond %{QUERY_STRING} a=([a-z]*) [NC]\n" + "RewriteRule .* - [E=X-Test:%1]", "/c?a=aAa",
++ "/c", "a=aAa", "aAa");
+ }
+
+
+@@ -669,12 +763,16 @@
+ // were written into the request target
+ Assert.assertEquals(400, rc);
+ } else {
++ // If there is an expected URI, the request should be successful
++ Assert.assertEquals(200, rc);
+ String body = res.toString();
+ RequestDescriptor requestDesc = SnoopResult.parse(body);
+ String requestURI = requestDesc.getRequestInfo("REQUEST-URI");
+ Assert.assertEquals(expectedURI, requestURI);
+
+- if (expectedQueryString != null) {
++ if (expectedQueryString == null) {
++ Assert.assertTrue(requestDesc.getParams().isEmpty());
++ } else {
+ String queryString = requestDesc.getRequestInfo("REQUEST-QUERY-STRING");
+ Assert.assertEquals(expectedQueryString, queryString);
+ }
+--- webapps/docs/changelog.xml
++++ webapps/docs/changelog.xml
+@@ -113,6 +113,10 @@
+ /WEB-INF/lib when running a web application from a packed
+ WAR file. (markt)
+
++
++ Fix handling of QSA and QSD flags in
++ RewriteValve. (markt)
++
+
+
+
diff --git a/SOURCES/tomcat-9.0-digest.script b/SOURCES/tomcat-9.0-digest.script
old mode 100644
new mode 100755
diff --git a/SOURCES/tomcat-9.0-tool-wrapper.script b/SOURCES/tomcat-9.0-tool-wrapper.script
old mode 100644
new mode 100755
diff --git a/SOURCES/tomcat-9.0.wrapper b/SOURCES/tomcat-9.0.wrapper
old mode 100644
new mode 100755
diff --git a/SOURCES/tomcat-functions b/SOURCES/tomcat-functions
old mode 100644
new mode 100755
diff --git a/SOURCES/tomcat-preamble b/SOURCES/tomcat-preamble
old mode 100644
new mode 100755
diff --git a/SOURCES/tomcat-server b/SOURCES/tomcat-server
old mode 100644
new mode 100755
diff --git a/SPECS/pki-servlet-engine.spec b/SPECS/pki-servlet-engine.spec
index a0623d2..1029487 100644
--- a/SPECS/pki-servlet-engine.spec
+++ b/SPECS/pki-servlet-engine.spec
@@ -58,7 +58,7 @@
Name: pki-servlet-engine
Epoch: 1
Version: %{major_version}.%{minor_version}.%{micro_version}
-Release: 1%{?dist}.2
+Release: 1%{?dist}.3
Summary: Apache Servlet/JSP Engine, RI for Servlet %{servletspec}/JSP %{jspspec} API
Group: System Environment/Daemons
License: ASL 2.0
@@ -83,6 +83,9 @@ Patch2: tomcat-%{major_version}.%{minor_version}-catalina-policy.patch
Patch3: exclude-OSGi-metadata.patch
Patch4: rhbz-2314686.patch
Patch5: rhbz-2332817.patch
+Patch6: message-byte-conversion.patch
+Patch7: rhbz-2362782.patch
+Patch8: rhbz-2406591.patch
BuildArch: noarch
@@ -147,6 +150,9 @@ find . -type f \( -name "*.bat" -o -name "*.class" -o -name Thumbs.db -o -name "
%patch3 -p0
%patch4 -p0
%patch5 -p0
+%patch6 -p0
+%patch7 -p0
+%patch8 -p0
# Since we don't support ECJ in RHEL anymore, remove the class that requires it
%{__rm} -f java/org/apache/jasper/compiler/JDTCompiler.java
@@ -389,6 +395,12 @@ fi
%{_javadir}/tomcat-servlet-%{servletspec}*.jar
%changelog
+* Wed Dec 10 2025 Adam Krajcik - 1:9.0.50-1.el9_2.3
+- Resolves: RHEL-124509
+ pki-servlet-engine: Directory traversal via rewrite with possible RCE (CVE-2025-55752)
+- Resolves: RHEL-91744
+ pki-servlet-engine: Bypass of rules in Rewrite Valve (CVE-2025-31651)
+
* Fri Jan 17 2025 Dimitris Soumis - 1:9.0.50-1.el9_2.2
- Resolves: RHEL-71715
pki-servlet-engine: RCE due to TOCTOU issue in JSP compilation (CVE-2024-50379)