This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/main by this push:
new caf68b54f26e CAMEL-24717: camel-opa - do not publish the bearerToken
in the health-check id (#26415)
caf68b54f26e is described below
commit caf68b54f26e2a732664db75099fe7ac52564dd6
Author: Andrea Cosentino <[email protected]>
AuthorDate: Mon Sep 14 17:23:13 2026 +0200
CAMEL-24717: camel-opa - do not publish the bearerToken in the health-check
id (#26415)
The camel-opa producer health check embedded the configured bearerToken in
its health-check id,
exposing the credential in health-check output. The id no longer includes
the token.
Co-authored-by: Claude Opus 4.8 <[email protected]>
---
.../component/opa/OpaProducerHealthCheck.java | 7 +-
.../opa/OpaHealthCheckRegistrationTest.java | 77 ++++++++++++++++++++++
.../component/opa/OpaProducerHealthCheckTest.java | 25 +++++++
3 files changed, 107 insertions(+), 2 deletions(-)
diff --git
a/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaProducerHealthCheck.java
b/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaProducerHealthCheck.java
index c2479d9e4ab9..31d754769dfd 100644
---
a/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaProducerHealthCheck.java
+++
b/components/camel-opa/src/main/java/org/apache/camel/component/opa/OpaProducerHealthCheck.java
@@ -26,6 +26,7 @@ import java.util.Map;
import org.apache.camel.health.HealthCheckResultBuilder;
import org.apache.camel.impl.health.AbstractHealthCheck;
import org.apache.camel.util.ObjectHelper;
+import org.apache.camel.util.URISupport;
/**
* Readiness check for the OPA server a producer sends its decisions to.
@@ -49,7 +50,9 @@ public class OpaProducerHealthCheck extends
AbstractHealthCheck {
private final String policyPath;
public OpaProducerHealthCheck(String serverUrl, String bearerToken, String
policyPath, String id) {
- super("camel", "producer:opa-" + id);
+ // the id is built from the endpoint URI so that two endpoints sharing
a policy path stay distinct, but that
+ // URI carries the bearerToken in the clear and the id is published in
the health output, so sanitize it
+ super("camel", "producer:opa-" + URISupport.sanitizeUri(id));
this.serverUrl = serverUrl;
this.bearerToken = bearerToken;
this.policyPath = policyPath;
@@ -57,7 +60,7 @@ public class OpaProducerHealthCheck extends
AbstractHealthCheck {
@Override
protected void doCall(HealthCheckResultBuilder builder, Map<String,
Object> options) {
- builder.detail("opa.serverUrl", serverUrl);
+ builder.detail("opa.serverUrl", URISupport.sanitizeUri(serverUrl));
builder.detail("opa.policyPath", policyPath);
HttpRequest.Builder request = HttpRequest.newBuilder()
diff --git
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaHealthCheckRegistrationTest.java
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaHealthCheckRegistrationTest.java
new file mode 100644
index 000000000000..e62274016043
--- /dev/null
+++
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaHealthCheckRegistrationTest.java
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.opa;
+
+import java.util.List;
+
+import org.apache.camel.builder.RouteBuilder;
+import org.apache.camel.health.HealthCheck;
+import org.apache.camel.health.HealthCheckHelper;
+import org.apache.camel.health.WritableHealthCheckRepository;
+import org.apache.camel.test.junit6.CamelTestSupport;
+import org.junit.jupiter.api.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/**
+ * Checks what the producer actually registers in the health registry.
+ * <p/>
+ * {@link OpaProducerHealthCheckTest} covers the check in isolation; this one
goes through the route so that the id the
+ * producer hands it is covered too, which is where the token used to leak
from.
+ */
+public class OpaHealthCheckRegistrationTest extends CamelTestSupport {
+
+ private static final String TOKEN = "s3cr3t-token";
+
+ @Override
+ protected RouteBuilder createRouteBuilder() {
+ return new RouteBuilder() {
+ @Override
+ public void configure() {
+ from("direct:primary")
+
.to("opa:authz/allow?serverUrl=http://opa-primary:8181&bearerToken=" + TOKEN);
+ from("direct:secondary")
+
.to("opa:authz/allow?serverUrl=http://opa-secondary:8181");
+ }
+ };
+ }
+
+ private List<HealthCheck> registeredChecks() {
+ WritableHealthCheckRepository repository =
HealthCheckHelper.getHealthCheckRepository(
+ context, "producers", WritableHealthCheckRepository.class);
+ assertThat(repository).isNotNull();
+ // producer health checks are disabled globally by default, so enable
the repository to read them back
+ repository.setEnabled(true);
+ return repository.stream().toList();
+ }
+
+ @Test
+ void neverPublishesTheBearerTokenInAnyRegisteredId() {
+ assertThat(registeredChecks())
+ .isNotEmpty()
+ .allSatisfy(check ->
assertThat(check.getId()).doesNotContain(TOKEN));
+ }
+
+ @Test
+ void keepsEndpointsOnDifferentServersDistinct() {
+ List<String> ids =
registeredChecks().stream().map(HealthCheck::getId).toList();
+
+ assertThat(ids).hasSize(2).doesNotHaveDuplicates();
+ assertThat(ids).anySatisfy(id ->
assertThat(id).contains("opa-primary"));
+ assertThat(ids).anySatisfy(id ->
assertThat(id).contains("opa-secondary"));
+ }
+}
diff --git
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaProducerHealthCheckTest.java
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaProducerHealthCheckTest.java
index 0797c491c393..649c373ac548 100644
---
a/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaProducerHealthCheckTest.java
+++
b/components/camel-opa/src/test/java/org/apache/camel/component/opa/OpaProducerHealthCheckTest.java
@@ -57,6 +57,31 @@ class OpaProducerHealthCheckTest {
return check.call(Map.of());
}
+ @Test
+ void neverPublishesTheBearerTokenInTheHealthCheckId() {
+ // the id is derived from the endpoint URI, which carries the token in
the clear; the id reaches the
+ // health output, so the token must not survive into it
+ OpaProducerHealthCheck check = new OpaProducerHealthCheck(
+ "http://localhost:8181", "s3cr3t-token", "authz/allow",
+
"opa://authz/allow?bearerToken=s3cr3t-token&serverUrl=http://localhost:8181");
+
+ assertThat(check.getId()).doesNotContain("s3cr3t-token");
+ assertThat(check.getId()).contains("serverUrl=http://localhost:8181");
+ }
+
+ @Test
+ void givesEndpointsOnDifferentServersDistinctIds() {
+ OpaProducerHealthCheck primary = new OpaProducerHealthCheck(
+ "http://opa-primary:8181", null, "authz/allow",
+ "opa://authz/allow?serverUrl=http://opa-primary:8181");
+ OpaProducerHealthCheck secondary = new OpaProducerHealthCheck(
+ "http://opa-secondary:8181", null, "authz/allow",
+ "opa://authz/allow?serverUrl=http://opa-secondary:8181");
+
+ assertThat(primary.getId()).isNotEqualTo(secondary.getId());
+ assertThat(primary).isNotEqualTo(secondary);
+ }
+
@Test
void isUpWhenTheServerIsHealthy() throws Exception {
HealthCheck.Result result = call(startServer(200));