Federico Mariani created CAMEL-24164:
----------------------------------------

             Summary: 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
         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)

Reply via email to