This is an automated email from the ASF dual-hosted git repository.

davsclaus 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 315adc6c8394 chore: camel-jbang - the screens mask the secrets of an 
endpoint URI (#29597)
315adc6c8394 is described below

commit 315adc6c8394a8b9aeeba0d12218dd41800769d9
Author: Claus Ibsen <[email protected]>
AuthorDate: Wed Nov 11 10:29:31 2026 +0100

    chore: camel-jbang - the screens mask the secrets of an endpoint URI 
(#29597)
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../dsl/jbang/core/commands/tui/StatusParser.java  | 41 ++++++++++++-------
 .../dsl/jbang/core/commands/tui/Timeline.java      |  4 +-
 .../jbang/core/commands/tui/StatusParserTest.java  | 47 ++++++++++++++++++++++
 .../dsl/jbang/core/commands/tui/TimelineTest.java  | 11 +++++
 4 files changed, 86 insertions(+), 17 deletions(-)

diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
index ace32c6c4231..b549c4d05c8b 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParser.java
@@ -34,6 +34,7 @@ import org.apache.camel.dsl.jbang.core.common.EndpointScope;
 import org.apache.camel.dsl.jbang.core.common.ProcessHelper;
 import org.apache.camel.dsl.jbang.run.common.VersionHelper;
 import org.apache.camel.util.TimeUtils;
+import org.apache.camel.util.URISupport;
 import org.apache.camel.util.json.JsonArray;
 import org.apache.camel.util.json.JsonObject;
 import org.apache.camel.util.json.Jsoner;
@@ -271,7 +272,7 @@ final class StatusParser {
                 ri.description = rj.getString("description");
                 ri.source = rj.getString("source");
                 ri.group = rj.getString("group");
-                ri.from = rj.getString("from");
+                ri.from = masked(rj, "from");
                 ri.state = rj.getString("state");
                 ri.supportsSuspension = 
rj.getBooleanOrDefault("supportsSuspension", false);
                 ri.uptime = rj.getString("uptime");
@@ -328,7 +329,7 @@ final class StatusParser {
                         pi.processor = pj.getString("processor");
                         pi.source = pj.getString("source");
                         pi.level = pj.getIntegerOrDefault("level", 0);
-                        pi.uri = pj.getString("uri");
+                        pi.uri = masked(pj, "uri");
                         // the body types the step has been reached with, and 
their kinds (CAMEL-25750)
                         pi.bodyTypes = stringList(pj.get("bodyTypes"));
                         pi.bodyKinds = stringList(pj.get("bodyKinds"));
@@ -466,7 +467,7 @@ final class StatusParser {
                     JsonObject cj = (JsonObject) c;
                     ConsumerInfo ci = new ConsumerInfo();
                     ci.id = cj.getString("id");
-                    ci.uri = cj.getString("uri");
+                    ci.uri = masked(cj, "uri");
                     ci.state = cj.getString("state");
                     // the consumers console says clazz (class in older 
versions)
                     ci.className = cj.getStringOrDefault("clazz", 
cj.getString("class"));
@@ -543,7 +544,7 @@ final class StatusParser {
                 for (Object p : producerList) {
                     JsonObject pj = (JsonObject) p;
                     ProducerInfo pi = new ProducerInfo();
-                    pi.uri = pj.getString("uri");
+                    pi.uri = masked(pj, "uri");
                     pi.state = pj.getString("state");
                     pi.className = pj.getStringOrDefault("clazz", 
pj.getString("class"));
                     pi.routeId = pj.getString("routeId");
@@ -585,7 +586,7 @@ final class StatusParser {
                     RouteControllerInfo rc = new RouteControllerInfo();
                     rc.routeId = rj.getString("routeId");
                     rc.status = rj.getString("status");
-                    rc.uri = rj.getString("uri");
+                    rc.uri = masked(rj, "uri");
                     rc.supervising = rj.getString("supervising");
                     rc.attempts = rj.getLongOrDefault("attempts", 0);
                     rc.lastAttempt = rj.getLongOrDefault("lastAttempt", 0);
@@ -609,7 +610,7 @@ final class StatusParser {
                 for (Object e : endpointList) {
                     JsonObject ej = (JsonObject) e;
                     EndpointInfo ep = new EndpointInfo();
-                    ep.uri = ej.getString("uri");
+                    ep.uri = masked(ej, "uri");
                     ep.direction = ej.getString("direction");
                     ep.routeId = ej.getString("routeId");
                     ep.hits = TuiHelper.objToLong(ej.get("hits"));
@@ -648,8 +649,8 @@ final class StatusParser {
                     si.direction = sj.getString("direction");
                     si.hosted = Boolean.TRUE.equals(sj.get("hosted"));
                     si.protocol = sj.getString("protocol");
-                    si.serviceUrl = sj.getString("serviceUrl");
-                    si.endpointUri = sj.getString("endpointUri");
+                    si.serviceUrl = masked(sj, "serviceUrl");
+                    si.endpointUri = masked(sj, "endpointUri");
                     si.routeId = sj.getString("routeId");
                     si.hits = TuiHelper.objToLong(sj.get("hits"));
                     info.services.add(si);
@@ -797,7 +798,7 @@ final class StatusParser {
                     JsonObject rj = (JsonObject) r;
                     HttpEndpointInfo ep = new HttpEndpointInfo();
                     ep.fromRest = true;
-                    ep.url = rj.getString("url");
+                    ep.url = masked(rj, "url");
                     ep.method = rj.getString("method");
                     if (ep.method != null) {
                         ep.method = ep.method.toUpperCase(Locale.ENGLISH);
@@ -838,6 +839,16 @@ final class StatusParser {
         fixRestUrlPorts(info);
     }
 
+    /**
+     * An endpoint URI or URL of the status, with its secrets masked as Camel 
masks them in its own logs
+     * (password=xxxxxx): the integration gives some as configured (the 
endpoints and services it has, with
+     * password=RAW(admin)), and the screens are not only for the developer of 
the integration.
+     */
+    static String masked(JsonObject json, String key) {
+        String value = json.getString(key);
+        return value != null ? URISupport.sanitizeUri(value) : null;
+    }
+
     /**
      * Configuration properties.
      */
@@ -1030,7 +1041,7 @@ final class StatusParser {
             } else {
                 entry.direction = entry.remoteEndpoint ? "*-->" : "*-> ";
             }
-            String uri = json.getString("endpointUri");
+            String uri = masked(json, "endpointUri");
             if (uri != null) {
                 entry.processor = indent + "from[" + uri + "]";
             } else {
@@ -1138,7 +1149,7 @@ final class StatusParser {
         }
         String indent = "  ".repeat(entry.nodeLevel);
         if (entry.first) {
-            String uri = json.getString("endpointUri");
+            String uri = masked(json, "endpointUri");
             if (uri != null) {
                 entry.processor = indent + "from[" + uri + "]";
             } else {
@@ -1318,7 +1329,7 @@ final class StatusParser {
             ep.fromRest = false;
             ep.management = management;
             ep.server = phpObj.getString("server");
-            ep.url = ej.getString("url");
+            ep.url = masked(ej, "url");
             ep.path = ej.getString("path");
             ep.method = ej.getString("verbs");
             ep.consumes = ej.getString("consumes");
@@ -1423,7 +1434,7 @@ final class StatusParser {
         for (Object c : consumers) {
             JsonObject cj = (JsonObject) c;
             String routeId = cj.getString("routeId");
-            String uri = cj.getString("uri");
+            String uri = masked(cj, "uri");
             JsonArray workers = (JsonArray) cj.get("workers");
             if (workers != null) {
                 for (Object w : workers) {
@@ -1538,8 +1549,8 @@ final class StatusParser {
             if (elapsed != null) {
                 ei.elapsed = elapsed;
             }
-            ei.endpointUri = ej.getString("endpointUri");
-            ei.fromEndpointUri = ej.getString("fromEndpointUri");
+            ei.endpointUri = masked(ej, "endpointUri");
+            ei.fromEndpointUri = masked(ej, "fromEndpointUri");
             JsonObject ex = (JsonObject) ej.get("exception");
             if (ex != null) {
                 ei.exceptionType = ex.getString("type");
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Timeline.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Timeline.java
index c6e4892b962f..7cb307733900 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Timeline.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/main/java/org/apache/camel/dsl/jbang/core/commands/tui/Timeline.java
@@ -293,12 +293,12 @@ final class Timeline {
         if (e.get("endpointSends") instanceof List<?> sends) {
             for (Object o : sends) {
                 if (o instanceof JsonObject s) {
-                    calls.add(new Call(s.getString("endpointUri"), 
s.getLongOrDefault("elapsed", 0)));
+                    calls.add(new Call(StatusParser.masked(s, "endpointUri"), 
s.getLongOrDefault("elapsed", 0)));
                 }
             }
         }
         return new Kept(
-                e.getString("exchangeId"), e.getString("routeId"), 
e.getString("fromEndpointUri"),
+                e.getString("exchangeId"), e.getString("routeId"), 
StatusParser.masked(e, "fromEndpointUri"),
                 e.getLongOrDefault("timestamp", 0), 
e.getLongOrDefault("elapsed", 0), e.getBooleanOrDefault("failed", false),
                 e.getBooleanOrDefault("handled", false), 
e.getBooleanOrDefault("redelivered", false),
                 e.getString("exception"), e.getString("chaosRule"), 
List.copyOf(calls));
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParserTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParserTest.java
index dc90a7f944f5..22a9250cc499 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParserTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/StatusParserTest.java
@@ -75,6 +75,53 @@ class StatusParserTest {
         assertEquals(List.of("remote", "internal", "local"), 
info.endpoints.stream().map(ep -> ep.scope).toList());
     }
 
+    @Test
+    void theSecretsOfAnEndpointAreMasked() {
+        // connect-service/ftp: the endpoints and services the integration has 
carry its URI as configured
+        String uri = 
"ftp://localhost:2121/courier?passiveMode=true&password=RAW(admin)&username=RAW(admin)";
+        JsonObject ep = new JsonObject();
+        ep.put("uri", uri);
+        JsonArray endpoints = new JsonArray();
+        endpoints.add(ep);
+        JsonObject endpointHolder = new JsonObject();
+        endpointHolder.put("endpoints", endpoints);
+        JsonObject service = new JsonObject();
+        service.put("endpointUri", uri);
+        service.put("serviceUrl", "ftp://admin:secret@localhost:2121";);
+        JsonArray services = new JsonArray();
+        services.add(service);
+        JsonObject serviceHolder = new JsonObject();
+        serviceHolder.put("services", services);
+        JsonObject root = new JsonObject();
+        root.put("context", new JsonObject());
+        root.put("endpoints", endpointHolder);
+        root.put("services", serviceHolder);
+
+        IntegrationInfo info = 
StatusParser.parseIntegration(ProcessHandle.current(), root);
+
+        String masked = 
"ftp://localhost:2121/courier?passiveMode=true&password=xxxxxx&username=xxxxxx";;
+        assertEquals(masked, info.endpoints.get(0).uri);
+        assertEquals(masked, info.services.get(0).endpointUri);
+        assertFalse(info.services.get(0).serviceUrl.contains("secret"), 
info.services.get(0).serviceUrl);
+    }
+
+    @Test
+    void theSecretsOfTheEndpointsOfAnErrorAreMasked() {
+        JsonObject error = new JsonObject();
+        error.put("routeId", "courier");
+        error.put("endpointUri", 
"ftp://localhost:2121/courier?password=RAW(admin)");
+        error.put("fromEndpointUri", 
"sftp://localhost:2222/inbox?password=RAW(secret)");
+        JsonArray errors = new JsonArray();
+        errors.add(error);
+        JsonObject root = new JsonObject();
+        root.put("errors", errors);
+
+        ErrorInfo ei = StatusParser.parseErrors(root).get(0);
+
+        assertEquals("ftp://localhost:2121/courier?password=xxxxxx";, 
ei.endpointUri);
+        assertEquals("sftp://localhost:2222/inbox?password=xxxxxx";, 
ei.fromEndpointUri);
+    }
+
     @Test
     void aFailedReloadIsReadAsCamelPsReadsIt() {
         JsonObject root = new JsonObject();
diff --git 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTest.java
 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTest.java
index c8880cc97e2a..1f7c5efc4309 100644
--- 
a/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTest.java
+++ 
b/dsl/camel-jbang/camel-jbang-plugin-tui/src/test/java/org/apache/camel/dsl/jbang/core/commands/tui/TimelineTest.java
@@ -116,6 +116,17 @@ class TimelineTest {
         assertThat(b.calls()).containsExactly(new 
Timeline.Call("http://stock";, 850), new Timeline.Call("kafka:orders", 5));
     }
 
+    @Test
+    void theSecretsOfTheEndpointsAnExchangeCalledAreMasked() {
+        timeline.merge("1234", new TimelineJson(S0, 1)
+                .exchange("A", "orders", 0, 20, "completed", null, null,
+                        "ftp://localhost:2121/courier?password=RAW(admin)@5")
+                .json(), false);
+
+        Timeline.Kept a = timeline.exchanges("1234", S0, S0).get(0);
+        assertThat(a.calls()).containsExactly(new 
Timeline.Call("ftp://localhost:2121/courier?password=xxxxxx";, 5));
+    }
+
     @Test
     void 
theIntegrationRecordsTheFaultsOfTheChaosRulesAndTheRoutesTheyHadDown() throws 
Exception {
         JsonObject json = new TimelineJson(S0 + 2, 3).route("orders", 
"completed", 0, 1)

Reply via email to