{"repro_id":"REPRO-2026-00267","version":6,"title":"Apache Camel embedded HTTP/management servers can bypass authentication on subpaths when a non-root context path is configured, allowing unauthenticated access to protected routes and management endpoints.","repro_type":"security","status":"published","severity":"medium","description":"When authentication is enabled for Apache Camel's embedded HTTP server or embedded management server (camel-platform-http-main) and a non-root context path (e.g., /api or /admin) is configured via `camel.server.path` or `camel.management.path`, the authentication handler is registered only for the exact context path. Due to the Vert.x sub-router mounting model (`_path_*`), subpaths are not protected unless `camel.server.authenticationPath` / `camel.management.authenticationPath` is explicitly set. As a result, unauthenticated requests to subpaths (e.g., `/api/_route_` or `/admin/observe/info`) can reach protected business routes and management endpoints.","root_cause":"# RCA Report — CVE-2026-40022\n\n## Summary\n\nApache Camel's embedded HTTP server (`camel-platform-http-main`) enables an\nauthentication bypass on subpaths whenever a **non-root context path** (e.g.\n`/api` or `/admin`) is configured via `camel.server.path` /\n`camel.management.path` and the operator does **not** explicitly set\n`camel.server.authenticationPath` / `camel.management.authenticationPath`.\nThe `BasicAuthenticationConfigurer` and `JWTAuthenticationConfigurer` derive\nthe authentication-protected path from `properties.getPath()` (the context path)\nand only special-case the exact root `/`. Because the Vert.x sub-router that\nhosts the platform-http routes is mounted with `router.route(path + \"*\").subRouter(subRouter)`,\nsub-router route matching is performed relative to the consumed context prefix,\nso an auth handler registered at the literal context path (e.g. `/api`) never\nmatches the relative subpaths that the business routes are served on. The net\neffect is that unauthenticated requests to any subpath (e.g. `/api/hello` or\n`/admin/observe/info`) reach protected routes and management endpoints without\nbeing challenged for credentials.\n\n## Impact\n\n- **Package / component affected:** `org.apache.camel:camel-platform-http-main`\n  (and the Vert.x engine `camel-platform-http-vertx` it drives). The same\n  `*AuthenticationConfigurer` classes protect both the embedded HTTP server\n  (`HttpServerConfigurationProperties`) and the embedded management server\n  (`HttpManagementServerConfigurationProperties`).\n- **Affected versions:** `camel-platform-http-main` 4.14.1 before 4.14.6,\n  and 4.18.0 before 4.18.2 (fixed in 4.14.6, 4.18.2, and 4.20.0).\n- **Risk level:** High (advisory severity moderate). Unauthenticated remote\n  access to protected business routes and management endpoints. The management\n  `/observe/info` endpoint can disclose runtime metadata (user, working/home\n  directory, process id, JVM and OS info).\n\n## Impact Parity\n\n- **Disclosed / claimed maximum impact:** Authentication bypass\n  (`authz_bypass`) — unauthenticated HTTP access to protected routes and\n  management endpoints on the embedded server.\n- **Reproduced impact from this run:** `authz_bypass` on the `api_remote`\n  surface — an unauthenticated `GET /api/hello` to a real running Camel Main\n  embedded HTTP server returns `200 OK` (body `ok`) on the vulnerable build,\n  while the identical configuration on the fixed build returns\n  `401 Unauthorized`. Authentication is demonstrably *enabled* (credentials\n  succeed, and the fixed build rejects without them).\n- **Parity:** `full` for the claimed authentication-bypass surface. The\n  management-server variant (`/admin/observe/info`) shares the identical\n  `*AuthenticationConfigurer` code path and root cause; this run focuses on the\n  server-route surface, which is the primary claimed remote vector.\n- **Not demonstrated:** No code execution, memory corruption, or crash is\n  claimed or produced — the issue is purely an authorization bypass, which is\n  what was reproduced.\n\n## Root Cause\n\nIn the vulnerable releases the authentication path is resolved inline inside\neach configurer, e.g. in `BasicAuthenticationConfigurer.configureAuthentication(...)`:\n\n```java\nString path = isNotEmpty(properties.getAuthenticationPath())\n        ? properties.getAuthenticationPath()\n        : properties.getPath();          // <-- falls back to the CONTEXT path\n// root means to authenticate everything\nif (\"/\".equals(path)) {\n    path = \"/*\";\n}\n```\n\nWhen `camel.server.authenticationPath` is unset, `path` becomes the configured\ncontext path (e.g. `/api`). Only the exact root `\"/\"` is widened to `\"/*\"`;\nany other context path is left as an **exact** path. That path is then stored\non the `AuthenticationConfigEntry` and registered on the Vert.x sub-router in\n`VertxPlatformHttpServer`:\n\n```java\n// auth handler registered on the sub-router at the (exact) context path\nauthenticationConfig.getEntries()\n    .forEach(entry -> subRouter.route(entry.getPath()).handler(entry.createAuthenticationHandler(vertx)));\n...\n// sub-router mounted for the context prefix\nrouter.route(configuration.getPath() + \"*\").subRouter(subRouter);   // e.g. router.route(\"/api*\").subRouter(subRouter)\n```\n\nThe platform-http consumer route (`platform-http:/hello`) is registered on the\n*same* sub-router as `subRouter.route(\"/hello\")`. Because `Route.subRouter()`\ndelegates routing to the sub-router **relative to the consumed `/api` prefix**,\na request to `/api/hello` is matched inside the sub-router against the relative\npath `/hello`. The auth handler registered at the literal `/api` therefore\nnever matches a relative subpath, so the `/hello` route is reached without the\nauthentication handler ever executing. A request without credentials returns\n`200 OK` (the route body) instead of `401 Unauthorized`.\n\n### Fix\n\nThe fix (tags `camel-4.14.6`, `camel-4.18.2`, `camel-4.20.0`) centralizes path\nresolution in a new default method on `MainAuthenticationConfigurer` and makes\nboth configurers call it:\n\n```java\n/**\n * Resolves the effective authentication path. When no explicit authentication\n * path is configured, defaults to {@code /*} so that all subpaths under the\n * context path are protected.\n */\ndefault String resolveAuthenticationPath(String authenticationPath, String contextPath) {\n    if (ObjectHelper.isNotEmpty(authenticationPath)) {\n        return authenticationPath;\n    }\n    return \"/*\";\n}\n```\n\nWith the default now `\"/*\"`, the auth handler is registered at\n`subRouter.route(\"/*\")`, which matches the relative subpath `/hello` (and every\nother subpath) → an unauthenticated request is challenged with `401`.\n\n- Advisory: https://camel.apache.org/security/CVE-2026-40022.html\n- Fix location: `components/camel-platform-http-main/src/main/java/org/apache/camel/component/platform/http/main/authentication/`\n  (`MainAuthenticationConfigurer`, `BasicAuthenticationConfigurer`,\n  `JWTAuthenticationConfigurer`) in tags `camel-4.14.6` / `camel-4.18.2` /\n  `camel-4.20.0`.\n\n## Reproduction Steps\n\n1. The self-contained script is `bundle/repro/reproduction_steps.sh`.\n2. It materializes a minimal Camel `Main` application that:\n   - registers a route `from(\"platform-http:/hello\").setBody(constant(\"ok\"))`,\n   - enables the embedded HTTP server with `camel.server.enabled=true`,\n     `camel.server.path=/api`, `camel.server.port=8080`,\n   - enables basic authentication with `camel.server.authenticationEnabled=true`\n     and `camel.server.basicPropertiesFile=auth.properties`\n     (`user.admin=secret`), **without** setting `camel.server.authenticationPath`\n     (the vulnerable precondition).\n   It builds and runs the **real** embedded Camel HTTP server twice with\n   **identical** application code and configuration, changing only the Camel\n   version: `4.14.5` (vulnerable) and `4.14.6` (fixed). For each it sends real\n   HTTP requests to `http://localhost:8080/api/hello` with and without\n   credentials and records the responses. It confirms the bypass when the\n   vulnerable build returns `200` without credentials while the fixed build\n   returns `401` without credentials, then writes\n   `bundle/repro/runtime_manifest.json`.\n3. Expected evidence:\n   - `bundle/logs/vulnerable_4.14.5_no_creds.txt` → `HTTP/1.1 200 OK` body `ok`\n   - `bundle/logs/fixed_4.14.6_no_creds.txt` → `HTTP/1.1 401 Unauthorized` with\n     `WWW-Authenticate: Basic realm=\"vertx-web\"`\n   - `bundle/logs/vulnerable_4.14.5_app.log` and `fixed_4.14.6_app.log` showing\n     `camel.server.authenticationEnabled = true`, `camel.server.path = /api`,\n     `camel.server.basicPropertiesFile = auth.properties`, and\n     `Vert.x HttpServer started on 0.0.0.0:8080` for the respective version.\n   - `bundle/repro/runtime_manifest.json` with `service_started`,\n     `healthcheck_passed`, and `target_path_reached` all `true`.\n\n## Evidence\n\n- `bundle/logs/reproduction_summary.log` — contrast table and verdict.\n- `bundle/logs/vulnerable_4.14.5_no_creds.txt`:\n  ```\n  HTTP/1.1 200 OK\n  transfer-encoding: chunked\n\n  ok\n  ```\n- `bundle/logs/fixed_4.14.6_no_creds.txt`:\n  ```\n  HTTP/1.1 401 Unauthorized\n  WWW-Authenticate: Basic realm=\"vertx-web\"\n  content-length: 12\n\n  Unauthorized\n  ```\n- `bundle/logs/vulnerable_4.14.5_app.log` (key lines):\n  ```\n  ... camel.server.authenticationEnabled = true\n  ... camel.server.basicPropertiesFile = auth.properties\n  ... camel.server.path = /api\n  ... Apache Camel 4.14.5 (camel-1) is starting\n  ... Vert.x HttpServer started on 0.0.0.0:8080\n  ... Apache Camel 4.14.5 (camel-1) started in 149ms\n  ```\n- `bundle/logs/fixed_4.14.6_app.log` (key lines):\n  ```\n  ... camel.server.authenticationEnabled = true\n  ... camel.server.basicPropertiesFile = auth.properties\n  ... camel.server.path = /api\n  ... Apache Camel 4.14.6 (camel-1) is starting\n  ... Vert.x HttpServer started on 0.0.0.0:8080\n  ... Apache Camel 4.14.6 (camel-1) started in 154ms\n  ```\n- Both `with_creds` probes return `200 OK`, proving credentials are accepted and\n  the route itself is healthy.\n- Environment: OpenJDK 17.0.19, Apache Maven 3.9.12, Apache Camel\n  `camel-platform-http-main` 4.14.5 / 4.14.6 (Vert.x 4.5.24 engine),\n  Linux x86_64.\n\n## Recommendations / Next Steps\n\n- **Upgrade** `camel-platform-http-main` to 4.14.6 (4.14.x LTS), 4.18.2\n  (4.18.x LTS), or 4.20.0 as appropriate.\n- **Defense in depth:** explicitly set `camel.server.authenticationPath=/*`\n  (and `camel.management.authenticationPath=/*`) even on patched versions so\n  protection does not depend on the default-resolution behavior.\n- **Audit:** review any deployed Camel Main apps using a non-root\n  `camel.server.path` / `camel.management.path` with authentication enabled but\n  no explicit `authenticationPath`; those are the exposed instances.\n- **Regression test:** add an integration test in Camel that asserts an\n  unauthenticated request to a subpath under a non-root context path returns\n  `401` when authentication is enabled (this run's contrast is a direct\n  template: vulnerable `200` vs fixed `401`).\n\n## Additional Notes\n\n- **Idempotency:** `reproduction_steps.sh` was run twice consecutively; both\n  runs exited `0` and reproduced the identical contrast (`vulnerable 200` /\n  `fixed 401`). The script frees port 8080 and rebuilds cleanly each run.\n- **Real product, not a mock:** the proof runs the actual `org.apache.camel.main.Main`\n  embedded HTTP server (Vert.x/Netty) and drives it with real `curl` HTTP\n  requests over localhost — a genuine `api_remote` boundary.\n- **Property note:** the ticket's reproduction text references\n  `camel.server.authenticationMechanism=basic` and\n  `camel.server.authenticationUsers[0].username/password`. Those properties do\n  not exist in the vulnerable 4.14.x / 4.18.x releases; the real basic-auth\n  mechanism in those versions is selected by setting\n  `camel.server.basicPropertiesFile` (a Vert.x `PropertyFileAuthentication`\n  file), which is what this reproduction uses. The authentication-bypass root\n  cause is identical regardless of how credentials are configured.\n- **Exact-context-path behavior:** on the vulnerable build a request to the\n  exact context path `/api` (no creds) returns `404`, not `401`, because the\n  auth handler registered at the literal `/api` never matches a relative\n  (prefix-stripped) sub-router path — so in practice *no* path is protected,\n  making the bypass total. The fixed build returns `401` for both `/api` and\n  `/api/hello` because the handler is registered at `/*`.\n","cve_id":"CVE-2026-40022","cwe_id":"CWE-288","source_url":"https://nvd.nist.gov/vuln/detail/CVE-2026-40022","package":{"name":"org.apache.camel:camel-platform-http-main","ecosystem":"maven","affected_versions":"4.14.1 before 4.14.6, 4.18.0 before 4.18.2","fixed_version":"4.14.6 / 4.18.2 / 4.20.0"},"reproduced_at":"2026-07-07T21:08:51.815012+00:00","duration_secs":1226.0,"tool_calls":239,"handoffs":3,"total_cost_usd":3.618659580000001,"agent_costs":{"coding":0.6620964900000003,"judge":0.012838350000000002,"repro":1.3807641599999998,"support":0.05244852,"vuln_variant":1.5105120600000002},"cost_breakdown":{"coding":{"accounts/fireworks/routers/glm-5p2-fast":0.6620964900000003},"judge":{"gpt-5.4-mini":0.012838350000000002},"repro":{"accounts/fireworks/routers/glm-5p2-fast":1.3807641599999998},"support":{"accounts/fireworks/routers/glm-5p2-fast":0.05244852},"vuln_variant":{"accounts/fireworks/routers/glm-5p2-fast":1.5105120600000002}},"quality":{"confidence":"high","idempotent_verified":false,"community_verifications":0},"environment":{"sandbox_image":"ghcr.io/n3mes1s/pruva-sandbox@sha256:8096b2518d6022e13d68f885c3b8ded6b4fe607098b1a1ccbfb99abc004d1dc1"},"published_at":"2026-07-07T21:09:21.302134+00:00","retracted":false,"artifacts":[{"path":"bundle/repro/reproduction_steps.sh","filename":"reproduction_steps.sh","size":12508,"category":"reproduction_script"},{"path":"bundle/repro/rca_report.md","filename":"rca_report.md","size":11479,"category":"analysis"},{"path":"bundle/vuln_variant/reproduction_steps.sh","filename":"reproduction_steps.sh","size":18260,"category":"reproduction_script"},{"path":"bundle/vuln_variant/rca_report.md","filename":"rca_report.md","size":13712,"category":"analysis"},{"path":"bundle/coding/proposed_fix.diff","filename":"proposed_fix.diff","size":7394,"category":"patch"},{"path":"bundle/artifact_promotion_manifest.json","filename":"artifact_promotion_manifest.json","size":14535,"category":"other"},{"path":"bundle/artifact_promotion_report.json","filename":"artifact_promotion_report.json","size":14553,"category":"other"},{"path":"bundle/vuln_variant/source_identity.json","filename":"source_identity.json","size":2879,"category":"other"},{"path":"bundle/vuln_variant/root_cause_equivalence.json","filename":"root_cause_equivalence.json","size":3733,"category":"other"},{"path":"bundle/repro/runtime_manifest.json","filename":"runtime_manifest.json","size":894,"category":"other"},{"path":"bundle/repro/validation_verdict.json","filename":"validation_verdict.json","size":921,"category":"other"},{"path":"bundle/logs/vulnerable_4.14.5_no_creds.txt","filename":"vulnerable_4.14.5_no_creds.txt","size":49,"category":"other"},{"path":"bundle/logs/fixed_4.14.6_no_creds.txt","filename":"fixed_4.14.6_no_creds.txt","size":104,"category":"other"},{"path":"bundle/logs/reproduction_summary.log","filename":"reproduction_summary.log","size":672,"category":"log"},{"path":"bundle/logs/vulnerable_4.14.5_app.log","filename":"vulnerable_4.14.5_app.log","size":2423,"category":"log"},{"path":"bundle/logs/fixed_4.14.6_app.log","filename":"fixed_4.14.6_app.log","size":2423,"category":"log"},{"path":"bundle/logs/vulnerable_4.14.5_with_creds.txt","filename":"vulnerable_4.14.5_with_creds.txt","size":49,"category":"other"},{"path":"bundle/logs/fixed_4.14.6_with_creds.txt","filename":"fixed_4.14.6_with_creds.txt","size":49,"category":"other"},{"path":"bundle/vuln_variant/variant_manifest.json","filename":"variant_manifest.json","size":6715,"category":"other"},{"path":"bundle/vuln_variant/validation_verdict.json","filename":"validation_verdict.json","size":3188,"category":"other"},{"path":"bundle/vuln_variant/patch_analysis.md","filename":"patch_analysis.md","size":8250,"category":"documentation"},{"path":"bundle/vuln_variant/runtime_manifest.json","filename":"runtime_manifest.json","size":1904,"category":"other"},{"path":"bundle/logs/variant_summary.log","filename":"variant_summary.log","size":1149,"category":"log"},{"path":"bundle/logs/fixed_biz_bypass_no_creds.txt","filename":"fixed_biz_bypass_no_creds.txt","size":49,"category":"other"},{"path":"bundle/logs/fixed_mgmt_bypass_info_no_creds.txt","filename":"fixed_mgmt_bypass_info_no_creds.txt","size":619,"category":"other"},{"path":"bundle/logs/fixed_biz_default_no_creds.txt","filename":"fixed_biz_default_no_creds.txt","size":104,"category":"other"},{"path":"bundle/logs/fixed_mgmt_default_info_no_creds.txt","filename":"fixed_mgmt_default_info_no_creds.txt","size":104,"category":"other"},{"path":"bundle/logs/fixed_biz_bypass_app.log","filename":"fixed_biz_bypass_app.log","size":2546,"category":"log"},{"path":"bundle/logs/fixed_mgmt_bypass_app.log","filename":"fixed_mgmt_bypass_app.log","size":3901,"category":"log"},{"path":"bundle/coding/verify_fix.sh","filename":"verify_fix.sh","size":12406,"category":"other"},{"path":"bundle/coding/summary_report.md","filename":"summary_report.md","size":9467,"category":"documentation"},{"path":"bundle/coding/verify_contrast.log","filename":"verify_contrast.log","size":456,"category":"log"}]}