pki-servlet-engine/SOURCES/rhbz-2406591.patch
2026-06-23 05:02:07 -04:00

259 lines
12 KiB
Diff

--- 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">