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

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


The following commit(s) were added to refs/heads/main by this push:
     new 9cab3498630a CAMEL-26272: camel-duckdb - honour the 
CamelDuckDbDatabasePath header only with allowDatabasePathFromHeader=true, and 
document the headers that override the endpoint (#29081)
9cab3498630a is described below

commit 9cab3498630a3ed1a7776bcf1ab606782959c851
Author: Andrea Cosentino <[email protected]>
AuthorDate: Tue Nov 3 12:11:05 2026 +0100

    CAMEL-26272: camel-duckdb - honour the CamelDuckDbDatabasePath header only 
with allowDatabasePathFromHeader=true, and document the headers that override 
the endpoint (#29081)
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
---
 .../apache/camel/catalog/components/duckdb.json    |  5 +-
 .../camel/catalog/docs/duckdb-component.adoc       | 21 +++++-
 .../component/duckdb/DuckDbEndpointConfigurer.java |  6 ++
 .../component/duckdb/DuckDbEndpointUriFactory.java |  3 +-
 .../org/apache/camel/component/duckdb/duckdb.json  |  5 +-
 .../src/main/docs/duckdb-component.adoc            | 21 +++++-
 .../camel/component/duckdb/DuckDbConstants.java    |  6 +-
 .../camel/component/duckdb/DuckDbEndpoint.java     | 23 +++++--
 .../camel/component/duckdb/DuckDbProducer.java     |  4 +-
 .../camel/component/duckdb/DuckDbProducerTest.java | 78 ++++++++++++++++++++--
 .../java/org/apache/camel/util/SecurityUtils.java  |  3 +
 .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc    | 11 +++
 .../endpoint/dsl/DuckDbEndpointBuilderFactory.java | 46 ++++++++++++-
 13 files changed, 207 insertions(+), 25 deletions(-)

diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/duckdb.json
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/duckdb.json
index a3915689bcac..6cf60bdcd0a0 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/duckdb.json
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/duckdb.json
@@ -32,7 +32,7 @@
   },
   "headers": {
     "CamelDuckDbOperation": { "index": 0, "kind": "header", "displayName": "", 
"group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Overrides the operation configured on the endpoint.", 
"constantName": "org.apache.camel.component.duckdb.DuckDbConstants#OPERATION" },
-    "CamelDuckDbDatabasePath": { "index": 1, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Overrides the database path configured on the 
endpoint.", "constantName": 
"org.apache.camel.component.duckdb.DuckDbConstants#DATABASE_PATH" },
+    "CamelDuckDbDatabasePath": { "index": 1, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Overrides the database of the endpoint for one message: 
a short-lived connection to the DuckDB file at this path is opened, and DuckDB 
creates the file when it does not exist. This header is ignored unless the 
endpoint enables allowDataba [...]
     "CamelDuckDbTable": { "index": 2, "kind": "header", "displayName": "", 
"group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Overrides the target table configured on the endpoint.", 
"constantName": "org.apache.camel.component.duckdb.DuckDbConstants#TABLE" },
     "CamelDuckDbQuery": { "index": 3, "kind": "header", "displayName": "", 
"group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Overrides the SQL for query or execute operations. Takes 
precedence over the query option and the message body.", "constantName": 
"org.apache.camel.component.duckdb.DuckDbConstants#QUERY" },
     "CamelDuckDbRowsWritten": { "index": 4, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": "long", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The number of rows written or affected.", 
"constantName": 
"org.apache.camel.component.duckdb.DuckDbConstants#ROWS_WRITTEN" },
@@ -53,6 +53,7 @@
     "readOnly": { "index": 6, "kind": "parameter", "displayName": "Read Only", 
"group": "producer", "label": "", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "description": "Open the embedded database file 
in read-only mode. Only applies to connections created by this endpoint from 
databasePath or jdbcUrl, and requires an existing database file." },
     "resultFormat": { "index": 7, "kind": "parameter", "displayName": "Result 
Format", "group": "producer", "label": "", "required": false, "type": "enum", 
"javaType": "org.apache.camel.component.duckdb.DuckDbResultFormat", "enum": [ 
"LIST_MAP", "JSON" ], "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": "LIST_MAP", "description": "How query results are returned: 
LIST_MAP (List of Map) or JSON array string." },
     "table": { "index": 8, "kind": "parameter", "displayName": "Table", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "Target table for insert and copy operations. 
Can be overridden per message with the CamelDuckDbTable header." },
-    "lazyStartProducer": { "index": 9, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produc [...]
+    "lazyStartProducer": { "index": 9, "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 [...]
+    "allowDatabasePathFromHeader": { "index": 10, "kind": "parameter", 
"displayName": "Allow Database Path From Header", "group": "security", "label": 
"producer,security", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"security": "insecure:dev", "insecureValue": "true", "defaultValue": false, 
"description": "Whether to allow overriding the database of the endpoint with 
the CamelDuckDbDatabasePath header, which opens [...]
   }
 }
diff --git 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/duckdb-component.adoc
 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/duckdb-component.adoc
index 727bc281c342..5dd2297012c0 100644
--- 
a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/duckdb-component.adoc
+++ 
b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/duckdb-component.adoc
@@ -66,9 +66,24 @@ A statement that returns rows fails with `execute`, and a 
statement that returns
 `query`; the error says which operation to use instead. DuckDB has run the 
statement when the error is raised.
 
 The endpoint reuses one JDBC connection for its configured `databasePath` / 
`jdbcUrl` and serializes access for
-thread safety. Prefer a shared `DataSource` when you need concurrent 
producers. Set the
-`CamelDuckDbDatabasePath` header to open a short-lived connection to a 
different database file for a single
-message.
+thread safety. Prefer a shared `DataSource` when you need concurrent producers.
+
+== Headers that override the endpoint
+
+These headers take the place of an endpoint option for one message:
+
+* `CamelDuckDbOperation` replaces the `operation` option.
+* `CamelDuckDbQuery` replaces the `query` option and the message body as the 
SQL of `execute` and `query`.
+* `CamelDuckDbTable` replaces the `table` option of `insert` and `copy`.
+
+So a message that carries them chooses the operation, the SQL that runs and 
the table that is written to.
+
+The `CamelDuckDbDatabasePath` header is ignored unless the endpoint sets 
`allowDatabasePathFromHeader=true`. With
+that option, the header opens a short-lived connection to the DuckDB file at 
that path for one message, instead of
+the `databasePath`, `jdbcUrl` or `DataSource` of the endpoint, and DuckDB 
creates the file when it does not exist.
+
+When the messages come from an untrusted producer through a consumer that does 
not filter out `Camel*` headers,
+remove these headers with `removeHeaders("CamelDuckDb*")` before the `duckdb` 
endpoint.
 
 == Example
 
diff --git 
a/components/camel-duckdb/src/generated/java/org/apache/camel/component/duckdb/DuckDbEndpointConfigurer.java
 
b/components/camel-duckdb/src/generated/java/org/apache/camel/component/duckdb/DuckDbEndpointConfigurer.java
index 408807ca5808..1795ccde9b86 100644
--- 
a/components/camel-duckdb/src/generated/java/org/apache/camel/component/duckdb/DuckDbEndpointConfigurer.java
+++ 
b/components/camel-duckdb/src/generated/java/org/apache/camel/component/duckdb/DuckDbEndpointConfigurer.java
@@ -24,6 +24,8 @@ public class DuckDbEndpointConfigurer extends 
PropertyConfigurerSupport implemen
     public boolean configure(CamelContext camelContext, Object obj, String 
name, Object value, boolean ignoreCase) {
         DuckDbEndpoint target = (DuckDbEndpoint) obj;
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
+        case "allowdatabasepathfromheader":
+        case "allowDatabasePathFromHeader": 
target.setAllowDatabasePathFromHeader(property(camelContext, boolean.class, 
value)); return true;
         case "batchsize":
         case "batchSize": target.setBatchSize(property(camelContext, 
int.class, value)); return true;
         case "format": target.setFormat(property(camelContext, 
java.lang.String.class, value)); return true;
@@ -45,6 +47,8 @@ public class DuckDbEndpointConfigurer extends 
PropertyConfigurerSupport implemen
     @Override
     public Class<?> getOptionType(String name, boolean ignoreCase) {
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
+        case "allowdatabasepathfromheader":
+        case "allowDatabasePathFromHeader": return boolean.class;
         case "batchsize":
         case "batchSize": return int.class;
         case "format": return java.lang.String.class;
@@ -67,6 +71,8 @@ public class DuckDbEndpointConfigurer extends 
PropertyConfigurerSupport implemen
     public Object getOptionValue(Object obj, String name, boolean ignoreCase) {
         DuckDbEndpoint target = (DuckDbEndpoint) obj;
         switch (ignoreCase ? name.toLowerCase(Locale.ROOT) : name) {
+        case "allowdatabasepathfromheader":
+        case "allowDatabasePathFromHeader": return 
target.isAllowDatabasePathFromHeader();
         case "batchsize":
         case "batchSize": return target.getBatchSize();
         case "format": return target.getFormat();
diff --git 
a/components/camel-duckdb/src/generated/java/org/apache/camel/component/duckdb/DuckDbEndpointUriFactory.java
 
b/components/camel-duckdb/src/generated/java/org/apache/camel/component/duckdb/DuckDbEndpointUriFactory.java
index f65906f70adf..9ecdadd310a8 100644
--- 
a/components/camel-duckdb/src/generated/java/org/apache/camel/component/duckdb/DuckDbEndpointUriFactory.java
+++ 
b/components/camel-duckdb/src/generated/java/org/apache/camel/component/duckdb/DuckDbEndpointUriFactory.java
@@ -24,7 +24,8 @@ public class DuckDbEndpointUriFactory extends 
org.apache.camel.support.component
     private static final Set<String> ENDPOINT_IDENTITY_PROPERTY_NAMES;
     private static final Map<String, String> MULTI_VALUE_PREFIXES;
     static {
-        Set<String> props = new HashSet<>(10);
+        Set<String> props = new HashSet<>(11);
+        props.add("allowDatabasePathFromHeader");
         props.add("batchSize");
         props.add("databasePath");
         props.add("format");
diff --git 
a/components/camel-duckdb/src/generated/resources/META-INF/org/apache/camel/component/duckdb/duckdb.json
 
b/components/camel-duckdb/src/generated/resources/META-INF/org/apache/camel/component/duckdb/duckdb.json
index a3915689bcac..6cf60bdcd0a0 100644
--- 
a/components/camel-duckdb/src/generated/resources/META-INF/org/apache/camel/component/duckdb/duckdb.json
+++ 
b/components/camel-duckdb/src/generated/resources/META-INF/org/apache/camel/component/duckdb/duckdb.json
@@ -32,7 +32,7 @@
   },
   "headers": {
     "CamelDuckDbOperation": { "index": 0, "kind": "header", "displayName": "", 
"group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Overrides the operation configured on the endpoint.", 
"constantName": "org.apache.camel.component.duckdb.DuckDbConstants#OPERATION" },
-    "CamelDuckDbDatabasePath": { "index": 1, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Overrides the database path configured on the 
endpoint.", "constantName": 
"org.apache.camel.component.duckdb.DuckDbConstants#DATABASE_PATH" },
+    "CamelDuckDbDatabasePath": { "index": 1, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Overrides the database of the endpoint for one message: 
a short-lived connection to the DuckDB file at this path is opened, and DuckDB 
creates the file when it does not exist. This header is ignored unless the 
endpoint enables allowDataba [...]
     "CamelDuckDbTable": { "index": 2, "kind": "header", "displayName": "", 
"group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Overrides the target table configured on the endpoint.", 
"constantName": "org.apache.camel.component.duckdb.DuckDbConstants#TABLE" },
     "CamelDuckDbQuery": { "index": 3, "kind": "header", "displayName": "", 
"group": "producer", "label": "", "required": false, "javaType": "String", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "Overrides the SQL for query or execute operations. Takes 
precedence over the query option and the message body.", "constantName": 
"org.apache.camel.component.duckdb.DuckDbConstants#QUERY" },
     "CamelDuckDbRowsWritten": { "index": 4, "kind": "header", "displayName": 
"", "group": "producer", "label": "", "required": false, "javaType": "long", 
"deprecated": false, "deprecationNote": "", "autowired": false, "secret": 
false, "description": "The number of rows written or affected.", 
"constantName": 
"org.apache.camel.component.duckdb.DuckDbConstants#ROWS_WRITTEN" },
@@ -53,6 +53,7 @@
     "readOnly": { "index": 6, "kind": "parameter", "displayName": "Read Only", 
"group": "producer", "label": "", "required": false, "type": "boolean", 
"javaType": "boolean", "deprecated": false, "autowired": false, "secret": 
false, "defaultValue": false, "description": "Open the embedded database file 
in read-only mode. Only applies to connections created by this endpoint from 
databasePath or jdbcUrl, and requires an existing database file." },
     "resultFormat": { "index": 7, "kind": "parameter", "displayName": "Result 
Format", "group": "producer", "label": "", "required": false, "type": "enum", 
"javaType": "org.apache.camel.component.duckdb.DuckDbResultFormat", "enum": [ 
"LIST_MAP", "JSON" ], "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": "LIST_MAP", "description": "How query results are returned: 
LIST_MAP (List of Map) or JSON array string." },
     "table": { "index": 8, "kind": "parameter", "displayName": "Table", 
"group": "producer", "label": "", "required": false, "type": "string", 
"javaType": "java.lang.String", "deprecated": false, "autowired": false, 
"secret": false, "description": "Target table for insert and copy operations. 
Can be overridden per message with the CamelDuckDbTable header." },
-    "lazyStartProducer": { "index": 9, "kind": "parameter", "displayName": 
"Lazy Start Producer", "group": "producer (advanced)", "label": 
"producer,advanced", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"defaultValue": false, "description": "Whether the producer should be started 
lazy (on the first message). By starting lazy you can use this to allow 
CamelContext and routes to startup in situations where a produc [...]
+    "lazyStartProducer": { "index": 9, "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 [...]
+    "allowDatabasePathFromHeader": { "index": 10, "kind": "parameter", 
"displayName": "Allow Database Path From Header", "group": "security", "label": 
"producer,security", "required": false, "type": "boolean", "javaType": 
"boolean", "deprecated": false, "autowired": false, "secret": false, 
"security": "insecure:dev", "insecureValue": "true", "defaultValue": false, 
"description": "Whether to allow overriding the database of the endpoint with 
the CamelDuckDbDatabasePath header, which opens [...]
   }
 }
diff --git a/components/camel-duckdb/src/main/docs/duckdb-component.adoc 
b/components/camel-duckdb/src/main/docs/duckdb-component.adoc
index 727bc281c342..5dd2297012c0 100644
--- a/components/camel-duckdb/src/main/docs/duckdb-component.adoc
+++ b/components/camel-duckdb/src/main/docs/duckdb-component.adoc
@@ -66,9 +66,24 @@ A statement that returns rows fails with `execute`, and a 
statement that returns
 `query`; the error says which operation to use instead. DuckDB has run the 
statement when the error is raised.
 
 The endpoint reuses one JDBC connection for its configured `databasePath` / 
`jdbcUrl` and serializes access for
-thread safety. Prefer a shared `DataSource` when you need concurrent 
producers. Set the
-`CamelDuckDbDatabasePath` header to open a short-lived connection to a 
different database file for a single
-message.
+thread safety. Prefer a shared `DataSource` when you need concurrent producers.
+
+== Headers that override the endpoint
+
+These headers take the place of an endpoint option for one message:
+
+* `CamelDuckDbOperation` replaces the `operation` option.
+* `CamelDuckDbQuery` replaces the `query` option and the message body as the 
SQL of `execute` and `query`.
+* `CamelDuckDbTable` replaces the `table` option of `insert` and `copy`.
+
+So a message that carries them chooses the operation, the SQL that runs and 
the table that is written to.
+
+The `CamelDuckDbDatabasePath` header is ignored unless the endpoint sets 
`allowDatabasePathFromHeader=true`. With
+that option, the header opens a short-lived connection to the DuckDB file at 
that path for one message, instead of
+the `databasePath`, `jdbcUrl` or `DataSource` of the endpoint, and DuckDB 
creates the file when it does not exist.
+
+When the messages come from an untrusted producer through a consumer that does 
not filter out `Camel*` headers,
+remove these headers with `removeHeaders("CamelDuckDb*")` before the `duckdb` 
endpoint.
 
 == Example
 
diff --git 
a/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbConstants.java
 
b/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbConstants.java
index ea3c5179a8b4..2be7f8b67d10 100644
--- 
a/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbConstants.java
+++ 
b/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbConstants.java
@@ -22,7 +22,11 @@ public final class DuckDbConstants {
 
     @Metadata(description = "Overrides the operation configured on the 
endpoint.", javaType = "String")
     public static final String OPERATION = "CamelDuckDbOperation";
-    @Metadata(description = "Overrides the database path configured on the 
endpoint.", javaType = "String")
+    @Metadata(description = "Overrides the database of the endpoint for one 
message: a short-lived connection to the"
+                            + " DuckDB file at this path is opened, and DuckDB 
creates the file when it does not exist."
+                            + " This header is ignored unless the endpoint 
enables allowDatabasePathFromHeader=true"
+                            + " (disabled by default).",
+              javaType = "String")
     public static final String DATABASE_PATH = "CamelDuckDbDatabasePath";
     @Metadata(description = "Overrides the target table configured on the 
endpoint.", javaType = "String")
     public static final String TABLE = "CamelDuckDbTable";
diff --git 
a/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbEndpoint.java
 
b/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbEndpoint.java
index fd8a46099d9b..8e6afe2ec117 100644
--- 
a/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbEndpoint.java
+++ 
b/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbEndpoint.java
@@ -101,6 +101,13 @@ public class DuckDbEndpoint extends DefaultEndpoint {
     @UriParam(defaultValue = "LIST_MAP",
               description = "How query results are returned: LIST_MAP (List of 
Map) or JSON array string.")
     private DuckDbResultFormat resultFormat = DuckDbResultFormat.LIST_MAP;
+    @UriParam(defaultValue = "false", label = "producer,security", security = 
"insecure:dev", insecureValue = "true",
+              description = "Whether to allow overriding the database of the 
endpoint with the CamelDuckDbDatabasePath"
+                            + " header, which opens a short-lived connection 
to the DuckDB file at that path (DuckDB"
+                            + " creates the file when it does not exist) 
instead of using the databasePath, jdbcUrl or"
+                            + " DataSource of the endpoint. Disabled by 
default; enable it only when the header source is"
+                            + " trusted, since it lets a message choose the 
database file.")
+    private boolean allowDatabasePathFromHeader;
 
     public DuckDbEndpoint() {
     }
@@ -122,10 +129,10 @@ public class DuckDbEndpoint extends DefaultEndpoint {
     /**
      * Opens a connection scope for one producer invocation.
      * <p>
-     * When {@code databasePathOverride} is set (from the {@code 
CamelDuckDbDatabasePath} header), a short-lived
-     * connection to that path is opened and closed after use. Otherwise the 
endpoint-owned shared connection is used
-     * under a lock so concurrent exchanges do not share a JDBC connection 
unsafely. DataSource connections are also
-     * short-lived and closed after use.
+     * When {@code databasePathOverride} is set (from the {@code 
CamelDuckDbDatabasePath} header, which the producer
+     * only reads when {@code allowDatabasePathFromHeader} is enabled), a 
short-lived connection to that path is opened
+     * and closed after use. Otherwise the endpoint-owned shared connection is 
used under a lock so concurrent exchanges
+     * do not share a JDBC connection unsafely. DataSource connections are 
also short-lived and closed after use.
      */
     DuckDbConnectionScope openConnectionScope(String databasePathOverride) 
throws Exception {
         if (ObjectHelper.isNotEmpty(databasePathOverride)) {
@@ -265,6 +272,14 @@ public class DuckDbEndpoint extends DefaultEndpoint {
         this.resultFormat = resultFormat;
     }
 
+    public boolean isAllowDatabasePathFromHeader() {
+        return allowDatabasePathFromHeader;
+    }
+
+    public void setAllowDatabasePathFromHeader(boolean 
allowDatabasePathFromHeader) {
+        this.allowDatabasePathFromHeader = allowDatabasePathFromHeader;
+    }
+
     String resolvedJdbcUrl() {
         return DuckDbJdbcSupport.resolveJdbcUrl(jdbcUrl, databasePath);
     }
diff --git 
a/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbProducer.java
 
b/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbProducer.java
index 72e5e58454f9..dce1c8e8421b 100644
--- 
a/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbProducer.java
+++ 
b/components/camel-duckdb/src/main/java/org/apache/camel/component/duckdb/DuckDbProducer.java
@@ -47,7 +47,9 @@ public class DuckDbProducer extends DefaultProducer {
     @Override
     public void process(Exchange exchange) throws Exception {
         DuckDbOperation operation = resolveOperation(exchange);
-        String databasePathOverride = 
exchange.getIn().getHeader(DuckDbConstants.DATABASE_PATH, String.class);
+        // the header may only move the endpoint to another database file when 
the endpoint opts in
+        String databasePathOverride = endpoint.isAllowDatabasePathFromHeader()
+                ? exchange.getIn().getHeader(DuckDbConstants.DATABASE_PATH, 
String.class) : null;
         try (DuckDbConnectionScope scope = 
endpoint.openConnectionScope(databasePathOverride)) {
             Connection connection = scope.connection();
             switch (operation) {
diff --git 
a/components/camel-duckdb/src/test/java/org/apache/camel/component/duckdb/DuckDbProducerTest.java
 
b/components/camel-duckdb/src/test/java/org/apache/camel/component/duckdb/DuckDbProducerTest.java
index 7ed34cfc0e47..a719031c1d2d 100644
--- 
a/components/camel-duckdb/src/test/java/org/apache/camel/component/duckdb/DuckDbProducerTest.java
+++ 
b/components/camel-duckdb/src/test/java/org/apache/camel/component/duckdb/DuckDbProducerTest.java
@@ -204,19 +204,15 @@ class DuckDbProducerTest extends CamelTestSupport {
     }
 
     @Test
-    void databasePathHeaderOverridesEndpointDatabase(@TempDir Path tempDir) 
throws Exception {
+    void databasePathHeaderOverridesEndpointDatabaseWhenAllowed(@TempDir Path 
tempDir) throws Exception {
         Path otherDb = tempDir.resolve("other.db");
-        try (Connection connection = 
DriverManager.getConnection("jdbc:duckdb:" + otherDb.toAbsolutePath());
-             Statement statement = connection.createStatement()) {
-            statement.execute("CREATE TABLE events (id INTEGER, name 
VARCHAR)");
-            statement.execute("INSERT INTO events VALUES (1, 'header-db')");
-        }
+        createEventsDatabase(otherDb, "header-db");
 
         context.addRoutes(new RouteBuilder() {
             @Override
             public void configure() {
                 from("direct:query-override")
-                        .to("duckdb::memory:?operation=query");
+                        
.to("duckdb::memory:?operation=query&allowDatabasePathFromHeader=true");
             }
         });
 
@@ -229,4 +225,72 @@ class DuckDbProducerTest extends CamelTestSupport {
                 List.class);
         assertThat(rows).extracting(row -> 
row.get("name")).containsExactly("header-db");
     }
+
+    @Test
+    void databasePathHeaderIsIgnoredByDefault(@TempDir Path tempDir) throws 
Exception {
+        Path otherDb = tempDir.resolve("other.db");
+        createEventsDatabase(otherDb, "header-db");
+        Path endpointDb = tempDir.resolve("endpoint.db");
+        createEventsDatabase(endpointDb, "endpoint-db");
+        String uri = jdbcEndpoint(endpointDb);
+
+        // the query runs on the database of the endpoint, not on the file 
that the header names
+        @SuppressWarnings("unchecked")
+        List<Map<String, Object>> rows = template.requestBodyAndHeader(uri + 
"&operation=query", "SELECT name FROM events",
+                DuckDbConstants.DATABASE_PATH, 
otherDb.toAbsolutePath().toString(), List.class);
+        assertThat(rows).extracting(row -> 
row.get("name")).containsExactly("endpoint-db");
+    }
+
+    @Test
+    void databasePathHeaderDoesNotCreateFileByDefault(@TempDir Path tempDir) 
throws Exception {
+        Path headerDb = tempDir.resolve("created-by-header.db");
+        String uri = jdbcEndpoint(tempDir.resolve("endpoint.db"));
+
+        template.sendBodyAndHeader(uri + "&operation=execute", "CREATE TABLE 
events (id INTEGER, name VARCHAR)",
+                DuckDbConstants.DATABASE_PATH, 
headerDb.toAbsolutePath().toString());
+
+        assertThat(headerDb).doesNotExist();
+        // the statement ran on the database of the endpoint
+        @SuppressWarnings("unchecked")
+        List<Map<String, Object>> rows
+                = template.requestBody(uri + "&operation=query", "SELECT 
count(*) AS total FROM events", List.class);
+        assertThat(rows).extracting(row -> 
row.get("total")).containsExactly(0L);
+    }
+
+    @Test
+    void operationHeaderOverridesEndpointOperation(@TempDir Path tempDir) {
+        String uri = jdbcEndpoint(tempDir.resolve("test.db"));
+
+        // the endpoint is configured for execute, the header switches it to 
ping
+        Object result = template.requestBodyAndHeader(uri + 
"&operation=execute", "not sql",
+                DuckDbConstants.OPERATION, "ping");
+
+        assertThat(result).isEqualTo(true);
+    }
+
+    @Test
+    void tableHeaderOverridesEndpointTable(@TempDir Path tempDir) throws 
Exception {
+        String uri = jdbcEndpoint(tempDir.resolve("test.db"));
+        template.sendBody(uri + "&operation=execute", "CREATE TABLE events (id 
INTEGER, name VARCHAR)");
+        template.sendBody(uri + "&operation=execute", "CREATE TABLE audit (id 
INTEGER, name VARCHAR)");
+
+        template.sendBodyAndHeader(uri + "&operation=insert&table=events", 
List.of(Map.of("id", 1, "name", "ada")),
+                DuckDbConstants.TABLE, "audit");
+
+        @SuppressWarnings("unchecked")
+        List<Map<String, Object>> audit = template.requestBody(uri + 
"&operation=query", "SELECT name FROM audit", List.class);
+        assertThat(audit).extracting(row -> 
row.get("name")).containsExactly("ada");
+        @SuppressWarnings("unchecked")
+        List<Map<String, Object>> events
+                = template.requestBody(uri + "&operation=query", "SELECT name 
FROM events", List.class);
+        assertThat(events).isEmpty();
+    }
+
+    private static void createEventsDatabase(Path dbFile, String name) throws 
SQLException {
+        try (Connection connection = 
DriverManager.getConnection("jdbc:duckdb:" + dbFile.toAbsolutePath());
+             Statement statement = connection.createStatement()) {
+            statement.execute("CREATE TABLE events (id INTEGER, name 
VARCHAR)");
+            statement.execute("INSERT INTO events VALUES (1, '" + name + "')");
+        }
+    }
 }
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 97d28b73c6db..82cf73024ba3 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
@@ -61,6 +61,7 @@ public final class SecurityUtils {
         map.put("acceptallobjects", new SecurityOption(INSECURE_SERIALIZATION, 
"true"));
         map.put("acceptanyspiffeid", new SecurityOption(INSECURE_SSL, "true"));
         map.put("allowcontrolheaders", new SecurityOption(INSECURE_DEV, 
"true"));
+        map.put("allowdatabasepathfromheader", new 
SecurityOption(INSECURE_DEV, "true"));
         map.put("allowexternalentities", new SecurityOption(INSECURE_DEV, 
"true"));
         map.put("allowfilepathsource", new SecurityOption(INSECURE_DEV, 
"true"));
         map.put("allowjavaserializedobject", new 
SecurityOption(INSECURE_SERIALIZATION, "true"));
@@ -123,6 +124,8 @@ public final class SecurityUtils {
                 "component:jgroups"));
         owners.put("allowcontrolheaders", Set.of(
                 "component:exec"));
+        owners.put("allowdatabasepathfromheader", Set.of(
+                "component:duckdb"));
         owners.put("allowexternalentities", Set.of(
                 "component:smooks"));
         owners.put("allowfilepathsource", Set.of(
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
index a03f79d36c49..7e73cbe50c42 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc
@@ -4992,6 +4992,17 @@ query had no message to run on. The consumer and 
scheduler options (such as `del
 `sendEmptyMessageWhenIdle`, `bridgeErrorHandler` and the `scheduler` and 
`backoff` options) are removed from the
 endpoint, and `bridgeErrorHandler` from the component.
 
+=== camel-duckdb - the CamelDuckDbDatabasePath header needs the 
allowDatabasePathFromHeader option
+
+The `CamelDuckDbDatabasePath` header made the `duckdb` producer open a 
short-lived connection to the DuckDB file at
+the path that the header named, instead of using the `databasePath`, `jdbcUrl` 
or `DataSource` of the endpoint, and
+DuckDB created that file when it did not exist. The header is now ignored 
unless the endpoint sets the new
+`allowDatabasePathFromHeader=true` option (disabled by default), so a message 
no longer moves the endpoint to another
+database file. A route that relies on the header must set this option on the 
endpoint.
+
+The `CamelDuckDbOperation`, `CamelDuckDbQuery` and `CamelDuckDbTable` headers 
still override the operation, the SQL and
+the table of the endpoint, as before.
+
 === camel-schematron - no body fails, and bytes are read in the encoding of 
the XML declaration
 
 A message without a body now fails with an `InvalidPayloadException` instead 
of a `NullPointerException`.
diff --git 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DuckDbEndpointBuilderFactory.java
 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DuckDbEndpointBuilderFactory.java
index 93a849b44cca..0583bc5060e3 100644
--- 
a/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DuckDbEndpointBuilderFactory.java
+++ 
b/dsl/camel-endpointdsl/src/generated/java/org/apache/camel/builder/endpoint/dsl/DuckDbEndpointBuilderFactory.java
@@ -241,6 +241,46 @@ public interface DuckDbEndpointBuilderFactory {
             doSetProperty("table", table);
             return this;
         }
+        /**
+         * Whether to allow overriding the database of the endpoint with the
+         * CamelDuckDbDatabasePath header, which opens a short-lived connection
+         * to the DuckDB file at that path (DuckDB creates the file when it 
does
+         * not exist) instead of using the databasePath, jdbcUrl or DataSource
+         * of the endpoint. Disabled by default; enable it only when the header
+         * source is trusted, since it lets a message choose the database file.
+         * 
+         * The option is a: <code>boolean</code> type.
+         * 
+         * Default: false
+         * Group: security
+         * 
+         * @param allowDatabasePathFromHeader the value to set
+         * @return the dsl builder
+         */
+        default DuckDbEndpointBuilder allowDatabasePathFromHeader(boolean 
allowDatabasePathFromHeader) {
+            doSetProperty("allowDatabasePathFromHeader", 
allowDatabasePathFromHeader);
+            return this;
+        }
+        /**
+         * Whether to allow overriding the database of the endpoint with the
+         * CamelDuckDbDatabasePath header, which opens a short-lived connection
+         * to the DuckDB file at that path (DuckDB creates the file when it 
does
+         * not exist) instead of using the databasePath, jdbcUrl or DataSource
+         * of the endpoint. Disabled by default; enable it only when the header
+         * source is trusted, since it lets a message choose the database file.
+         * 
+         * The option will be converted to a <code>boolean</code> type.
+         * 
+         * Default: false
+         * Group: security
+         * 
+         * @param allowDatabasePathFromHeader the value to set
+         * @return the dsl builder
+         */
+        default DuckDbEndpointBuilder allowDatabasePathFromHeader(String 
allowDatabasePathFromHeader) {
+            doSetProperty("allowDatabasePathFromHeader", 
allowDatabasePathFromHeader);
+            return this;
+        }
     }
 
     /**
@@ -383,7 +423,11 @@ public interface DuckDbEndpointBuilderFactory {
             return "CamelDuckDbOperation";
         }
         /**
-         * Overrides the database path configured on the endpoint.
+         * Overrides the database of the endpoint for one message: a 
short-lived
+         * connection to the DuckDB file at this path is opened, and DuckDB
+         * creates the file when it does not exist. This header is ignored
+         * unless the endpoint enables allowDatabasePathFromHeader=true
+         * (disabled by default).
          * 
          * The option is a: {@code String} type.
          * 

Reply via email to