This is an automated email from the ASF dual-hosted git repository.
davsclaus pushed a commit to branch camel-4.22.x
in repository https://gitbox.apache.org/repos/asf/camel.git
The following commit(s) were added to refs/heads/camel-4.22.x by this push:
new 55003e484022 [backport camel-4.22.x] CAMEL-26284:
camel-azure-storage-blob - validate the copyBlob source account and container
names (#29766)
55003e484022 is described below
commit 55003e4840227eb2a4fd89e6fa49eb7bdd0a3d73
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Nov 13 08:01:43 2026 +0100
[backport camel-4.22.x] CAMEL-26284: camel-azure-storage-blob - validate
the copyBlob source account and container names (#29766)
Backport of #29254 onto camel-4.22.x (replaces #29255). copyBlob validates
the
source account and container names against the Azure naming rules. Straight:
BlobOperations and BlobOperationsTest are those of #29255; the upgrade-guide
notes already live on main.
Co-authored-by: jiriOndrusek-agent <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[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 6c13d74215aa..ea162b32cbe2 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
@@ -27,6 +27,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;
@@ -84,6 +85,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;
@@ -459,6 +464,15 @@ public class BlobOperations {
if (ObjectHelper.isEmpty(sourceContainerName)) {
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 9c35ec259f94..f31467ccfe86 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
@@ -629,6 +629,42 @@ class BlobOperationsTest extends CamelTestSupport {
.get("x-test-header").getValue());
}
+ @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,