Skip to content

CAMEL-24161: camel-salesforce - fix medium-severity bugs from code review#24857

Merged
davsclaus merged 1 commit into
mainfrom
fix/CAMEL-24161
Jul 17, 2026
Merged

CAMEL-24161: camel-salesforce - fix medium-severity bugs from code review#24857
davsclaus merged 1 commit into
mainfrom
fix/CAMEL-24161

Conversation

@davsclaus

Copy link
Copy Markdown
Contributor

Summary

Fixes 11 medium-severity bugs found during the July 2026 deep code review of camel-salesforce (CAMEL-24161). Companion to #24848 which fixed the high-severity findings.

Auth / session / HTTP client:

  • Mask refreshToken in SalesforceLoginConfig.toString() — was printed in clear text while all other secrets were masked (since CAMEL-15425)
  • Check body.rewind() return value in SalesforceSecurityHandler — multipart uploads silently sent empty body on 401 retry
  • Detect Jetty failure chunks in AbstractClientBase replay-buffering loop — mid-read I/O error produced silently truncated requests
  • Use put() instead of add() in DefaultRawClient.setAccessToken() — auth retry produced duplicate X-SFDC-Session/Authorization headers
  • Coordinate re-logins via session.login() on worker pool in SalesforceSecurityHandler — N concurrent 401s now share one login instead of each firing its own

Streaming / Pub/Sub:

  • Use getOrDefault() in SubscriptionHelper connect listener and clean up channelsToSubscribe on unsubscribe — prevents NPE and silent channel loss on concurrent unsubscribe
  • Invoke consumer.getExceptionHandler() on per-event decode failures in PubSubApiClient — events were silently lost (log only, replay ID advanced past them)
  • Stop infinite retry on corrupted replay ID when fallbackToLatestReplayId=false in PubSubApiClient, and make replay preset state per-observer instead of shared — prevents infinite loop and cross-consumer preset overwrite

REST / Bulk / Composite:

  • Clean up RESPONSE_CLASS_DEFERRED/RESPONSE_CLASS_PREFIX exchange properties in JsonRestProcessor.processResponse() — leaked into subsequent producers on the same exchange
  • Preserve exception when Salesforce returns both error body and exception in CompositeApiProcessor — HTTP failures were silently reported as success in raw/batch/composite modes
  • Fix deleteSObjectWithId in AbstractRestProcessor to restore DTO fields before completing the exchange (matching all sibling operations), and null-check external-id field value to prevent NPE

Test plan

  • All existing camel-salesforce-component tests pass (190 run, 0 failures)
  • Updated PubSubApiTest.testInvokesExceptionHandlerWhenReplayIdIsCorruptedAndFallbackToLatestReplayIdIsDisabled to verify fixed behavior (stops after one attempt instead of infinite retry)
  • CI green

Claude Code on behalf of davsclaus

🤖 Generated with Claude Code

Co-Authored-By: Claude Opus 4.6 noreply@anthropic.com

…view

Fixes 11 bugs found during a deep code review of camel-salesforce:

- SalesforceLoginConfig: mask refreshToken in toString() to prevent
  credential leak to debug logs (other secrets were already masked)
- SalesforceSecurityHandler: check body.rewind() return value to detect
  unreplayable multipart content on 401 retry
- AbstractClientBase: detect Jetty failure chunks in replay-buffering
  loop to prevent silently truncated requests
- DefaultRawClient: use put() instead of add() for auth headers to
  prevent duplicate X-SFDC-Session/Authorization on retry
- SalesforceSecurityHandler: coordinate re-logins via session.login()
  on worker pool so concurrent 401s share one login request instead
  of each firing its own
- SubscriptionHelper: use getOrDefault() to prevent NPE when channel
  is concurrently unsubscribed, and remove from channelsToSubscribe
  on unsubscribe to prevent stale entries
- PubSubApiClient: invoke consumer exception handler on per-event
  decode failures instead of silently swallowing them
- PubSubApiClient: stop infinite retry on corrupted replay ID when
  fallbackToLatestReplayId=false, and make replay state per-observer
  to prevent preset overwrite across consumers
- JsonRestProcessor: clean up RESPONSE_CLASS_DEFERRED and
  RESPONSE_CLASS_PREFIX exchange properties in processResponse()
  to match processStreamResultResponse() cleanup
- CompositeApiProcessor: preserve exception when Salesforce returns
  both error body and exception (raw, batch, and composite response)
- AbstractRestProcessor: fix deleteSObjectWithId to restore DTO fields
  before completing exchange (matching all sibling operations), and
  null-check external-id field value to prevent NPE

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Claus Ibsen <claus.ibsen@gmail.com>
@github-actions

Copy link
Copy Markdown
Contributor

🌟 Thank you for your contribution to the Apache Camel project! 🌟
🤖 CI automation will test this PR automatically.

🐫 Apache Camel Committers, please review the following items:

  • First-time contributors require MANUAL approval for the GitHub Actions to run
  • You can use the command /component-test (camel-)component-name1 (camel-)component-name2.. to request a test from the test bot although they are normally detected and executed by CI.
  • You can label PRs using skip-tests and test-dependents to fine-tune the checks executed by this PR.
  • Build and test logs are available in the summary page. Only Apache Camel committers have access to the summary.

⚠️ Be careful when sharing logs. Review their contents before sharing them publicly.

@davsclaus davsclaus added bug Something isn't working and removed components labels Jul 17, 2026
@davsclaus davsclaus self-assigned this Jul 17, 2026
@davsclaus
davsclaus requested review from gnodet and jeremyross July 17, 2026 14:15

@gnodet gnodet left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code on behalf of gnodet

Excellent work — thorough, well-organized fix for 11 distinct medium-severity bugs in camel-salesforce. Each fix is narrow, clearly motivated, and well-documented in the PR description.

Changes verified:

Auth / session / HTTP client:

  1. SalesforceLoginConfig.toString() — refreshToken masking
    refreshToken was the only secret printed in cleartext while clientSecret, password, and keystore were already masked since CAMEL-15425. Trivially correct.

  2. AbstractClientBase — failure chunk detection
    Follows the Jetty Content.Chunk API contract: isFailure() must be checked before isLast(). Without this, a mid-read I/O error silently truncated the request body. Properly releases the chunk before throwing.

  3. DefaultRawClient.setAccessToken()put() vs add()
    Confirmed add() in the current main branch — would produce duplicate X-SFDC-Session/Authorization headers on auth retry. put() correctly replaces.

  4. SalesforceSecurityHandler — coordinated re-login via worker pool
    Moves from per-401 inline login to session.login() on the worker pool for single-flight coordination. Also checks body.rewind() return value — appropriate warning-only approach since bodyless requests (GET/DELETE) would still succeed.

Streaming / Pub/Sub:

  1. PubSubApiClient — per-observer replay state
    Moving initialReplayPreset, initialReplayId, fallbackToLatestReplayId from class-level to FetchResponseObserver instance fields prevents cross-consumer preset overwrite when multiple consumers share one PubSubApiClient. Clean solution.

  2. PubSubApiClient — stop infinite retry on corrupt replay ID
    When fallbackToLatestReplayId=false, the return after setting replayId = null correctly stops the retry loop. The exception handler is already invoked to report the error.

  3. PubSubApiClient — per-event decode failure handling
    Previously only logged errors — events were silently lost (replay ID advanced past them). Now routes to consumer.getExceptionHandler().

  4. SubscriptionHelpergetOrDefault() and channelsToSubscribe cleanup
    Prevents NPE from concurrent unsubscribe race, and removes stale entries from channelsToSubscribe to prevent silent channel loss on reconnect.

REST / Bulk / Composite:

  1. JsonRestProcessorRESPONSE_CLASS_DEFERRED/RESPONSE_CLASS_PREFIX property cleanup
    RESPONSE_CLASS was already cleaned up but these two were leaked into subsequent producers on the same exchange.

  2. CompositeApiProcessor — exception preservation
    Critical fix — three response methods all had the same bug: exception was silently swallowed. All three now set exchange.setException(exception) when non-null.

  3. AbstractRestProcessordeleteSobjectWithId fixes

    • Ordering: confirmed all sibling methods (getSobjectWithId, upsertSobject) call restoreFields before processResponse — this was the only one with reversed order.
    • Null-check: oldValue.toString() NPE risk confirmed in main branch. Correct fallback to getParameter().

Test update:

PubSubApiTest correctly reflects the behavioral change — expects 1 subscribe call and 1 exception handler call (was 3 of each). Thread.sleep(1000) removal is a bonus.

No issues found.

@github-actions

Copy link
Copy Markdown
Contributor

🧪 CI tested the following changed modules:

  • components/camel-salesforce/camel-salesforce-component

🔬 Scalpel shadow comparison — Scalpel: 11 tested, 29 compile-only — current: 11 all tested

Maveniverse Scalpel detected 40 affected modules (current approach: 11).

⚠️ Modules only in Scalpel (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

Skip-tests mode would test 11 modules (1 direct + 10 downstream), skip tests for 29 (generated code, meta-modules)

Modules Scalpel would test (11)
  • camel-jbang-mcp
  • camel-jbang-plugin-mcp
  • camel-jbang-plugin-route-parser
  • camel-jbang-plugin-tui
  • camel-jbang-plugin-validate
  • camel-launcher-container
  • camel-salesforce
  • camel-salesforce-codegen
  • camel-salesforce-maven-plugin
  • camel-yaml-dsl-validator
  • camel-yaml-dsl-validator-maven-plugin
Modules with tests skipped (29)
  • apache-camel
  • camel-allcomponents
  • camel-catalog
  • camel-catalog-console
  • camel-catalog-lucene
  • camel-catalog-maven
  • camel-catalog-suggest
  • camel-componentdsl
  • camel-csimple-maven-plugin
  • camel-endpointdsl
  • camel-endpointdsl-support
  • camel-itest
  • camel-jbang-core
  • camel-jbang-it
  • camel-jbang-main
  • camel-jbang-plugin-edit
  • camel-jbang-plugin-generate
  • camel-jbang-plugin-kubernetes
  • camel-jbang-plugin-test
  • camel-kamelet-main
  • camel-launcher
  • camel-report-maven-plugin
  • camel-route-parser
  • camel-yaml-dsl
  • camel-yaml-dsl-deserializers
  • camel-yaml-dsl-maven-plugin
  • coverage
  • docs
  • dummy-component

ℹ️ Shadow mode — Scalpel observes but does not affect test execution. Learn more

All tested modules (40 modules)
  • Camel :: All Components Sync point
  • Camel :: Assembly
  • Camel :: Catalog :: CSimple Maven Plugin (deprecated)
  • Camel :: Catalog :: Camel Catalog
  • Camel :: Catalog :: Camel Report Maven Plugin
  • Camel :: Catalog :: Camel Route Parser
  • Camel :: Catalog :: Console
  • Camel :: Catalog :: Dummy Component
  • Camel :: Catalog :: Lucene (deprecated)
  • Camel :: Catalog :: Maven
  • Camel :: Catalog :: Suggest
  • Camel :: Component DSL
  • Camel :: Coverage
  • Camel :: Docs
  • Camel :: Endpoint DSL
  • Camel :: Endpoint DSL :: Support
  • Camel :: Integration Tests
  • Camel :: JBang :: Core
  • Camel :: JBang :: Integration tests
  • Camel :: JBang :: MCP
  • Camel :: JBang :: Main
  • Camel :: JBang :: Plugin :: Edit
  • Camel :: JBang :: Plugin :: Generate
  • Camel :: JBang :: Plugin :: Kubernetes
  • Camel :: JBang :: Plugin :: MCP
  • Camel :: JBang :: Plugin :: Route Parser
  • Camel :: JBang :: Plugin :: TUI
  • Camel :: JBang :: Plugin :: Testing
  • Camel :: JBang :: Plugin :: Validate
  • Camel :: Kamelet Main
  • Camel :: Launcher
  • Camel :: Launcher :: Container
  • Camel :: Salesforce
  • Camel :: Salesforce :: CodeGen
  • Camel :: Salesforce :: Maven Plugin
  • Camel :: YAML DSL
  • Camel :: YAML DSL :: Deserializers
  • Camel :: YAML DSL :: Maven Plugins
  • Camel :: YAML DSL :: Validator
  • Camel :: YAML DSL :: Validator Maven Plugin

⚙️ View full build and test results

@davsclaus davsclaus added this to the 4.22.0 milestone Jul 17, 2026
@davsclaus
davsclaus merged commit 701eb46 into main Jul 17, 2026
5 checks passed
@davsclaus
davsclaus deleted the fix/CAMEL-24161 branch July 17, 2026 15:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants