Skip to content

Commit 87d86a1

Browse files
committed
Limit the length of content codec list that can be processed automatically
1 parent 15f9d4f commit 87d86a1

File tree

2 files changed

+55
-3
lines changed

2 files changed

+55
-3
lines changed

httpclient5/src/main/java/org/apache/hc/client5/http/impl/classic/ContentCompressionExec.java

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import org.apache.hc.core5.http.HttpEntity;
5555
import org.apache.hc.core5.http.HttpException;
5656
import org.apache.hc.core5.http.HttpHeaders;
57+
import org.apache.hc.core5.http.ProtocolException;
5758
import org.apache.hc.core5.http.config.Lookup;
5859
import org.apache.hc.core5.http.config.RegistryBuilder;
5960
import org.apache.hc.core5.http.message.BasicHeaderValueParser;
@@ -77,14 +78,18 @@
7778
@Internal
7879
public final class ContentCompressionExec implements ExecChainHandler {
7980

81+
public static final int MAX_CODEC_LIST_LEN = 5;
82+
8083
private final Header acceptEncoding;
8184
private final Lookup<InputStreamFactory> decoderRegistry;
8285
private final boolean ignoreUnknown;
86+
private final int maxCodecListLen;
8387

8488
public ContentCompressionExec(
8589
final List<String> acceptEncoding,
8690
final Lookup<InputStreamFactory> decoderRegistry,
87-
final boolean ignoreUnknown) {
91+
final boolean ignoreUnknown,
92+
final int maxCodecListLen) {
8893

8994
final boolean brotliSupported = decoderRegistry == null && BrotliDecompressingEntity.isAvailable();
9095
if (acceptEncoding != null) {
@@ -112,10 +117,22 @@ public ContentCompressionExec(
112117
this.decoderRegistry = builder.build();
113118
}
114119
this.ignoreUnknown = ignoreUnknown;
120+
this.maxCodecListLen = maxCodecListLen;
121+
}
122+
123+
public ContentCompressionExec(
124+
final List<String> acceptEncoding,
125+
final Lookup<InputStreamFactory> decoderRegistry,
126+
final boolean ignoreUnknown) {
127+
this(acceptEncoding, decoderRegistry, ignoreUnknown, MAX_CODEC_LIST_LEN);
115128
}
116129

117130
public ContentCompressionExec(final boolean ignoreUnknown) {
118-
this(null, null, ignoreUnknown);
131+
this(null, null, ignoreUnknown, MAX_CODEC_LIST_LEN);
132+
}
133+
134+
public ContentCompressionExec(final int maxCodecListLen) {
135+
this(null, null, true, maxCodecListLen);
119136
}
120137

121138
/**
@@ -128,7 +145,7 @@ public ContentCompressionExec(final boolean ignoreUnknown) {
128145
* </ul>
129146
*/
130147
public ContentCompressionExec() {
131-
this(null, null, true);
148+
this(null, null, true, MAX_CODEC_LIST_LEN);
132149
}
133150

134151

@@ -158,6 +175,9 @@ public ClassicHttpResponse execute(
158175
if (contentEncoding != null) {
159176
final ParserCursor cursor = new ParserCursor(0, contentEncoding.length());
160177
final HeaderElement[] codecs = BasicHeaderValueParser.INSTANCE.parseElements(contentEncoding, cursor);
178+
if (maxCodecListLen > 0 && codecs.length > maxCodecListLen) {
179+
throw new ProtocolException("Codec list exceeds maximum of " + maxCodecListLen + " elements");
180+
}
161181
for (final HeaderElement codec : codecs) {
162182
final String codecname = codec.getName().toLowerCase(Locale.ROOT);
163183
final InputStreamFactory decoderFactory = decoderRegistry.lookup(codecname);

httpclient5/src/test/java/org/apache/hc/client5/http/impl/classic/TestContentCompressionExec.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
import org.apache.hc.core5.http.HttpException;
4141
import org.apache.hc.core5.http.HttpHost;
4242
import org.apache.hc.core5.http.Method;
43+
import org.apache.hc.core5.http.ProtocolException;
4344
import org.apache.hc.core5.http.io.entity.StringEntity;
4445
import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
4546
import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
@@ -236,4 +237,35 @@ void testContentEncodingRequestParameter() throws Exception {
236237
Assertions.assertFalse(entity instanceof GzipDecompressingEntity);
237238
}
238239

240+
@Test
241+
void testContentEncodingExceedsCodecListLenMax() throws Exception {
242+
impl = new ContentCompressionExec(5);
243+
244+
final ClassicHttpRequest request = new BasicClassicHttpRequest(Method.GET, host, "/");
245+
final ClassicHttpResponse response1 = new BasicClassicHttpResponse(200, "OK");
246+
final HttpEntity original1 = EntityBuilder.create()
247+
.setText("encoded stuff")
248+
.setContentEncoding("gzip,gzip,gzip,gzip,gzip")
249+
.build();
250+
response1.setEntity(original1);
251+
252+
Mockito.when(execChain.proceed(request, scope)).thenReturn(response1);
253+
254+
final HttpEntity entity = response1.getEntity();
255+
Assertions.assertNotNull(entity);
256+
Assertions.assertFalse(entity instanceof GzipDecompressingEntity);
257+
258+
final ClassicHttpResponse response2 = new BasicClassicHttpResponse(200, "OK");
259+
final HttpEntity original2 = EntityBuilder.create()
260+
.setText("encoded stuff")
261+
.setContentEncoding("gzip,gzip,gzip,gzip,gzip,gzip")
262+
.build();
263+
response2.setEntity(original2);
264+
265+
Mockito.when(execChain.proceed(request, scope)).thenReturn(response2);
266+
267+
final ProtocolException exception = Assertions.assertThrows(ProtocolException.class, () -> impl.execute(request, scope, execChain));
268+
Assertions.assertEquals("Codec list exceeds maximum of 5 elements", exception.getMessage());
269+
}
270+
239271
}

0 commit comments

Comments
 (0)