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

adoroszlai pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/master by this push:
     new ade69e3f82 HDDS-12649. Include name of volume or bucket in length 
validation error (#8322)
ade69e3f82 is described below

commit ade69e3f821ea6c6a5ff199c8aac7987e44d48cf
Author: Roland Elek <[email protected]>
AuthorDate: Mon Apr 28 22:15:59 2025 +0200

    HDDS-12649. Include name of volume or bucket in length validation error 
(#8322)
---
 .../hadoop/hdds/scm/client/HddsClientUtils.java    |  35 +++++--
 .../hdds/scm/client/TestHddsClientUtils.java       | 101 ++++++++++++++++++++-
 2 files changed, 125 insertions(+), 11 deletions(-)

diff --git 
a/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/client/HddsClientUtils.java
 
b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/client/HddsClientUtils.java
index a09ab64eae..ea5fc908a9 100644
--- 
a/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/client/HddsClientUtils.java
+++ 
b/hadoop-hdds/client/src/main/java/org/apache/hadoop/hdds/scm/client/HddsClientUtils.java
@@ -48,6 +48,14 @@
 @InterfaceAudience.Public
 @InterfaceStability.Unstable
 public final class HddsClientUtils {
+  static final int MAX_BUCKET_NAME_LENGTH_IN_LOG =
+      2 * OzoneConsts.OZONE_MAX_BUCKET_NAME_LENGTH;
+
+  private static final String VALID_LENGTH_MESSAGE = String.format(
+      "valid length is %d-%d characters",
+      OzoneConsts.OZONE_MIN_BUCKET_NAME_LENGTH,
+      OzoneConsts.OZONE_MAX_BUCKET_NAME_LENGTH);
+
   private static final List<Class<? extends Exception>> EXCEPTION_LIST =
       ImmutableList.<Class<? extends Exception>>builder()
           .add(TimeoutException.class)
@@ -68,10 +76,26 @@ private static void doNameChecks(String resName, String 
resType) {
       throw new IllegalArgumentException(resType + " name is null");
     }
 
-    if (resName.length() < OzoneConsts.OZONE_MIN_BUCKET_NAME_LENGTH ||
-        resName.length() > OzoneConsts.OZONE_MAX_BUCKET_NAME_LENGTH) {
+    if (resName.length() < OzoneConsts.OZONE_MIN_BUCKET_NAME_LENGTH) {
+      throw new IllegalArgumentException(resType +
+          " name '" + resName + "' is too short, " +
+          VALID_LENGTH_MESSAGE);
+    }
+
+    if (resName.length() > OzoneConsts.OZONE_MAX_BUCKET_NAME_LENGTH) {
+      String nameToReport;
+
+      if (resName.length() > MAX_BUCKET_NAME_LENGTH_IN_LOG) {
+        nameToReport = String.format(
+            "%s...",
+            resName.substring(0, MAX_BUCKET_NAME_LENGTH_IN_LOG));
+      } else {
+        nameToReport = resName;
+      }
+
       throw new IllegalArgumentException(resType +
-          " length is illegal, " + "valid length is 3-63 characters");
+          " name '" + nameToReport + "' is too long, " +
+          VALID_LENGTH_MESSAGE);
     }
 
     if (resName.charAt(0) == '.' || resName.charAt(0) == '-') {
@@ -150,9 +174,6 @@ public static void verifyResourceName(String resName, 
String resType) {
    * @throws IllegalArgumentException
    */
   public static void verifyResourceName(String resName, String resType, 
boolean isStrictS3) {
-
-    doNameChecks(resName, resType);
-
     boolean isIPv4 = true;
     char prev = (char) 0;
 
@@ -169,6 +190,8 @@ public static void verifyResourceName(String resName, 
String resType, boolean is
       throw new IllegalArgumentException(resType +
           " name cannot be an IPv4 address or all numeric");
     }
+
+    doNameChecks(resName, resType);
   }
 
   /**
diff --git 
a/hadoop-hdds/client/src/test/java/org/apache/hadoop/hdds/scm/client/TestHddsClientUtils.java
 
b/hadoop-hdds/client/src/test/java/org/apache/hadoop/hdds/scm/client/TestHddsClientUtils.java
index 8d0eae226c..281cc12fa3 100644
--- 
a/hadoop-hdds/client/src/test/java/org/apache/hadoop/hdds/scm/client/TestHddsClientUtils.java
+++ 
b/hadoop-hdds/client/src/test/java/org/apache/hadoop/hdds/scm/client/TestHddsClientUtils.java
@@ -23,12 +23,15 @@
 import static 
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_CLIENT_PORT_DEFAULT;
 import static 
org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_CLIENT_PORT_KEY;
 import static org.apache.hadoop.hdds.scm.ScmConfigKeys.OZONE_SCM_NAMES;
+import static org.assertj.core.api.Assertions.assertThat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import com.google.common.collect.ImmutableList;
 import java.io.IOException;
 import java.net.ConnectException;
 import java.net.InetSocketAddress;
@@ -191,6 +194,10 @@ public void testBlockClientFallbackToClientWithPort() {
     assertEquals(OZONE_SCM_BLOCK_CLIENT_PORT_DEFAULT, address.getPort());
   }
 
+  private String getInvalidNameMessage(String invalidString) {
+    return "Did not reject invalid string [" + invalidString + "] as a name";
+  }
+
   @Test
   public void testVerifyResourceName() {
     final String validName = "my-bucket.01";
@@ -209,8 +216,7 @@ public void testVerifyResourceName() {
     final String endDot = "notaname.";
     final String startDot = ".notaname";
     final String unicodeCharacters = "zzz";
-    final String tooShort = StringUtils.repeat("a",
-        OzoneConsts.OZONE_MIN_BUCKET_NAME_LENGTH - 1);
+    // Other tests cover the "too short" case.
 
     List<String> invalidNames = new ArrayList<>();
     invalidNames.add(ipaddr);
@@ -221,14 +227,99 @@ public void testVerifyResourceName() {
     invalidNames.add(endDot);
     invalidNames.add(startDot);
     invalidNames.add(unicodeCharacters);
-    invalidNames.add(tooShort);
 
     for (String name : invalidNames) {
       assertThrows(IllegalArgumentException.class, () -> 
HddsClientUtils.verifyResourceName(name),
-          "Did not reject invalid string [" + name + "] as a name");
+          getInvalidNameMessage(name));
+    }
+  }
+
+  private void doTestBadResourceNameLengthReported(String name, String reason) 
{
+    // The message should include the name, resource type, range for acceptable
+    // length, and expected reason for rejecting the name.
+    List<String> resourceTypes = ImmutableList.of("bucket", "volume");
+    for (int i = 0; i < 2; i++) {
+      String resType = resourceTypes.get(i);
+      String otherResType = resourceTypes.get(1 - i);
+      Throwable thrown = assertThrows(
+          IllegalArgumentException.class,
+          () -> HddsClientUtils.verifyResourceName(name, resType, true),
+          getInvalidNameMessage(name));
+
+      String message = thrown.getMessage();
+      assertNotNull(message);
+      assertThat(message).contains(resType);
+      assertThat(message).doesNotContain(otherResType);
+      assertThat(message).contains(name);
+      assertThat(message).contains(reason);
+      assertThat(message).contains(
+          Integer.toString(OzoneConsts.OZONE_MIN_BUCKET_NAME_LENGTH));
+      assertThat(message).contains(
+          Integer.toString(OzoneConsts.OZONE_MAX_BUCKET_NAME_LENGTH));
     }
   }
 
+  @Test
+  public void testNameTooShort() {
+    final String tooShort = StringUtils.repeat("a",
+        OzoneConsts.OZONE_MIN_BUCKET_NAME_LENGTH - 1);
+
+    doTestBadResourceNameLengthReported(tooShort, "too short");
+  }
+
+  @Test
+  public void testNameTooLong() {
+    // Too long, but within the limit for logging (no truncation).
+    final String tooLong = StringUtils.repeat("a",
+        OzoneConsts.OZONE_MAX_BUCKET_NAME_LENGTH + 1);
+
+    doTestBadResourceNameLengthReported(tooLong, "too long");
+  }
+
+  @Test
+  public void testNameTooLongCapped() {
+    // Logging arbitrarily long names is a readability concern and possibly a
+    // vulnerability. Report a prefix with a truncation marker instead if the
+    // maximum length is exceeded by a large margin.
+
+    final String exceedsLogLimit = "x" + StringUtils.repeat(
+        "a", HddsClientUtils.MAX_BUCKET_NAME_LENGTH_IN_LOG);
+    final String truncationMarker = "...";
+
+    Throwable thrown = assertThrows(
+        IllegalArgumentException.class,
+        () -> HddsClientUtils.verifyResourceName(exceedsLogLimit),
+        getInvalidNameMessage(exceedsLogLimit));
+
+    String message = thrown.getMessage();
+    assertNotNull(message);
+
+    String truncatedName = exceedsLogLimit.substring(
+        0,
+        HddsClientUtils.MAX_BUCKET_NAME_LENGTH_IN_LOG);
+    String expectedInMessage = truncatedName + truncationMarker;
+
+    assertThat(message).contains(expectedInMessage);
+    assertThat(message).doesNotContain(exceedsLogLimit);
+  }
+
+  @Test
+  public void testInvalidCharactersNotReported() {
+    // Names of illegal length may appear in logs through the exception
+    // message. If they also contain invalid characters, they should not be
+    // included verbatim. This is to avoid vulnerabilities like Log4Shell.
+
+    final String tooShortInvalidChar = "$a";
+    Throwable thrown = assertThrows(
+        IllegalArgumentException.class,
+        () -> HddsClientUtils.verifyResourceName(tooShortInvalidChar),
+        getInvalidNameMessage(tooShortInvalidChar));
+
+    String message = thrown.getMessage();
+    assertNotNull(message);
+    assertThat(message).doesNotContain(tooShortInvalidChar);
+  }
+
   @Test
   void testVerifyKeyName() throws IllegalArgumentException {
     List<String> invalidNames = new ArrayList<>();
@@ -250,7 +341,7 @@ void testVerifyKeyName() throws IllegalArgumentException {
 
     for (String name : invalidNames) {
       assertThrows(IllegalArgumentException.class, () -> 
HddsClientUtils.verifyKeyName(name),
-          "Did not reject invalid string [" + name + "] as a name");
+          getInvalidNameMessage(name));
     }
 
     List<String> validNames = new ArrayList<>();


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to