Skip to content

Commit 51155c4

Browse files
committed
Add basic filtering for trailer fields
1 parent 5339a24 commit 51155c4

File tree

3 files changed

+157
-2
lines changed

3 files changed

+157
-2
lines changed

java/org/apache/coyote/http11/filters/ChunkedOutputFilter.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -181,10 +181,10 @@ public void end() throws IOException {
181181
if (disallowedTrailerFieldNames.contains(trailerField.getKey().toLowerCase(Locale.ENGLISH))) {
182182
continue;
183183
}
184-
osw.write(trailerField.getKey());
184+
osw.write(filterForHeaders(trailerField.getKey()));
185185
osw.write(':');
186186
osw.write(' ');
187-
osw.write(trailerField.getValue());
187+
osw.write(filterForHeaders(trailerField.getValue()));
188188
osw.write("\r\n");
189189
}
190190
}
@@ -198,6 +198,33 @@ public void end() throws IOException {
198198
}
199199

200200

201+
/*
202+
* Filters out CTLs excluding TAB and any code points above 255 (since this is meant to be ISO-8859-1).
203+
*
204+
* This doesn't perform full HTTP validation. For example, it does not limit field names to tokens.
205+
*
206+
* Strictly, correct trailer fields is an application concern. The filtering here is a basic attempt to help
207+
* mis-behaving applications prevent the worst of the potential side-effects of invalid trailer fields.
208+
*/
209+
// package private so it is visible for testing
210+
static String filterForHeaders(String input) {
211+
char[] chars = input.toCharArray();
212+
boolean updated = false;
213+
for (int i = 0; i < chars.length; i++) {
214+
if (chars[i] < 32 && chars [i] != 9 || chars[i] == 127 || chars[i] > 255) {
215+
chars[i] = ' ';
216+
updated = true;
217+
}
218+
}
219+
220+
if (updated) {
221+
return new String(chars);
222+
} else {
223+
return input;
224+
}
225+
}
226+
227+
201228
@Override
202229
public void recycle() {
203230
response = null;
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.coyote.http11.filters;
18+
19+
import java.util.ArrayList;
20+
import java.util.Collection;
21+
import java.util.List;
22+
23+
import org.junit.Assert;
24+
import org.junit.Test;
25+
import org.junit.runner.RunWith;
26+
import org.junit.runners.Parameterized;
27+
import org.junit.runners.Parameterized.Parameter;
28+
29+
@RunWith(Parameterized.class)
30+
public class TestChunkedOutputFilter {
31+
32+
private final String VALID_STRING = "aaa";
33+
34+
@Parameterized.Parameters(name = "{index}")
35+
public static Collection<Object[]> parameters() {
36+
List<Object[]> parameterSets = new ArrayList<>();
37+
38+
for (int i = 0; i < 500; i++) {
39+
Boolean valid;
40+
if (i < 32 && i != 9 || i == 127 || i > 255) {
41+
valid = Boolean.FALSE;
42+
} else {
43+
valid = Boolean.TRUE;
44+
}
45+
parameterSets.add(new Object[] { Character.valueOf((char) i), valid});
46+
}
47+
return parameterSets;
48+
}
49+
50+
51+
@Parameter(0)
52+
public Character charUnderTest;
53+
@Parameter(1)
54+
public boolean valid;
55+
56+
57+
@Test
58+
public void testAtStart() {
59+
StringBuilder sb = new StringBuilder(4);
60+
sb.append(charUnderTest);
61+
sb.append(VALID_STRING);
62+
63+
String result = ChunkedOutputFilter.filterForHeaders(sb.toString());
64+
65+
String expected;
66+
if (valid) {
67+
expected = sb.toString();
68+
} else {
69+
StringBuilder esb = new StringBuilder(4);
70+
esb.append(' ');
71+
esb.append(VALID_STRING);
72+
expected = esb.toString();
73+
}
74+
75+
Assert.assertEquals(expected, result);
76+
}
77+
78+
79+
@Test
80+
public void testInMiddle() {
81+
StringBuilder sb = new StringBuilder(4);
82+
sb.append(VALID_STRING);
83+
sb.append(charUnderTest);
84+
sb.append(VALID_STRING);
85+
86+
String result = ChunkedOutputFilter.filterForHeaders(sb.toString());
87+
88+
String expected;
89+
if (valid) {
90+
expected = sb.toString();
91+
} else {
92+
StringBuilder esb = new StringBuilder(4);
93+
esb.append(VALID_STRING);
94+
esb.append(' ');
95+
esb.append(VALID_STRING);
96+
expected = esb.toString();
97+
}
98+
99+
Assert.assertEquals(expected, result);
100+
}
101+
102+
103+
@Test
104+
public void testAtEnd() {
105+
StringBuilder sb = new StringBuilder(4);
106+
sb.append(VALID_STRING);
107+
sb.append(charUnderTest);
108+
109+
String result = ChunkedOutputFilter.filterForHeaders(sb.toString());
110+
111+
String expected;
112+
if (valid) {
113+
expected = sb.toString();
114+
} else {
115+
StringBuilder esb = new StringBuilder(4);
116+
esb.append(VALID_STRING);
117+
esb.append(' ');
118+
expected = esb.toString();
119+
}
120+
121+
Assert.assertEquals(expected, result);
122+
}
123+
}

webapps/docs/changelog.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,11 @@
237237
Ensure the HTTP/2 request header read buffer is reset (including
238238
restoration to default size) after a stream reset. (markt)
239239
</fix>
240+
<add>
241+
Provide trailer field filtering equivalent to that provided for
242+
non-trailer fields. Control characters (excluding TAB), and characters
243+
with code points above 255 will be replaced with a space. (markt)
244+
</add>
240245
</changelog>
241246
</subsection>
242247
<subsection name="Jasper">

0 commit comments

Comments
 (0)