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

davsclaus pushed a commit to branch backport/CAMEL-24464-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 9da596385067a2bafd44fedd4114af6ecb939449
Author: Omar Atie <[email protected]>
AuthorDate: Mon Aug 31 13:08:45 2026 -0700

    CAMEL-24464: Clarify exec control headers require allowControlHeaders
    
    Since CAMEL-23315 (Camel 4.20), CamelExecCommand* in-headers only
    override URI options when allowControlHeaders=true, but the component
    docs still implied they always worked, which misled users on the
    default (false) setting.
    
    Update the @Metadata docs, generated catalog, and exec-component.adoc
    to state the allowControlHeaders prerequisite for all control headers.
    Log a once-per-endpoint WARN in DefaultExecBinding when control
    headers are present but ignored. Mark allowControlHeaders with
    security="insecure:dev" and register it in SecurityUtils so
    camel.main.profile=prod blocks the opt-in by default. Add a 4.23
    upgrade-guide entry for camel-exec.
    
    Co-authored-by: Cursor Agent <[email protected]>
    Co-authored-by: Guillaume Nodet <[email protected]>
    Co-authored-by: Claude Sonnet 5 <[email protected]>
    
    Closes #25846
    
    (cherry picked from commit 19e69f186fb715e198b30d1876e83bca7941a66a)
---
 .../org/apache/camel/catalog/components/exec.json  |  20 +--
 .../apache/camel/catalog/docs/exec-component.adoc  |  41 +++++++
 .../org/apache/camel/component/exec/exec.json      |  20 +--
 .../camel-exec/src/main/docs/exec-component.adoc   |  41 +++++++
 .../apache/camel/component/exec/ExecBinding.java   |  28 +++--
 .../apache/camel/component/exec/ExecComponent.java |  10 +-
 .../apache/camel/component/exec/ExecEndpoint.java  |  10 +-
 .../component/exec/impl/DefaultExecBinding.java    |  36 ++++++
 .../component/exec/DefaultExecBindingTest.java     | 136 ++++++++++++++++++---
 .../camel/component/exec/ExecProducerTest.java     |  19 +++
 .../java/org/apache/camel/util/SecurityUtils.java  |   1 +
 .../ROOT/pages/camel-4x-upgrade-guide-4_22.adoc    |  12 ++
 .../component/dsl/ExecComponentBuilderFactory.java |  12 +-
 .../endpoint/dsl/ExecEndpointBuilderFactory.java   |  50 +++++---
 14 files changed, 364 insertions(+), 72 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/exec.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/exec.json
index ee2f679c33ac..79d5f450c64e 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/exec.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/exec.json
@@ -27,22 +27,22 @@
     "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 [...]
     "timeout": { "index": 1, "kind": "property", "displayName": "Timeout", 
"group": "producer", "label": "", "required": false, "type": "integer", 
"javaType": "long", "deprecated": false, "autowired": false, "secret": false, 
"description": "The timeout, in milliseconds, after which the executable should 
be terminated. If execution has not completed within the timeout, the component 
will send a termination request." },
     "workingDir": { "index": 2, "kind": "property", "displayName": "Working 
Dir", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "The directory in which the command should be 
executed. If null, the working directory of the current process will be used." 
},
-    "allowControlHeaders": { "index": 3, "kind": "property", "displayName": 
"Allow Control Headers", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether to allow to use Camel headers or not (default false). Enabling this 
allows to specify dynamic command line arguments via message header. However 
this can be seen as a potential securi [...]
+    "allowControlHeaders": { "index": 3, "kind": "property", "displayName": 
"Allow Control Headers", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:dev", 
"defaultValue": false, "description": "Whether {code CamelExec} in-headers may 
override URI options (default false since Camel 4.20). When false, 
CamelExecCommandExecutable, CamelExecCommandArgs, CamelE [...]
     "autowiredEnabled": { "index": 4, "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 [...]
     "binding": { "index": 5, "kind": "property", "displayName": "Binding", 
"group": "advanced", "label": "advanced", "required": false, "type": "object", 
"javaType": "org.apache.camel.component.exec.ExecBinding", "deprecated": false, 
"autowired": false, "secret": false, "description": "To use a custom 
org.apache.commons.exec.ExecBinding for advanced use-cases." },
     "commandExecutor": { "index": 6, "kind": "property", "displayName": 
"Command Executor", "group": "advanced", "label": "advanced", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.component.exec.ExecCommandExecutor", "deprecated": false, 
"autowired": false, "secret": false, "description": "To use a custom 
org.apache.commons.exec.ExecCommandExecutor that customizes the command 
execution. The default command executor utilizes the commons-exec library, 
which adds a shut [...]
   },
   "headers": {
-    "CamelExecCommandExecutable": { "index": 0, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The name of the system command that will be 
executed. Overrides executable in the URI.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_COMMAND_EXECUTABLE" },
-    "CamelExecCommandArgs": { "index": 1, "kind": "header", "displayName": "", 
"group": "in", "label": "in", "required": false, "javaType": 
"java.util.List<String> or String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Command-line argument(s) 
to pass to the executed process. The argument(s) is\/are used literally - no 
quoting is applied. Overrides any existing args in the URI.", "constantName": 
"org.apache.camel.component.exec.ExecBi [...]
-    "CamelExecCommandOutFile": { "index": 2, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The name of a file, created by the executable, that 
should be considered as its output. Overrides any existing outFile in the 
URI.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_COMMAND_OUT_FILE" },
-    "CamelExecCommandWorkingDir": { "index": 3, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The directory in which the command should be 
executed. Overrides any existing workingDir in the URI.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_COMMAND_WORKING_DIR" },
-    "CamelExecCommandTimeout": { "index": 4, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "long", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The timeout, in milliseconds, after which the executable 
should be terminated. Overrides any existing timeout in the URI.", 
"constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_COMMAND_TIMEOUT" },
-    "CamelExecExitValues": { "index": 5, "kind": "header", "displayName": "", 
"group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The exit values for successful execution of the process. 
Overrides any existing exitValues in the URI.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_COMMAND_EXIT_VALUES" },
+    "CamelExecCommandExecutable": { "index": 0, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The name of the system command that will be 
executed. Overrides executable in the URI. Requires allowControlHeaders=true on 
the exec endpoint or component (default is false since Camel 4.20).", 
"constantName": "org.apache.camel.component.e [...]
+    "CamelExecCommandArgs": { "index": 1, "kind": "header", "displayName": "", 
"group": "in", "label": "in", "required": false, "javaType": 
"java.util.List<String> or String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Command-line argument(s) 
to pass to the executed process. The argument(s) is\/are used literally - no 
quoting is applied. Overrides any existing args in the URI. Requires 
allowControlHeaders=true on the exec endpoint or [...]
+    "CamelExecCommandOutFile": { "index": 2, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The name of a file, created by the executable, that 
should be considered as its output. Overrides any existing outFile in the URI. 
Requires allowControlHeaders=true on the exec endpoint or component (default is 
false since Camel 4.20).", "con [...]
+    "CamelExecCommandWorkingDir": { "index": 3, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The directory in which the command should be 
executed. Overrides any existing workingDir in the URI. Requires 
allowControlHeaders=true on the exec endpoint or component (default is false 
since Camel 4.20).", "constantName": "org.apache.cam [...]
+    "CamelExecCommandTimeout": { "index": 4, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "long", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The timeout, in milliseconds, after which the executable 
should be terminated. Overrides any existing timeout in the URI. Requires 
allowControlHeaders=true on the exec endpoint or component (default is false 
since Camel 4.20).", "constantName": [...]
+    "CamelExecExitValues": { "index": 5, "kind": "header", "displayName": "", 
"group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The exit values for successful execution of the process. 
Overrides any existing exitValues in the URI. Requires allowControlHeaders=true 
on the exec endpoint or component (default is false since Camel 4.20).", 
"constantName": "org.apache.camel.co [...]
     "CamelExecStderr": { "index": 6, "kind": "header", "displayName": "", 
"group": "out", "label": "out", "required": false, "javaType": 
"java.io.InputStream", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "The value of this header points to the 
standard error stream (stderr) of the executable. If no stderr is written, the 
value is null.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_STDERR" },
     "CamelExecExitValue": { "index": 7, "kind": "header", "displayName": "", 
"group": "out", "label": "out", "required": false, "javaType": "int", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "important": true, "description": "The value of this header is the _exit 
value_ of the executable. Non-zero exit values typically indicate abnormal 
termination. Note that the exit value is OS-dependent.", "constantName": 
"org.apache.camel.component.exec.ExecBindin [...]
-    "CamelExecUseStderrOnEmptyStdout": { "index": 8, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"boolean", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Indicates that when stdout is empty, this 
component will populate the Camel Message Body with stderr. This behavior is 
disabled (false) by default.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_USE_STDERR_ON_EMP [...]
-    "CamelExecCommandLogLevel": { "index": 9, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Logging level to be used for commands during execution. 
The default value is DEBUG. Possible values are TRACE, DEBUG, INFO, WARN, ERROR 
or OFF (Values of LoggingLevel enum)", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_ [...]
+    "CamelExecUseStderrOnEmptyStdout": { "index": 8, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"boolean", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Indicates that when stdout is empty, this 
component will populate the Camel Message Body with stderr. This behavior is 
disabled (false) by default. Requires allowControlHeaders=true on the exec 
endpoint or component (default is false [...]
+    "CamelExecCommandLogLevel": { "index": 9, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Logging level to be used for commands during execution. 
The default value is DEBUG. Possible values are TRACE, DEBUG, INFO, WARN, ERROR 
or OFF (Values of LoggingLevel enum). Requires allowControlHeaders=true on the 
exec endpoint or component [...]
   },
   "properties": {
     "executable": { "index": 0, "kind": "path", "displayName": "Executable", 
"group": "producer", "label": "", "required": true, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Sets the executable to be 
executed. The executable must not be empty or null." },
@@ -54,7 +54,7 @@
     "useStderrOnEmptyStdout": { "index": 6, "kind": "parameter", 
"displayName": "Use Stderr On Empty Stdout", "group": "producer", "label": "", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": false, 
"description": "A boolean indicating that when stdout is empty, this component 
will populate the Camel Message Body with stderr. This behavior is disabled 
(false) by default." },
     "workingDir": { "index": 7, "kind": "parameter", "displayName": "Working 
Dir", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "The directory in which the command should be 
executed. If null, the working directory of the current process will be used." 
},
     "lazyStartProducer": { "index": 8, "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 [...]
-    "allowControlHeaders": { "index": 9, "kind": "parameter", "displayName": 
"Allow Control Headers", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether to allow to use Camel headers or not (default false). Enabling this 
allows to specify dynamic command line arguments via message header. However 
this can be seen as a potential secur [...]
+    "allowControlHeaders": { "index": 9, "kind": "parameter", "displayName": 
"Allow Control Headers", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:dev", 
"defaultValue": false, "description": "Whether {code CamelExec} in-headers may 
override URI options (default false since Camel 4.20). When false, 
CamelExecCommandExecutable, CamelExecCommandArgs, Camel [...]
     "binding": { "index": 10, "kind": "parameter", "displayName": "Binding", 
"group": "advanced", "label": "advanced", "required": false, "type": "object", 
"javaType": "org.apache.camel.component.exec.ExecBinding", "deprecated": false, 
"autowired": false, "secret": false, "description": "To use a custom 
org.apache.commons.exec.ExecBinding for advanced use-cases." },
     "commandExecutor": { "index": 11, "kind": "parameter", "displayName": 
"Command Executor", "group": "advanced", "label": "advanced", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.component.exec.ExecCommandExecutor", "deprecated": false, 
"autowired": false, "secret": false, "description": "To use a custom 
org.apache.commons.exec.ExecCommandExecutor that customizes the command 
execution. The default command executor utilizes the commons-exec library, 
which adds a sh [...]
   }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/exec-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/exec-component.adoc
index ad82f90cec84..64ca34aae1f8 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/exec-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/exec-component.adoc
@@ -48,6 +48,47 @@ include::partial$component-endpoint-headers.adoc[]
 
 == Usage
 
+=== Control headers
+
+Since Camel 4.20, the exec in-headers that override URI options 
(`CamelExecCommand*`,
+`CamelExecExitValues`, and `CamelExecUseStderrOnEmptyStdout`) are disabled by 
default. This prevents untrusted message sources from redirecting
+command execution. To use dynamic executable, arguments, working directory, or
+other command settings from headers, enable the opt-in flag on the endpoint or
+component:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:run")
+    .setHeader("CamelExecCommandArgs", constant("ARGS-WORK"))
+    .to("exec:echo?allowControlHeaders=true");
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:run
+      steps:
+        - setHeader:
+            name: CamelExecCommandArgs
+            constant: "ARGS-WORK"
+        - to:
+            uri: exec:echo
+            parameters:
+              allowControlHeaders: true
+----
+====
+
+When `allowControlHeaders` is `false` (the default), those in-headers on the 
exchange
+are ignored (they are not removed), and a WARN is logged once per exec 
endpoint.
+URI parameters such as `args` continue to work as before.
+
 === Message body
 
 If the component receives an `in` message body that is
diff --git 
a/components/camel-exec/src/generated/resources/META-INF/org/apache/camel/component/exec/exec.json
 
b/components/camel-exec/src/generated/resources/META-INF/org/apache/camel/component/exec/exec.json
index ee2f679c33ac..79d5f450c64e 100644
--- 
a/components/camel-exec/src/generated/resources/META-INF/org/apache/camel/component/exec/exec.json
+++ 
b/components/camel-exec/src/generated/resources/META-INF/org/apache/camel/component/exec/exec.json
@@ -27,22 +27,22 @@
     "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 [...]
     "timeout": { "index": 1, "kind": "property", "displayName": "Timeout", 
"group": "producer", "label": "", "required": false, "type": "integer", 
"javaType": "long", "deprecated": false, "autowired": false, "secret": false, 
"description": "The timeout, in milliseconds, after which the executable should 
be terminated. If execution has not completed within the timeout, the component 
will send a termination request." },
     "workingDir": { "index": 2, "kind": "property", "displayName": "Working 
Dir", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "The directory in which the command should be 
executed. If null, the working directory of the current process will be used." 
},
-    "allowControlHeaders": { "index": 3, "kind": "property", "displayName": 
"Allow Control Headers", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether to allow to use Camel headers or not (default false). Enabling this 
allows to specify dynamic command line arguments via message header. However 
this can be seen as a potential securi [...]
+    "allowControlHeaders": { "index": 3, "kind": "property", "displayName": 
"Allow Control Headers", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:dev", 
"defaultValue": false, "description": "Whether {code CamelExec} in-headers may 
override URI options (default false since Camel 4.20). When false, 
CamelExecCommandExecutable, CamelExecCommandArgs, CamelE [...]
     "autowiredEnabled": { "index": 4, "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 [...]
     "binding": { "index": 5, "kind": "property", "displayName": "Binding", 
"group": "advanced", "label": "advanced", "required": false, "type": "object", 
"javaType": "org.apache.camel.component.exec.ExecBinding", "deprecated": false, 
"autowired": false, "secret": false, "description": "To use a custom 
org.apache.commons.exec.ExecBinding for advanced use-cases." },
     "commandExecutor": { "index": 6, "kind": "property", "displayName": 
"Command Executor", "group": "advanced", "label": "advanced", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.component.exec.ExecCommandExecutor", "deprecated": false, 
"autowired": false, "secret": false, "description": "To use a custom 
org.apache.commons.exec.ExecCommandExecutor that customizes the command 
execution. The default command executor utilizes the commons-exec library, 
which adds a shut [...]
   },
   "headers": {
-    "CamelExecCommandExecutable": { "index": 0, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The name of the system command that will be 
executed. Overrides executable in the URI.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_COMMAND_EXECUTABLE" },
-    "CamelExecCommandArgs": { "index": 1, "kind": "header", "displayName": "", 
"group": "in", "label": "in", "required": false, "javaType": 
"java.util.List<String> or String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Command-line argument(s) 
to pass to the executed process. The argument(s) is\/are used literally - no 
quoting is applied. Overrides any existing args in the URI.", "constantName": 
"org.apache.camel.component.exec.ExecBi [...]
-    "CamelExecCommandOutFile": { "index": 2, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The name of a file, created by the executable, that 
should be considered as its output. Overrides any existing outFile in the 
URI.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_COMMAND_OUT_FILE" },
-    "CamelExecCommandWorkingDir": { "index": 3, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The directory in which the command should be 
executed. Overrides any existing workingDir in the URI.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_COMMAND_WORKING_DIR" },
-    "CamelExecCommandTimeout": { "index": 4, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "long", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The timeout, in milliseconds, after which the executable 
should be terminated. Overrides any existing timeout in the URI.", 
"constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_COMMAND_TIMEOUT" },
-    "CamelExecExitValues": { "index": 5, "kind": "header", "displayName": "", 
"group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The exit values for successful execution of the process. 
Overrides any existing exitValues in the URI.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_COMMAND_EXIT_VALUES" },
+    "CamelExecCommandExecutable": { "index": 0, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The name of the system command that will be 
executed. Overrides executable in the URI. Requires allowControlHeaders=true on 
the exec endpoint or component (default is false since Camel 4.20).", 
"constantName": "org.apache.camel.component.e [...]
+    "CamelExecCommandArgs": { "index": 1, "kind": "header", "displayName": "", 
"group": "in", "label": "in", "required": false, "javaType": 
"java.util.List<String> or String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Command-line argument(s) 
to pass to the executed process. The argument(s) is\/are used literally - no 
quoting is applied. Overrides any existing args in the URI. Requires 
allowControlHeaders=true on the exec endpoint or [...]
+    "CamelExecCommandOutFile": { "index": 2, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The name of a file, created by the executable, that 
should be considered as its output. Overrides any existing outFile in the URI. 
Requires allowControlHeaders=true on the exec endpoint or component (default is 
false since Camel 4.20).", "con [...]
+    "CamelExecCommandWorkingDir": { "index": 3, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"String", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "The directory in which the command should be 
executed. Overrides any existing workingDir in the URI. Requires 
allowControlHeaders=true on the exec endpoint or component (default is false 
since Camel 4.20).", "constantName": "org.apache.cam [...]
+    "CamelExecCommandTimeout": { "index": 4, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "long", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The timeout, in milliseconds, after which the executable 
should be terminated. Overrides any existing timeout in the URI. Requires 
allowControlHeaders=true on the exec endpoint or component (default is false 
since Camel 4.20).", "constantName": [...]
+    "CamelExecExitValues": { "index": 5, "kind": "header", "displayName": "", 
"group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The exit values for successful execution of the process. 
Overrides any existing exitValues in the URI. Requires allowControlHeaders=true 
on the exec endpoint or component (default is false since Camel 4.20).", 
"constantName": "org.apache.camel.co [...]
     "CamelExecStderr": { "index": 6, "kind": "header", "displayName": "", 
"group": "out", "label": "out", "required": false, "javaType": 
"java.io.InputStream", "deprecated": false, "deprecationNote": "", "autowired": 
false, "secret": false, "description": "The value of this header points to the 
standard error stream (stderr) of the executable. If no stderr is written, the 
value is null.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_STDERR" },
     "CamelExecExitValue": { "index": 7, "kind": "header", "displayName": "", 
"group": "out", "label": "out", "required": false, "javaType": "int", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "important": true, "description": "The value of this header is the _exit 
value_ of the executable. Non-zero exit values typically indicate abnormal 
termination. Note that the exit value is OS-dependent.", "constantName": 
"org.apache.camel.component.exec.ExecBindin [...]
-    "CamelExecUseStderrOnEmptyStdout": { "index": 8, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"boolean", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Indicates that when stdout is empty, this 
component will populate the Camel Message Body with stderr. This behavior is 
disabled (false) by default.", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_USE_STDERR_ON_EMP [...]
-    "CamelExecCommandLogLevel": { "index": 9, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Logging level to be used for commands during execution. 
The default value is DEBUG. Possible values are TRACE, DEBUG, INFO, WARN, ERROR 
or OFF (Values of LoggingLevel enum)", "constantName": 
"org.apache.camel.component.exec.ExecBinding#EXEC_ [...]
+    "CamelExecUseStderrOnEmptyStdout": { "index": 8, "kind": "header", 
"displayName": "", "group": "in", "label": "in", "required": false, "javaType": 
"boolean", "deprecated": false, "deprecationNote": "", "autowired": false, 
"secret": false, "description": "Indicates that when stdout is empty, this 
component will populate the Camel Message Body with stderr. This behavior is 
disabled (false) by default. Requires allowControlHeaders=true on the exec 
endpoint or component (default is false [...]
+    "CamelExecCommandLogLevel": { "index": 9, "kind": "header", "displayName": 
"", "group": "in", "label": "in", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Logging level to be used for commands during execution. 
The default value is DEBUG. Possible values are TRACE, DEBUG, INFO, WARN, ERROR 
or OFF (Values of LoggingLevel enum). Requires allowControlHeaders=true on the 
exec endpoint or component [...]
   },
   "properties": {
     "executable": { "index": 0, "kind": "path", "displayName": "Executable", 
"group": "producer", "label": "", "required": true, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", 
"autowired": false, "secret": false, "description": "Sets the executable to be 
executed. The executable must not be empty or null." },
@@ -54,7 +54,7 @@
     "useStderrOnEmptyStdout": { "index": 6, "kind": "parameter", 
"displayName": "Use Stderr On Empty Stdout", "group": "producer", "label": "", 
"required": false, "type": "boolean", "javaType": "boolean", "deprecated": 
false, "autowired": false, "secret": false, "defaultValue": false, 
"description": "A boolean indicating that when stdout is empty, this component 
will populate the Camel Message Body with stderr. This behavior is disabled 
(false) by default." },
     "workingDir": { "index": 7, "kind": "parameter", "displayName": "Working 
Dir", "group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "The directory in which the command should be 
executed. If null, the working directory of the current process will be used." 
},
     "lazyStartProducer": { "index": 8, "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 [...]
-    "allowControlHeaders": { "index": 9, "kind": "parameter", "displayName": 
"Allow Control Headers", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "defaultValue": false, "description": 
"Whether to allow to use Camel headers or not (default false). Enabling this 
allows to specify dynamic command line arguments via message header. However 
this can be seen as a potential secur [...]
+    "allowControlHeaders": { "index": 9, "kind": "parameter", "displayName": 
"Allow Control Headers", "group": "advanced", "label": "advanced", "required": 
false, "type": "boolean", "javaType": "boolean", "deprecated": false, 
"autowired": false, "secret": false, "security": "insecure:dev", 
"defaultValue": false, "description": "Whether {code CamelExec} in-headers may 
override URI options (default false since Camel 4.20). When false, 
CamelExecCommandExecutable, CamelExecCommandArgs, Camel [...]
     "binding": { "index": 10, "kind": "parameter", "displayName": "Binding", 
"group": "advanced", "label": "advanced", "required": false, "type": "object", 
"javaType": "org.apache.camel.component.exec.ExecBinding", "deprecated": false, 
"autowired": false, "secret": false, "description": "To use a custom 
org.apache.commons.exec.ExecBinding for advanced use-cases." },
     "commandExecutor": { "index": 11, "kind": "parameter", "displayName": 
"Command Executor", "group": "advanced", "label": "advanced", "required": 
false, "type": "object", "javaType": 
"org.apache.camel.component.exec.ExecCommandExecutor", "deprecated": false, 
"autowired": false, "secret": false, "description": "To use a custom 
org.apache.commons.exec.ExecCommandExecutor that customizes the command 
execution. The default command executor utilizes the commons-exec library, 
which adds a sh [...]
   }
diff --git a/components/camel-exec/src/main/docs/exec-component.adoc 
b/components/camel-exec/src/main/docs/exec-component.adoc
index ad82f90cec84..64ca34aae1f8 100644
--- a/components/camel-exec/src/main/docs/exec-component.adoc
+++ b/components/camel-exec/src/main/docs/exec-component.adoc
@@ -48,6 +48,47 @@ include::partial$component-endpoint-headers.adoc[]
 
 == Usage
 
+=== Control headers
+
+Since Camel 4.20, the exec in-headers that override URI options 
(`CamelExecCommand*`,
+`CamelExecExitValues`, and `CamelExecUseStderrOnEmptyStdout`) are disabled by 
default. This prevents untrusted message sources from redirecting
+command execution. To use dynamic executable, arguments, working directory, or
+other command settings from headers, enable the opt-in flag on the endpoint or
+component:
+
+[tabs]
+====
+Java::
++
+[source,java]
+----
+from("direct:run")
+    .setHeader("CamelExecCommandArgs", constant("ARGS-WORK"))
+    .to("exec:echo?allowControlHeaders=true");
+----
+
+YAML::
++
+[source,yaml]
+----
+- route:
+    from:
+      uri: direct:run
+      steps:
+        - setHeader:
+            name: CamelExecCommandArgs
+            constant: "ARGS-WORK"
+        - to:
+            uri: exec:echo
+            parameters:
+              allowControlHeaders: true
+----
+====
+
+When `allowControlHeaders` is `false` (the default), those in-headers on the 
exchange
+are ignored (they are not removed), and a WARN is logged once per exec 
endpoint.
+URI parameters such as `args` continue to work as before.
+
 === Message body
 
 If the component receives an `in` message body that is
diff --git 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecBinding.java
 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecBinding.java
index 449a3bf4272a..8801cf4cbe83 100644
--- 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecBinding.java
+++ 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecBinding.java
@@ -36,7 +36,8 @@ public interface ExecBinding {
      */
     @Metadata(label = "in", description = """
             The name of the system command that will be executed. Overrides
-            `executable` in the URI.""",
+            `executable` in the URI. Requires `allowControlHeaders=true` on 
the exec
+            endpoint or component (default is `false` since Camel 4.20).""",
               javaType = "String")
     String EXEC_COMMAND_EXECUTABLE = "CamelExecCommandExecutable";
 
@@ -49,7 +50,8 @@ public interface ExecBinding {
     @Metadata(label = "in", description = """
             Command-line argument(s) to pass to the executed process. The 
argument(s)
             is/are used literally - no quoting is applied. Overrides any 
existing
-            `args` in the URI.""",
+            `args` in the URI. Requires `allowControlHeaders=true` on the exec 
endpoint
+            or component (default is `false` since Camel 4.20).""",
               javaType = "java.util.List<String> or String")
     String EXEC_COMMAND_ARGS = "CamelExecCommandArgs";
 
@@ -61,7 +63,9 @@ public interface ExecBinding {
      */
     @Metadata(label = "in", description = """
             The name of a file, created by the executable, that should be 
considered
-            as its output. Overrides any existing `outFile` in the URI.""",
+            as its output. Overrides any existing `outFile` in the URI. 
Requires
+            `allowControlHeaders=true` on the exec endpoint or component 
(default is
+            `false` since Camel 4.20).""",
               javaType = "String")
     String EXEC_COMMAND_OUT_FILE = "CamelExecCommandOutFile";
 
@@ -72,7 +76,8 @@ public interface ExecBinding {
      */
     @Metadata(label = "in", description = """
             The directory in which the command should be executed. Overrides 
any
-            existing `workingDir` in the URI.""",
+            existing `workingDir` in the URI. Requires 
`allowControlHeaders=true` on
+            the exec endpoint or component (default is `false` since Camel 
4.20).""",
               javaType = "String")
     String EXEC_COMMAND_WORKING_DIR = "CamelExecCommandWorkingDir";
 
@@ -82,7 +87,9 @@ public interface ExecBinding {
      */
     @Metadata(label = "in", description = """
             The timeout, in milliseconds, after which the executable should be
-            terminated. Overrides any existing `timeout` in the URI.""",
+            terminated. Overrides any existing `timeout` in the URI. Requires
+            `allowControlHeaders=true` on the exec endpoint or component 
(default is
+            `false` since Camel 4.20).""",
               javaType = "long")
     String EXEC_COMMAND_TIMEOUT = "CamelExecCommandTimeout";
 
@@ -93,7 +100,9 @@ public interface ExecBinding {
      */
     @Metadata(label = "in", description = """
             The exit values for successful execution of the process.
-            Overrides any existing `exitValues` in the URI.""",
+            Overrides any existing `exitValues` in the URI. Requires
+            `allowControlHeaders=true` on the exec endpoint or component 
(default is
+            `false` since Camel 4.20).""",
               javaType = "String")
     String EXEC_COMMAND_EXIT_VALUES = "CamelExecExitValues";
 
@@ -124,7 +133,8 @@ public interface ExecBinding {
     @Metadata(label = "in", description = """
             Indicates that when `stdout` is empty, this component will 
populate the
             Camel Message Body with `stderr`. This behavior is disabled 
(`false`) by
-            default.""",
+            default. Requires `allowControlHeaders=true` on the exec endpoint 
or
+            component (default is `false` since Camel 4.20).""",
               javaType = "boolean")
     String EXEC_USE_STDERR_ON_EMPTY_STDOUT = "CamelExecUseStderrOnEmptyStdout";
 
@@ -135,7 +145,9 @@ public interface ExecBinding {
     @Metadata(label = "in",
               description = """
                       Logging level to be used for commands during execution. 
The default value is DEBUG.
-                      Possible values are TRACE, DEBUG, INFO, WARN, ERROR or 
OFF (Values of LoggingLevel enum)""",
+                      Possible values are TRACE, DEBUG, INFO, WARN, ERROR or 
OFF (Values of LoggingLevel enum).
+                      Requires `allowControlHeaders=true` on the exec endpoint 
or component (default is `false`
+                      since Camel 4.20).""",
               javaType = "String")
     String EXEC_COMMAND_LOG_LEVEL = "CamelExecCommandLogLevel";
 
diff --git 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecComponent.java
 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecComponent.java
index dc1fb8058ec6..11bc8519c0e1 100644
--- 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecComponent.java
+++ 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecComponent.java
@@ -37,7 +37,7 @@ public class ExecComponent extends DefaultComponent {
 
     @Metadata
     private String workingDir;
-    @Metadata(label = "advanced")
+    @Metadata(label = "advanced", security = "insecure:dev")
     private boolean allowControlHeaders;
     @Metadata(label = "advanced")
     private ExecCommandExecutor commandExecutor;
@@ -102,9 +102,11 @@ public class ExecComponent extends DefaultComponent {
     }
 
     /**
-     * Whether to allow to use Camel headers or not (default false). Enabling 
this allows to specify dynamic command
-     * line arguments via message header. However this can be seen as a 
potential security vulnerability if the header
-     * is coming from a malicious user, so use this with care.
+     * Whether {@code CamelExec*} in-headers may override URI options (default 
{@code false} since Camel 4.20). When
+     * {@code false}, {@code CamelExecCommandExecutable}, {@code 
CamelExecCommandArgs}, {@code CamelExecCommandOutFile},
+     * {@code CamelExecCommandWorkingDir}, {@code CamelExecCommandTimeout}, 
{@code CamelExecExitValues},
+     * {@code CamelExecUseStderrOnEmptyStdout}, and {@code 
CamelExecCommandLogLevel} are ignored. Enable only when those
+     * headers come from a trusted route, not from an untrusted consumer.
      */
     public void setAllowControlHeaders(boolean allowControlHeaders) {
         this.allowControlHeaders = allowControlHeaders;
diff --git 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
index dbe3c6edc276..4339c2304e5b 100644
--- 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
+++ 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecEndpoint.java
@@ -61,7 +61,7 @@ public class ExecEndpoint extends DefaultEndpoint {
     private boolean useStderrOnEmptyStdout;
     @UriParam(defaultValue = "DEBUG")
     private LoggingLevel commandLogLevel = LoggingLevel.DEBUG;
-    @UriParam(label = "advanced")
+    @UriParam(label = "advanced", security = "insecure:dev")
     private boolean allowControlHeaders;
 
     public ExecEndpoint(String uri, ExecComponent component) {
@@ -211,9 +211,11 @@ public class ExecEndpoint extends DefaultEndpoint {
     }
 
     /**
-     * Whether to allow to use Camel headers or not (default false). Enabling 
this allows to specify dynamic command
-     * line arguments via message header. However this can be seen as a 
potential security vulnerability if the header
-     * is coming from a malicious user, so use this with care.
+     * Whether {@code CamelExec*} in-headers may override URI options (default 
{@code false} since Camel 4.20). When
+     * {@code false}, {@code CamelExecCommandExecutable}, {@code 
CamelExecCommandArgs}, {@code CamelExecCommandOutFile},
+     * {@code CamelExecCommandWorkingDir}, {@code CamelExecCommandTimeout}, 
{@code CamelExecExitValues},
+     * {@code CamelExecUseStderrOnEmptyStdout}, and {@code 
CamelExecCommandLogLevel} are ignored. Enable only when those
+     * headers come from a trusted route, not from an untrusted consumer.
      */
     public void setAllowControlHeaders(boolean allowControlHeaders) {
         this.allowControlHeaders = allowControlHeaders;
diff --git 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecBinding.java
 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecBinding.java
index b8905e073536..df10f7889406 100644
--- 
a/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecBinding.java
+++ 
b/components/camel-exec/src/main/java/org/apache/camel/component/exec/impl/DefaultExecBinding.java
@@ -18,9 +18,11 @@ package org.apache.camel.component.exec.impl;
 
 import java.io.File;
 import java.io.InputStream;
+import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.camel.Exchange;
 import org.apache.camel.LoggingLevel;
@@ -45,12 +47,26 @@ public class DefaultExecBinding implements ExecBinding {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(DefaultExecBinding.class);
 
+    private static final String[] CONTROL_HEADERS = {
+            EXEC_COMMAND_EXECUTABLE,
+            EXEC_COMMAND_ARGS,
+            EXEC_COMMAND_OUT_FILE,
+            EXEC_COMMAND_WORKING_DIR,
+            EXEC_COMMAND_TIMEOUT,
+            EXEC_COMMAND_EXIT_VALUES,
+            EXEC_USE_STDERR_ON_EMPTY_STDOUT,
+            EXEC_COMMAND_LOG_LEVEL
+    };
+
+    private final Set<String> ignoredControlHeadersWarnedEndpoints = 
ConcurrentHashMap.newKeySet();
+
     @Override
     @SuppressWarnings("unchecked")
     public ExecCommand readInput(Exchange exchange, ExecEndpoint endpoint) {
         ObjectHelper.notNull(exchange, "exchange");
         ObjectHelper.notNull(endpoint, "endpoint");
 
+        warnIgnoredControlHeaders(exchange, endpoint);
         // do not convert args as we do that manually later
         Object args = endpoint.isAllowControlHeaders() ? 
exchange.getIn().removeHeader(EXEC_COMMAND_ARGS) : null;
         String cmd = getAndRemoveHeader(endpoint, exchange.getIn(), 
EXEC_COMMAND_EXECUTABLE, endpoint.getExecutable(),
@@ -153,4 +169,24 @@ public class DefaultExecBinding implements ExecBinding {
         }
         return h;
     }
+
+    private void warnIgnoredControlHeaders(Exchange exchange, ExecEndpoint 
endpoint) {
+        if (endpoint.isAllowControlHeaders()) {
+            return;
+        }
+        Message in = exchange.getIn();
+        List<String> ignored = new ArrayList<>();
+        for (String headerName : CONTROL_HEADERS) {
+            if (in.getHeader(headerName) != null) {
+                ignored.add(headerName);
+            }
+        }
+        if (!ignored.isEmpty() && 
ignoredControlHeadersWarnedEndpoints.add(endpoint.getEndpointUri())) {
+            LOG.warn(
+                    "Control header(s) {} are set but ignored because 
allowControlHeaders=false on {}. "
+                     + "Set allowControlHeaders=true on the exec endpoint or 
component to enable dynamic command configuration. "
+                     + "This warning is logged only once per exec endpoint.",
+                    ignored, endpoint);
+        }
+    }
 }
diff --git 
a/components/camel-exec/src/test/java/org/apache/camel/component/exec/DefaultExecBindingTest.java
 
b/components/camel-exec/src/test/java/org/apache/camel/component/exec/DefaultExecBindingTest.java
index 6d85a2d4d446..83ea91058968 100644
--- 
a/components/camel-exec/src/test/java/org/apache/camel/component/exec/DefaultExecBindingTest.java
+++ 
b/components/camel-exec/src/test/java/org/apache/camel/component/exec/DefaultExecBindingTest.java
@@ -19,42 +19,150 @@ package org.apache.camel.component.exec;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 import org.apache.camel.Exchange;
 import org.apache.camel.component.exec.impl.DefaultExecBinding;
 import org.apache.camel.test.junit6.CamelTestSupport;
+import org.apache.logging.log4j.Level;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.core.LogEvent;
+import org.apache.logging.log4j.core.Logger;
+import org.apache.logging.log4j.core.appender.AbstractAppender;
+import org.apache.logging.log4j.core.config.Property;
 import org.junit.jupiter.api.Test;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNull;
 
-public class DefaultExecBindingTest extends CamelTestSupport {
+class DefaultExecBindingTest extends CamelTestSupport {
 
     @Test
-    public void testReadInput() throws Exception {
-        ExecCommand command = readInput("exec:test", Collections.EMPTY_LIST);
-        assertEquals(Collections.EMPTY_LIST, command.getArgs(), "Get a wrong 
args.");
+    void shouldReadArgsFromHeaderWhenControlHeadersEnabled() throws Exception {
         List<String> args = Arrays.asList("arg1", "arg2");
-        command = readInput("exec:test", args);
-        assertEquals(args, command.getArgs(), "Get a wrong args.");
+        ExecCommand command = readInput("exec:test", args, true);
+        assertEquals(args, command.getArgs());
 
-        command = readInput("exec:test", "arg1 arg2");
-        assertEquals(args, command.getArgs(), "Get a wrong args.");
+        command = readInput("exec:test", "arg1 arg2", true);
+        assertEquals(args, command.getArgs());
 
-        command = readInput("exec:test?args=arg1 arg2", null);
-        assertEquals(args, command.getArgs(), "Get a wrong args.");
+        command = readInput("exec:test?args=arg1 arg2", null, true);
+        assertEquals(args, command.getArgs());
+
+        command = readInput("exec:test", Collections.emptyList(), true);
+        assertEquals(Collections.emptyList(), command.getArgs());
+    }
+
+    @Test
+    void shouldIgnoreControlHeadersByDefault() throws Exception {
+        DefaultExecBinding binding = new DefaultExecBinding();
+        ExecEndpoint execEndpoint = createExecEndpoint("exec:hostname", false);
+        Exchange exchange = execEndpoint.createExchange();
+        exchange.getIn().setHeader(ExecBinding.EXEC_COMMAND_EXECUTABLE, 
"whoami");
+        exchange.getIn().setHeader(ExecBinding.EXEC_COMMAND_ARGS, "ARGS-WORK");
+
+        ExecCommand command = binding.readInput(exchange, execEndpoint);
+
+        assertEquals("hostname", command.getExecutable());
+        assertEquals(Collections.emptyList(), command.getArgs());
+        assertEquals("whoami", 
exchange.getIn().getHeader(ExecBinding.EXEC_COMMAND_EXECUTABLE));
+        assertEquals("ARGS-WORK", 
exchange.getIn().getHeader(ExecBinding.EXEC_COMMAND_ARGS));
+    }
+
+    @Test
+    void shouldApplyControlHeadersWhenEnabled() throws Exception {
+        DefaultExecBinding binding = new DefaultExecBinding();
+        ExecEndpoint execEndpoint = createExecEndpoint("exec:hostname", true);
+        Exchange exchange = execEndpoint.createExchange();
+        exchange.getIn().setHeader(ExecBinding.EXEC_COMMAND_EXECUTABLE, 
"whoami");
+        exchange.getIn().setHeader(ExecBinding.EXEC_COMMAND_ARGS, "ARGS-WORK");
+
+        ExecCommand command = binding.readInput(exchange, execEndpoint);
+
+        assertEquals("whoami", command.getExecutable());
+        assertEquals(List.of("ARGS-WORK"), command.getArgs());
+        
assertNull(exchange.getIn().getHeader(ExecBinding.EXEC_COMMAND_EXECUTABLE));
+        assertNull(exchange.getIn().getHeader(ExecBinding.EXEC_COMMAND_ARGS));
+    }
+
+    @Test
+    void shouldKeepUriArgsWhenControlHeadersDisabled() throws Exception {
+        ExecCommand command = readInput("exec:echo?args=URIARGS-WORK", 
"ARGS-WORK", false);
+
+        assertEquals(List.of("URIARGS-WORK"), command.getArgs());
+        assertEquals("echo", command.getExecutable());
+    }
+
+    @Test
+    void shouldWarnOncePerEndpointWhenControlHeadersIgnored() throws Exception 
{
+        List<String> warnings = new CopyOnWriteArrayList<>();
+        AbstractAppender appender = new AbstractAppender("CaptureWarn", null, 
null, true, Property.EMPTY_ARRAY) {
+            @Override
+            public void append(LogEvent event) {
+                if (event.getLevel() == Level.WARN
+                        && 
event.getMessage().getFormattedMessage().contains("Control header")) {
+                    warnings.add(event.getMessage().getFormattedMessage());
+                }
+            }
+        };
+        appender.start();
+        Logger logger = (Logger) 
LogManager.getLogger(DefaultExecBinding.class);
+        logger.addAppender(appender);
+
+        try {
+            DefaultExecBinding binding = new DefaultExecBinding();
+            ExecComponent component = context.getComponent("exec", 
ExecComponent.class);
+            component.setAllowControlHeaders(false);
+            component.setBinding(binding);
+
+            ExecEndpoint endpoint1 = (ExecEndpoint) 
component.createEndpoint("exec:hostname");
+            ExecEndpoint endpoint2 = (ExecEndpoint) 
component.createEndpoint("exec:echo");
+
+            Exchange exchange1 = endpoint1.createExchange();
+            exchange1.getIn().setHeader(ExecBinding.EXEC_COMMAND_EXECUTABLE, 
"whoami");
+            binding.readInput(exchange1, endpoint1);
+
+            Exchange exchange2 = endpoint2.createExchange();
+            exchange2.getIn().setHeader(ExecBinding.EXEC_COMMAND_EXECUTABLE, 
"whoami");
+            binding.readInput(exchange2, endpoint2);
+
+            Exchange exchange3 = endpoint1.createExchange();
+            exchange3.getIn().setHeader(ExecBinding.EXEC_COMMAND_EXECUTABLE, 
"whoami");
+            binding.readInput(exchange3, endpoint1);
+
+            assertEquals(2, warnings.size(), "Expected one warning per 
distinct exec endpoint");
+        } finally {
+            logger.removeAppender(appender);
+            appender.stop();
+        }
+    }
+
+    @Test
+    void shouldApplyControlHeadersFromEndpointOption() throws Exception {
+        DefaultExecBinding binding = new DefaultExecBinding();
+        ExecComponent component = context.getComponent("exec", 
ExecComponent.class);
+        component.setAllowControlHeaders(false);
+        ExecEndpoint execEndpoint = (ExecEndpoint) 
component.createEndpoint("exec:hostname?allowControlHeaders=true");
+        Exchange exchange = execEndpoint.createExchange();
+        exchange.getIn().setHeader(ExecBinding.EXEC_COMMAND_EXECUTABLE, 
"whoami");
+
+        ExecCommand command = binding.readInput(exchange, execEndpoint);
+
+        assertEquals("whoami", command.getExecutable());
+        
assertNull(exchange.getIn().getHeader(ExecBinding.EXEC_COMMAND_EXECUTABLE));
     }
 
-    private ExecCommand readInput(String execEndpointUri, Object args) throws 
Exception {
+    private ExecCommand readInput(String execEndpointUri, Object args, boolean 
allowControlHeaders) throws Exception {
         DefaultExecBinding binding = new DefaultExecBinding();
-        ExecEndpoint execEndpoint = createExecEndpoint(execEndpointUri);
+        ExecEndpoint execEndpoint = createExecEndpoint(execEndpointUri, 
allowControlHeaders);
         Exchange exchange = execEndpoint.createExchange();
         exchange.getIn().setHeader(ExecBinding.EXEC_COMMAND_ARGS, args);
         return binding.readInput(exchange, execEndpoint);
     }
 
-    private ExecEndpoint createExecEndpoint(String uri) throws Exception {
+    private ExecEndpoint createExecEndpoint(String uri, boolean 
allowControlHeaders) throws Exception {
         ExecComponent component = context.getComponent("exec", 
ExecComponent.class);
-        component.setAllowControlHeaders(true);
+        component.setAllowControlHeaders(allowControlHeaders);
         return (ExecEndpoint) component.createEndpoint(uri);
     }
 
diff --git 
a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecProducerTest.java
 
b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecProducerTest.java
index fb77c129d2d4..fdc886f06e64 100644
--- 
a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecProducerTest.java
+++ 
b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecProducerTest.java
@@ -67,6 +67,25 @@ public class ExecProducerTest {
         assertEquals("mockedByCommandExecutorMock.exe", 
execCommandExecutorMock.lastCommandResult.getCommand().getExecutable());
     }
 
+    @Test
+    @DirtiesContext
+    public void testIgnoreControlHeadersByDefault() {
+        final String command = "java";
+
+        producerTemplate.send(new Processor() {
+
+            public void process(Exchange exchange) {
+                exchange.getIn().setBody("noinput");
+                exchange.getIn().setHeader(EXEC_COMMAND_EXECUTABLE, command);
+                exchange.getIn().setHeader(EXEC_COMMAND_ARGS, 
Arrays.asList("-version"));
+            }
+        });
+
+        assertEquals("mockedByCommandExecutorMock.exe",
+                
execCommandExecutorMock.lastCommandResult.getCommand().getExecutable());
+        
assertTrue(execCommandExecutorMock.lastCommandResult.getCommand().getArgs().isEmpty());
+    }
+
     @Test
     @DirtiesContext
     public void testOverrideExecutable() {
diff --git 
a/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java 
b/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
index f9a474d827fc..22da8e66497e 100644
--- a/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
+++ b/core/camel-util/src/main/java/org/apache/camel/util/SecurityUtils.java
@@ -57,6 +57,7 @@ public final class SecurityUtils {
         Map<String, SecurityOption> map = new HashMap<>();
         // Generated by camel build tools - do NOT edit this map!
         // SECURITY-OPTIONS: START
+        map.put("allowcontrolheaders", new SecurityOption(INSECURE_DEV, 
"true"));
         map.put("allowfilepathsource", new SecurityOption(INSECURE_DEV, 
"true"));
         map.put("allowjavaserializedobject", new 
SecurityOption(INSECURE_SERIALIZATION, "true"));
         map.put("allowlocalwebhookurls", new SecurityOption(INSECURE_DEV, 
"true"));
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
index e6bb96e52b7b..625ae75e2620 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_22.adoc
@@ -33,6 +33,18 @@ The same default is now also applied to the `ClientConfig` 
that Camel builds for
 endpoints, when neither a referenced `ClientConfig` nor `hazelcastConfigUri` 
is supplied. Client mode
 previously behaved differently from node mode for an otherwise identical 
endpoint configuration.
 
+=== camel-exec
+
+`allowControlHeaders` is now annotated `security = "insecure:dev"`.
+With `camel.main.profile = prod` the default policy for that category is 
`fail`,
+so an endpoint or component that sets `allowControlHeaders=true` will not start
+unless you relax `camel.security.insecureDevPolicy`.
+
+When the flag is `false` (the default), any remaining `CamelExecCommand*`,
+`CamelExecExitValues`, or `CamelExecUseStderrOnEmptyStdout` headers are ignored
+and a WARN is logged once per exec endpoint. Those headers never overrode the
+URI without the flag; they were just silent before.
+
 == Upgrading Camel 4.21 to 4.22
 
 === camel-tika
diff --git 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ExecComponentBuilderFactory.java
 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ExecComponentBuilderFactory.java
index 5768d84bf5c4..61c316b92cab 100644
--- 
a/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ExecComponentBuilderFactory.java
+++ 
b/dsl/camel-componentdsl/src/generated/java/org/apache/camel/builder/component/dsl/ExecComponentBuilderFactory.java
@@ -110,11 +110,13 @@ public interface ExecComponentBuilderFactory {
     
         
         /**
-         * Whether to allow to use Camel headers or not (default false).
-         * Enabling this allows to specify dynamic command line arguments via
-         * message header. However this can be seen as a potential security
-         * vulnerability if the header is coming from a malicious user, so use
-         * this with care.
+         * Whether {code CamelExec} in-headers may override URI options 
(default
+         * false since Camel 4.20). When false, CamelExecCommandExecutable,
+         * CamelExecCommandArgs, CamelExecCommandOutFile,
+         * CamelExecCommandWorkingDir, CamelExecCommandTimeout,
+         * CamelExecExitValues, CamelExecUseStderrOnEmptyStdout, and
+         * CamelExecCommandLogLevel are ignored. Enable only when those headers
+         * come from a trusted route, not from an untrusted consumer.
          * 
          * The option is a: &lt;code&gt;boolean&lt;/code&gt; type.
          * 
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ExecEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ExecEndpointBuilderFactory.java
index b87fdca5846a..541e42bf451d 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ExecEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/ExecEndpointBuilderFactory.java
@@ -266,11 +266,13 @@ public interface ExecEndpointBuilderFactory {
             return this;
         }
         /**
-         * Whether to allow to use Camel headers or not (default false).
-         * Enabling this allows to specify dynamic command line arguments via
-         * message header. However this can be seen as a potential security
-         * vulnerability if the header is coming from a malicious user, so use
-         * this with care.
+         * Whether {code CamelExec} in-headers may override URI options 
(default
+         * false since Camel 4.20). When false, CamelExecCommandExecutable,
+         * CamelExecCommandArgs, CamelExecCommandOutFile,
+         * CamelExecCommandWorkingDir, CamelExecCommandTimeout,
+         * CamelExecExitValues, CamelExecUseStderrOnEmptyStdout, and
+         * CamelExecCommandLogLevel are ignored. Enable only when those headers
+         * come from a trusted route, not from an untrusted consumer.
          * 
          * The option is a: <code>boolean</code> type.
          * 
@@ -285,11 +287,13 @@ public interface ExecEndpointBuilderFactory {
             return this;
         }
         /**
-         * Whether to allow to use Camel headers or not (default false).
-         * Enabling this allows to specify dynamic command line arguments via
-         * message header. However this can be seen as a potential security
-         * vulnerability if the header is coming from a malicious user, so use
-         * this with care.
+         * Whether {code CamelExec} in-headers may override URI options 
(default
+         * false since Camel 4.20). When false, CamelExecCommandExecutable,
+         * CamelExecCommandArgs, CamelExecCommandOutFile,
+         * CamelExecCommandWorkingDir, CamelExecCommandTimeout,
+         * CamelExecExitValues, CamelExecUseStderrOnEmptyStdout, and
+         * CamelExecCommandLogLevel are ignored. Enable only when those headers
+         * come from a trusted route, not from an untrusted consumer.
          * 
          * The option will be converted to a <code>boolean</code> type.
          * 
@@ -445,7 +449,8 @@ public interface ExecEndpointBuilderFactory {
 
         /**
          * The name of the system command that will be executed. Overrides
-         * executable in the URI.
+         * executable in the URI. Requires allowControlHeaders=true on the exec
+         * endpoint or component (default is false since Camel 4.20).
          * 
          * The option is a: {@code String} type.
          * 
@@ -459,7 +464,8 @@ public interface ExecEndpointBuilderFactory {
         /**
          * Command-line argument(s) to pass to the executed process. The
          * argument(s) is/are used literally - no quoting is applied. Overrides
-         * any existing args in the URI.
+         * any existing args in the URI. Requires allowControlHeaders=true on
+         * the exec endpoint or component (default is false since Camel 4.20).
          * 
          * The option is a: {@code java.util.List<String> or String} type.
          * 
@@ -473,6 +479,8 @@ public interface ExecEndpointBuilderFactory {
         /**
          * The name of a file, created by the executable, that should be
          * considered as its output. Overrides any existing outFile in the URI.
+         * Requires allowControlHeaders=true on the exec endpoint or component
+         * (default is false since Camel 4.20).
          * 
          * The option is a: {@code String} type.
          * 
@@ -485,7 +493,8 @@ public interface ExecEndpointBuilderFactory {
         }
         /**
          * The directory in which the command should be executed. Overrides any
-         * existing workingDir in the URI.
+         * existing workingDir in the URI. Requires allowControlHeaders=true on
+         * the exec endpoint or component (default is false since Camel 4.20).
          * 
          * The option is a: {@code String} type.
          * 
@@ -498,7 +507,9 @@ public interface ExecEndpointBuilderFactory {
         }
         /**
          * The timeout, in milliseconds, after which the executable should be
-         * terminated. Overrides any existing timeout in the URI.
+         * terminated. Overrides any existing timeout in the URI. Requires
+         * allowControlHeaders=true on the exec endpoint or component (default
+         * is false since Camel 4.20).
          * 
          * The option is a: {@code long} type.
          * 
@@ -511,7 +522,9 @@ public interface ExecEndpointBuilderFactory {
         }
         /**
          * The exit values for successful execution of the process. Overrides
-         * any existing exitValues in the URI.
+         * any existing exitValues in the URI. Requires 
allowControlHeaders=true
+         * on the exec endpoint or component (default is false since Camel
+         * 4.20).
          * 
          * The option is a: {@code String} type.
          * 
@@ -552,7 +565,8 @@ public interface ExecEndpointBuilderFactory {
         /**
          * Indicates that when stdout is empty, this component will populate 
the
          * Camel Message Body with stderr. This behavior is disabled (false) by
-         * default.
+         * default. Requires allowControlHeaders=true on the exec endpoint or
+         * component (default is false since Camel 4.20).
          * 
          * The option is a: {@code boolean} type.
          * 
@@ -566,7 +580,9 @@ public interface ExecEndpointBuilderFactory {
         /**
          * Logging level to be used for commands during execution. The default
          * value is DEBUG. Possible values are TRACE, DEBUG, INFO, WARN, ERROR
-         * or OFF (Values of LoggingLevel enum).
+         * or OFF (Values of LoggingLevel enum). Requires
+         * allowControlHeaders=true on the exec endpoint or component (default
+         * is false since Camel 4.20).
          * 
          * The option is a: {@code String} type.
          * 

Reply via email to