This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch ex in repository https://gitbox.apache.org/repos/asf/camel.git
commit b0a1501974246f137138680e13466ebc7a2d1bd3 Author: Claus Ibsen <[email protected]> AuthorDate: Mon Apr 13 15:54:53 2026 +0200 CAMEL-23315: camel-exec - Add option to turn on|off control headers for camel-exec to make it more restricted by default. --- .../org/apache/camel/catalog/components/exec.json | 26 +-- .../component/exec/ExecComponentConfigurer.java | 24 +++ .../component/exec/ExecEndpointConfigurer.java | 8 +- .../component/exec/ExecEndpointUriFactory.java | 3 +- .../org/apache/camel/component/exec/exec.json | 26 +-- .../apache/camel/component/exec/ExecComponent.java | 87 +++++++++- .../apache/camel/component/exec/ExecEndpoint.java | 34 ++-- .../component/exec/impl/DefaultExecBinding.java | 32 ++-- .../component/exec/DefaultExecBindingTest.java | 4 +- .../exec/ExecJavaProcessRecipientListTest.java | 3 + .../camel/component/exec/ExecJavaProcessTest.java | 2 +- .../camel/component/exec/ExecOutFileTest.java | 6 +- .../camel/component/exec/ExecProducerTest.java | 14 +- .../component/exec/exec-mock-executor-context.xml | 5 + .../ROOT/pages/camel-4x-upgrade-guide-4_20.adoc | 7 + .../component/dsl/ExecComponentBuilderFactory.java | 96 +++++++++++ .../endpoint/dsl/ExecEndpointBuilderFactory.java | 176 +++++++++++++-------- 17 files changed, 425 insertions(+), 128 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 33f90104fe41..0b2fc3bc98a1 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 @@ -25,7 +25,12 @@ }, "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 [...] + "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 [...] + "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" }, @@ -42,14 +47,15 @@ "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." }, "args": { "index": 1, "kind": "parameter", "displayName": "Args", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The arguments may be one or many whitespace-separated tokens." }, - "binding": { "index": 2, "kind": "parameter", "displayName": "Binding", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.exec.ExecBinding", "deprecated": false, "autowired": false, "secret": false, "description": "A reference to a org.apache.commons.exec.ExecBinding in the Registry." }, - "commandExecutor": { "index": 3, "kind": "parameter", "displayName": "Command Executor", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.exec.ExecCommandExecutor", "deprecated": false, "autowired": false, "secret": false, "description": "A reference to a org.apache.commons.exec.ExecCommandExecutor in the Registry that customizes the command execution. The default command executor utilizes the commons-exec library, which a [...] - "commandLogLevel": { "index": 4, "kind": "parameter", "displayName": "Command Log 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": "DEBUG", "description": "Logging level to be used for commands during execution. The default value is DEBUG. Possible values are TRACE, DEBUG, INFO, [...] - "exitValues": { "index": 5, "kind": "parameter", "displayName": "Exit Values", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The exit values of successful executions. If the process exits with another value, an exception is raised. Comma-separated list of exit values. And empty list (the default) sets no expected exit values and disables the check." }, - "outFile": { "index": 6, "kind": "parameter", "displayName": "Out File", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a file, created by the executable, that should be considered as its output. If no outFile is set, the standard output (stdout) of the executable will be used instead." }, - "timeout": { "index": 7, "kind": "parameter", "displayName": "Timeout", "group": "producer", "label": "", "required": false, "type": "duration", "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." }, - "useStderrOnEmptyStdout": { "index": 8, "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": 9, "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": 10, "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 produ [...] + "commandLogLevel": { "index": 2, "kind": "parameter", "displayName": "Command Log 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": "DEBUG", "description": "Logging level to be used for commands during execution. The default value is DEBUG. Possible values are TRACE, DEBUG, INFO, [...] + "exitValues": { "index": 3, "kind": "parameter", "displayName": "Exit Values", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The exit values of successful executions. If the process exits with another value, an exception is raised. Comma-separated list of exit values. And empty list (the default) sets no expected exit values and disables the check." }, + "outFile": { "index": 4, "kind": "parameter", "displayName": "Out File", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a file, created by the executable, that should be considered as its output. If no outFile is set, the standard output (stdout) of the executable will be used instead." }, + "timeout": { "index": 5, "kind": "parameter", "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." }, + "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 [...] + "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/generated/java/org/apache/camel/component/exec/ExecComponentConfigurer.java b/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecComponentConfigurer.java index 1ef790cbab80..7cda185a8135 100644 --- a/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecComponentConfigurer.java +++ b/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecComponentConfigurer.java @@ -23,10 +23,18 @@ public class ExecComponentConfigurer extends PropertyConfigurerSupport implement public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { ExecComponent target = (ExecComponent) obj; switch (ignoreCase ? name.toLowerCase() : name) { + case "allowcontrolheaders": + case "allowControlHeaders": target.setAllowControlHeaders(property(camelContext, boolean.class, value)); return true; case "autowiredenabled": case "autowiredEnabled": target.setAutowiredEnabled(property(camelContext, boolean.class, value)); return true; + case "binding": target.setBinding(property(camelContext, org.apache.camel.component.exec.ExecBinding.class, value)); return true; + case "commandexecutor": + case "commandExecutor": target.setCommandExecutor(property(camelContext, org.apache.camel.component.exec.ExecCommandExecutor.class, value)); return true; case "lazystartproducer": case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; + case "timeout": target.setTimeout(property(camelContext, long.class, value)); return true; + case "workingdir": + case "workingDir": target.setWorkingDir(property(camelContext, java.lang.String.class, value)); return true; default: return false; } } @@ -34,10 +42,18 @@ public class ExecComponentConfigurer extends PropertyConfigurerSupport implement @Override public Class<?> getOptionType(String name, boolean ignoreCase) { switch (ignoreCase ? name.toLowerCase() : name) { + case "allowcontrolheaders": + case "allowControlHeaders": return boolean.class; case "autowiredenabled": case "autowiredEnabled": return boolean.class; + case "binding": return org.apache.camel.component.exec.ExecBinding.class; + case "commandexecutor": + case "commandExecutor": return org.apache.camel.component.exec.ExecCommandExecutor.class; case "lazystartproducer": case "lazyStartProducer": return boolean.class; + case "timeout": return long.class; + case "workingdir": + case "workingDir": return java.lang.String.class; default: return null; } } @@ -46,10 +62,18 @@ public class ExecComponentConfigurer extends PropertyConfigurerSupport implement public Object getOptionValue(Object obj, String name, boolean ignoreCase) { ExecComponent target = (ExecComponent) obj; switch (ignoreCase ? name.toLowerCase() : name) { + case "allowcontrolheaders": + case "allowControlHeaders": return target.isAllowControlHeaders(); case "autowiredenabled": case "autowiredEnabled": return target.isAutowiredEnabled(); + case "binding": return target.getBinding(); + case "commandexecutor": + case "commandExecutor": return target.getCommandExecutor(); case "lazystartproducer": case "lazyStartProducer": return target.isLazyStartProducer(); + case "timeout": return target.getTimeout(); + case "workingdir": + case "workingDir": return target.getWorkingDir(); default: return null; } } diff --git a/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointConfigurer.java b/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointConfigurer.java index 30aeff3160e2..0ef33deb4fdd 100644 --- a/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointConfigurer.java +++ b/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointConfigurer.java @@ -23,6 +23,8 @@ public class ExecEndpointConfigurer extends PropertyConfigurerSupport implements public boolean configure(CamelContext camelContext, Object obj, String name, Object value, boolean ignoreCase) { ExecEndpoint target = (ExecEndpoint) obj; switch (ignoreCase ? name.toLowerCase() : name) { + case "allowcontrolheaders": + case "allowControlHeaders": target.setAllowControlHeaders(property(camelContext, boolean.class, value)); return true; case "args": target.setArgs(property(camelContext, java.lang.String.class, value)); return true; case "binding": target.setBinding(property(camelContext, org.apache.camel.component.exec.ExecBinding.class, value)); return true; case "commandexecutor": @@ -35,7 +37,7 @@ public class ExecEndpointConfigurer extends PropertyConfigurerSupport implements case "lazyStartProducer": target.setLazyStartProducer(property(camelContext, boolean.class, value)); return true; case "outfile": case "outFile": target.setOutFile(property(camelContext, java.lang.String.class, value)); return true; - case "timeout": target.setTimeout(property(camelContext, java.time.Duration.class, value).toMillis()); return true; + case "timeout": target.setTimeout(property(camelContext, long.class, value)); return true; case "usestderronemptystdout": case "useStderrOnEmptyStdout": target.setUseStderrOnEmptyStdout(property(camelContext, boolean.class, value)); return true; case "workingdir": @@ -47,6 +49,8 @@ public class ExecEndpointConfigurer extends PropertyConfigurerSupport implements @Override public Class<?> getOptionType(String name, boolean ignoreCase) { switch (ignoreCase ? name.toLowerCase() : name) { + case "allowcontrolheaders": + case "allowControlHeaders": return boolean.class; case "args": return java.lang.String.class; case "binding": return org.apache.camel.component.exec.ExecBinding.class; case "commandexecutor": @@ -72,6 +76,8 @@ public class ExecEndpointConfigurer extends PropertyConfigurerSupport implements public Object getOptionValue(Object obj, String name, boolean ignoreCase) { ExecEndpoint target = (ExecEndpoint) obj; switch (ignoreCase ? name.toLowerCase() : name) { + case "allowcontrolheaders": + case "allowControlHeaders": return target.isAllowControlHeaders(); case "args": return target.getArgs(); case "binding": return target.getBinding(); case "commandexecutor": diff --git a/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointUriFactory.java b/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointUriFactory.java index 2659656b7190..e09a4effb712 100644 --- a/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointUriFactory.java +++ b/components/camel-exec/src/generated/java/org/apache/camel/component/exec/ExecEndpointUriFactory.java @@ -23,7 +23,8 @@ public class ExecEndpointUriFactory extends org.apache.camel.support.component.E private static final Set<String> SECRET_PROPERTY_NAMES; private static final Map<String, String> MULTI_VALUE_PREFIXES; static { - Set<String> props = new HashSet<>(11); + Set<String> props = new HashSet<>(12); + props.add("allowControlHeaders"); props.add("args"); props.add("binding"); props.add("commandExecutor"); 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 33f90104fe41..0b2fc3bc98a1 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 @@ -25,7 +25,12 @@ }, "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 [...] + "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 [...] + "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" }, @@ -42,14 +47,15 @@ "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." }, "args": { "index": 1, "kind": "parameter", "displayName": "Args", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The arguments may be one or many whitespace-separated tokens." }, - "binding": { "index": 2, "kind": "parameter", "displayName": "Binding", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.exec.ExecBinding", "deprecated": false, "autowired": false, "secret": false, "description": "A reference to a org.apache.commons.exec.ExecBinding in the Registry." }, - "commandExecutor": { "index": 3, "kind": "parameter", "displayName": "Command Executor", "group": "producer", "label": "", "required": false, "type": "object", "javaType": "org.apache.camel.component.exec.ExecCommandExecutor", "deprecated": false, "autowired": false, "secret": false, "description": "A reference to a org.apache.commons.exec.ExecCommandExecutor in the Registry that customizes the command execution. The default command executor utilizes the commons-exec library, which a [...] - "commandLogLevel": { "index": 4, "kind": "parameter", "displayName": "Command Log 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": "DEBUG", "description": "Logging level to be used for commands during execution. The default value is DEBUG. Possible values are TRACE, DEBUG, INFO, [...] - "exitValues": { "index": 5, "kind": "parameter", "displayName": "Exit Values", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The exit values of successful executions. If the process exits with another value, an exception is raised. Comma-separated list of exit values. And empty list (the default) sets no expected exit values and disables the check." }, - "outFile": { "index": 6, "kind": "parameter", "displayName": "Out File", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a file, created by the executable, that should be considered as its output. If no outFile is set, the standard output (stdout) of the executable will be used instead." }, - "timeout": { "index": 7, "kind": "parameter", "displayName": "Timeout", "group": "producer", "label": "", "required": false, "type": "duration", "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." }, - "useStderrOnEmptyStdout": { "index": 8, "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": 9, "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": 10, "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 produ [...] + "commandLogLevel": { "index": 2, "kind": "parameter", "displayName": "Command Log 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": "DEBUG", "description": "Logging level to be used for commands during execution. The default value is DEBUG. Possible values are TRACE, DEBUG, INFO, [...] + "exitValues": { "index": 3, "kind": "parameter", "displayName": "Exit Values", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The exit values of successful executions. If the process exits with another value, an exception is raised. Comma-separated list of exit values. And empty list (the default) sets no expected exit values and disables the check." }, + "outFile": { "index": 4, "kind": "parameter", "displayName": "Out File", "group": "producer", "label": "", "required": false, "type": "string", "javaType": "java.lang.String", "deprecated": false, "autowired": false, "secret": false, "description": "The name of a file, created by the executable, that should be considered as its output. If no outFile is set, the standard output (stdout) of the executable will be used instead." }, + "timeout": { "index": 5, "kind": "parameter", "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." }, + "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 [...] + "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/java/org/apache/camel/component/exec/ExecComponent.java b/components/camel-exec/src/main/java/org/apache/camel/component/exec/ExecComponent.java index cec200384584..dc1fb8058ec6 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 @@ -17,12 +17,17 @@ package org.apache.camel.component.exec; import java.net.URLDecoder; +import java.nio.charset.StandardCharsets; import java.util.Map; import org.apache.camel.Endpoint; +import org.apache.camel.component.exec.impl.DefaultExecBinding; +import org.apache.camel.spi.Metadata; import org.apache.camel.spi.annotations.Component; import org.apache.camel.support.DefaultComponent; +import static org.apache.camel.component.exec.ExecEndpoint.NO_TIMEOUT; + /** * Represents the component that manages {@link ExecEndpoint}. With the component it is possible to execute system * commands. @@ -30,14 +35,94 @@ import org.apache.camel.support.DefaultComponent; @Component("exec") public class ExecComponent extends DefaultComponent { + @Metadata + private String workingDir; + @Metadata(label = "advanced") + private boolean allowControlHeaders; + @Metadata(label = "advanced") + private ExecCommandExecutor commandExecutor; + @Metadata(label = "advanced") + private ExecBinding binding = new DefaultExecBinding(); + @Metadata + private long timeout = NO_TIMEOUT; + public ExecComponent() { } @Override protected Endpoint createEndpoint(String uri, String remaining, Map<String, Object> parameters) throws Exception { ExecEndpoint endpoint = new ExecEndpoint(uri, this); + endpoint.setWorkingDir(workingDir); + endpoint.setAllowControlHeaders(allowControlHeaders); + endpoint.setCommandExecutor(commandExecutor); + endpoint.setBinding(binding); + endpoint.setTimeout(timeout); setProperties(endpoint, parameters); - endpoint.setExecutable(URLDecoder.decode(remaining, "UTF-8")); + endpoint.setExecutable(URLDecoder.decode(remaining, StandardCharsets.UTF_8)); return endpoint; } + + public String getWorkingDir() { + return workingDir; + } + + /** + * The directory in which the command should be executed. If null, the working directory of the current process will + * be used. + */ + public void setWorkingDir(String workingDir) { + this.workingDir = workingDir; + } + + public ExecCommandExecutor getCommandExecutor() { + return commandExecutor; + } + + /** + * 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 shutdown hook for every executed command. + */ + public void setCommandExecutor(ExecCommandExecutor commandExecutor) { + this.commandExecutor = commandExecutor; + } + + public ExecBinding getBinding() { + return binding; + } + + /** + * To use a custom org.apache.commons.exec.ExecBinding for advanced use-cases. + */ + public void setBinding(ExecBinding binding) { + this.binding = binding; + } + + public boolean isAllowControlHeaders() { + return allowControlHeaders; + } + + /** + * 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. + */ + public void setAllowControlHeaders(boolean allowControlHeaders) { + this.allowControlHeaders = allowControlHeaders; + } + + public long getTimeout() { + return timeout; + } + + /** + * 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. + */ + public void setTimeout(long timeout) { + if (timeout <= 0) { + throw new IllegalArgumentException("The timeout must be a positive long!"); + } + this.timeout = timeout; + } + } 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 8f2ddfebe9e2..dbe3c6edc276 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 @@ -21,13 +21,11 @@ import org.apache.camel.Consumer; import org.apache.camel.LoggingLevel; import org.apache.camel.Processor; import org.apache.camel.Producer; -import org.apache.camel.component.exec.impl.DefaultExecBinding; import org.apache.camel.spi.Metadata; import org.apache.camel.spi.UriEndpoint; import org.apache.camel.spi.UriParam; import org.apache.camel.spi.UriPath; import org.apache.camel.support.DefaultEndpoint; -import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.StringHelper; /** @@ -49,25 +47,25 @@ public class ExecEndpoint extends DefaultEndpoint { private String args; @UriParam private String workingDir; - @UriParam(javaType = "java.time.Duration") + @UriParam private long timeout; @UriParam private String exitValues; @UriParam private String outFile; - @UriParam + @UriParam(label = "advanced") private ExecCommandExecutor commandExecutor; - @UriParam + @UriParam(label = "advanced") private ExecBinding binding; @UriParam private boolean useStderrOnEmptyStdout; @UriParam(defaultValue = "DEBUG") private LoggingLevel commandLogLevel = LoggingLevel.DEBUG; + @UriParam(label = "advanced") + private boolean allowControlHeaders; public ExecEndpoint(String uri, ExecComponent component) { super(uri, component); - this.timeout = NO_TIMEOUT; - this.binding = new DefaultExecBinding(); } @Override @@ -166,12 +164,10 @@ public class ExecEndpoint extends DefaultEndpoint { } /** - * A reference to a org.apache.commons.exec.ExecCommandExecutor in the Registry that customizes the command - * execution. The default command executor utilizes the commons-exec library, which adds a shutdown hook for every - * executed command. + * 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 shutdown hook for every executed command. */ public void setCommandExecutor(ExecCommandExecutor commandExecutor) { - ObjectHelper.notNull(commandExecutor, "commandExecutor"); this.commandExecutor = commandExecutor; } @@ -180,10 +176,9 @@ public class ExecEndpoint extends DefaultEndpoint { } /** - * A reference to a org.apache.commons.exec.ExecBinding in the Registry. + * To use a custom org.apache.commons.exec.ExecBinding for advanced use-cases. */ public void setBinding(ExecBinding binding) { - ObjectHelper.notNull(binding, "binding"); this.binding = binding; } @@ -210,4 +205,17 @@ public class ExecEndpoint extends DefaultEndpoint { public void setCommandLogLevel(LoggingLevel commandLogLevel) { this.commandLogLevel = commandLogLevel; } + + public boolean isAllowControlHeaders() { + return allowControlHeaders; + } + + /** + * 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. + */ + 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 6957bf07d44b..b8905e073536 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 @@ -52,16 +52,20 @@ public class DefaultExecBinding implements ExecBinding { ObjectHelper.notNull(endpoint, "endpoint"); // do not convert args as we do that manually later - Object args = exchange.getIn().removeHeader(EXEC_COMMAND_ARGS); - String cmd = getAndRemoveHeader(exchange.getIn(), EXEC_COMMAND_EXECUTABLE, endpoint.getExecutable(), String.class); - String dir = getAndRemoveHeader(exchange.getIn(), EXEC_COMMAND_WORKING_DIR, endpoint.getWorkingDir(), String.class); - long timeout = getAndRemoveHeader(exchange.getIn(), EXEC_COMMAND_TIMEOUT, endpoint.getTimeout(), Long.class); + Object args = endpoint.isAllowControlHeaders() ? exchange.getIn().removeHeader(EXEC_COMMAND_ARGS) : null; + String cmd = getAndRemoveHeader(endpoint, exchange.getIn(), EXEC_COMMAND_EXECUTABLE, endpoint.getExecutable(), + String.class); + String dir = getAndRemoveHeader(endpoint, exchange.getIn(), EXEC_COMMAND_WORKING_DIR, endpoint.getWorkingDir(), + String.class); + long timeout = getAndRemoveHeader(endpoint, exchange.getIn(), EXEC_COMMAND_TIMEOUT, endpoint.getTimeout(), Long.class); String exitValuesString - = getAndRemoveHeader(exchange.getIn(), EXEC_COMMAND_EXIT_VALUES, endpoint.getExitValues(), String.class); - String outFilePath = getAndRemoveHeader(exchange.getIn(), EXEC_COMMAND_OUT_FILE, endpoint.getOutFile(), String.class); - boolean useStderrOnEmptyStdout = getAndRemoveHeader(exchange.getIn(), EXEC_USE_STDERR_ON_EMPTY_STDOUT, + = getAndRemoveHeader(endpoint, exchange.getIn(), EXEC_COMMAND_EXIT_VALUES, endpoint.getExitValues(), + String.class); + String outFilePath + = getAndRemoveHeader(endpoint, exchange.getIn(), EXEC_COMMAND_OUT_FILE, endpoint.getOutFile(), String.class); + boolean useStderrOnEmptyStdout = getAndRemoveHeader(endpoint, exchange.getIn(), EXEC_USE_STDERR_ON_EMPTY_STDOUT, endpoint.isUseStderrOnEmptyStdout(), Boolean.class); - LoggingLevel commandLogLevel = getAndRemoveHeader(exchange.getIn(), EXEC_COMMAND_LOG_LEVEL, + LoggingLevel commandLogLevel = getAndRemoveHeader(endpoint, exchange.getIn(), EXEC_COMMAND_LOG_LEVEL, endpoint.getCommandLogLevel(), LoggingLevel.class); InputStream input = exchange.getIn().getBody(InputStream.class); @@ -83,7 +87,7 @@ public class DefaultExecBinding implements ExecBinding { } Set<Integer> exitValues = new HashSet<>(); - if (exitValuesString != null && exitValuesString.length() > 0) { + if (exitValuesString != null && !exitValuesString.isEmpty()) { exitValues = new HashSet<>(splitCommaSeparatedToListOfInts(exitValuesString)); } @@ -140,9 +144,13 @@ public class DefaultExecBinding implements ExecBinding { * Gets and removes the <code> <code>headerName</code> header form the input <code>message</code> (the header will * not be propagated) */ - protected <T> T getAndRemoveHeader(Message message, String headerName, T defaultValue, Class<T> headerType) { - T h = message.getHeader(headerName, defaultValue, headerType); - message.removeHeader(headerName); + protected <T> T getAndRemoveHeader( + ExecEndpoint endpoint, Message message, String headerName, T defaultValue, Class<T> headerType) { + T h = defaultValue; + if (endpoint.isAllowControlHeaders()) { + h = message.getHeader(headerName, defaultValue, headerType); + message.removeHeader(headerName); + } return h; } } 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 e408ed447f96..6d85a2d4d446 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 @@ -20,7 +20,6 @@ import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.apache.camel.Component; import org.apache.camel.Exchange; import org.apache.camel.component.exec.impl.DefaultExecBinding; import org.apache.camel.test.junit6.CamelTestSupport; @@ -54,7 +53,8 @@ public class DefaultExecBindingTest extends CamelTestSupport { } private ExecEndpoint createExecEndpoint(String uri) throws Exception { - Component component = context.getComponent("exec"); + ExecComponent component = context.getComponent("exec", ExecComponent.class); + component.setAllowControlHeaders(true); return (ExecEndpoint) component.createEndpoint(uri); } diff --git a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJavaProcessRecipientListTest.java b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJavaProcessRecipientListTest.java index f6189b3cd4c6..9e0121c5282b 100644 --- a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJavaProcessRecipientListTest.java +++ b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJavaProcessRecipientListTest.java @@ -293,6 +293,9 @@ public class ExecJavaProcessRecipientListTest extends CamelTestSupport { protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { public void configure() { + // turn on dynamic headers on exec component + context.getComponent("exec", ExecComponent.class).setAllowControlHeaders(true); + from("direct:input") .recipientList(header("whereTo")).to("mock:output"); diff --git a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJavaProcessTest.java b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJavaProcessTest.java index 0b47a2b2cd18..3c3405b338b9 100644 --- a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJavaProcessTest.java +++ b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecJavaProcessTest.java @@ -480,7 +480,7 @@ public class ExecJavaProcessTest extends CamelTestSupport { protected RouteBuilder createRouteBuilder() { return new RouteBuilder() { public void configure() { - from("direct:input").to("exec:java").to("mock:output"); + from("direct:input").to("exec:java?allowControlHeaders=true").to("mock:output"); } }; } diff --git a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecOutFileTest.java b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecOutFileTest.java index a6e424b943a7..0945890f8c55 100644 --- a/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecOutFileTest.java +++ b/components/camel-exec/src/test/java/org/apache/camel/component/exec/ExecOutFileTest.java @@ -48,8 +48,8 @@ public class ExecOutFileTest { private static final File FILE = new File("target/outfiletest.xml"); - @Produce("direct:input") - private ProducerTemplate producerTemplate; + @Produce("direct:input2") + private ProducerTemplate producerTemplate2; @BeforeEach public void setUp() throws IOException { @@ -106,7 +106,7 @@ public class ExecOutFileTest { } private Exchange sendWithMockedExecutor() { - Exchange e = producerTemplate.send(new Processor() { + Exchange e = producerTemplate2.send(new Processor() { public void process(Exchange exchange) { exchange.getIn().setHeader(EXEC_COMMAND_OUT_FILE, FILE.getPath()); exchange.getIn().setBody(FILE_CONTENT); 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 5b454b5ab056..fb77c129d2d4 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 @@ -53,6 +53,8 @@ public class ExecProducerTest { @Produce("direct:input") private ProducerTemplate producerTemplate; + @Produce("direct:input2") + private ProducerTemplate producerTemplate2; @Autowired private ExecCommandExecutorMock execCommandExecutorMock; @@ -70,7 +72,7 @@ public class ExecProducerTest { public void testOverrideExecutable() { final String command = "java"; - producerTemplate.send(new Processor() { + producerTemplate2.send(new Processor() { public void process(Exchange exchange) { exchange.getIn().setBody("noinput"); @@ -88,7 +90,7 @@ public class ExecProducerTest { @DirtiesContext public void testOverrideArgs() { final String[] args = { "-version", "classpath:c:/program files/test/" }; - producerTemplate.send(new Processor() { + producerTemplate2.send(new Processor() { public void process(Exchange exchange) { exchange.getIn().setBody("noinput"); @@ -104,7 +106,7 @@ public class ExecProducerTest { @Test @DirtiesContext public void testOverrideTimeout() { - producerTemplate.send(new Processor() { + producerTemplate2.send(new Processor() { public void process(Exchange exchange) { exchange.getIn().setBody("noinput"); @@ -117,7 +119,7 @@ public class ExecProducerTest { @Test @DirtiesContext public void testExitValues() { - producerTemplate.send(new Processor() { + producerTemplate2.send(new Processor() { public void process(Exchange exchange) { exchange.getIn().setBody("noinput"); @@ -130,7 +132,7 @@ public class ExecProducerTest { @Test @DirtiesContext public void testExitValueNone() { - producerTemplate.send(new Processor() { + producerTemplate2.send(new Processor() { public void process(Exchange exchange) { exchange.getIn().setBody("noinput"); @@ -191,7 +193,7 @@ public class ExecProducerTest { public void testOverrideWorkingDir() { final String workingDir = "c:/program files/test"; - producerTemplate.send(new Processor() { + producerTemplate2.send(new Processor() { public void process(Exchange exchange) { exchange.getIn().setBody(""); exchange.getIn().setHeader(EXEC_COMMAND_WORKING_DIR, workingDir); diff --git a/components/camel-exec/src/test/resources/org/apache/camel/component/exec/exec-mock-executor-context.xml b/components/camel-exec/src/test/resources/org/apache/camel/component/exec/exec-mock-executor-context.xml index 1cc52ad82173..820297aa06e5 100644 --- a/components/camel-exec/src/test/resources/org/apache/camel/component/exec/exec-mock-executor-context.xml +++ b/components/camel-exec/src/test/resources/org/apache/camel/component/exec/exec-mock-executor-context.xml @@ -31,6 +31,11 @@ <to uri="exec:mockedByCommandExecutorMock.exe?commandExecutor=#commandExecutorMock"/> </route> + <route> + <from uri="direct:input2"/> + <to + uri="exec:mockedByCommandExecutorMock.exe?commandExecutor=#commandExecutorMock&allowControlHeaders=true"/> + </route> </camelContext> <bean id="commandExecutorMock" diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_20.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_20.adoc index f2c4391e8c88..e81069682919 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_20.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_20.adoc @@ -13,6 +13,13 @@ See the xref:camel-upgrade-recipes-tool.adoc[documentation] page for details. == Upgrading Camel 4.19 to 4.20 +=== camel-exec + +The `camel-exec` component is made more restrict by default, as the command line arguments, and other parameters +cannot be dynamic provided from Camel message headers. To allow this you now have to enable this by setting +`allowControlHeaders=true` on either the component (globally) or per endpoint when needed. + + === camel-pulsar The Apache Pulsar client library has been upgraded from 4.1.3 to 4.2.0. 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 f024a853157e..5768d84bf5c4 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 @@ -75,6 +75,60 @@ public interface ExecComponentBuilderFactory { return this; } + /** + * 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. + * + * The option is a: <code>long</code> type. + * + * Group: producer + * + * @param timeout the value to set + * @return the dsl builder + */ + default ExecComponentBuilder timeout(long timeout) { + doSetProperty("timeout", timeout); + return this; + } + + /** + * The directory in which the command should be executed. If null, the + * working directory of the current process will be used. + * + * The option is a: <code>java.lang.String</code> type. + * + * Group: producer + * + * @param workingDir the value to set + * @return the dsl builder + */ + default ExecComponentBuilder workingDir(java.lang.String workingDir) { + doSetProperty("workingDir", workingDir); + 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. + * + * The option is a: <code>boolean</code> type. + * + * Default: false + * Group: advanced + * + * @param allowControlHeaders the value to set + * @return the dsl builder + */ + default ExecComponentBuilder allowControlHeaders(boolean allowControlHeaders) { + doSetProperty("allowControlHeaders", allowControlHeaders); + return this; + } + /** * Whether autowiring is enabled. This is used for automatic autowiring @@ -96,6 +150,43 @@ public interface ExecComponentBuilderFactory { doSetProperty("autowiredEnabled", autowiredEnabled); return this; } + + /** + * To use a custom org.apache.commons.exec.ExecBinding for advanced + * use-cases. + * + * The option is a: + * <code>org.apache.camel.component.exec.ExecBinding</code> + * type. + * + * Group: advanced + * + * @param binding the value to set + * @return the dsl builder + */ + default ExecComponentBuilder binding(org.apache.camel.component.exec.ExecBinding binding) { + doSetProperty("binding", binding); + return this; + } + + /** + * 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 shutdown hook for + * every executed command. + * + * The option is a: + * <code>org.apache.camel.component.exec.ExecCommandExecutor</code> type. + * + * Group: advanced + * + * @param commandExecutor the value to set + * @return the dsl builder + */ + default ExecComponentBuilder commandExecutor(org.apache.camel.component.exec.ExecCommandExecutor commandExecutor) { + doSetProperty("commandExecutor", commandExecutor); + return this; + } } class ExecComponentBuilderImpl @@ -112,7 +203,12 @@ public interface ExecComponentBuilderFactory { Object value) { switch (name) { case "lazyStartProducer": ((ExecComponent) component).setLazyStartProducer((boolean) value); return true; + case "timeout": ((ExecComponent) component).setTimeout((long) value); return true; + case "workingDir": ((ExecComponent) component).setWorkingDir((java.lang.String) value); return true; + case "allowControlHeaders": ((ExecComponent) component).setAllowControlHeaders((boolean) value); return true; case "autowiredEnabled": ((ExecComponent) component).setAutowiredEnabled((boolean) value); return true; + case "binding": ((ExecComponent) component).setBinding((org.apache.camel.component.exec.ExecBinding) value); return true; + case "commandExecutor": ((ExecComponent) component).setCommandExecutor((org.apache.camel.component.exec.ExecCommandExecutor) value); return true; default: return false; } } 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 caff8ec882b6..92b503e52e0b 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 @@ -58,74 +58,6 @@ public interface ExecEndpointBuilderFactory { doSetProperty("args", args); return this; } - /** - * A reference to a org.apache.commons.exec.ExecBinding in the Registry. - * - * The option is a: - * <code>org.apache.camel.component.exec.ExecBinding</code> type. - * - * Group: producer - * - * @param binding the value to set - * @return the dsl builder - */ - default ExecEndpointBuilder binding(org.apache.camel.component.exec.ExecBinding binding) { - doSetProperty("binding", binding); - return this; - } - /** - * A reference to a org.apache.commons.exec.ExecBinding in the Registry. - * - * The option will be converted to a - * <code>org.apache.camel.component.exec.ExecBinding</code> type. - * - * Group: producer - * - * @param binding the value to set - * @return the dsl builder - */ - default ExecEndpointBuilder binding(String binding) { - doSetProperty("binding", binding); - return this; - } - /** - * A reference to a org.apache.commons.exec.ExecCommandExecutor in the - * Registry that customizes the command execution. The default command - * executor utilizes the commons-exec library, which adds a shutdown - * hook for every executed command. - * - * The option is a: - * <code>org.apache.camel.component.exec.ExecCommandExecutor</code> - * type. - * - * Group: producer - * - * @param commandExecutor the value to set - * @return the dsl builder - */ - default ExecEndpointBuilder commandExecutor(org.apache.camel.component.exec.ExecCommandExecutor commandExecutor) { - doSetProperty("commandExecutor", commandExecutor); - return this; - } - /** - * A reference to a org.apache.commons.exec.ExecCommandExecutor in the - * Registry that customizes the command execution. The default command - * executor utilizes the commons-exec library, which adds a shutdown - * hook for every executed command. - * - * The option will be converted to a - * <code>org.apache.camel.component.exec.ExecCommandExecutor</code> - * type. - * - * Group: producer - * - * @param commandExecutor the value to set - * @return the dsl builder - */ - default ExecEndpointBuilder commandExecutor(String commandExecutor) { - doSetProperty("commandExecutor", commandExecutor); - return this; - } /** * Logging level to be used for commands during execution. The default * value is DEBUG. Possible values are TRACE, DEBUG, INFO, WARN, ERROR @@ -333,6 +265,114 @@ public interface ExecEndpointBuilderFactory { doSetProperty("lazyStartProducer", lazyStartProducer); 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. + * + * The option is a: <code>boolean</code> type. + * + * Default: false + * Group: advanced + * + * @param allowControlHeaders the value to set + * @return the dsl builder + */ + default AdvancedExecEndpointBuilder allowControlHeaders(boolean allowControlHeaders) { + doSetProperty("allowControlHeaders", allowControlHeaders); + 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. + * + * The option will be converted to a <code>boolean</code> type. + * + * Default: false + * Group: advanced + * + * @param allowControlHeaders the value to set + * @return the dsl builder + */ + default AdvancedExecEndpointBuilder allowControlHeaders(String allowControlHeaders) { + doSetProperty("allowControlHeaders", allowControlHeaders); + return this; + } + /** + * To use a custom org.apache.commons.exec.ExecBinding for advanced + * use-cases. + * + * The option is a: + * <code>org.apache.camel.component.exec.ExecBinding</code> type. + * + * Group: advanced + * + * @param binding the value to set + * @return the dsl builder + */ + default AdvancedExecEndpointBuilder binding(org.apache.camel.component.exec.ExecBinding binding) { + doSetProperty("binding", binding); + return this; + } + /** + * To use a custom org.apache.commons.exec.ExecBinding for advanced + * use-cases. + * + * The option will be converted to a + * <code>org.apache.camel.component.exec.ExecBinding</code> type. + * + * Group: advanced + * + * @param binding the value to set + * @return the dsl builder + */ + default AdvancedExecEndpointBuilder binding(String binding) { + doSetProperty("binding", binding); + return this; + } + /** + * 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 shutdown hook for + * every executed command. + * + * The option is a: + * <code>org.apache.camel.component.exec.ExecCommandExecutor</code> + * type. + * + * Group: advanced + * + * @param commandExecutor the value to set + * @return the dsl builder + */ + default AdvancedExecEndpointBuilder commandExecutor(org.apache.camel.component.exec.ExecCommandExecutor commandExecutor) { + doSetProperty("commandExecutor", commandExecutor); + return this; + } + /** + * 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 shutdown hook for + * every executed command. + * + * The option will be converted to a + * <code>org.apache.camel.component.exec.ExecCommandExecutor</code> + * type. + * + * Group: advanced + * + * @param commandExecutor the value to set + * @return the dsl builder + */ + default AdvancedExecEndpointBuilder commandExecutor(String commandExecutor) { + doSetProperty("commandExecutor", commandExecutor); + return this; + } } public interface ExecBuilders {
