From 573ddff776876660ca3a63b95086695328982d80 Mon Sep 17 00:00:00 2001 From: Andrea Cosentino Date: Fri, 12 Jun 2026 14:46:58 +0200 Subject: [PATCH] CAMEL-23588: camel-undertow - extend UndertowHeaderFilterStrategy to filter the legacy websocket.* exchange-header prefix Backport to camel-4.18.x of the main-branch change (d5a717dd0f8). Adds the legacy websocket.* prefix to the undertow HeaderFilterStrategy in/out filters (in addition to Camel* / camel* / org.apache.camel.*) and documents it in the 4.18 upgrade guide, alongside the CAMEL-23532 vertx-websocket / atmosphere-websocket / iggy family. --- .../UndertowHeaderFilterStrategy.java | 19 ++++++++ .../pages/camel-4x-upgrade-guide-4_18.adoc | 47 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHeaderFilterStrategy.java b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHeaderFilterStrategy.java index 23c0d894d7d76..2d92318ed5be7 100644 --- a/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHeaderFilterStrategy.java +++ b/components/camel-undertow/src/main/java/org/apache/camel/component/undertow/UndertowHeaderFilterStrategy.java @@ -21,10 +21,29 @@ public class UndertowHeaderFilterStrategy extends HttpHeaderFilterStrategy { + /** + * Legacy {@code websocket.*} Exchange-header prefix used by {@code UndertowConstants} for the dispatch and event + * headers ({@code websocket.connectionKey}, {@code websocket.connectionKey.list}, {@code websocket.sendToAll}, + * {@code websocket.eventType}, {@code websocket.eventTypeEnum}, {@code websocket.channel}, + * {@code websocket.exchange}). Added to the in/out filter prefixes (CAMEL-23588) so the undertow boundary does not + * propagate these values onto outbound wire frames or map them in from inbound HTTP-style headers. This is + * defence-in-depth — cross-component routes that flow an untrusted message into an undertow producer should also + * {@code .removeHeaders("websocket.*")} at the trust boundary, because the producer reads these headers via + * {@code in.getHeader(...)} which bypasses the {@code HeaderFilterStrategy}. + */ + static final String WEBSOCKET_FILTER_STARTS_WITH = "websocket."; + public UndertowHeaderFilterStrategy() { initialize(); } + @Override + protected void initialize() { + super.initialize(); + setOutFilterStartsWith("Camel", "camel", WEBSOCKET_FILTER_STARTS_WITH); + setInFilterStartsWith("Camel", "camel", WEBSOCKET_FILTER_STARTS_WITH); + } + @Override public boolean applyFilterToExternalHeaders(String headerName, Object headerValue, Exchange exchange) { boolean skip = io.undertow.util.HttpString.tryFromString(headerName) == null; diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc index 1d040ae9e08b1..a63bb754b8bcc 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc @@ -1203,6 +1203,53 @@ directions, aligning the component with the rest of the Camel component catalog. `Camel`-prefixed user-header names from Iggy messages can supply a custom `headerFilterStrategy` to restore the previous behaviour. +=== camel-undertow - potential breaking change + +`UndertowHeaderFilterStrategy` now also filters the legacy `websocket.*` +Exchange-header prefix (in addition to the `Camel*` / `camel*` / +`org.apache.camel.*` prefixes it already filtered). This applies to both the +in (wire -> exchange) and out (exchange -> wire) directions and follows the +dedicated-filter-strategy shape used by CAMEL-23532 for +`camel-vertx-websocket` / `camel-atmosphere-websocket` / `camel-iggy`. + +The constants in `UndertowConstants` (`CONNECTION_KEY`, `CONNECTION_KEY_LIST`, +`SEND_TO_ALL`, `EVENT_TYPE`, `EVENT_TYPE_ENUM`, `CHANNEL`, `EXCHANGE`) keep +their existing string values (`websocket.connectionKey`, +`websocket.connectionKey.list`, `websocket.sendToAll`, etc.) because they are +part of the undertow component's externally-visible API contract; routes +referencing them (symbolically or by literal value) continue to work +unchanged within an undertow route. + +The behaviour change applies at undertow's transport boundary: + +* Outbound (exchange -> wire): if an exchange ends up at an undertow producer + carrying an Exchange header whose name starts with `websocket.`, that + header will no longer be propagated onto the outbound HTTP/websocket + request as a wire-level header. +* Inbound (wire -> exchange): if an undertow consumer receives a request + whose wire-level headers include a name starting with `websocket.`, that + header will no longer be mapped into the resulting Camel exchange. + +Note that the `HeaderFilterStrategy` only governs the transport boundary; it +does not prevent cross-component header injection (for example, an +`http -> undertow` route where the HTTP consumer maps an attacker-supplied +`websocket.connectionKey` header into the exchange and the undertow producer +then reads it via `in.getHeader(...)` to dispatch to a specific peer). For +defence in depth at the trust boundary, route authors should explicitly strip +these headers from untrusted inbound traffic, for example: + +[source,java] +---- +from("jetty:http://0.0.0.0:8080/api") + .removeHeaders("websocket.*") + .to("undertow:ws://internal-broker/notifications"); +---- + +Routes that intentionally relied on undertow mapping `websocket.*` wire +headers in or out can supply a custom `headerFilterStrategy` endpoint option +to restore the previous behaviour. + + === camel-aws2-sqs `Sqs2HeaderFilterStrategy` now also configures an inbound filter aligned with the existing