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,
