import Oracle_OSS pki-servlet-engine-9.0.50-1.el9_2.3
This commit is contained in:
parent
70450851a4
commit
6bf27bfddb
483
SOURCES/message-byte-conversion.patch
Normal file
483
SOURCES/message-byte-conversion.patch
Normal file
@ -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;
|
||||
--- <unnamed>
|
||||
+++ 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<Object[]> parameters() {
|
||||
+ List<MessageBytesType> previousTypes = new ArrayList<>();
|
||||
+ previousTypes.add(MessageBytesType.BYTE);
|
||||
+ previousTypes.add(MessageBytesType.CHAR);
|
||||
+ previousTypes.add(MessageBytesType.STRING);
|
||||
+ previousTypes.add(MessageBytesType.NULL);
|
||||
+
|
||||
+ List<MessageBytesType> setTypes = new ArrayList<>();
|
||||
+ setTypes.add(MessageBytesType.BYTE);
|
||||
+ setTypes.add(MessageBytesType.CHAR);
|
||||
+ setTypes.add(MessageBytesType.STRING);
|
||||
+
|
||||
+ List<Object[]> parameterSets = new ArrayList<>();
|
||||
+
|
||||
+ List<List<MessageBytesType>> previousPermutations = permutations(previousTypes);
|
||||
+ List<List<MessageBytesType>> checkPermutations = permutations(setTypes);
|
||||
+
|
||||
+ for (List<MessageBytesType> setPermutation : previousPermutations) {
|
||||
+ for (MessageBytesType setType : setTypes) {
|
||||
+ for (List<MessageBytesType> checkPermutation : checkPermutations) {
|
||||
+ parameterSets.add(new Object[] {
|
||||
+ setPermutation.get(0), setPermutation.get(1), setPermutation.get(2), setPermutation.get(3),
|
||||
+ setType,
|
||||
+ checkPermutation.get(0), checkPermutation.get(1), checkPermutation.get(2)});
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ return parameterSets;
|
||||
+ }
|
||||
+
|
||||
+ @Parameter(0)
|
||||
+ public MessageBytesType setFirst;
|
||||
+ @Parameter(1)
|
||||
+ public MessageBytesType setSecond;
|
||||
+ @Parameter(2)
|
||||
+ public MessageBytesType setThird;
|
||||
+ @Parameter(3)
|
||||
+ public MessageBytesType setFourth;
|
||||
+
|
||||
+ @Parameter(4)
|
||||
+ public MessageBytesType expected;
|
||||
+
|
||||
+ @Parameter(5)
|
||||
+ public MessageBytesType checkFirst;
|
||||
+ @Parameter(6)
|
||||
+ public MessageBytesType checkSecond;
|
||||
+ @Parameter(7)
|
||||
+ public MessageBytesType checkThird;
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testConversion() {
|
||||
+ MessageBytes mb = MessageBytes.newInstance();
|
||||
+
|
||||
+ setFirst.setPrevious(mb);
|
||||
+ setSecond.setPrevious(mb);
|
||||
+ setThird.setPrevious(mb);
|
||||
+ setFourth.setPrevious(mb);
|
||||
+
|
||||
+ expected.setExpected(mb);
|
||||
+
|
||||
+ checkFirst.checkExpected(mb);
|
||||
+ checkSecond.checkExpected(mb);
|
||||
+ checkThird.checkExpected(mb);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testConversionNull() {
|
||||
+ MessageBytes mb = MessageBytes.newInstance();
|
||||
+
|
||||
+ setFirst.setPrevious(mb);
|
||||
+ setSecond.setPrevious(mb);
|
||||
+ setThird.setPrevious(mb);
|
||||
+ setFourth.setPrevious(mb);
|
||||
+
|
||||
+ mb.setString(null);
|
||||
+
|
||||
+ checkFirst.checkNull(mb);
|
||||
+ checkSecond.checkNull(mb);
|
||||
+ checkThird.checkNull(mb);
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ public static enum MessageBytesType {
|
||||
+ BYTE((x) -> x.setBytes(PREVIOUS_BYTES, 0, PREVIOUS_BYTES.length),
|
||||
+ (x) -> x.setBytes(EXPECTED_BYTES, 0, EXPECTED_BYTES.length),
|
||||
+ (x) -> {x.toBytes(); Assert.assertArrayEquals(EXPECTED_BYTES, x.getByteChunk().getBytes());},
|
||||
+ (x) -> {x.toBytes(); Assert.assertTrue(x.getByteChunk().isNull());}),
|
||||
+
|
||||
+ CHAR((x) -> x.setChars(PREVIOUS_CHARS, 0, PREVIOUS_CHARS.length),
|
||||
+ (x) -> x.setChars(EXPECTED_CHARS, 0, EXPECTED_CHARS.length),
|
||||
+ (x) -> {x.toChars(); Assert.assertArrayEquals(EXPECTED_CHARS, x.getCharChunk().getChars());},
|
||||
+ (x) -> {x.toChars(); Assert.assertTrue(x.getCharChunk().isNull());}),
|
||||
+
|
||||
+ STRING((x) -> x.setString(PREVIOUS_STRING),
|
||||
+ (x) -> x.setString(EXPECTED_STRING),
|
||||
+ (x) -> Assert.assertEquals(EXPECTED_STRING, x.toString()),
|
||||
+ (x) -> Assert.assertNull(x.toString())),
|
||||
+
|
||||
+ NULL((x) -> x.setString(null),
|
||||
+ (x) -> x.setString(null),
|
||||
+ (x) -> Assert.assertTrue(x.isNull()),
|
||||
+ (x) -> Assert.assertTrue(x.isNull()));
|
||||
+
|
||||
+ private final Consumer<MessageBytes> setPrevious;
|
||||
+ private final Consumer<MessageBytes> setExpected;
|
||||
+ private final Consumer<MessageBytes> checkExpected;
|
||||
+ private final Consumer<MessageBytes> checkNull;
|
||||
+
|
||||
+ private MessageBytesType(Consumer<MessageBytes> setPrevious, Consumer<MessageBytes> setExpected,
|
||||
+ Consumer<MessageBytes> checkExpected, Consumer<MessageBytes> checkNull) {
|
||||
+ this.setPrevious = setPrevious;
|
||||
+ this.setExpected = setExpected;
|
||||
+ this.checkExpected = checkExpected;
|
||||
+ this.checkNull = checkNull;
|
||||
+ }
|
||||
+
|
||||
+ public void setPrevious(MessageBytes mb) {
|
||||
+ setPrevious.accept(mb);
|
||||
+ }
|
||||
+
|
||||
+ public void setExpected(MessageBytes mb) {
|
||||
+ setExpected.accept(mb);
|
||||
+ }
|
||||
+
|
||||
+ public void checkExpected(MessageBytes mb) {
|
||||
+ checkExpected.accept(mb);
|
||||
+ }
|
||||
+
|
||||
+ public void checkNull(MessageBytes mb) {
|
||||
+ checkNull.accept(mb);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private static <T> List<List<T>> permutations(List<T> items) {
|
||||
+ List<List<T>> results = new ArrayList<>();
|
||||
+
|
||||
+ if (items.size() == 1) {
|
||||
+ results.add(items);
|
||||
+ } else {
|
||||
+ List<T> others = new ArrayList<>(items);
|
||||
+ T first = others.remove(0);
|
||||
+ List<List<T>> subPermutations = permutations(others);
|
||||
+
|
||||
+ for (List<T> subPermutation : subPermutations) {
|
||||
+ for (int i = 0; i <= subPermutation.size(); i++) {
|
||||
+ List<T> result = new ArrayList<>(subPermutation);
|
||||
+ result.add(i, first);
|
||||
+ results.add(result);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ return results;
|
||||
+ }
|
||||
+}
|
||||
|
||||
582
SOURCES/rhbz-2362782.patch
Normal file
582
SOURCES/rhbz-2362782.patch
Normal file
@ -0,0 +1,582 @@
|
||||
--- java/org/apache/catalina/connector/CoyoteAdapter.java
|
||||
+++ java/org/apache/catalina/connector/CoyoteAdapter.java
|
||||
@@ -663,21 +663,19 @@
|
||||
response.sendError(400, "Invalid URI");
|
||||
}
|
||||
} else {
|
||||
- /* The URI is chars or String, and has been sent using an in-memory
|
||||
- * protocol handler. The following assumptions are made:
|
||||
- * - req.requestURI() has been set to the 'original' non-decoded,
|
||||
- * non-normalized URI
|
||||
- * - req.decodedURI() has been set to the decoded, normalized form
|
||||
- * of req.requestURI()
|
||||
+ /*
|
||||
+ * The URI is chars or String, and has been sent using an in-memory protocol handler. The following
|
||||
+ * assumptions are made:
|
||||
+ *
|
||||
+ * - req.requestURI() has been set to the 'original' non-decoded, non-normalized URI that includes path
|
||||
+ * parameters (if any)
|
||||
+ *
|
||||
+ * - req.decodedURI() has been set to the decoded, normalized form of req.requestURI() with any path
|
||||
+ * parameters removed
|
||||
+ *
|
||||
+ * - 'suspicious' URI filtering, if required, has already been performed
|
||||
*/
|
||||
decodedURI.toChars();
|
||||
- // Remove all path parameters; any needed path parameter should be set
|
||||
- // using the request object rather than passing it in the URL
|
||||
- CharChunk uriCC = decodedURI.getCharChunk();
|
||||
- int semicolon = uriCC.indexOf(';');
|
||||
- if (semicolon > 0) {
|
||||
- decodedURI.setChars(uriCC.getBuffer(), uriCC.getStart(), semicolon);
|
||||
- }
|
||||
}
|
||||
|
||||
// Request mapping.
|
||||
--- java/org/apache/catalina/valves/rewrite/RewriteValve.java
|
||||
+++ java/org/apache/catalina/valves/rewrite/RewriteValve.java
|
||||
@@ -21,6 +21,7 @@
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringReader;
|
||||
+import java.net.URLDecoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
@@ -68,6 +69,24 @@
|
||||
*/
|
||||
public class RewriteValve extends ValveBase {
|
||||
|
||||
+ private static final URLEncoder REWRITE_DEFAULT_ENCODER;
|
||||
+ private static final URLEncoder REWRITE_QUERY_ENCODER;
|
||||
+
|
||||
+ static {
|
||||
+ /*
|
||||
+ * See the detailed explanation of encoding/decoding during URL re-writing in the invoke() method.
|
||||
+ *
|
||||
+ * These encoders perform the second stage of encoding, after re-writing has completed. These rewrite specific
|
||||
+ * encoders treat '%' as a safe character so that URLs and query strings already processed by encodeForRewrite()
|
||||
+ * do not end up with double encoding of '%' characters.
|
||||
+ */
|
||||
+ REWRITE_DEFAULT_ENCODER = (URLEncoder) URLEncoder.DEFAULT.clone();
|
||||
+ REWRITE_DEFAULT_ENCODER.addSafeCharacter('%');
|
||||
+
|
||||
+ REWRITE_QUERY_ENCODER = (URLEncoder) URLEncoder.QUERY.clone();
|
||||
+ REWRITE_QUERY_ENCODER.addSafeCharacter('%');
|
||||
+ }
|
||||
+
|
||||
/**
|
||||
* The rewrite rules that the valve will use.
|
||||
*/
|
||||
@@ -305,22 +324,51 @@
|
||||
|
||||
invoked.set(Boolean.TRUE);
|
||||
|
||||
- // As long as MB isn't a char sequence or affiliated, this has to be
|
||||
- // converted to a string
|
||||
+ // 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();
|
||||
urlMB.toChars();
|
||||
CharSequence urlDecoded = urlMB.getCharChunk();
|
||||
+
|
||||
+ /*
|
||||
+ * The URL presented to the rewrite valve is the URL that is used for request mapping. That URL has been
|
||||
+ * processed to: remove path parameters; remove the query string; decode; and normalize the URL. It may
|
||||
+ * contain literal '%', '?' and/or ';' characters at this point.
|
||||
+ *
|
||||
+ * The re-write rules need to be able to process URLs with literal '?' characters and add query strings
|
||||
+ * without the two becoming confused. The re-write rules also need to be able to insert literal '%'
|
||||
+ * characters without them being confused with %nn encoding.
|
||||
+ *
|
||||
+ * The re-write rules cannot insert path parameters.
|
||||
+ *
|
||||
+ * To meet these requirement, the URL is processed as follows.
|
||||
+ *
|
||||
+ * Step 1. The URL is partially re-encoded by encodeForRewrite(). This method encodes any literal '%', ';'
|
||||
+ * and/or '?' characters in the URL using the standard %nn form.
|
||||
+ *
|
||||
+ * Step 2. The re-write processing runs with the provided re-write rules against the partially encoded URL.
|
||||
+ * If a re-write rule needs to insert a literal '%', ';' or '?', it must do so in %nn encoded form.
|
||||
+ *
|
||||
+ * Step 3. The URL (and query string if present) is re-encoded using the re-write specific encoders
|
||||
+ * (REWRITE_DEFAULT_ENCODER and REWRITE_QUERY_ENCODER) that behave the same was as the standard encoders
|
||||
+ * apart from '%' being treated as a safe character. This prevents double encoding of any '%' characters
|
||||
+ * present in the URL from steps 1 or 2.
|
||||
+ */
|
||||
+
|
||||
+ // Step 1. Encode URL for processing by the re-write rules.
|
||||
+ CharSequence urlRewriteEncoded = encodeForRewrite(urlDecoded);
|
||||
CharSequence host = request.getServerName();
|
||||
boolean rewritten = false;
|
||||
boolean done = false;
|
||||
boolean qsa = false;
|
||||
boolean qsd = false;
|
||||
+
|
||||
+ // Step 2. Process the URL using the re-write rules.
|
||||
for (int i = 0; i < rules.length; i++) {
|
||||
RewriteRule rule = rules[i];
|
||||
- CharSequence test = (rule.isHost()) ? host : urlDecoded;
|
||||
+ CharSequence test = (rule.isHost()) ? host : urlRewriteEncoded;
|
||||
CharSequence newtest = rule.evaluate(test, resolver);
|
||||
if (newtest != null && !test.equals(newtest.toString())) {
|
||||
if (containerLog.isDebugEnabled()) {
|
||||
@@ -330,7 +378,7 @@
|
||||
if (rule.isHost()) {
|
||||
host = newtest;
|
||||
} else {
|
||||
- urlDecoded = newtest;
|
||||
+ urlRewriteEncoded = newtest;
|
||||
}
|
||||
rewritten = true;
|
||||
}
|
||||
@@ -363,29 +411,30 @@
|
||||
if (rule.isRedirect() && newtest != null) {
|
||||
// Append the query string to the url if there is one and it
|
||||
// hasn't been rewritten
|
||||
- String urlStringDecoded = urlDecoded.toString();
|
||||
- int index = urlStringDecoded.indexOf('?');
|
||||
- String rewrittenQueryStringDecoded;
|
||||
+ String urlStringRewriteEncoded = urlRewriteEncoded.toString();
|
||||
+ int index = urlStringRewriteEncoded.indexOf('?');
|
||||
+ String rewrittenQueryStringRewriteEncoded;
|
||||
if (index == -1) {
|
||||
- rewrittenQueryStringDecoded = null;
|
||||
+ rewrittenQueryStringRewriteEncoded = null;
|
||||
} else {
|
||||
- rewrittenQueryStringDecoded = urlStringDecoded.substring(index + 1);
|
||||
- urlStringDecoded = urlStringDecoded.substring(0, index);
|
||||
- }
|
||||
-
|
||||
+ rewrittenQueryStringRewriteEncoded = urlStringRewriteEncoded.substring(index + 1);
|
||||
+ urlStringRewriteEncoded = urlStringRewriteEncoded.substring(0, index);
|
||||
+ }
|
||||
+
|
||||
+ // Step 3. Complete the 2nd stage to encoding.
|
||||
StringBuilder urlStringEncoded =
|
||||
- new StringBuilder(URLEncoder.DEFAULT.encode(urlStringDecoded, uriCharset));
|
||||
- if (!qsd && originalQueryStringEncoded != null
|
||||
- && originalQueryStringEncoded.length() > 0) {
|
||||
- if (rewrittenQueryStringDecoded == null) {
|
||||
+ new StringBuilder(REWRITE_DEFAULT_ENCODER.encode(urlStringRewriteEncoded, uriCharset));
|
||||
+
|
||||
+ if (!qsd && originalQueryStringEncoded != null && !originalQueryStringEncoded.isEmpty()) {
|
||||
+ if (rewrittenQueryStringRewriteEncoded == null) {
|
||||
urlStringEncoded.append('?');
|
||||
urlStringEncoded.append(originalQueryStringEncoded);
|
||||
} else {
|
||||
if (qsa) {
|
||||
// if qsa is specified append the query
|
||||
urlStringEncoded.append('?');
|
||||
- urlStringEncoded.append(URLEncoder.QUERY.encode(
|
||||
- rewrittenQueryStringDecoded, uriCharset));
|
||||
+ urlStringEncoded.append(
|
||||
+ REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset));
|
||||
urlStringEncoded.append('&');
|
||||
urlStringEncoded.append(originalQueryStringEncoded);
|
||||
} else if (index == urlStringEncoded.length() - 1) {
|
||||
@@ -394,14 +443,14 @@
|
||||
urlStringEncoded.deleteCharAt(index);
|
||||
} else {
|
||||
urlStringEncoded.append('?');
|
||||
- urlStringEncoded.append(URLEncoder.QUERY.encode(
|
||||
- rewrittenQueryStringDecoded, uriCharset));
|
||||
+ urlStringEncoded.append(
|
||||
+ REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset));
|
||||
}
|
||||
}
|
||||
- } else if (rewrittenQueryStringDecoded != null) {
|
||||
+ } else if (rewrittenQueryStringRewriteEncoded != null) {
|
||||
urlStringEncoded.append('?');
|
||||
- urlStringEncoded.append(
|
||||
- URLEncoder.QUERY.encode(rewrittenQueryStringDecoded, uriCharset));
|
||||
+ urlStringEncoded
|
||||
+ .append(REWRITE_QUERY_ENCODER.encode(rewrittenQueryStringRewriteEncoded, uriCharset));
|
||||
}
|
||||
|
||||
// Insert the context if
|
||||
@@ -479,12 +528,12 @@
|
||||
if (rewritten) {
|
||||
if (!done) {
|
||||
// See if we need to replace the query string
|
||||
- String urlStringDecoded = urlDecoded.toString();
|
||||
- String queryStringDecoded = null;
|
||||
- int queryIndex = urlStringDecoded.indexOf('?');
|
||||
+ String urlStringRewriteEncoded = urlRewriteEncoded.toString();
|
||||
+ String queryStringRewriteEncoded = null;
|
||||
+ int queryIndex = urlStringRewriteEncoded.indexOf('?');
|
||||
if (queryIndex != -1) {
|
||||
- queryStringDecoded = urlStringDecoded.substring(queryIndex+1);
|
||||
- urlStringDecoded = urlStringDecoded.substring(0, queryIndex);
|
||||
+ queryStringRewriteEncoded = urlStringRewriteEncoded.substring(queryIndex + 1);
|
||||
+ urlStringRewriteEncoded = urlStringRewriteEncoded.substring(0, queryIndex);
|
||||
}
|
||||
// Save the current context path before re-writing starts
|
||||
String contextPath = null;
|
||||
@@ -498,24 +547,25 @@
|
||||
// This is neither decoded nor normalized
|
||||
chunk.append(contextPath);
|
||||
}
|
||||
- chunk.append(URLEncoder.DEFAULT.encode(urlStringDecoded, uriCharset));
|
||||
+
|
||||
+ // 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
|
||||
- urlStringDecoded = RequestUtil.normalize(urlStringDecoded);
|
||||
+ urlStringRewriteEncoded = RequestUtil.normalize(urlStringRewriteEncoded);
|
||||
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(urlStringDecoded);
|
||||
+ chunk.append(URLDecoder.decode(urlStringRewriteEncoded, String.valueOf(uriCharset)));
|
||||
// Set the new Query if there is one
|
||||
- if (queryStringDecoded != null) {
|
||||
+ if (queryStringRewriteEncoded != null) {
|
||||
request.getCoyoteRequest().queryString().setChars(MessageBytes.EMPTY_CHAR_ARRAY, 0, 0);
|
||||
chunk = request.getCoyoteRequest().queryString().getCharChunk();
|
||||
- chunk.append(URLEncoder.QUERY.encode(queryStringDecoded, uriCharset));
|
||||
- if (qsa && originalQueryStringEncoded != null &&
|
||||
- originalQueryStringEncoded.length() > 0) {
|
||||
+ chunk.append(REWRITE_QUERY_ENCODER.encode(queryStringRewriteEncoded, uriCharset));
|
||||
+ if (qsa && originalQueryStringEncoded != null && !originalQueryStringEncoded.isEmpty()) {
|
||||
chunk.append('&');
|
||||
chunk.append(originalQueryStringEncoded);
|
||||
}
|
||||
@@ -786,4 +836,31 @@
|
||||
throw new IllegalArgumentException(sm.getString("rewriteValve.invalidFlags", line, flag));
|
||||
}
|
||||
}
|
||||
+
|
||||
+
|
||||
+ private CharSequence encodeForRewrite(CharSequence input) {
|
||||
+ StringBuilder result = null;
|
||||
+ int pos = 0;
|
||||
+ int mark = 0;
|
||||
+ while (pos < input.length()) {
|
||||
+ char c = input.charAt(pos);
|
||||
+ if (c == '%' || c == ';' || c == '?') {
|
||||
+ if (result == null) {
|
||||
+ result = new StringBuilder((int) (input.length() * 1.1));
|
||||
+ }
|
||||
+ result.append(input.subSequence(mark, pos));
|
||||
+ result.append('%');
|
||||
+ result.append(Character.forDigit((c >> 4) & 0xF, 16));
|
||||
+ result.append(Character.forDigit(c & 0xF, 16));
|
||||
+ mark = pos + 1;
|
||||
+ }
|
||||
+ pos++;
|
||||
+ }
|
||||
+ if (result != null) {
|
||||
+ result.append(input.subSequence(mark, input.length()));
|
||||
+ return result;
|
||||
+ } else {
|
||||
+ return input;
|
||||
+ }
|
||||
+ }
|
||||
}
|
||||
--- test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
|
||||
+++ test/org/apache/catalina/valves/rewrite/TestRewriteValve.java
|
||||
@@ -17,10 +17,13 @@
|
||||
package org.apache.catalina.valves.rewrite;
|
||||
|
||||
import java.net.HttpURLConnection;
|
||||
+import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
+
|
||||
+import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
@@ -50,7 +53,7 @@
|
||||
|
||||
@Test
|
||||
public void testBackslashPercentSign() throws Exception {
|
||||
- doTestRewrite("RewriteRule ^(.*) /a/\\%5A", "/", "/a/%255A");
|
||||
+ doTestRewrite("RewriteRule ^(.*) /a/\\%5A", "/", "/a/%5A");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -227,7 +230,7 @@
|
||||
public void testNonAsciiQueryStringWithB() throws Exception {
|
||||
doTestRewrite("RewriteRule ^/b/(.*)/id=(.*) /c?filename=$1&id=$2 [B]",
|
||||
"/b/file01/id=%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95", "/c",
|
||||
- "filename=file01&id=%25E5%259C%25A8%25E7%25BA%25BF%25E6%25B5%258B%25E8%25AF%2595");
|
||||
+ "filename=file01&id=%E5%9C%A8%E7%BA%BF%E6%B5%8B%E8%AF%95");
|
||||
}
|
||||
|
||||
|
||||
@@ -235,9 +238,8 @@
|
||||
public void testNonAsciiQueryStringAndPathAndRedirectWithB() throws Exception {
|
||||
// Note the double encoding of the result (httpd produces the same result)
|
||||
doTestRewrite("RewriteRule ^/b/(.*)/(.*)/id=(.*) /c/$1?filename=$2&id=$3 [B,R]",
|
||||
- "/b/%E5%9C%A8%E7%BA%BF/file01/id=%E6%B5%8B%E8%AF%95",
|
||||
- "/c/%25E5%259C%25A8%25E7%25BA%25BF",
|
||||
- "filename=file01&id=%25E6%25B5%258B%25E8%25AF%2595");
|
||||
+ "/b/%E5%9C%A8%E7%BA%BF/file01/id=%E6%B5%8B%E8%AF%95", "/c/%E5%9C%A8%E7%BA%BF",
|
||||
+ "filename=file01&id=%E6%B5%8B%E8%AF%95");
|
||||
}
|
||||
|
||||
|
||||
@@ -252,8 +254,8 @@
|
||||
@Test
|
||||
public void testUtf8WithBothQsFlagsB() throws Exception {
|
||||
// Note %C2%A1 == \u00A1
|
||||
- doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B]",
|
||||
- "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/%C2%A1%25C2%25A1", "id=%25C2%25A1");
|
||||
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B]", "/b/%C2%A1/id=%C2%A1?di=%C2%AE",
|
||||
+ "/c/%C2%A1%C2%A1", "id=%C2%A1");
|
||||
}
|
||||
|
||||
|
||||
@@ -268,8 +270,8 @@
|
||||
@Test
|
||||
public void testUtf8WithBothQsFlagsRB() throws Exception {
|
||||
// Note %C2%A1 == \u00A1
|
||||
- doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B]",
|
||||
- "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/%C2%A1%25C2%25A1", "id=%25C2%25A1");
|
||||
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B]", "/b/%C2%A1/id=%C2%A1?di=%C2%AE",
|
||||
+ "/c/%C2%A1%C2%A1", "id=%C2%A1");
|
||||
}
|
||||
|
||||
|
||||
@@ -296,9 +298,8 @@
|
||||
@Test
|
||||
public void testUtf8WithBothQsFlagsBQSA() throws Exception {
|
||||
// Note %C2%A1 == \u00A1
|
||||
- doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B,QSA]",
|
||||
- "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/%C2%A1%25C2%25A1",
|
||||
- "id=%25C2%25A1&di=%C2%AE");
|
||||
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B,QSA]", "/b/%C2%A1/id=%C2%A1?di=%C2%AE",
|
||||
+ "/c/%C2%A1%C2%A1", "id=%C2%A1&di=%C2%AE");
|
||||
}
|
||||
|
||||
|
||||
@@ -314,9 +315,8 @@
|
||||
@Test
|
||||
public void testUtf8WithBothQsFlagsRBQSA() throws Exception {
|
||||
// Note %C2%A1 == \u00A1
|
||||
- doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B,QSA]",
|
||||
- "/b/%C2%A1/id=%C2%A1?di=%C2%AE", "/c/%C2%A1%25C2%25A1",
|
||||
- "id=%25C2%25A1&di=%C2%AE");
|
||||
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B,QSA]", "/b/%C2%A1/id=%C2%A1?di=%C2%AE",
|
||||
+ "/c/%C2%A1%C2%A1", "id=%C2%A1&di=%C2%AE");
|
||||
}
|
||||
|
||||
|
||||
@@ -351,8 +351,8 @@
|
||||
@Test
|
||||
public void testUtf8WithOriginalQsFlagsB() throws Exception {
|
||||
// Note %C2%A1 == \u00A1
|
||||
- doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [B]",
|
||||
- "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%25C2%25A1", "id=%C2%A1");
|
||||
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [B]", "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%C2%A1",
|
||||
+ "id=%C2%A1");
|
||||
}
|
||||
|
||||
|
||||
@@ -367,8 +367,8 @@
|
||||
@Test
|
||||
public void testUtf8WithOriginalQsFlagsRB() throws Exception {
|
||||
// Note %C2%A1 == \u00A1
|
||||
- doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B]",
|
||||
- "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%25C2%25A1", "id=%C2%A1");
|
||||
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B]", "/b/%C2%A1?id=%C2%A1", "/c/%C2%A1%C2%A1",
|
||||
+ "id=%C2%A1");
|
||||
}
|
||||
|
||||
|
||||
@@ -403,8 +403,8 @@
|
||||
@Test
|
||||
public void testUtf8WithRewriteQsFlagsB() throws Exception {
|
||||
// Note %C2%A1 == \u00A1
|
||||
- doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B]",
|
||||
- "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%25C2%25A1", "id=%25C2%25A1");
|
||||
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [B]", "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%C2%A1",
|
||||
+ "id=%C2%A1");
|
||||
}
|
||||
|
||||
|
||||
@@ -428,8 +428,8 @@
|
||||
@Test
|
||||
public void testUtf8WithRewriteQsFlagsRB() throws Exception {
|
||||
// Note %C2%A1 == \u00A1
|
||||
- doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B]",
|
||||
- "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%25C2%25A1", "id=%25C2%25A1");
|
||||
+ doTestRewrite("RewriteRule ^/b/(.*)/(.*) /c/\u00A1$1?$2 [R,B]", "/b/%C2%A1/id=%C2%A1", "/c/%C2%A1%C2%A1",
|
||||
+ "id=%C2%A1");
|
||||
}
|
||||
|
||||
|
||||
@@ -472,7 +472,7 @@
|
||||
@Test
|
||||
public void testUtf8FlagsB() throws Exception {
|
||||
// Note %C2%A1 == \u00A1
|
||||
- doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [B]", "/b/%C2%A1", "/c/%C2%A1%25C2%25A1");
|
||||
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [B]", "/b/%C2%A1", "/c/%C2%A1%C2%A1");
|
||||
}
|
||||
|
||||
|
||||
@@ -486,7 +486,7 @@
|
||||
@Test
|
||||
public void testUtf8FlagsRB() throws Exception {
|
||||
// Note %C2%A1 == \u00A1
|
||||
- doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B]", "/b/%C2%A1", "/c/%C2%A1%25C2%25A1");
|
||||
+ doTestRewrite("RewriteRule ^/b/(.*) /c/\u00A1$1 [R,B]", "/b/%C2%A1", "/c/%C2%A1%C2%A1");
|
||||
}
|
||||
|
||||
|
||||
@@ -652,6 +652,7 @@
|
||||
rewriteValve.setConfiguration(config);
|
||||
|
||||
Tomcat.addServlet(ctx, "snoop", new SnoopServlet());
|
||||
+ ctx.addServletMappingDecoded("/a/Z", "snoop");
|
||||
ctx.addServletMappingDecoded("/a/%5A", "snoop");
|
||||
ctx.addServletMappingDecoded("/c/*", "snoop");
|
||||
Tomcat.addServlet(ctx, "default", new DefaultServlet());
|
||||
@@ -722,4 +723,87 @@
|
||||
Assert.assertEquals(expectedStatusCode, rc);
|
||||
}
|
||||
}
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testEncodedUriSimple() throws Exception {
|
||||
+ doTestRewriteWithEncoding("aaa");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testEncodedUriEncodedQuestionMark01() throws Exception {
|
||||
+ doTestRewriteWithEncoding("a%3fa");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testEncodedUriEncodedQuestionMark02() throws Exception {
|
||||
+ doTestRewriteWithEncoding("%3faa");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testEncodedUriEncodedQuestionMark03() throws Exception {
|
||||
+ doTestRewriteWithEncoding("aa%3f");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testEncodedUriEncodedQuestionMarkAndQueryString() throws Exception {
|
||||
+ doTestRewriteWithEncoding("a%3fa?b=c", "a%3fa", "b=c");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testEncodedUriEncodedSemicolon01() throws Exception {
|
||||
+ doTestRewriteWithEncoding("a%3ba");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testEncodedUriEncodedSemicolon02() throws Exception {
|
||||
+ doTestRewriteWithEncoding("%3baa");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ @Test
|
||||
+ public void testEncodedUriEncodedSemicolon03() throws Exception {
|
||||
+ doTestRewriteWithEncoding("aa%3b");
|
||||
+ }
|
||||
+
|
||||
+
|
||||
+ private void doTestRewriteWithEncoding(String segment) throws Exception {
|
||||
+ doTestRewriteWithEncoding(segment, segment, null);
|
||||
+ }
|
||||
+
|
||||
+ private void doTestRewriteWithEncoding(String segment, String expectedSegment, String expectedQueryString)
|
||||
+ throws Exception {
|
||||
+ Tomcat tomcat = getTomcatInstance();
|
||||
+
|
||||
+ // No file system docBase required
|
||||
+ Context ctx = tomcat.addContext("", null);
|
||||
+
|
||||
+ RewriteValve rewriteValve = new RewriteValve();
|
||||
+ tomcat.getHost().getPipeline().addValve(rewriteValve);
|
||||
+
|
||||
+ rewriteValve.setConfiguration("RewriteRule ^/source/(.*)$ /target/$1");
|
||||
+
|
||||
+ Tomcat.addServlet(ctx, "snoop", new SnoopServlet());
|
||||
+ ctx.addServletMappingDecoded("/target/*", "snoop");
|
||||
+
|
||||
+ tomcat.start();
|
||||
+
|
||||
+ ByteChunk res = new ByteChunk();
|
||||
+ int rc = getUrl("http://localhost:" + getPort() + "/source/" + segment, res, false);
|
||||
+
|
||||
+ Assert.assertEquals(HttpServletResponse.SC_OK, rc);
|
||||
+
|
||||
+ res.setCharset(StandardCharsets.UTF_8);
|
||||
+ String body = res.toString();
|
||||
+ Assert.assertTrue(body, body.contains("REQUEST-URI: /target/" + expectedSegment));
|
||||
+ Assert.assertTrue(body, body.contains("PATH-INFO: /" +
|
||||
+ URLDecoder.decode(expectedSegment, String.valueOf(StandardCharsets.UTF_8))));
|
||||
+ Assert.assertTrue(body, body.contains("REQUEST-QUERY-STRING: " + expectedQueryString));
|
||||
+ }
|
||||
}
|
||||
--- webapps/docs/changelog.xml
|
||||
+++ webapps/docs/changelog.xml
|
||||
@@ -105,6 +105,16 @@
|
||||
issues do not "pop up" wrt. others).
|
||||
-->
|
||||
<section name="Tomcat 9.0.50-redhat (szappis)">
|
||||
+ <subsection name="Catalina">
|
||||
+ <changelog>
|
||||
+ <fix>
|
||||
+ <bug>60940</bug>: Improve the handling of the <code>META-INF/</code> and
|
||||
+ <code>META-INF/MANIFEST.MF</code> entries for Jar files located in
|
||||
+ <code>/WEB-INF/lib</code> when running a web application from a packed
|
||||
+ WAR file. (markt)
|
||||
+ </fix>
|
||||
+ </changelog>
|
||||
+ </subsection>
|
||||
<subsection name="Coyote">
|
||||
<changelog>
|
||||
<fix>
|
||||
--- webapps/docs/rewrite.xml
|
||||
+++ webapps/docs/rewrite.xml
|
||||
@@ -52,6 +52,28 @@
|
||||
<p>It can also be in the context.xml of a webapp.
|
||||
The valve will then use a <code>rewrite.config</code> file containing the
|
||||
rewrite directives, it must be placed in the WEB-INF folder of the web application
|
||||
+ </p>
|
||||
+
|
||||
+</section>
|
||||
+
|
||||
+<section name="Using rewrite rules with special characters">
|
||||
+
|
||||
+ <p>The URL presented to the rewrite valve is the same URL used for request
|
||||
+ mapping with any literal <code>'%'</code>, <code>';'</code> and/or
|
||||
+ <code>'?'</code> characters encoded in <code>%nn</code> form.</p>
|
||||
+
|
||||
+ <p>A rewrite rule that wishes to insert a literal <code>'%'</code>,
|
||||
+ <code>';'</code>, <code>'?'</code>, <code>'&'</code> or <code>'='</code>
|
||||
+ character should do so in <code>%nn</code> form. Other characters maybe
|
||||
+ inserted in either literal or <code>%nn</code> form.</p>
|
||||
+
|
||||
+ <p>This enables the rewrite rules to:
|
||||
+ <ul>
|
||||
+ <li>process URLs containing literal <code>'?'</code> characters;</li>
|
||||
+ <li>add a query string;</li>
|
||||
+ <li>insert a literal <code>'%'</code> character without it being confused with
|
||||
+ <code>%nn</code> encoding.</li>
|
||||
+ </ul>
|
||||
</p>
|
||||
|
||||
</section>
|
||||
258
SOURCES/rhbz-2406591.patch
Normal file
258
SOURCES/rhbz-2406591.patch
Normal file
@ -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 @@
|
||||
<code>/WEB-INF/lib</code> when running a web application from a packed
|
||||
WAR file. (markt)
|
||||
</fix>
|
||||
+ <fix>
|
||||
+ Fix handling of <code>QSA</code> and <code>QSD</code> flags in
|
||||
+ <code>RewriteValve</code>. (markt)
|
||||
+ </fix>
|
||||
</changelog>
|
||||
</subsection>
|
||||
<subsection name="Coyote">
|
||||
0
SOURCES/tomcat-9.0-digest.script
Normal file → Executable file
0
SOURCES/tomcat-9.0-digest.script
Normal file → Executable file
0
SOURCES/tomcat-9.0-tool-wrapper.script
Normal file → Executable file
0
SOURCES/tomcat-9.0-tool-wrapper.script
Normal file → Executable file
0
SOURCES/tomcat-9.0.wrapper
Normal file → Executable file
0
SOURCES/tomcat-9.0.wrapper
Normal file → Executable file
0
SOURCES/tomcat-functions
Normal file → Executable file
0
SOURCES/tomcat-functions
Normal file → Executable file
0
SOURCES/tomcat-preamble
Normal file → Executable file
0
SOURCES/tomcat-preamble
Normal file → Executable file
0
SOURCES/tomcat-server
Normal file → Executable file
0
SOURCES/tomcat-server
Normal file → Executable file
@ -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 <akrajcik@redhat.com> - 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 <dsoumis@redhat.com> - 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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user