[
https://issues.apache.org/jira/browse/CAMEL-24164?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen resolved CAMEL-24164.
---------------------------------
Resolution: Fixed
> camel-jaxb - CamelJaxbPartClass header permanently mutates the shared data
> format (state pollution + data race)
> ---------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-24164
> URL: https://issues.apache.org/jira/browse/CAMEL-24164
> Project: Camel
> Issue Type: Bug
> Components: camel-jaxb
> Reporter: Federico Mariani
> Assignee: Federico Mariani
> Priority: Major
> Labels: ai-assisted-review, dataformat
> Fix For: 4.22.0
>
> Attachments: JaxbPartClassHeaderStatePollutionIssueTest.java
>
>
> *Severity:* High (correctness + security-adjacent)
> *Affects:* 4.x (predates the 2020 reifier split)
> h3. Problem
> {{JaxbDataFormat}} unmarshal and marshal assign the header-derived part class
> into the *instance field*:
> {code:java}
> partClass =
> camelContext.getClassResolver().resolveMandatoryClass(partClassFromHeader,
> ...);
> {code}
> A {{DataFormat}} is shared across all exchanges of a route step, so:
> * *Sticky*: after any one exchange carries the {{CamelJaxbPartClass}} header,
> {{partClass != null}} forever — every subsequent header-less exchange is
> silently unmarshalled as a partial {{JAXBElement}} bound to the leaked class.
> * *Race*: two concurrent exchanges with different {{CamelJaxbPartClass}}
> headers can unmarshal into each other's class.
> h3. Security note
> {{CamelJaxbPartClass}}/{{CamelJaxbPartNamespace}} are plain message headers.
> On a consumer that does not filter {{Camel*}} headers, an untrusted sender
> can pick the unmarshal target class and, via the stickiness, poison all later
> exchanges (header-abuse family, CVE-2025-27636 lineage). Mitigated by a
> compliant default {{HeaderFilterStrategy}}, but the persistence makes any
> single leak durable.
> (XXE was reviewed and is not affected — all unmarshal paths go through the
> hardened {{StaxConverter}}.)
> h3. Failure scenario
> Route {{from(direct).unmarshal(jaxb)}} with no partClass configured. Exchange
> 1 arrives with header {{CamelJaxbPartClass=Partial}} -> unmarshals as
> {{Partial}} (fine). Exchange 2 (a normal {{<purchaseOrder>}} document, *no
> header*) -> unmarshalled as {{Partial}} instead of {{PurchaseOrder}}.
> h3. Suggested fix
> Resolve the header-derived class into a *local variable* per call ({{Class<?>
> effectivePartClass = partClassFromHeader != null ? resolve(...) :
> this.partClass;}}); never assign the instance field from exchange data.
> h3. Reproducer
> Attached {{JaxbPartClassHeaderStatePollutionIssueTest.java}}:
> single-threaded, deterministic; exchange 2's body leaks to
> {{org.apache.camel.example.Partial}} (expected {{PurchaseOrder}}). RED on
> current code.
> ----
> _Reported by Claude Code on behalf of Federico Mariani (Croway). A failing
> JUnit reproducer (confirmed RED on 4.22.0-SNAPSHOT) is attached._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)