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

davsclaus pushed a commit to branch backport/26284-to-camel-4-18-x
in repository https://gitbox.apache.org/repos/asf/camel.git

commit 0da25f02f59d3eb71bacb847db90d0fc137449c4
Author: jiriOndrusek-agent <[email protected]>
AuthorDate: Thu Nov 12 21:46:51 2026 +0100

    [camel-4.18.x] CAMEL-26284: camel-azure-storage-blob - validate the source 
account and container name in copyBlob
    
    copyBlob now checks the source account and container name headers against
    the Azure naming rules and fails with an IllegalArgumentException before the
    source URL is built.
    
    Backport of #29254 (6140a7cd05f3), the same change as the approved backport
    by JiriOndrusek, without the upgrade-guide notes, which are on main.
    
    Co-Authored-By: Claude Opus 5.5 <[email protected]>
    (cherry picked from commit 6140a7cd05f3)
    Signed-off-by: Claus Ibsen <[email protected]>
---
 .../storage/blob/operations/BlobOperations.java    | 14 +++++++++
 .../blob/operations/BlobOperationsTest.java        | 36 ++++++++++++++++++++++
 2 files changed, 50 insertions(+)

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 17bb0435d368..f3e79f90555f 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
@@ -29,6 +29,7 @@ import java.util.Collections;
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
+import java.util.regex.Pattern;
 
 import com.azure.core.http.rest.PagedIterable;
 import com.azure.core.http.rest.Response;
@@ -78,6 +79,10 @@ import org.slf4j.LoggerFactory;
 public class BlobOperations {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(BlobOperations.class);
+    // Azure naming rules for storage accounts and containers ($root and $web 
are the reserved containers)
+    private static final Pattern ACCOUNT_NAME_PATTERN = 
Pattern.compile("[a-z0-9]{3,24}");
+    private static final Pattern CONTAINER_NAME_PATTERN
+            = 
Pattern.compile("\\$root|\\$web|(?=.{3,63}$)[a-z0-9]+(-[a-z0-9]+)*");
     public static final String MISSING_EXCHANGE = "exchange cannot be null";
 
     private final BlobClientWrapper client;
@@ -440,6 +445,15 @@ public class BlobOperations {
         if (ObjectHelper.isEmpty(sourceAccountName)) {
             throw new IllegalArgumentException("Source Container Name must be 
specified for copyBlob Operation");
         }
+        if (!ACCOUNT_NAME_PATTERN.matcher(sourceAccountName).matches()) {
+            throw new IllegalArgumentException(
+                    "Invalid Source Account Name for copyBlob Operation: it 
must have 3 to 24 lowercase letters or digits");
+        }
+        if (!CONTAINER_NAME_PATTERN.matcher(sourceContainerName).matches()) {
+            throw new IllegalArgumentException(
+                    "Invalid Source Container Name for copyBlob Operation: it 
must have 3 to 63 lowercase letters, digits"
+                                               + " or single hyphens, and 
start and end with a letter or digit, or be $root or $web");
+        }
         final String response
                 = client.copyBlob(sourceBlobName, sourceAccountName, 
sourceContainerName,
                         
configurationProxy.getConfiguration().getSourceBlobAccessKey());
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 f8744d961d0f..3f46b5b98bc7 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
@@ -239,6 +239,42 @@ class BlobOperationsTest extends CamelTestSupport {
         assertTrue((boolean) response.getBody());
     }
 
+    @Test
+    void testCopyBlobValidatesSourceNames() {
+        when(client.copyBlob(any(), any(), any(), any())).thenReturn("copyId");
+        final BlobOperations operations = new BlobOperations(configuration, 
client);
+
+        for (String account : new String[] { "abc", "account1", 
"a23456789012345678901234" }) {
+            for (String container : new String[] { "abc", "container-1", 
"$root", "$web", "a".repeat(63) }) {
+                assertEquals("copyId", 
operations.copyBlob(copyBlobExchange(account, container)).getBody());
+            }
+        }
+
+        // invalid account names: URL injection (host/path, query, fragment, 
userinfo, port), length, uppercase, hyphen
+        for (String account : new String[] {
+                "ab", "a234567890123456789012345", "Account", "acc.ount", 
"acc/ount", "acc?x", "acc#x", "user@acc",
+                "acc:8080", "acc-1" }) {
+            assertThrows(IllegalArgumentException.class,
+                    () -> operations.copyBlob(copyBlobExchange(account, 
"container")), account);
+        }
+        // invalid container names: URL injection (path, query, fragment, 
userinfo, port), length, uppercase, hyphens, $logs
+        for (String container : new String[] {
+                "ab", "a".repeat(64), "Container", "con.tainer", "con/tainer", 
"con?x", "con#x", "con@x", "con:80",
+                "-container", "container-", "con--tainer", "$logs" }) {
+            assertThrows(IllegalArgumentException.class,
+                    () -> operations.copyBlob(copyBlobExchange("account", 
container)), container);
+        }
+        verify(client, times(15)).copyBlob(any(), any(), any(), any());
+    }
+
+    private Exchange copyBlobExchange(String accountName, String 
containerName) {
+        final Exchange exchange = new DefaultExchange(context);
+        exchange.getMessage().setHeader(BlobConstants.BLOB_NAME, "blob");
+        
exchange.getMessage().setHeader(BlobConstants.SOURCE_BLOB_ACCOUNT_NAME, 
accountName);
+        
exchange.getMessage().setHeader(BlobConstants.SOURCE_BLOB_CONTAINER_NAME, 
containerName);
+        return exchange;
+    }
+
     private BlobProperties createBlobProperties() {
         return new BlobProperties(
                 OffsetDateTime.now(), null, null, 0L, null, null, null, null, 
null,

Reply via email to