Add 88 new defect entries to HIGH and MEDIUM tables:
HIGH: mysql-0001/0002, mariadb-0001, redis-0001/0002, valkey-0001/0002, openvpn-0001,
vlc-0001, prometheus-0001, otel-collector-0001, cockroachdb-0001..0004,
tidb-0001..0008, kubernetes-0001/0002, go-0001, kotlin-0002, scala-0001,
allegro5-0001, sdl2-0001, grafana-0001, clickhouse-0001, duckdb-0001,
mongodb-0001, envoy-0001, istio-0001, cilium-0001, linkerd2-0001,
linux-0001/0002/0003, tor-0002/0003, curl-0001, julia-0001, lua-0001,
perl5-0001, nats-0001, spring-0003/0004, tomcat-0001, onos-0002, odl-0002
MEDIUM: helm-0001, mariadb-0002, openssl-0001/0002, memcached-0001,
cassandra-0001..0004, flink-0001, storm-0001/0002, zookeeper-0001..0003,
pip-0001, gradle-0001, nginx-0001, haproxy-0001, caddy-0001, varnish-0001,
ffmpeg-0001, gstreamer-0001, raylib-0001, love2d-0001, php-0001/0002,
r-source-0001, cpython-0002, ruby-0001, rabbitmq-0003/0004, activemq-0001,
ovs-0001, onos-0003, odl-0002, jetty-0001
PDF: 976K
2.5 KiB
jetty-0001: HttpFields.formatCsvExcludingExisting — QuotedCSV.getValues() List.contains() O(n²)
Target: Eclipse Jetty
File: jetty-core/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java
CWE: CWE-407 — Inefficient Algorithmic Complexity
Severity: MEDIUM
Status: PATCHED
Description
HttpFields.Mutable.addCSV() (both header and name variants) calls the private
helper formatCsvExcludingExisting(QuotedCSV existing, String... values). Inside
that helper the loop iterates over all incoming values and calls
existing.getValues().contains(unquoted) on each iteration.
QuotedCSV.getValues() returns the internal _values field, which is
ArrayList<String> (line 107). Each .contains() call is therefore O(m) where
m is the number of existing CSV values. With V values to add and M existing
values, the total is O(V × M).
When addCSV() is called per-request (e.g. to add Vary or Cache-Control
token lists), V and M grow with the number of directives/values, making this
O(n²) under adversarial or large header inputs.
// HttpFields.java line 1576-1591
private static String formatCsvExcludingExisting(QuotedCSV existing, String... values) {
boolean add = true;
if (existing != null && !existing.isEmpty()) {
add = false;
for (int i = values.length; i-- > 0; ) {
String unquoted = QuotedCSV.unquote(values[i]);
if (existing.getValues().contains(unquoted)) // O(m) scan per value → O(V×M)
values[i] = null;
else
add = true;
}
}
...
}
// QuotedCSV.java line 107
private final List<String> _values = new ArrayList<>(); // O(n) contains()
Fix
Convert the existing values to a HashSet<String> once before the loop, then
call existingSet.contains(unquoted) at O(1) per lookup.
private static String formatCsvExcludingExisting(QuotedCSV existing, String... values) {
boolean add = true;
if (existing != null && !existing.isEmpty()) {
add = false;
Set<String> existingSet = new HashSet<>(existing.getValues()); // O(m) once
for (int i = values.length; i-- > 0; ) {
String unquoted = QuotedCSV.unquote(values[i]);
if (existingSet.contains(unquoted)) // O(1) per lookup
values[i] = null;
else
add = true;
}
}
...
}
Patch
defects/jetty/patch/jetty-0001.patch
Unit Test
defects/jetty/unit/JettyHttpFieldsCsvTest.java