From eaf478de2315b8d35f27d39f7f5c92328d5b8066 Mon Sep 17 00:00:00 2001 From: Karan Ray <2006karanray@gmail.com> Date: Wed, 10 Jun 2026 17:03:57 +0000 Subject: [PATCH] fix(shell): preserve '=' in S3Url credentials when parsing S3Url.parse split each query parameter on every '=' via split("="), so access/secret keys that contain '=' (common in base64-encoded credentials, including '=' padding) were either rejected with "Invalid parameter format" or silently truncated. Split on the first '=' only via split("=", 2) to preserve the full value. Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> --- .../src/main/java/com/automq/shell/model/S3Url.java | 4 +++- .../apache/kafka/tools/automq/model/S3UrlTest.java | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/automq-shell/src/main/java/com/automq/shell/model/S3Url.java b/automq-shell/src/main/java/com/automq/shell/model/S3Url.java index bc8d0167b6..474286b7d2 100644 --- a/automq-shell/src/main/java/com/automq/shell/model/S3Url.java +++ b/automq-shell/src/main/java/com/automq/shell/model/S3Url.java @@ -86,7 +86,9 @@ public static S3Url parse(String s3Url) throws IllegalArgumentException { boolean s3PathStyle = false; for (String param : params) { - String[] keyValue = param.split("="); + // Only split on the first '=' so that values containing '=' (e.g. base64-encoded + // access/secret keys, which may include '=' padding) are preserved. + String[] keyValue = param.split("=", 2); if (keyValue.length != 2) { throw new IllegalArgumentException("Invalid parameter format: " + param); } diff --git a/tools/src/test/java/org/apache/kafka/tools/automq/model/S3UrlTest.java b/tools/src/test/java/org/apache/kafka/tools/automq/model/S3UrlTest.java index 35918eef3b..44345829c6 100644 --- a/tools/src/test/java/org/apache/kafka/tools/automq/model/S3UrlTest.java +++ b/tools/src/test/java/org/apache/kafka/tools/automq/model/S3UrlTest.java @@ -42,6 +42,19 @@ void parseTest() { } + @Test + void parseWithEqualsSignInCredentials() { + // base64-encoded secret keys frequently contain '=' (padding) and '/' characters. + // The value must be preserved verbatim instead of being split on every '='. + String secretKey = "abc/def+ghi=="; + String s3Url = "s3://s3.us-east-1.amazonaws.com?s3-access-key=AKIA=EXAMPLE&s3-secret-key=" + secretKey + + "&s3-region=us-east-1&s3-endpoint-protocol=https&s3-data-bucket=data&s3-ops-bucket=ops&cluster-id=abc"; + S3Url s3UrlObj = S3Url.parse(s3Url); + assertEquals("AKIA=EXAMPLE", s3UrlObj.getS3AccessKey()); + assertEquals(secretKey, s3UrlObj.getS3SecretKey()); + assertEquals("us-east-1", s3UrlObj.getS3Region()); + } + @Test void parseS3UrlValFromArgs() { String s3Url = "s3://s3.cn-northwest-1.amazonaws.com.cn?s3-access-key=xxx&s3-secret-key=yyy&s3-region=cn-northwest-1&s3-endpoint-protocol=https&s3-data-bucket=wanshao-test&s3-ops-bucket=automq-ops-bucket&cluster-id=fZGPJht6Rf-o7WgrUakLxQ";