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 f71bafdd344d [backport camel-4.18.x] CAMEL-23765: remote-file 
consumers - contain localWorkDirectory downloads within the work directory 
(#24219)
f71bafdd344d is described below

commit f71bafdd344d42d1004e7e4cbf55cbd9cbe77d2c
Author: Andrea Cosentino <[email protected]>
AuthorDate: Wed Jun 24 15:20:32 2026 +0200

    [backport camel-4.18.x] CAMEL-23765: remote-file consumers - contain 
localWorkDirectory downloads within the work directory (#24219)
    
    CAMEL-23765: remote-file consumers - contain localWorkDirectory downloads 
within the work directory
    
    Backport of #24180 to camel-4.18.x.
    
    When localWorkDirectory was enabled, the remote-file consumers built the 
local
    work file path from the remote file name without ensuring the result stayed
    within the configured work directory, so a remote file name containing ../
    sequences could resolve to a path outside it. A shared
    GenericFileHelper.jailToLocalWorkDirectory containment check (compactPath +
    startsWith, mirroring the file producer's jail) is now applied to both the
    in-progress temp file and the final file, before mkdirs, in FtpOperations,
    SftpOperations, MinaSftpOperations, FilesOperations and SmbOperations. It 
reuses
    the existing jailStartingDirectory option (default true).
    
    Source-only backport: the main PR's jailStartingDirectory producer->common 
label
    change and its regenerated metadata are intentionally omitted to keep the
    maintenance-branch change minimal; the upgrade-guide entry lives on main.
    
    Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
---
 .../component/file/azure/FilesOperations.java      |  8 ++++
 .../camel/component/file/GenericFileHelper.java    | 23 +++++++++++
 .../component/file/GenericFileHelperTest.java      | 48 ++++++++++++++++++++++
 .../camel/component/file/remote/FtpOperations.java |  8 ++++
 .../component/file/remote/SftpOperations.java      |  8 ++++
 .../file/remote/mina/MinaSftpOperations.java       |  8 ++++
 .../apache/camel/component/smb/SmbOperations.java  | 12 +++++-
 7 files changed, 113 insertions(+), 2 deletions(-)

diff --git 
a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesOperations.java
 
b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesOperations.java
index ef66688ebfd3..af30ccf5c56a 100644
--- 
a/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesOperations.java
+++ 
b/components/camel-azure/camel-azure-files/src/main/java/org/apache/camel/component/file/azure/FilesOperations.java
@@ -47,6 +47,7 @@ import org.apache.camel.component.file.FileComponent;
 import org.apache.camel.component.file.GenericFile;
 import org.apache.camel.component.file.GenericFileEndpoint;
 import org.apache.camel.component.file.GenericFileExist;
+import org.apache.camel.component.file.GenericFileHelper;
 import org.apache.camel.component.file.GenericFileOperationFailedException;
 import org.apache.camel.component.file.remote.RemoteFile;
 import org.apache.camel.component.file.remote.RemoteFileConfiguration;
@@ -301,9 +302,16 @@ public class FilesOperations extends NormalizedOperations {
                     "Exchange should have the " + 
FileComponent.FILE_EXCHANGE_FILE + " set");
             String relativeName = target.getRelativeFilePath();
 
+            File localWorkDir = local;
             inProgress = new File(local, relativeName + ".inprogress");
             local = new File(local, relativeName);
 
+            // ensure the local work file stays within the local work 
directory (CAMEL-23765)
+            if (endpoint.isJailStartingDirectory()) {
+                GenericFileHelper.jailToLocalWorkDirectory(inProgress, 
localWorkDir);
+                GenericFileHelper.jailToLocalWorkDirectory(local, 
localWorkDir);
+            }
+
             // create directory to local work file
             boolean result = local.mkdirs();
             if (!result) {
diff --git 
a/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileHelper.java
 
b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileHelper.java
index b86351c010d6..ba40b2948e78 100644
--- 
a/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileHelper.java
+++ 
b/components/camel-file/src/main/java/org/apache/camel/component/file/GenericFileHelper.java
@@ -16,16 +16,39 @@
  */
 package org.apache.camel.component.file;
 
+import java.io.File;
 import java.util.function.Supplier;
 
 import org.apache.camel.Exchange;
 import org.apache.camel.support.MessageHelper;
+import org.apache.camel.util.FileUtil;
 
 public final class GenericFileHelper {
 
     private GenericFileHelper() {
     }
 
+    /**
+     * Ensures the resolved local work file stays within the configured local 
work directory. The remote file name used
+     * to build the local work file path may contain {@code ../} sequences 
that would otherwise resolve to a path
+     * outside the work directory.
+     *
+     * @param  target                              the resolved local work 
file (or its in-progress temp file)
+     * @param  localWorkDirectory                  the local work directory 
the file must stay within
+     * @throws GenericFileOperationFailedException if the target resolves 
outside the local work directory
+     */
+    public static void jailToLocalWorkDirectory(File target, File 
localWorkDirectory) {
+        // compact first as the remote relative name can use ../ etc
+        String compactTarget = FileUtil.compactPath(target.getPath());
+        String compactWork = 
FileUtil.compactPath(localWorkDirectory.getPath());
+        if (!compactTarget.startsWith(compactWork)) {
+            throw new GenericFileOperationFailedException(
+                    "Cannot retrieve file to local work file: " + compactTarget
+                                                          + " as it is jailed 
to the local work directory: "
+                                                          + compactWork);
+        }
+    }
+
     public static String asExclusiveReadLockKey(GenericFile file, String key) {
         // use the copy from absolute path as that was the original path of the
         // file when the lock was acquired
diff --git 
a/components/camel-file/src/test/java/org/apache/camel/component/file/GenericFileHelperTest.java
 
b/components/camel-file/src/test/java/org/apache/camel/component/file/GenericFileHelperTest.java
new file mode 100644
index 000000000000..5eefdbfcd907
--- /dev/null
+++ 
b/components/camel-file/src/test/java/org/apache/camel/component/file/GenericFileHelperTest.java
@@ -0,0 +1,48 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.camel.component.file;
+
+import java.io.File;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+public class GenericFileHelperTest {
+
+    private final File workDir = new File("target/localwork");
+
+    @Test
+    public void shouldAllowFilesWithinLocalWorkDirectory() {
+        // a plain name, a nested name, and a ../ that still resolves within 
the work directory are all allowed
+        assertDoesNotThrow(() -> 
GenericFileHelper.jailToLocalWorkDirectory(new File(workDir, "file.txt"), 
workDir));
+        assertDoesNotThrow(() -> 
GenericFileHelper.jailToLocalWorkDirectory(new File(workDir, 
"sub/dir/file.txt"), workDir));
+        assertDoesNotThrow(() -> 
GenericFileHelper.jailToLocalWorkDirectory(new File(workDir, 
"sub/../file.txt"), workDir));
+    }
+
+    @Test
+    public void shouldRejectFilesEscapingLocalWorkDirectory() {
+        // a remote file name that resolves outside the configured local work 
directory must be rejected
+        assertThrows(GenericFileOperationFailedException.class,
+                () -> GenericFileHelper.jailToLocalWorkDirectory(new 
File(workDir, "../escape.txt"), workDir));
+        assertThrows(GenericFileOperationFailedException.class,
+                () -> GenericFileHelper.jailToLocalWorkDirectory(new 
File(workDir, "../../etc/passwd"), workDir));
+        assertThrows(GenericFileOperationFailedException.class,
+                () -> GenericFileHelper.jailToLocalWorkDirectory(new 
File(workDir, "sub/../../escape.txt"), workDir));
+    }
+}
diff --git 
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java
 
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java
index 2f7df650e63d..0b942b47248a 100644
--- 
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java
+++ 
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/FtpOperations.java
@@ -32,6 +32,7 @@ import org.apache.camel.component.file.FileComponent;
 import org.apache.camel.component.file.GenericFile;
 import org.apache.camel.component.file.GenericFileEndpoint;
 import org.apache.camel.component.file.GenericFileExist;
+import org.apache.camel.component.file.GenericFileHelper;
 import org.apache.camel.component.file.GenericFileOperationFailedException;
 import org.apache.camel.support.ObjectHelper;
 import org.apache.camel.support.task.BlockingTask;
@@ -525,9 +526,16 @@ public class FtpOperations implements 
RemoteFileOperations<FTPFile> {
                     "Exchange should have the " + 
FileComponent.FILE_EXCHANGE_FILE + " set");
             String relativeName = target.getRelativeFilePath();
 
+            File localWorkDir = local;
             temp = new File(local, relativeName + ".inprogress");
             local = new File(local, relativeName);
 
+            // ensure the local work file stays within the local work 
directory (CAMEL-23765)
+            if (endpoint.isJailStartingDirectory()) {
+                GenericFileHelper.jailToLocalWorkDirectory(temp, localWorkDir);
+                GenericFileHelper.jailToLocalWorkDirectory(local, 
localWorkDir);
+            }
+
             // create directory to local work file
             boolean result = local.mkdirs();
             if (!result) {
diff --git 
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java
 
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java
index 75fac8ab9acd..903707b6f138 100644
--- 
a/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java
+++ 
b/components/camel-ftp/src/main/java/org/apache/camel/component/file/remote/SftpOperations.java
@@ -57,6 +57,7 @@ import org.apache.camel.component.file.FileComponent;
 import org.apache.camel.component.file.GenericFile;
 import org.apache.camel.component.file.GenericFileEndpoint;
 import org.apache.camel.component.file.GenericFileExist;
+import org.apache.camel.component.file.GenericFileHelper;
 import org.apache.camel.component.file.GenericFileOperationFailedException;
 import org.apache.camel.spi.CamelLogger;
 import org.apache.camel.support.ResourceHelper;
@@ -1037,9 +1038,16 @@ public class SftpOperations implements 
RemoteFileOperations<SftpRemoteFile> {
             // use relative filename in local work directory
             String relativeName = file.getRelativeFilePath();
 
+            File localWorkDir = local;
             temp = new File(local, relativeName + ".inprogress");
             local = new File(local, relativeName);
 
+            // ensure the local work file stays within the local work 
directory (CAMEL-23765)
+            if (endpoint.isJailStartingDirectory()) {
+                GenericFileHelper.jailToLocalWorkDirectory(temp, localWorkDir);
+                GenericFileHelper.jailToLocalWorkDirectory(local, 
localWorkDir);
+            }
+
             // create directory to local work file
             local.mkdirs();
 
diff --git 
a/components/camel-mina-sftp/src/main/java/org/apache/camel/component/file/remote/mina/MinaSftpOperations.java
 
b/components/camel-mina-sftp/src/main/java/org/apache/camel/component/file/remote/mina/MinaSftpOperations.java
index 925f7cdcce5f..9476416998e1 100644
--- 
a/components/camel-mina-sftp/src/main/java/org/apache/camel/component/file/remote/mina/MinaSftpOperations.java
+++ 
b/components/camel-mina-sftp/src/main/java/org/apache/camel/component/file/remote/mina/MinaSftpOperations.java
@@ -42,6 +42,7 @@ import org.apache.camel.component.file.FileComponent;
 import org.apache.camel.component.file.GenericFile;
 import org.apache.camel.component.file.GenericFileEndpoint;
 import org.apache.camel.component.file.GenericFileExist;
+import org.apache.camel.component.file.GenericFileHelper;
 import org.apache.camel.component.file.GenericFileOperationFailedException;
 import org.apache.camel.component.file.remote.FtpConstants;
 import org.apache.camel.component.file.remote.RemoteFile;
@@ -1224,8 +1225,15 @@ public class MinaSftpOperations implements 
RemoteFileOperations<SftpRemoteFile>
 
         try {
             String relativeName = file.getRelativeFilePath();
+            File localWorkDir = local;
             temp = new File(local, relativeName + ".inprogress");
             local = new File(local, relativeName);
+
+            // ensure the local work file stays within the local work 
directory (CAMEL-23765)
+            if (endpoint.isJailStartingDirectory()) {
+                GenericFileHelper.jailToLocalWorkDirectory(temp, localWorkDir);
+                GenericFileHelper.jailToLocalWorkDirectory(local, 
localWorkDir);
+            }
             local.mkdirs();
 
             if (temp.exists()) {
diff --git 
a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbOperations.java
 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbOperations.java
index 567e532f3abe..e5a382c6ec26 100644
--- 
a/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbOperations.java
+++ 
b/components/camel-smb/src/main/java/org/apache/camel/component/smb/SmbOperations.java
@@ -45,6 +45,7 @@ import org.apache.camel.component.file.FileComponent;
 import org.apache.camel.component.file.GenericFile;
 import org.apache.camel.component.file.GenericFileEndpoint;
 import org.apache.camel.component.file.GenericFileExist;
+import org.apache.camel.component.file.GenericFileHelper;
 import org.apache.camel.component.file.GenericFileOperationFailedException;
 import org.apache.camel.util.FileUtil;
 import org.apache.camel.util.IOHelper;
@@ -325,11 +326,18 @@ public class SmbOperations implements SmbFileOperations {
             // use relative filename in local work directory
             String relativeName = file.getRelativeFilePath();
 
+            java.io.File localWorkDir = local;
             temp = new java.io.File(local, relativeName + ".inprogress");
+            local = new java.io.File(local, relativeName);
+
+            // ensure the local work file stays within the local work 
directory (CAMEL-23765)
+            if (endpoint.isJailStartingDirectory()) {
+                GenericFileHelper.jailToLocalWorkDirectory(temp, localWorkDir);
+                GenericFileHelper.jailToLocalWorkDirectory(local, 
localWorkDir);
+            }
 
             // create directory to local work file
-            local.mkdirs();
-            local = new java.io.File(local, relativeName);
+            localWorkDir.mkdirs();
 
             // delete any existing files
             if (temp.exists()) {

Reply via email to