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

oscerd pushed a commit to branch camel-4.18.x
in repository https://gitbox.apache.org/repos/asf/camel.git


The following commit(s) were added to refs/heads/camel-4.18.x by this push:
     new 007929bb732a [backport camel-4.18.x] CAMEL-23942: contain Azure 
Storage Blob/DataLake fileDir downloads within the configured directory (#24581)
007929bb732a is described below

commit 007929bb732a8c5e3e49d4d3c6c8adc2aab13340
Author: Andrea Cosentino <[email protected]>
AuthorDate: Fri Jul 10 15:19:14 2026 +0200

    [backport camel-4.18.x] CAMEL-23942: contain Azure Storage Blob/DataLake 
fileDir downloads within the configured directory (#24581)
    
    Signed-off-by: Andrea Cosentino <[email protected]>
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
 .../azure/storage/blob/operations/BlobOperations.java   | 17 ++++++++++++++++-
 .../storage/blob/operations/BlobOperationsTest.java     | 13 +++++++++++++
 .../datalake/operations/DataLakeFileOperations.java     | 17 ++++++++++++++++-
 .../datalake/operations/DataLakeFileOperationTest.java  | 14 ++++++++++++++
 .../modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc | 10 ++++++++++
 5 files changed, 69 insertions(+), 2 deletions(-)

diff --git 
a/components/camel-azure/camel-azure-storage-blob/src/main/java/org/apache/camel/component/azure/storage/blob/operations/BlobOperations.java
 
b/components/camel-azure/camel-azure-storage-blob/src/main/java/org/apache/camel/component/azure/storage/blob/operations/BlobOperations.java
index c577d3604715..59ede92f47fa 100644
--- 
a/components/camel-azure/camel-azure-storage-blob/src/main/java/org/apache/camel/component/azure/storage/blob/operations/BlobOperations.java
+++ 
b/components/camel-azure/camel-azure-storage-blob/src/main/java/org/apache/camel/component/azure/storage/blob/operations/BlobOperations.java
@@ -20,6 +20,7 @@ import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
+import java.nio.file.Path;
 import java.time.Duration;
 import java.time.OffsetDateTime;
 import java.util.Collections;
@@ -145,7 +146,7 @@ public class BlobOperations {
             throw new IllegalArgumentException("In order to download a blob, 
you will need to specify the fileDir in the URI");
         }
 
-        final File fileToDownload = new File(fileDir, client.getBlobName());
+        final File fileToDownload = resolveWithinDirectory(fileDir, 
client.getBlobName());
         final BlobCommonRequestOptions commonRequestOptions = 
getCommonRequestOptions(exchange);
         final BlobRange blobRange = configurationProxy.getBlobRange(exchange);
         final ParallelTransferOptions parallelTransferOptions = 
configurationProxy.getParallelTransferOptions(exchange);
@@ -635,4 +636,18 @@ public class BlobOperations {
             leaseClient.releaseLease();
         }
     }
+
+    private static File resolveWithinDirectory(String fileDir, String name) {
+        final File target = new File(fileDir, name);
+        // normalize lexically (removes ./ and ../ segments) and compare on 
path-segment boundaries so a sibling
+        // directory whose name merely extends fileDir is not considered 
contained
+        final Path normalizedDir = new File(fileDir).toPath().normalize();
+        final Path normalizedTarget = target.toPath().normalize();
+        if (!normalizedTarget.startsWith(normalizedDir)) {
+            throw new IllegalArgumentException(
+                    "Cannot download to file '" + name
+                                               + "' as it resolves outside the 
configured fileDir directory: " + fileDir);
+        }
+        return target;
+    }
 }
diff --git 
a/components/camel-azure/camel-azure-storage-blob/src/test/java/org/apache/camel/component/azure/storage/blob/operations/BlobOperationsTest.java
 
b/components/camel-azure/camel-azure-storage-blob/src/test/java/org/apache/camel/component/azure/storage/blob/operations/BlobOperationsTest.java
index 3e7755113dfe..6c4b23671a29 100644
--- 
a/components/camel-azure/camel-azure-storage-blob/src/test/java/org/apache/camel/component/azure/storage/blob/operations/BlobOperationsTest.java
+++ 
b/components/camel-azure/camel-azure-storage-blob/src/test/java/org/apache/camel/component/azure/storage/blob/operations/BlobOperationsTest.java
@@ -124,6 +124,19 @@ class BlobOperationsTest extends CamelTestSupport {
         assertEquals("tag1", response3.getHeaders().get(BlobConstants.E_TAG));
     }
 
+    @Test
+    void testDownloadBlobToFileRejectsPathTraversalName() {
+        // a blob name is influenced by whoever writes to the container and 
may contain ../ segments;
+        // the download must stay within the configured fileDir
+        configuration.setFileDir("/tmp/camel-azure-blob-download");
+        when(client.getBlobName()).thenReturn("../escaped/PROOF_PWNED");
+
+        final BlobOperations operations = new BlobOperations(configuration, 
client);
+        final Exchange exchange = new DefaultExchange(context);
+
+        assertThrows(IllegalArgumentException.class, () -> 
operations.downloadBlobToFile(exchange));
+    }
+
     @Test
     void testUploadBlockBlob() throws Exception {
         // mocking
diff --git 
a/components/camel-azure/camel-azure-storage-datalake/src/main/java/org/apache/camel/component/azure/storage/datalake/operations/DataLakeFileOperations.java
 
b/components/camel-azure/camel-azure-storage-datalake/src/main/java/org/apache/camel/component/azure/storage/datalake/operations/DataLakeFileOperations.java
index 72ab8856a898..6a2c92a403af 100644
--- 
a/components/camel-azure/camel-azure-storage-datalake/src/main/java/org/apache/camel/component/azure/storage/datalake/operations/DataLakeFileOperations.java
+++ 
b/components/camel-azure/camel-azure-storage-datalake/src/main/java/org/apache/camel/component/azure/storage/datalake/operations/DataLakeFileOperations.java
@@ -21,6 +21,7 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.nio.file.OpenOption;
+import java.nio.file.Path;
 import java.time.Duration;
 import java.time.OffsetDateTime;
 import java.util.Map;
@@ -106,7 +107,7 @@ public class DataLakeFileOperations {
             throw new IllegalArgumentException("to download a file, you need 
to specify the fileDir in the URI");
         }
         final DataLakeFileClientWrapper fileClientWrapper = 
getFileClientWrapper(exchange);
-        final File recieverFile = new File(fileDir, 
fileClientWrapper.getFileName());
+        final File recieverFile = resolveWithinDirectory(fileDir, 
fileClientWrapper.getFileName());
         final FileCommonRequestOptions commonRequestOptions = 
getCommonRequestOptions(exchange);
         final FileRange fileRange = configurationProxy.getFileRange(exchange);
         final ParallelTransferOptions parallelTransferOptions = 
configurationProxy.getParallelTransferOptions(exchange);
@@ -253,4 +254,18 @@ public class DataLakeFileOperations {
         final DataLakeFileClient fileClient = 
configurationProxy.getFileClient(exchange);
         return null == fileClient ? client : new 
DataLakeFileClientWrapper(fileClient);
     }
+
+    private static File resolveWithinDirectory(String fileDir, String name) {
+        final File target = new File(fileDir, name);
+        // normalize lexically (removes ./ and ../ segments) and compare on 
path-segment boundaries so a sibling
+        // directory whose name merely extends fileDir is not considered 
contained
+        final Path normalizedDir = new File(fileDir).toPath().normalize();
+        final Path normalizedTarget = target.toPath().normalize();
+        if (!normalizedTarget.startsWith(normalizedDir)) {
+            throw new IllegalArgumentException(
+                    "Cannot download to file '" + name
+                                               + "' as it resolves outside the 
configured fileDir directory: " + fileDir);
+        }
+        return target;
+    }
 }
diff --git 
a/components/camel-azure/camel-azure-storage-datalake/src/test/java/org/apache/camel/component/azure/storage/datalake/operations/DataLakeFileOperationTest.java
 
b/components/camel-azure/camel-azure-storage-datalake/src/test/java/org/apache/camel/component/azure/storage/datalake/operations/DataLakeFileOperationTest.java
index 0c3380d43b7f..d2db2a7ac0f8 100644
--- 
a/components/camel-azure/camel-azure-storage-datalake/src/test/java/org/apache/camel/component/azure/storage/datalake/operations/DataLakeFileOperationTest.java
+++ 
b/components/camel-azure/camel-azure-storage-datalake/src/test/java/org/apache/camel/component/azure/storage/datalake/operations/DataLakeFileOperationTest.java
@@ -44,6 +44,7 @@ import org.mockito.junit.jupiter.MockitoExtension;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyLong;
@@ -93,6 +94,19 @@ public class DataLakeFileOperationTest extends 
CamelTestSupport {
         assertNotNull(secondResponse.getHeaders());
     }
 
+    @Test
+    void testDownloadToFileRejectsPathTraversalName() {
+        // a file name is influenced by whoever writes to the file system and 
may contain ../ segments;
+        // the download must stay within the configured fileDir
+        configuration.setFileDir("/tmp/camel-azure-datalake-download");
+        when(client.getFileName()).thenReturn("../escaped/PROOF_PWNED");
+
+        final DataLakeFileOperations operations = new 
DataLakeFileOperations(configuration, client);
+        final Exchange exchange = new DefaultExchange(context);
+
+        assertThrows(IllegalArgumentException.class, () -> 
operations.downloadToFile(exchange));
+    }
+
     @Test
     void testUploadFile() throws Exception {
         final OffsetDateTime time = OffsetDateTime.now();
diff --git 
a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc 
b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
index 0358aaeba807..88002166e0b9 100644
--- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
+++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_18.adoc
@@ -1488,3 +1488,13 @@ non-`Camel`-prefixed application headers and map them to 
the corresponding
 the `salesforce:` `to`. As defence-in-depth, strip inbound Camel-internal 
headers
 arriving from untrusted producers with `removeHeaders("CamelSalesforce*")` (or 
the
 broader `removeHeaders("Camel*")`) before the producer.
+
+=== camel-azure-storage-blob / camel-azure-storage-datalake - download 
contained within fileDir
+
+When `fileDir` is configured, the Azure Storage Blob and DataLake consumers 
now ensure the downloaded
+local file stays within the configured directory, so a remote object name 
containing `../` sequences
+can no longer resolve to a path outside it. This is consistent with the 
containment already performed
+by the file-based consumers.
+
+Ordinary object names are unaffected. A name that resolves outside `fileDir` 
is now rejected with an
+`IllegalArgumentException`.

Reply via email to