# Patch Analysis — CVE-2026-40022 (camel-platform-http-main)

## What the fix changes

Fix commit: `a9ebee94af976ac2afd7906d2e76673067d8be86`
("chore: default authentication path to /* in platform-http-main")
Released in `camel-4.14.6`, `camel-4.18.2`, `camel-4.20.0` (tag `camel-4.14.6` → commit `699d9363ab93c54a255afdf97077ee35c3709cd2`).

Files changed (production code only — 3 files, +16 / −28 lines):

- `components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/authentication/MainAuthenticationConfigurer.java`
  — adds a new `default` method:
  ```java
  default String resolveAuthenticationPath(String authenticationPath, String contextPath) {
      if (ObjectHelper.isNotEmpty(authenticationPath)) {
          return authenticationPath;
      }
      return "/*";
  }
  ```
- `BasicAuthenticationConfigurer.java` — both `configureAuthentication(...)` overloads
  (server + management) replace the inline resolution:
  ```java
  // BEFORE (vulnerable):
  String path = isNotEmpty(properties.getAuthenticationPath())
          ? properties.getAuthenticationPath() : properties.getPath();
  if ("/".equals(path)) { path = "/*"; }

  // AFTER (fixed):
  String path = resolveAuthenticationPath(properties.getAuthenticationPath(), properties.getPath());
  ```
- `JWTAuthenticationConfigurer.java` — same replacement in both overloads.

The fix also adds tests `BasicAuthenticationNonRootPathTest` (default case) and
`BasicAuthenticationSelectivePathTest` (explicit `authenticationPath=/secure/*`).

### Behavior before vs after

| `authenticationPath` | context path | vulnerable 4.14.5 resolved path | fixed 4.14.6 resolved path |
|---|---|---|---|
| unset | `/` (root) | `/` → widened to `/*` (protected) | `/*` (protected) |
| unset | `/api` | `/api` (exact → subpaths unprotected) | `/*` (protected) ← **the CVE fix** |
| `/*` | `/api` | `/*` (protected) | `/*` (protected) |
| `/secure/*` | `/api` | `/secure/*` (selective) | `/secure/*` (selective) |
| `/api` | `/api` | `/api` (exact → subpaths unprotected) | `/api` (exact → subpaths unprotected) ← **gap** |
| `/api/*` | `/api` | `/api/*` (relative → matches `/api/api/*` only) | `/api/*` (same) ← **gap** |

## What invariant the fix relies on

The fix assumes a single invariant: **when the operator does NOT set
`camel.server.authenticationPath` / `camel.management.authenticationPath`,
protection should cover every subpath under the context**, achieved by
defaulting the registered path to `/*` on the Vert.x sub-router.

Crucially, the fix assumes that an **explicit** `authenticationPath` is always a
deliberate, correct, *relative-to-sub-router* value (e.g. `/secure/*` for
selective protection). `resolveAuthenticationPath` therefore returns any
non-empty explicit value **verbatim** and **ignores the `contextPath` argument
entirely** (it is passed but never read).

## What code paths the fix explicitly covers

- The **default** (no explicit `authenticationPath`) branch for:
  - `BasicAuthenticationConfigurer.configureAuthentication(AuthenticationConfig, HttpServerConfigurationProperties)`
  - `BasicAuthenticationConfigurer.configureAuthentication(AuthenticationConfig, HttpManagementServerConfigurationProperties)`
  - `JWTAuthenticationConfigurer.configureAuthentication(...)` (both overloads)
- The fix's own test `BasicAuthenticationSelectivePathTest` covers exactly ONE
  explicit form: `authenticationPath=/secure/*` with `camel.server.path=/api`,
  asserting `/api/secure/data` → 401 and `/api/public` → 200 (selective
  protection by design).

## What the fix does NOT cover (the gap this variant exploits)

1. **Explicit `authenticationPath` set to the context prefix** (e.g. `/api` or
   `/admin`). `resolveAuthenticationPath` returns it verbatim. It is then
   registered on the sub-router via
   `VertxPlatformHttpServer.addAuthenticationHandlersStartingFromMoreSpecificPaths`:
   ```java
   authenticationConfig.getEntries().stream()
       .sorted(this::compareUrlPathsSpecificity)
       .forEach(entry -> subRouter.route(entry.getPath()).handler(entry.createAuthenticationHandler(vertx)));
   ...
   router.route(configuration.getPath() + "*").subRouter(subRouter);  // e.g. router.route("/api*").subRouter(subRouter)
   ```
   Because `Route.subRouter()` matches **relative to the consumed context
   prefix**, `subRouter.route("/api")` matches only the relative path `/api`
   (absolute `/api/api`), **not** `/api/hello`. So every real subpath is
   served without the auth handler executing. The fix did not change this
   branch at all — it behaves identically on 4.14.5 and 4.14.6.

2. **Explicit `authenticationPath` of the form `/api/*`** — registered relative,
   matches only `/api/api/*`, still misses `/api/hello`.

3. **The management server** — the same gap applies via
   `camel.management.authenticationPath=/admin` on the management
   sub-router; the management `/observe/info` endpoint (info disclosure) is
   reachable unauthenticated on the patched build.

4. **No normalization, no warning, no doc clarification.** The `contextPath`
   parameter to `resolveAuthenticationPath` is dead. There is no startup check
   that warns when an explicit `authenticationPath` equals or starts with the
   context path (a configuration that silently downgrades protection from the
   fixed default `/*` to "protect nothing real").

## Why this is a real gap and not pure operator error

- The user-facing Javadoc for the property is:
  > `setAuthenticationPath`: "Set HTTP url path of embedded server that is
  > protected by authentication configuration."
  This wording describes **absolute-URL-path** semantics (parallel to
  `camel.server.path` = "Context-path to use for embedded HTTP server"), which
  actively encourages an operator to set `authenticationPath=/api` to "protect
  the /api URL path". The implementation instead treats it as
  **relative-to-sub-router**. The relative semantics live only in test code
  (`BasicAuthenticationSelectivePathTest`), not in user docs.
- An operator who leaves `authenticationPath` unset gets full protection (the
  fix's default `/*`). An operator who follows the doc and sets it to the
  context prefix gets a **security downgrade to zero effective protection** on
  the patched build, with no warning. This is the same root cause and the same
  sink as CVE-2026-40022, reached via a different data path the fix does not
  touch.

## Threat-model scope note

Camel's security advisory for CVE-2026-40022 scopes the original bug to the
case where `authenticationPath` "is not explicitly set". The fix matches that
scope and is **complete for the default branch**. The variant below is therefore
best characterized as an **incomplete fix + documentation footgun**: the same
auth-bypass sink is still reachable on the patched build through an explicit,
doc-encouraged `authenticationPath` value. A complete fix should normalize an
explicit `authenticationPath` that equals/starts with the context path (strip
the prefix → protect-all), or emit a startup warning, and the Javadoc should
clarify the relative-to-context semantics (and that unset = protect all).

## Behavior comparison (this run, identical app code, only Camel version differs)

| Version | surface | authenticationPath | subpath no-creds | with-creds |
|---|---|---|---|---|
| 4.14.5 | business | unset (default) | `/api/hello` 200 (CVE) | 200 |
| 4.14.5 | business | `/api` (explicit) | `/api/hello` 200 (bypass) | 200 |
| 4.14.5 | management | unset (default) | `/admin/observe/info` 200 (CVE) | 200 |
| 4.14.5 | management | `/admin` (explicit) | `/admin/observe/info` 200 (bypass) | 200 |
| 4.14.6 | business | unset (default) | `/api/hello` **401** (fix works) | 200 |
| 4.14.6 | business | `/api` (explicit) | `/api/hello` **200 (BYPASS on fixed)** | 200 |
| 4.14.6 | management | unset (default) | `/admin/observe/info` **401** (fix works) | 200 |
| 4.14.6 | management | `/admin` (explicit) | `/admin/observe/info` **200 (BYPASS on fixed, info disclosure)** | 200 |

The contrast on the fixed build (default → 401, explicit-prefix → 200) is the
decisive evidence: the fix closes the default but leaves the explicit-prefix
data path open.
