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

jamesnetherton pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-quarkus.git


The following commit(s) were added to refs/heads/main by this push:
     new 64521b045d Restrict Dev UI JSON-RPC bridge to declared console IDs, 
allowed options and values
64521b045d is described below

commit 64521b045d5bfd42e910c8e53fabd9a9a55420e7
Author: James Netherton <[email protected]>
AuthorDate: Fri Jul 24 14:16:51 2026 +0100

    Restrict Dev UI JSON-RPC bridge to declared console IDs, allowed options 
and values
    
    Co-authored-by: Claude Opus 4.6 <[email protected]>
---
 .../contributor-guide/create-new-dev-ui-page.adoc  | 34 ++++++++-
 .../devui/CamelCoreDevConsoleProcessor.java        | 80 +++++++++++++++++---
 .../core/deployment/devui/CamelDevUIConstants.java | 46 ++++++++++++
 .../devui/CamelQuarkusCoreDevUITest.java           | 27 +++++++
 .../quarkus/devui/CamelCoreDevUIRecorder.java      | 33 +++++++++
 .../camel/quarkus/devui/CamelCoreDevUIService.java | 85 +++++++++++++++++++++-
 .../devui/JasyptUtilsDevUIProcessor.java           |  4 +-
 .../deployment/devui/MicrometerDevUIProcessor.java |  4 +-
 .../MicroprofileFaultToleranceDevUIProcessor.java  |  4 +-
 .../devui/VertxWebsocketDevUIProcessor.java        |  4 +-
 10 files changed, 304 insertions(+), 17 deletions(-)

diff --git 
a/docs/modules/ROOT/pages/contributor-guide/create-new-dev-ui-page.adoc 
b/docs/modules/ROOT/pages/contributor-guide/create-new-dev-ui-page.adoc
index 0b5cdd58e9..b3315011a0 100644
--- a/docs/modules/ROOT/pages/contributor-guide/create-new-dev-ui-page.adoc
+++ b/docs/modules/ROOT/pages/contributor-guide/create-new-dev-ui-page.adoc
@@ -65,7 +65,39 @@ customElements.define('qwc-camel-foo', QwcCamelFoo);
 <4> When iterating over the console data you can access the returned JSON 
fields by referring to their name. E.g. if the console returns JSON `[{"dataA": 
"valueA", "dataB": "valueB"}]` you can refer to the data like `item.dataA` etc.
 <5> You must register the component at the end of the file.
 +
-3. Finally, you must create a `BuildStep` to add a UI link to the extension 
Dev UI card as described in the 
https://quarkus.io/guides/dev-ui#adding-pages-to-the-dev-ui[guide].
+3. In the `BuildStep` that registers the Dev UI card page, you must set the 
`consoleId` metadata on each page that uses a Camel console. This is required 
for security — only console IDs declared via metadata are accessible through 
the Dev UI JSON-RPC bridge.
++
+[source,java]
+----
+import org.apache.camel.quarkus.core.deployment.devui.CamelDevUIConstants;
+
+cardPageBuildItem.addPage(Page.webComponentPageBuilder()
+        .title("Foo")
+        .icon("font-awesome-solid:bars")
+        .componentLink("qwc-camel-foo.js")
+        .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, "foo"));
+----
++
+If a page with a `qwc-camel` component link does not declare this metadata, 
the build will fail.
+Pages that do not use the JSON-RPC console bridge (e.g. pages extending 
`LitElement` directly) should set the value to 
`CamelDevUIConstants.CONSOLE_ID_NONE`.
++
+If your page passes options to the Camel console (via the second argument to 
`super()` or via `super.putOption()`), you must also declare the allowed option 
keys and their permitted values:
++
+[source,java]
+----
+// Only the 'limit' option is allowed, with any value
+.metadata(CamelDevUIConstants.ALLOWED_OPTIONS_METADATA_KEY, "limit=*")
+
+// Only the 'dump' option is allowed, and only with value 'false'
+.metadata(CamelDevUIConstants.ALLOWED_OPTIONS_METADATA_KEY, "dump=false")
+
+// Multiple options: semicolon-separated. Multiple allowed values: 
pipe-separated
+.metadata(CamelDevUIConstants.ALLOWED_OPTIONS_METADATA_KEY, 
"key1=val1|val2;key2=*")
+----
++
+Options not declared in this metadata are silently stripped at runtime. Pages 
that do not use options need not declare this metadata — the secure default is 
to allow no options.
++
+4. Finally, the Dev UI card must be registered as described in the 
https://quarkus.io/guides/dev-ui#adding-pages-to-the-dev-ui[guide].
 
 === Finding the Camel console ID
 
diff --git 
a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/devui/CamelCoreDevConsoleProcessor.java
 
b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/devui/CamelCoreDevConsoleProcessor.java
index dabc356296..c3fdcf7b87 100644
--- 
a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/devui/CamelCoreDevConsoleProcessor.java
+++ 
b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/devui/CamelCoreDevConsoleProcessor.java
@@ -16,13 +16,22 @@
  */
 package org.apache.camel.quarkus.core.deployment.devui;
 
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
 import io.quarkus.deployment.IsDevelopment;
 import io.quarkus.deployment.annotations.BuildProducer;
 import io.quarkus.deployment.annotations.BuildStep;
 import io.quarkus.deployment.annotations.BuildSteps;
+import io.quarkus.deployment.annotations.ExecutionTime;
+import io.quarkus.deployment.annotations.Record;
 import io.quarkus.devui.spi.JsonRPCProvidersBuildItem;
 import io.quarkus.devui.spi.page.CardPageBuildItem;
 import io.quarkus.devui.spi.page.Page;
+import org.apache.camel.quarkus.devui.CamelCoreDevUIRecorder;
 import org.apache.camel.quarkus.devui.CamelCoreDevUIService;
 
 @BuildSteps(onlyIf = IsDevelopment.class)
@@ -35,47 +44,58 @@ public class CamelCoreDevConsoleProcessor {
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("Blocked Exchanges")
                 .icon("font-awesome-solid:circle-xmark")
-                .componentLink("qwc-camel-core-blocked-exchanges.js"));
+                .componentLink("qwc-camel-core-blocked-exchanges.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
"blocked"));
 
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("Browse")
                 .icon("font-awesome-solid:magnifying-glass")
-                .componentLink("qwc-camel-core-browse.js"));
+                .componentLink("qwc-camel-core-browse.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
"browse")
+                .metadata(CamelDevUIConstants.ALLOWED_OPTIONS_METADATA_KEY, 
"dump=false"));
 
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("Camel Context")
                 .icon("font-awesome-solid:gear")
-                .componentLink("qwc-camel-core-context.js"));
+                .componentLink("qwc-camel-core-context.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
"context"));
 
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("Diagram")
                 .icon("font-awesome-solid:sitemap")
-                .componentLink("qwc-camel-core-diagram.js"));
+                .componentLink("qwc-camel-core-diagram.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
CamelDevUIConstants.CONSOLE_ID_NONE));
 
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("Events")
                 .icon("font-awesome-solid:bolt-lightning")
-                .componentLink("qwc-camel-core-events.js"));
+                .componentLink("qwc-camel-core-events.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
"event"));
 
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("Inflight Exchanges")
                 .icon("font-awesome-solid:plane")
-                .componentLink("qwc-camel-core-inflight-exchanges.js"));
+                .componentLink("qwc-camel-core-inflight-exchanges.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
"inflight"));
 
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("REST")
                 .icon("font-awesome-solid:circle-nodes")
-                .componentLink("qwc-camel-core-rest.js"));
+                .componentLink("qwc-camel-core-rest.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
"rest"));
 
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("Routes")
                 .icon("font-awesome-solid:route")
-                .componentLink("qwc-camel-core-routes.js"));
+                .componentLink("qwc-camel-core-routes.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, "route")
+                .metadata(CamelDevUIConstants.ALLOWED_OPTIONS_METADATA_KEY, 
"limit=*"));
 
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("Variables")
                 .icon("font-awesome-solid:code")
-                .componentLink("qwc-camel-core-variables.js"));
+                .componentLink("qwc-camel-core-variables.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
"variables"));
 
         cardsProducer.produce(cardPageBuildItem);
     }
@@ -84,4 +104,46 @@ public class CamelCoreDevConsoleProcessor {
     JsonRPCProvidersBuildItem createJsonRPCServiceForCache() {
         return new JsonRPCProvidersBuildItem(CamelCoreDevUIService.class);
     }
+
+    @BuildStep
+    @Record(ExecutionTime.STATIC_INIT)
+    void recordAllowedConsoleMetadata(
+            CamelCoreDevUIRecorder recorder,
+            List<CardPageBuildItem> cardPages) {
+        Set<String> allowedIds = new HashSet<>();
+        Map<String, String> allowedOptions = new HashMap<>();
+
+        cardPages.stream()
+                .flatMap(card -> card.getPages().stream())
+                .forEach(pageBuilder -> {
+                    Page page = pageBuilder.build();
+                    String componentLink = page.getComponentLink();
+                    boolean isCamelPage = componentLink != null && 
componentLink.startsWith("qwc-camel");
+                    if (!isCamelPage) {
+                        return;
+                    }
+
+                    Map<String, String> metadata = page.getMetadata();
+                    boolean hasConsoleId = metadata != null
+                            && 
metadata.containsKey(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY);
+                    if (!hasConsoleId) {
+                        throw new IllegalStateException(
+                                "Camel Dev UI page '" + componentLink
+                                        + "' is missing required metadata key 
'" + CamelDevUIConstants.CONSOLE_ID_METADATA_KEY
+                                        + "'. Set it to the Camel dev console 
id, or to CamelDevUIConstants.CONSOLE_ID_NONE"
+                                        + " if the page does not use the 
JSON-RPC console bridge.");
+                    }
+
+                    String consoleId = 
metadata.get(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY);
+                    if 
(!CamelDevUIConstants.CONSOLE_ID_NONE.equals(consoleId)) {
+                        allowedIds.add(consoleId);
+                        if 
(metadata.containsKey(CamelDevUIConstants.ALLOWED_OPTIONS_METADATA_KEY)) {
+                            allowedOptions.put(consoleId, 
metadata.get(CamelDevUIConstants.ALLOWED_OPTIONS_METADATA_KEY));
+                        }
+                    }
+                });
+
+        recorder.setAllowedConsoleIds(allowedIds);
+        recorder.setAllowedConsoleOptions(allowedOptions);
+    }
 }
diff --git 
a/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/devui/CamelDevUIConstants.java
 
b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/devui/CamelDevUIConstants.java
new file mode 100644
index 0000000000..0500b6c6fa
--- /dev/null
+++ 
b/extensions-core/core/deployment/src/main/java/org/apache/camel/quarkus/core/deployment/devui/CamelDevUIConstants.java
@@ -0,0 +1,46 @@
+/*
+ * 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.quarkus.core.deployment.devui;
+
+/**
+ * Constants for Camel Dev UI page metadata.
+ */
+public final class CamelDevUIConstants {
+
+    /**
+     * Page metadata key for the Camel dev console id. Every {@code 
qwc-camel*} page must set this
+     * metadata so that the console id is included in the runtime allowlist 
for the JSON-RPC bridge.
+     */
+    public static final String CONSOLE_ID_METADATA_KEY = "consoleId";
+
+    /**
+     * Sentinel value for {@link #CONSOLE_ID_METADATA_KEY} indicating the page 
does not use the
+     * JSON-RPC console bridge (e.g. pages extending {@code LitElement} 
directly).
+     */
+    public static final String CONSOLE_ID_NONE = "none";
+
+    /**
+     * Page metadata key for allowed console options. The value is a 
semicolon-delimited
+     * specification of allowed option keys and their permitted values.
+     * Format: {@code key1=val1|val2;key2=*} where {@code *} means any value 
is accepted.
+     * Pages that do not declare this metadata allow no options (secure 
default).
+     */
+    public static final String ALLOWED_OPTIONS_METADATA_KEY = "allowedOptions";
+
+    private CamelDevUIConstants() {
+    }
+}
diff --git 
a/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/deployment/devui/CamelQuarkusCoreDevUITest.java
 
b/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/deployment/devui/CamelQuarkusCoreDevUITest.java
index 63708016f4..6802a498f7 100644
--- 
a/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/deployment/devui/CamelQuarkusCoreDevUITest.java
+++ 
b/extensions-core/core/deployment/src/test/java/org/apache/camel/quarkus/core/deployment/devui/CamelQuarkusCoreDevUITest.java
@@ -43,4 +43,31 @@ public class CamelQuarkusCoreDevUITest extends 
DevUIJsonRPCTest {
         assertEquals("camel-1", json.getString("name"));
         assertEquals("Started", json.getString("state"));
     }
+
+    @Test
+    void getNonAllowedConsoleReturnsEmptyJSON() throws Exception {
+        // 'send' console is registered from camel-console, but is not in the 
allowlist, so expect an empty JSON response
+        String result = super.executeJsonRPCMethod(String.class, 
"getConsoleJSON",
+                Map.of("id", "send", "options", Map.of()));
+        assertEquals("{}", result);
+    }
+
+    @Test
+    void browseConsoleStripsDisallowedDumpTrue() throws Exception {
+        String withBadOption = super.executeJsonRPCMethod(String.class, 
"getConsoleJSON",
+                Map.of("id", "browse", "options", Map.of("dump", "true")));
+        String withGoodOption = super.executeJsonRPCMethod(String.class, 
"getConsoleJSON",
+                Map.of("id", "browse", "options", Map.of("dump", "false")));
+        assertEquals(withGoodOption, withBadOption);
+    }
+
+    @Test
+    void contextConsoleStripsAllOptions() throws Exception {
+        // context has no allowed options, so malicious keys should be 
stripped and the console should still respond
+        String result = super.executeJsonRPCMethod(String.class, 
"getConsoleJSON",
+                Map.of("id", "context", "options", Map.of("malicious", 
"payload")));
+        JsonObject json = (JsonObject) Json.decodeValue(result);
+        assertEquals("camel-1", json.getString("name"));
+        assertEquals("Started", json.getString("state"));
+    }
 }
diff --git 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/devui/CamelCoreDevUIRecorder.java
 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/devui/CamelCoreDevUIRecorder.java
new file mode 100644
index 0000000000..6af0b5348c
--- /dev/null
+++ 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/devui/CamelCoreDevUIRecorder.java
@@ -0,0 +1,33 @@
+/*
+ * 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.quarkus.devui;
+
+import java.util.Map;
+import java.util.Set;
+
+import io.quarkus.runtime.annotations.Recorder;
+
+@Recorder
+public class CamelCoreDevUIRecorder {
+    public void setAllowedConsoleIds(Set<String> consoleIds) {
+        CamelCoreDevUIService.setAllowedConsoleIds(consoleIds);
+    }
+
+    public void setAllowedConsoleOptions(Map<String, String> optionSpecs) {
+        CamelCoreDevUIService.setAllowedConsoleOptions(optionSpecs);
+    }
+}
diff --git 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/devui/CamelCoreDevUIService.java
 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/devui/CamelCoreDevUIService.java
index 04b2ae053f..b6045aeaed 100644
--- 
a/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/devui/CamelCoreDevUIService.java
+++ 
b/extensions-core/core/runtime/src/main/java/org/apache/camel/quarkus/devui/CamelCoreDevUIService.java
@@ -17,8 +17,11 @@
 package org.apache.camel.quarkus.devui;
 
 import java.time.Duration;
+import java.util.Collections;
+import java.util.HashMap;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Set;
 import java.util.concurrent.ConcurrentHashMap;
 
 import io.quarkus.arc.Arc;
@@ -40,8 +43,38 @@ import org.jboss.logging.Logger;
 @ApplicationScoped
 public class CamelCoreDevUIService {
     private static final Logger LOG = 
Logger.getLogger(CamelCoreDevUIService.class);
+    private static volatile Set<String> ALLOWED_CONSOLE_IDS = 
Collections.emptySet();
+    private static volatile Map<String, Map<String, Set<String>>> 
ALLOWED_CONSOLE_OPTIONS = Collections.emptyMap();
     private final Map<String, ConsoleSubscription> PROCESSORS = new 
ConcurrentHashMap<>();
 
+    static void setAllowedConsoleIds(Set<String> consoleIds) {
+        ALLOWED_CONSOLE_IDS = Set.copyOf(consoleIds);
+    }
+
+    static void setAllowedConsoleOptions(Map<String, String> optionSpecs) {
+        Map<String, Map<String, Set<String>>> parsed = new HashMap<>();
+        for (Map.Entry<String, String> entry : optionSpecs.entrySet()) {
+            String spec = entry.getValue();
+            Map<String, Set<String>> optionRules = new HashMap<>();
+            if (spec != null && !spec.isEmpty()) {
+                for (String optionEntry : spec.split(";")) {
+                    String[] parts = optionEntry.split("=", 2);
+                    if (parts.length == 2) {
+                        String optionKey = parts[0].trim();
+                        String valuesStr = parts[1].trim();
+                        if ("*".equals(valuesStr)) {
+                            optionRules.put(optionKey, Set.of("*"));
+                        } else {
+                            optionRules.put(optionKey, 
Set.of(valuesStr.split("\\|")));
+                        }
+                    }
+                }
+            }
+            parsed.put(entry.getKey(), 
Collections.unmodifiableMap(optionRules));
+        }
+        ALLOWED_CONSOLE_OPTIONS = Collections.unmodifiableMap(parsed);
+    }
+
     /**
      * Deactivates a Dev UI subscription for the given Camel console id.
      *
@@ -67,7 +100,11 @@ public class CamelCoreDevUIService {
      * @return         JSON String representation of the Camel console
      */
     public String getConsoleJSON(String id, Map<String, Object> options) {
-        return getOrCreateConsoleSubscription(id, options).callDevConsole();
+        if (!isConsoleAllowed(id)) {
+            LOG.debugf("Rejected Dev UI request for console '%s' — not in the 
allowlist", id);
+            return "{}";
+        }
+        return getOrCreateConsoleSubscription(id, sanitizeOptions(id, 
options)).callDevConsole();
     }
 
     /**
@@ -78,7 +115,11 @@ public class CamelCoreDevUIService {
      * @return         {@link Multi} of JSON String representation of the 
Camel console
      */
     public Multi<String> streamConsole(String id, Map<String, Object> options) 
{
-        return getOrCreateConsoleSubscription(id, options).getBroadcaster();
+        if (!isConsoleAllowed(id)) {
+            LOG.debugf("Rejected Dev UI stream request for console '%s' — not 
in the allowlist", id);
+            return Multi.createFrom().empty();
+        }
+        return getOrCreateConsoleSubscription(id, sanitizeOptions(id, 
options)).getBroadcaster();
     }
 
     /**
@@ -89,12 +130,50 @@ public class CamelCoreDevUIService {
      * @return         {@code true} if the options were updated, else {@code 
false}
      */
     public boolean updateConsoleOptions(String id, Map<String, Object> 
options) {
+        if (!isConsoleAllowed(id)) {
+            LOG.debugf("Rejected Dev UI options update for console '%s' — not 
in the allowlist", id);
+            return false;
+        }
+        Map<String, Object> sanitized = sanitizeOptions(id, options);
         return PROCESSORS.computeIfPresent(id, (key, consoleSubscription) -> {
-            consoleSubscription.setOptions(options);
+            consoleSubscription.setOptions(sanitized);
             return consoleSubscription;
         }) != null;
     }
 
+    private static boolean isConsoleAllowed(String id) {
+        return ALLOWED_CONSOLE_IDS.contains(id);
+    }
+
+    private static Map<String, Object> sanitizeOptions(String id, Map<String, 
Object> options) {
+        if (options == null || options.isEmpty()) {
+            return Collections.emptyMap();
+        }
+        Map<String, Set<String>> rules = ALLOWED_CONSOLE_OPTIONS.get(id);
+        if (rules == null || rules.isEmpty()) {
+            if (!options.isEmpty()) {
+                LOG.debugf("Stripped all options for console '%s' — no options 
are allowed", id);
+            }
+            return Collections.emptyMap();
+        }
+        Map<String, Object> sanitized = new HashMap<>();
+        for (Map.Entry<String, Object> entry : options.entrySet()) {
+            String key = entry.getKey();
+            Set<String> allowedValues = rules.get(key);
+            if (allowedValues == null) {
+                LOG.debugf("Stripped disallowed option key '%s' for console 
'%s'", key, id);
+                continue;
+            }
+            String stringValue = String.valueOf(entry.getValue());
+            if (allowedValues.contains("*") || 
allowedValues.contains(stringValue)) {
+                sanitized.put(key, entry.getValue());
+            } else {
+                LOG.debugf("Stripped option '%s' for console '%s' — value '%s' 
is not allowed", key, id, stringValue);
+            }
+        }
+        return sanitized;
+    }
+
     ConsoleSubscription getOrCreateConsoleSubscription(String id, Map<String, 
Object> options) {
         return PROCESSORS.computeIfAbsent(id, consoleId -> new 
ConsoleSubscription(consoleId, options));
     }
diff --git 
a/extensions/jasypt/deployment/src/main/java/org/apache/camel/quarkus/component/jasypt/deployment/devui/JasyptUtilsDevUIProcessor.java
 
b/extensions/jasypt/deployment/src/main/java/org/apache/camel/quarkus/component/jasypt/deployment/devui/JasyptUtilsDevUIProcessor.java
index 96abf86c05..2e90d2f43e 100644
--- 
a/extensions/jasypt/deployment/src/main/java/org/apache/camel/quarkus/component/jasypt/deployment/devui/JasyptUtilsDevUIProcessor.java
+++ 
b/extensions/jasypt/deployment/src/main/java/org/apache/camel/quarkus/component/jasypt/deployment/devui/JasyptUtilsDevUIProcessor.java
@@ -26,6 +26,7 @@ import io.quarkus.devui.spi.page.CardPageBuildItem;
 import io.quarkus.devui.spi.page.Page;
 import org.apache.camel.quarkus.component.jasypt.CamelJasyptBuildTimeConfig;
 import org.apache.camel.quarkus.component.jasypt.CamelJasyptDevUIService;
+import org.apache.camel.quarkus.core.deployment.devui.CamelDevUIConstants;
 
 @BuildSteps(onlyIf = { IsDevelopment.class, 
JasyptUtilsDevUIProcessor.CamelJasyptEnabled.class })
 public class JasyptUtilsDevUIProcessor {
@@ -35,7 +36,8 @@ public class JasyptUtilsDevUIProcessor {
         card.addPage(Page.webComponentPageBuilder()
                 .title("Utilities")
                 .componentLink("qwc-camel-jasypt-utils.js")
-                .icon("font-awesome-solid:house-lock"));
+                .icon("font-awesome-solid:house-lock")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
CamelDevUIConstants.CONSOLE_ID_NONE));
         return card;
     }
 
diff --git 
a/extensions/micrometer/deployment/src/main/java/org/apache/camel/quarkus/component/micrometer/deployment/devui/MicrometerDevUIProcessor.java
 
b/extensions/micrometer/deployment/src/main/java/org/apache/camel/quarkus/component/micrometer/deployment/devui/MicrometerDevUIProcessor.java
index 660d06d46d..accd79a417 100644
--- 
a/extensions/micrometer/deployment/src/main/java/org/apache/camel/quarkus/component/micrometer/deployment/devui/MicrometerDevUIProcessor.java
+++ 
b/extensions/micrometer/deployment/src/main/java/org/apache/camel/quarkus/component/micrometer/deployment/devui/MicrometerDevUIProcessor.java
@@ -22,6 +22,7 @@ import io.quarkus.deployment.annotations.BuildStep;
 import io.quarkus.deployment.annotations.BuildSteps;
 import io.quarkus.devui.spi.page.CardPageBuildItem;
 import io.quarkus.devui.spi.page.Page;
+import org.apache.camel.quarkus.core.deployment.devui.CamelDevUIConstants;
 
 @BuildSteps(onlyIf = IsDevelopment.class)
 public class MicrometerDevUIProcessor {
@@ -32,7 +33,8 @@ public class MicrometerDevUIProcessor {
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("Metrics")
                 .icon("font-awesome-solid:chart-line")
-                .componentLink("qwc-camel-micrometer.js"));
+                .componentLink("qwc-camel-micrometer.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
"micrometer"));
 
         cardsProducer.produce(cardPageBuildItem);
     }
diff --git 
a/extensions/microprofile-fault-tolerance/deployment/src/main/java/org/apache/camel/quarkus/component/microprofile/fault/tolerance/deployment/devui/MicroprofileFaultToleranceDevUIProcessor.java
 
b/extensions/microprofile-fault-tolerance/deployment/src/main/java/org/apache/camel/quarkus/component/microprofile/fault/tolerance/deployment/devui/MicroprofileFaultToleranceDevUIProcessor.java
index ce36b79d72..a01b298d8e 100644
--- 
a/extensions/microprofile-fault-tolerance/deployment/src/main/java/org/apache/camel/quarkus/component/microprofile/fault/tolerance/deployment/devui/MicroprofileFaultToleranceDevUIProcessor.java
+++ 
b/extensions/microprofile-fault-tolerance/deployment/src/main/java/org/apache/camel/quarkus/component/microprofile/fault/tolerance/deployment/devui/MicroprofileFaultToleranceDevUIProcessor.java
@@ -22,6 +22,7 @@ import io.quarkus.deployment.annotations.BuildStep;
 import io.quarkus.deployment.annotations.BuildSteps;
 import io.quarkus.devui.spi.page.CardPageBuildItem;
 import io.quarkus.devui.spi.page.Page;
+import org.apache.camel.quarkus.core.deployment.devui.CamelDevUIConstants;
 
 @BuildSteps(onlyIf = IsDevelopment.class)
 public class MicroprofileFaultToleranceDevUIProcessor {
@@ -32,7 +33,8 @@ public class MicroprofileFaultToleranceDevUIProcessor {
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("Circuit Breakers")
                 .icon("font-awesome-solid:plug")
-                .componentLink("qwc-camel-microprofile-fault-tolerance.js"));
+                .componentLink("qwc-camel-microprofile-fault-tolerance.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
"fault-tolerance"));
 
         cardsProducer.produce(cardPageBuildItem);
     }
diff --git 
a/extensions/vertx-websocket/deployment/src/main/java/org/apache/camel/quarkus/component/vertx/websocket/deployment/devui/VertxWebsocketDevUIProcessor.java
 
b/extensions/vertx-websocket/deployment/src/main/java/org/apache/camel/quarkus/component/vertx/websocket/deployment/devui/VertxWebsocketDevUIProcessor.java
index 2fd7eed4b9..1ecc9f167b 100644
--- 
a/extensions/vertx-websocket/deployment/src/main/java/org/apache/camel/quarkus/component/vertx/websocket/deployment/devui/VertxWebsocketDevUIProcessor.java
+++ 
b/extensions/vertx-websocket/deployment/src/main/java/org/apache/camel/quarkus/component/vertx/websocket/deployment/devui/VertxWebsocketDevUIProcessor.java
@@ -22,6 +22,7 @@ import io.quarkus.deployment.annotations.BuildStep;
 import io.quarkus.deployment.annotations.BuildSteps;
 import io.quarkus.devui.spi.page.CardPageBuildItem;
 import io.quarkus.devui.spi.page.Page;
+import org.apache.camel.quarkus.core.deployment.devui.CamelDevUIConstants;
 
 @BuildSteps(onlyIf = IsDevelopment.class)
 public class VertxWebsocketDevUIProcessor {
@@ -32,7 +33,8 @@ public class VertxWebsocketDevUIProcessor {
         cardPageBuildItem.addPage(Page.webComponentPageBuilder()
                 .title("Endpoints")
                 .icon("font-awesome-solid:plug")
-                .componentLink("qwc-camel-vertx-websocket.js"));
+                .componentLink("qwc-camel-vertx-websocket.js")
+                .metadata(CamelDevUIConstants.CONSOLE_ID_METADATA_KEY, 
"vertx-websocket"));
 
         cardsProducer.produce(cardPageBuildItem);
     }

Reply via email to