Federico Mariani created CAMEL-24132:
----------------------------------------

             Summary: camel-jpa: medium-severity findings from July 2026 
component review (umbrella)
                 Key: CAMEL-24132
                 URL: https://issues.apache.org/jira/browse/CAMEL-24132
             Project: Camel
          Issue Type: Bug
          Components: camel-jpa
            Reporter: Federico Mariani


h3. Overview

This is an umbrella issue tracking 7 medium-severity findings from a broader 
code review of {{camel-jpa}} (July 2026), grouped here rather than filed 
individually since none is independently critical. High-severity findings from 
the same review were filed separately as standalone issues.

h3. M1 -- JpaPollingConsumer.receiveNoWait() can block indefinitely

JpaPollingConsumer.java:186-189 implements {{receiveNoWait()}} by simply 
delegating to {{receive()}}, which runs a full transaction and acquires a 
{{PESSIMISTIC_WRITE}} lock by default -- it can block on row locks for an 
unbounded time. This violates the {{PollingConsumer}} contract that 
{{receiveNoWait()}} returns immediately. At minimum it should apply a NOWAIT 
lock hint (or no lock) rather than reusing {{receive()}} as-is.

h3. M2 -- receive(timeout) never cancels the background task on timeout

JpaPollingConsumer.java:192-212: on {{TimeoutException}}, the method returns 
{{null}} but never calls {{future.cancel(true)}}. The submitted task keeps 
running in the background -- holding/awaiting DB locks -- and, once it 
eventually completes, leaks its {{EntityManager}} (see the related EM-leak 
issue) and its {{Exchange}}. Under repeated timeouts (e.g. {{pollEnrich}} 
against a locked table with a short timeout), abandoned tasks accumulate in the 
shared, bounded thread pool, and later polls start timing out purely from 
queueing.

h3. M3 -- JpaPollingConsumer + nativeQuery + default consumeLockEntity always 
fails

JpaPollingConsumer.java:143-145 calls {{innerQuery.setLockMode(...)}} whenever 
{{consumeLockEntity}} is {{true}} (the default). Per the JPA spec, 
{{Query.setLockMode}} throws {{IllegalStateException}} for anything other than 
a JPQL SELECT / CriteriaQuery -- i.e. for every native query (and for 
update-style named queries). {{pollEnrich("jpa:X?nativeQuery=...")}} can never 
succeed without the caller explicitly setting {{consumeLockEntity=false}}. The 
call should be guarded (skipped for native queries), or the requirement should 
be documented prominently.

h3. M4 -- JpaMessageIdRepository.add() turns a concurrent-insert race into a 
failed exchange instead of a duplicate signal

JpaMessageIdRepository.java:102-111 is check-then-insert. {{MessageProcessed}} 
has a unique constraint on {{(processorName, messageId)}}, so when two 
exchanges race on the same id (parallel threads or a clustered deployment 
sharing the table), the loser hits a constraint violation at {{flush()}}, which 
the {{catch}} block wraps and rethrows as a {{PersistenceException}} -- 
*failing the exchange* -- instead of treating it as "duplicate detected" and 
returning {{false}}. For a repository whose entire purpose is cluster-safe 
idempotency, the constraint-violation race should be caught and mapped to 
{{return false}}.

h3. M5 -- the CAMEL-22534 parallel-EntityManager fix only covers the Splitter

CAMEL-22534 made the Splitter strip the {{CamelEntityManager}} exchange 
property from parallel sub-exchange copies. {{multicast(parallelProcessing)}}, 
{{recipientList(parallel)}} and {{wireTap}} still copy exchange properties by 
default, so parallel branches through those EIPs can share the same 
non-thread-safe {{HashMap<String, EntityManager>}} 
({{JpaHelper.getEntityManagerMap}}) *and* the same {{EntityManager}} instances 
across threads -- both concurrent EM use and map corruption are possible. 
Consider moving the property-strip into a shared place (e.g. 
{{MulticastProcessor}}), or making the JPA helper's map thread-safe / 
per-thread.

h3. M6 -- JpaProducer.isUseExecuteUpdate() misclassifies legitimate SELECTs

JpaProducer.java:145-164 decides between {{executeUpdate()}} and 
{{getResultList()}} by testing whether the raw query string starts with 
{{"select"}} (case-insensitive, offset 0, no trimming). A query with leading 
whitespace/newline, a native CTE ({{WITH x AS (...) SELECT ...}}), or a 
parenthesized select is misclassified as an update and routed to 
{{executeUpdate()}}, which the JPA provider rejects. The string should be 
trimmed first, and {{WITH}} should also be recognized as a select-style query 
(or, more robustly, only auto-detect the update keywords 
{{insert|update|delete}} and default to select otherwise).

h3. Note

A 7th medium finding from the same review (header-supplied query parameters 
being auto-evaluated as a Simple expression) is *not* included here: it falls 
under the project's documented in-scope security vulnerability classes 
(expression injection where the framework itself evaluates untrusted input) and 
is being routed through the private SECURITY.md disclosure process instead of a 
public issue.

See also the four related high-severity issues filed from the same review.

_Claude Code on behalf of Croway_



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to