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 08b7b765432e CAMEL-25978: camel-controlbus - the language command is 
off by default, with an option that lists the languages it may use (#29750)
08b7b765432e is described below

commit 08b7b765432e63363ce964142b9bfbd7f52fcb30
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Nov 13 08:24:24 2026 +0100

    CAMEL-25978: camel-controlbus - the language command is off by default, 
with an option that lists the languages it may use (#29750)
    
    Co-Authored-By: Claude Opus 5.5 <[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 8b6a06b99231..e60fe669b26c 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
@@ -20,6 +20,18 @@ was unavailable and `failOpen=true`. Previously this marker 
was set only for sin
 that inspect the marker after a batch should account for it applying to any 
element; the per-element outcome
 list identifies the affected elements. The existing `CamelOpaBatchDecision` 
Boolean list is unchanged.
 
+=== 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