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

davsclaus pushed a commit to branch CAMEL-25978-controlbus-allowed-languages
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 34794467d9a943cc3243299ef3417d564e019d74
Author: Claus Ibsen <[email protected]>
AuthorDate: Thu Nov 12 20:10:22 2026 +0100

    CAMEL-25978: camel-controlbus - the language command is off by default, 
with an option that lists the languages it may use
    
    The language command (controlbus:language:<name>) evaluates the message
    body as an expression of the named language, and it worked for every
    language on the classpath without anything turning it on.
    
    New option allowedLanguages on the component and the endpoint: the names
    of the languages the language command may use, separated by comma. It is
    empty by default, so a controlbus:language endpoint whose language is not
    listed fails when it is created, with an error that names the option. The
    endpoint list takes precedence over the component list. The route command
    is not affected.
    
    Docs show the option in the language examples, and the changed default is
    in the 4.24 upgrade guide.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../camel/catalog/components/controlbus.json       |  6 +-
 .../camel/catalog/docs/controlbus-component.adoc   | 26 +++++++-
 .../controlbus/ControlBusComponentConfigurer.java  |  6 ++
 .../controlbus/ControlBusEndpointConfigurer.java   |  6 ++
 .../controlbus/ControlBusEndpointUriFactory.java   |  3 +-
 .../camel/component/controlbus/controlbus.json     |  6 +-
 .../src/main/docs/controlbus-component.adoc        | 26 +++++++-
 .../component/controlbus/ControlBusComponent.java  | 40 ++++++++++-
 .../component/controlbus/ControlBusEndpoint.java   | 16 +++++
 .../controlbus/ControlBusLanguageAllowedTest.java  | 78 ++++++++++++++++++++++
 .../ControlBusLanguageSimpleStartRouteTest.java    | 10 ++-
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    | 12 ++++
 .../dsl/ControlbusComponentBuilderFactory.java     | 21 ++++++
 .../dsl/ControlBusEndpointBuilderFactory.java      | 19 ++++++
 14 files changed, 264 insertions(+), 11 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/controlbus.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/controlbus.json
index 63dda925923f..2c0db8d547b4 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/controlbus.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/controlbus.json
@@ -25,7 +25,8 @@
   },
   "componentProperties": {
     "lazyStartProducer": { "index": 0, "kind": "property", "displayName": 
"Lazy Start Producer", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether the producer should be started lazy (on the first message). By 
starting lazy you can use this to allow CamelContext and routes to startup in 
situations where a producer may otherwise fail [...]
-    "autowiredEnabled": { "index": 1, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching t [...]
+    "autowiredEnabled": { "index": 1, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching t [...]
+    "allowedLanguages": { "index": 2, "kind": "property", "displayName": 
"Allowed Languages", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "The names of the languages 
the language command may use, separated by comma, such as simple. The language 
command evaluates the message body as an expression, so it is off until the 
language is in this list: a con [...]
   },
   "bodyTypes": {
     "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": 
"command", "value": "language", "carriers": [ "text", "bytes", "stream", "file" 
] } ], "description": "The route actions do not read the body. The language 
command evaluates the body as its expression, as text" },
@@ -39,6 +40,7 @@
     "loggingLevel": { "index": 4, "kind": "parameter", "displayName": "Logging 
Level", "group": "producer", "label": "", "required": false, "type": "enum", 
"javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", 
"INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "INFO", "description": "Logging level used for 
logging when task is done, or if any exceptions occurred during processing the 
task." },
     "restartDelay": { "index": 5, "kind": "parameter", "displayName": "Restart 
Delay", "group": "producer", "label": "", "required": false, "type": "integer", 
"javaType": "int", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": 1000, "description": "The delay in millis to use when 
restarting a route." },
     "routeId": { "index": 6, "kind": "parameter", "displayName": "Route Id", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "To specify a route by its id. The special 
keyword current indicates the current route." },
-    "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produc [...]
+    "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produc [...]
+    "allowedLanguages": { "index": 8, "kind": "parameter", "displayName": 
"Allowed Languages", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "The names of the languages 
the language command may use, separated by comma, such as simple. The language 
command evaluates the message body as an expression, so it is off until the 
language is in this list: a co [...]
   }
 }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/controlbus-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/controlbus-component.adoc
index 05f6af24b0ce..e691bba99b5e 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/controlbus-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/controlbus-component.adoc
@@ -180,7 +180,29 @@ String xml = 
template.requestBody("controlbus:route?action=stats", null, String.
 === Using Simple language
 
 You can use the xref:languages:simple-language.adoc[Simple] language with the 
control bus.
-For example, to stop a specific route, you can send a message to the 
`"controlbus:language:simple"` endpoint containing the following message:
+
+The `language` command evaluates the message body as an expression, so it is 
off by default.
+To turn it on, list the languages it may use in the `allowedLanguages` option, 
separated by comma.
+Set it on the component to apply to every endpoint:
+
+[source,properties]
+----
+camel.component.controlbus.allowed-languages = simple
+----
+
+Or set it on the endpoint itself, which takes precedence over the list of the 
component:
+
+[source,text]
+----
+controlbus:language:simple?allowedLanguages=simple
+----
+
+A `controlbus:language` endpoint whose language is not listed fails when it is 
created, with an error that names the `allowedLanguages` option.
+The `route` command does not need this option.
+
+IMPORTANT: The body is evaluated as an expression with the full power of the 
language: only send it text that comes from the route, never the content of a 
message from an untrusted sender.
+
+With `simple` allowed on the component, to stop a specific route, you can send 
a message to the `"controlbus:language:simple"` endpoint containing the 
following message:
 
 ._Java-only: Java test API (ProducerTemplate)_
 [source,java]
@@ -216,5 +238,5 @@ message we sent to the control bus component.
 
 [TIP]
 ====
-You can also use other languages such as 
xref:languages:groovy-language.adoc[Groovy], etc.
+You can also use other languages such as 
xref:languages:groovy-language.adoc[Groovy], etc., when they are listed in the 
`allowedLanguages` option.
 ====
diff --git 
a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusComponentConfigurer.java
 
b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusComponentConfigurer.java
index 0cef7a0fe01d..b06ee6bd19f4 100644
--- 
a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusComponentConfigurer.java
+++ 
b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusComponentConfigurer.java
@@ -24,6 +24,8 @@ public class ControlBusComponentConfigurer extends 
PropertyConfigurerSupport imp
     public boolean configure(CamelContext camelContext, Object obj, String 
name, Object value, boolean ignoreCase) {
         ControlBusComponent target = (ControlBusComponent) obj;
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
+        case "allowedlanguages":
+        case "allowedLanguages": 
target.setAllowedLanguages(property(camelContext, java.lang.String.class, 
value)); return true;
         case "autowiredenabled":
         case "autowiredEnabled": 
target.setAutowiredEnabled(property(camelContext, boolean.class, value)); 
return true;
         case "lazystartproducer":
@@ -35,6 +37,8 @@ public class ControlBusComponentConfigurer extends 
PropertyConfigurerSupport imp
     @Override
     public Class<?> getOptionType(String name, boolean ignoreCase) {
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
+        case "allowedlanguages":
+        case "allowedLanguages": return java.lang.String.class;
         case "autowiredenabled":
         case "autowiredEnabled": return boolean.class;
         case "lazystartproducer":
@@ -47,6 +51,8 @@ public class ControlBusComponentConfigurer extends 
PropertyConfigurerSupport imp
     public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
         ControlBusComponent target = (ControlBusComponent) obj;
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
+        case "allowedlanguages":
+        case "allowedLanguages": return target.getAllowedLanguages();
         case "autowiredenabled":
         case "autowiredEnabled": return target.isAutowiredEnabled();
         case "lazystartproducer":
diff --git 
a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointConfigurer.java
 
b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointConfigurer.java
index 93dba1587fe4..47cb752b4a8b 100644
--- 
a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointConfigurer.java
+++ 
b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointConfigurer.java
@@ -25,6 +25,8 @@ public class ControlBusEndpointConfigurer extends 
PropertyConfigurerSupport impl
         ControlBusEndpoint target = (ControlBusEndpoint) obj;
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
         case "action": target.setAction(property(camelContext, 
java.lang.String.class, value)); return true;
+        case "allowedlanguages":
+        case "allowedLanguages": 
target.setAllowedLanguages(property(camelContext, java.lang.String.class, 
value)); return true;
         case "async": target.setAsync(property(camelContext, boolean.class, 
value)); return true;
         case "lazystartproducer":
         case "lazyStartProducer": 
target.setLazyStartProducer(property(camelContext, boolean.class, value)); 
return true;
@@ -42,6 +44,8 @@ public class ControlBusEndpointConfigurer extends 
PropertyConfigurerSupport impl
     public Class<?> getOptionType(String name, boolean ignoreCase) {
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
         case "action": return java.lang.String.class;
+        case "allowedlanguages":
+        case "allowedLanguages": return java.lang.String.class;
         case "async": return boolean.class;
         case "lazystartproducer":
         case "lazyStartProducer": return boolean.class;
@@ -60,6 +64,8 @@ public class ControlBusEndpointConfigurer extends 
PropertyConfigurerSupport impl
         ControlBusEndpoint target = (ControlBusEndpoint) obj;
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
         case "action": return target.getAction();
+        case "allowedlanguages":
+        case "allowedLanguages": return target.getAllowedLanguages();
         case "async": return target.isAsync();
         case "lazystartproducer":
         case "lazyStartProducer": return target.isLazyStartProducer();
diff --git 
a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointUriFactory.java
 
b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointUriFactory.java
index 41ba4fbd1e87..9c27494504bc 100644
--- 
a/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointUriFactory.java
+++ 
b/components/camel-controlbus/src/generated/java/org/apache/camel/component/controlbus/ControlBusEndpointUriFactory.java
@@ -24,8 +24,9 @@ public class ControlBusEndpointUriFactory extends 
org.apache.camel.support.compo
     private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
     private static final Map<String, String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(8);
+        Set<String> props = new HashSet<>(9);
         props.add("action");
+        props.add("allowedLanguages");
         props.add("async");
         props.add("command");
         props.add("language");
diff --git 
a/components/camel-controlbus/src/generated/resources/META-INF/org/apache/camel/component/controlbus/controlbus.json
 
b/components/camel-controlbus/src/generated/resources/META-INF/org/apache/camel/component/controlbus/controlbus.json
index 63dda925923f..2c0db8d547b4 100644
--- 
a/components/camel-controlbus/src/generated/resources/META-INF/org/apache/camel/component/controlbus/controlbus.json
+++ 
b/components/camel-controlbus/src/generated/resources/META-INF/org/apache/camel/component/controlbus/controlbus.json
@@ -25,7 +25,8 @@
   },
   "componentProperties": {
     "lazyStartProducer": { "index": 0, "kind": "property", "displayName": 
"Lazy Start Producer", "group": "producer", "label": "producer", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether the producer should be started lazy (on the first message). By 
starting lazy you can use this to allow CamelContext and routes to startup in 
situations where a producer may otherwise fail [...]
-    "autowiredEnabled": { "index": 1, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching t [...]
+    "autowiredEnabled": { "index": 1, "kind": "property", "displayName": 
"Autowired Enabled", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": true, "description": 
"Whether autowiring is enabled. This is used for automatic autowiring options 
(the option must be marked as autowired) by looking up in the registry to find 
if there is a single instance of matching t [...]
+    "allowedLanguages": { "index": 2, "kind": "property", "displayName": 
"Allowed Languages", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "The names of the languages 
the language command may use, separated by comma, such as simple. The language 
command evaluates the message body as an expression, so it is off until the 
language is in this list: a con [...]
   },
   "bodyTypes": {
     "producerIn": { "carriers": [ "any" ], "byOption": [ { "option": 
"command", "value": "language", "carriers": [ "text", "bytes", "stream", "file" 
] } ], "description": "The route actions do not read the body. The language 
command evaluates the body as its expression, as text" },
@@ -39,6 +40,7 @@
     "loggingLevel": { "index": 4, "kind": "parameter", "displayName": "Logging 
Level", "group": "producer", "label": "", "required": false, "type": "enum", 
"javaType": "org.apache.camel.LoggingLevel", "enum": [ "TRACE", "DEBUG", 
"INFO", "WARN", "ERROR", "OFF" ], "deprecated": false, "autowired": false, 
"secret": false, "defaultValue": "INFO", "description": "Logging level used for 
logging when task is done, or if any exceptions occurred during processing the 
task." },
     "restartDelay": { "index": 5, "kind": "parameter", "displayName": "Restart 
Delay", "group": "producer", "label": "", "required": false, "type": "integer", 
"javaType": "int", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": 1000, "description": "The delay in millis to use when 
restarting a route." },
     "routeId": { "index": 6, "kind": "parameter", "displayName": "Route Id", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "To specify a route by its id. The special 
keyword current indicates the current route." },
-    "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produc [...]
+    "lazyStartProducer": { "index": 7, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produc [...]
+    "allowedLanguages": { "index": 8, "kind": "parameter", "displayName": 
"Allowed Languages", "group": "security", "label": "security", "required": 
false, "type": "string", "javaType": "java.lang.String", "deprecated": false, 
"autowired": false, "secret": false, "description": "The names of the languages 
the language command may use, separated by comma, such as simple. The language 
command evaluates the message body as an expression, so it is off until the 
language is in this list: a co [...]
   }
 }
diff --git 
a/components/camel-controlbus/src/main/docs/controlbus-component.adoc 
b/components/camel-controlbus/src/main/docs/controlbus-component.adoc
index 05f6af24b0ce..e691bba99b5e 100644
--- a/components/camel-controlbus/src/main/docs/controlbus-component.adoc
+++ b/components/camel-controlbus/src/main/docs/controlbus-component.adoc
@@ -180,7 +180,29 @@ String xml = 
template.requestBody("controlbus:route?action=stats", null, String.
 === Using Simple language
 
 You can use the xref:languages:simple-language.adoc[Simple] language with the 
control bus.
-For example, to stop a specific route, you can send a message to the 
`"controlbus:language:simple"` endpoint containing the following message:
+
+The `language` command evaluates the message body as an expression, so it is 
off by default.
+To turn it on, list the languages it may use in the `allowedLanguages` option, 
separated by comma.
+Set it on the component to apply to every endpoint:
+
+[source,properties]
+----
+camel.component.controlbus.allowed-languages = simple
+----
+
+Or set it on the endpoint itself, which takes precedence over the list of the 
component:
+
+[source,text]
+----
+controlbus:language:simple?allowedLanguages=simple
+----
+
+A `controlbus:language` endpoint whose language is not listed fails when it is 
created, with an error that names the `allowedLanguages` option.
+The `route` command does not need this option.
+
+IMPORTANT: The body is evaluated as an expression with the full power of the 
language: only send it text that comes from the route, never the content of a 
message from an untrusted sender.
+
+With `simple` allowed on the component, to stop a specific route, you can send 
a message to the `"controlbus:language:simple"` endpoint containing the 
following message:
 
 ._Java-only: Java test API (ProducerTemplate)_
 [source,java]
@@ -216,5 +238,5 @@ message we sent to the control bus component.
 
 [TIP]
 ====
-You can also use other languages such as 
xref:languages:groovy-language.adoc[Groovy], etc.
+You can also use other languages such as 
xref:languages:groovy-language.adoc[Groovy], etc., when they are listed in the 
`allowedLanguages` option.
 ====
diff --git 
a/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusComponent.java
 
b/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusComponent.java
index 592cd4f3ca36..fbcf01850955 100644
--- 
a/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusComponent.java
+++ 
b/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusComponent.java
@@ -20,8 +20,10 @@ import java.util.Map;
 import java.util.concurrent.ExecutorService;
 
 import org.apache.camel.Endpoint;
+import org.apache.camel.spi.Metadata;
 import org.apache.camel.spi.annotations.Component;
 import org.apache.camel.support.DefaultComponent;
+import org.apache.camel.util.ObjectHelper;
 
 /**
  * The <a href="http://camel.apache.org/controlbus.html";>Control Bus 
component</a> allows sending messages to a
@@ -30,25 +32,61 @@ import org.apache.camel.support.DefaultComponent;
 @Component("controlbus")
 public class ControlBusComponent extends DefaultComponent {
 
+    @Metadata(label = "security")
+    private String allowedLanguages;
+
     private ExecutorService executorService;
 
     public ControlBusComponent() {
     }
 
+    public String getAllowedLanguages() {
+        return allowedLanguages;
+    }
+
+    /**
+     * The names of the languages the language command may use, separated by 
comma, such as simple. The language command
+     * evaluates the message body as an expression, so it is off until the 
language is in this list: a
+     * controlbus:language endpoint whose language is not listed fails when it 
is created. The route actions are not
+     * affected. An endpoint can set its own list.
+     */
+    public void setAllowedLanguages(String allowedLanguages) {
+        this.allowedLanguages = allowedLanguages;
+    }
+
     @Override
     protected Endpoint createEndpoint(String uri, String remaining, 
Map<String, Object> parameters) throws Exception {
         ControlBusEndpoint answer = new ControlBusEndpoint(uri, this);
+        answer.setAllowedLanguages(allowedLanguages);
+        setProperties(answer, parameters);
 
         // does the control bus use a language
         if (remaining != null && remaining.startsWith("language:")) {
             String lan = remaining.substring(9);
+            if (!isAllowedLanguage(answer.getAllowedLanguages(), lan)) {
+                throw new IllegalArgumentException(
+                        "The language command of the control bus may not use 
the " + lan + " language: add " + lan
+                                                   + " to the allowedLanguages 
option of the controlbus component or"
+                                                   + " endpoint to turn it 
on");
+            }
             answer.setLanguage(getCamelContext().resolveLanguage(lan));
         }
 
-        setProperties(answer, parameters);
         return answer;
     }
 
+    private static boolean isAllowedLanguage(String allowedLanguages, String 
language) {
+        if (ObjectHelper.isEmpty(allowedLanguages) || 
ObjectHelper.isEmpty(language)) {
+            return false;
+        }
+        for (String name : allowedLanguages.split(",")) {
+            if (name.trim().equals(language)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     ExecutorService getExecutorService() {
         lock.lock();
         try {
diff --git 
a/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusEndpoint.java
 
b/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusEndpoint.java
index c15c643a35b3..9b7ca3b779bc 100644
--- 
a/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusEndpoint.java
+++ 
b/components/camel-controlbus/src/main/java/org/apache/camel/component/controlbus/ControlBusEndpoint.java
@@ -78,6 +78,8 @@ public class ControlBusEndpoint extends DefaultEndpoint {
     private boolean async;
     @UriParam(defaultValue = "INFO")
     private LoggingLevel loggingLevel = LoggingLevel.INFO;
+    @UriParam(label = "security")
+    private String allowedLanguages;
 
     private transient CamelLogger logger;
 
@@ -185,4 +187,18 @@ public class ControlBusEndpoint extends DefaultEndpoint {
     public void setLoggingLevel(LoggingLevel loggingLevel) {
         this.loggingLevel = loggingLevel;
     }
+
+    public String getAllowedLanguages() {
+        return allowedLanguages;
+    }
+
+    /**
+     * The names of the languages the language command may use, separated by 
comma, such as simple. The language command
+     * evaluates the message body as an expression, so it is off until the 
language is in this list: a
+     * controlbus:language endpoint whose language is not listed fails when it 
is created. The route actions are not
+     * affected. When not set, the list of the component is used.
+     */
+    public void setAllowedLanguages(String allowedLanguages) {
+        this.allowedLanguages = allowedLanguages;
+    }
 }
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageAllowedTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageAllowedTest.java
new file mode 100644
index 000000000000..81c186c86179
--- /dev/null
+++ 
b/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageAllowedTest.java
@@ -0,0 +1,78 @@
+/*
+ * 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.controlbus;
+
+import org.apache.camel.ContextTestSupport;
+import org.apache.camel.ResolveEndpointFailedException;
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * The language command of the control bus is off until its language is in the 
allowedLanguages option.
+ */
+public class ControlBusLanguageAllowedTest extends ContextTestSupport {
+
+    @Test
+    public void testLanguageOffByDefault() {
+        Exception e = assertThrows(Exception.class,
+                () -> context.getEndpoint("controlbus:language:simple"));
+        assertAllowedLanguagesError(e, "simple");
+    }
+
+    @Test
+    public void testLanguageNotInList() {
+        Exception e = assertThrows(Exception.class,
+                () -> 
context.getEndpoint("controlbus:language:simple?allowedLanguages=groovy"));
+        assertAllowedLanguagesError(e, "simple");
+    }
+
+    @Test
+    public void testLanguageInEndpointList() {
+        String name = 
template.requestBody("controlbus:language:simple?allowedLanguages=groovy, 
simple",
+                "${camelContext.name}", String.class);
+        assertEquals(context.getName(), name);
+    }
+
+    @Test
+    public void testLanguageInComponentList() {
+        context.getComponent("controlbus", 
ControlBusComponent.class).setAllowedLanguages("simple");
+
+        String name = template.requestBody("controlbus:language:simple", 
"${camelContext.name}", String.class);
+        assertEquals(context.getName(), name);
+    }
+
+    @Test
+    public void testEndpointListOverridesComponentList() {
+        context.getComponent("controlbus", 
ControlBusComponent.class).setAllowedLanguages("simple");
+
+        Exception e = assertThrows(Exception.class,
+                () -> 
context.getEndpoint("controlbus:language:simple?allowedLanguages=constant"));
+        assertAllowedLanguagesError(e, "simple");
+    }
+
+    private static void assertAllowedLanguagesError(Exception e, String 
language) {
+        ResolveEndpointFailedException re = 
assertInstanceOf(ResolveEndpointFailedException.class, e);
+        Throwable cause = re.getCause();
+        assertInstanceOf(IllegalArgumentException.class, cause);
+        assertTrue(cause.getMessage().contains("allowedLanguages"), 
cause.getMessage());
+        assertTrue(cause.getMessage().contains(" " + language + " "), 
cause.getMessage());
+    }
+}
diff --git 
a/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageSimpleStartRouteTest.java
 
b/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageSimpleStartRouteTest.java
index d6f81414b311..4e5a1aba6dd4 100644
--- 
a/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageSimpleStartRouteTest.java
+++ 
b/core/camel-core/src/test/java/org/apache/camel/component/controlbus/ControlBusLanguageSimpleStartRouteTest.java
@@ -16,6 +16,7 @@
  */
 package org.apache.camel.component.controlbus;
 
+import org.apache.camel.CamelContext;
 import org.apache.camel.ContextTestSupport;
 import org.apache.camel.builder.RouteBuilder;
 import org.junit.jupiter.api.Test;
@@ -23,7 +24,7 @@ import org.junit.jupiter.api.Test;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
 /**
- *
+ * The language command, turned on for simple with the allowedLanguages option 
of the component.
  */
 public class ControlBusLanguageSimpleStartRouteTest extends ContextTestSupport 
{
 
@@ -62,6 +63,13 @@ public class ControlBusLanguageSimpleStartRouteTest extends 
ContextTestSupport {
         assertEquals("Started", status);
     }
 
+    @Override
+    protected CamelContext createCamelContext() throws Exception {
+        CamelContext context = super.createCamelContext();
+        context.getComponent("controlbus", 
ControlBusComponent.class).setAllowedLanguages("simple");
+        return context;
+    }
+
     @Override
     protected RouteBuilder createRouteBuilder() {
         return new RouteBuilder() {
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index 93adc90a66f1..1bceea3a2c07 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -13,6 +13,18 @@ See the xref:camel-upgrade-recipes-tool.adoc[documentation] 
page for details.
 
 == Upgrading Camel 4.23 to 4.24
 
+=== camel-controlbus - the language command is off by default
+
+The `language` command of the control bus (`controlbus:language:<name>`) 
evaluates the message body as an expression of the named language.
+It is now off by default: a `controlbus:language` endpoint fails when it is 
created unless its language is listed in the new `allowedLanguages` option, on 
the component or on the endpoint:
+
+[source,properties]
+----
+camel.component.controlbus.allowed-languages = simple
+----
+
+The `route` command (`controlbus:route?action=...`) is not affected.
+
 === camel-observability-services - promoted to Stable
 
 The `camel-observability-services` component has been promoted from _Preview_ 
to _Stable_ support level.
diff --git 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ControlbusComponentBuilderFactory.java
 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ControlbusComponentBuilderFactory.java
index 61c69d7dbe84..d185bc06feb2 100644
--- 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ControlbusComponentBuilderFactory.java
+++ 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ControlbusComponentBuilderFactory.java
@@ -96,6 +96,26 @@ public interface ControlbusComponentBuilderFactory {
             doSetProperty("autowiredEnabled", autowiredEnabled);
             return this;
         }
+    
+        /**
+         * The names of the languages the language command may use, separated 
by
+         * comma, such as simple. The language command evaluates the message
+         * body as an expression, so it is off until the language is in this
+         * list: a controlbus:language endpoint whose language is not listed
+         * fails when it is created. The route actions are not affected. An
+         * endpoint can set its own list.
+         * 
+         * The option is a: &lt;code&gt;java.lang.String&lt;/code&gt; type.
+         * 
+         * Group: security
+         * 
+         * @param allowedLanguages the value to set
+         * @return the dsl builder
+         */
+        default ControlbusComponentBuilder allowedLanguages(java.lang.String 
allowedLanguages) {
+            doSetProperty("allowedLanguages", allowedLanguages);
+            return this;
+        }
     }
 
     class ControlbusComponentBuilderImpl
@@ -113,6 +133,7 @@ public interface ControlbusComponentBuilderFactory {
             switch (name) {
             case "lazyStartProducer": ((ControlBusComponent) 
component).setLazyStartProducer((boolean) value); return true;
             case "autowiredEnabled": ((ControlBusComponent) 
component).setAutowiredEnabled((boolean) value); return true;
+            case "allowedLanguages": ((ControlBusComponent) 
component).setAllowedLanguages((java.lang.String) value); return true;
             default: return false;
             }
         }
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ControlBusEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ControlBusEndpointBuilderFactory.java
index 4ab4b21fa479..480ec18aa0bb 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ControlBusEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ControlBusEndpointBuilderFactory.java
@@ -178,6 +178,25 @@ public interface ControlBusEndpointBuilderFactory {
             doSetProperty("routeId", routeId);
             return this;
         }
+        /**
+         * The names of the languages the language command may use, separated 
by
+         * comma, such as simple. The language command evaluates the message
+         * body as an expression, so it is off until the language is in this
+         * list: a controlbus:language endpoint whose language is not listed
+         * fails when it is created. The route actions are not affected. When
+         * not set, the list of the component is used.
+         * 
+         * The option is a: <code>java.lang.String</code> type.
+         * 
+         * Group: security
+         * 
+         * @param allowedLanguages the value to set
+         * @return the dsl builder
+         */
+        default ControlBusEndpointBuilder allowedLanguages(String 
allowedLanguages) {
+            doSetProperty("allowedLanguages", allowedLanguages);
+            return this;
+        }
     }
 
     /**

Reply via email to