From 8966e578fd3b443c3a4d051a235594e72d1efaca Mon Sep 17 00:00:00 2001 From: labkey-nicka Date: Thu, 14 Aug 2025 12:57:34 -0700 Subject: [PATCH 1/3] Issue 53699: BufferUnderflow and length validator test case --- src/org/labkey/test/tests/ClientAPITest.java | 65 +++++++++++++------- 1 file changed, 44 insertions(+), 21 deletions(-) diff --git a/src/org/labkey/test/tests/ClientAPITest.java b/src/org/labkey/test/tests/ClientAPITest.java index a9b4549d91..9ce6ebcbc9 100644 --- a/src/org/labkey/test/tests/ClientAPITest.java +++ b/src/org/labkey/test/tests/ClientAPITest.java @@ -57,6 +57,7 @@ import org.labkey.test.util.PermissionsHelper; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.StudyHelper; +import org.labkey.test.util.TestDataGenerator; import org.labkey.test.util.data.TestDataUtils; import org.labkey.test.util.UIUserHelper; import org.labkey.test.util.WikiHelper; @@ -77,9 +78,12 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.labkey.test.WebTestHelper.getHttpResponse; import static org.labkey.test.params.FieldDefinition.DOMAIN_TRICKY_CHARACTERS; @@ -93,8 +97,10 @@ public class ClientAPITest extends BaseWebDriverTest private static final String PROJECT_NAME = "ClientAPITestProject"; private static final String OTHER_PROJECT = "OtherClientAPITestProject"; // for cross-project query test - protected static final String FOLDER_NAME = "api folder"; + protected static final String API_FOLDER_NAME = "api folder"; + private static final String API_FOLDER_PATH = PROJECT_NAME + "/" + API_FOLDER_NAME; private static final String SUBFOLDER_NAME = "subfolder"; + private static final String SUBFOLDER_PATH = API_FOLDER_PATH + "/" + SUBFOLDER_NAME; private static final String TIME_STUDY_FOLDER = "timeStudyFolder"; private static final String TIME_STUDY_NAME = "timeStudyName"; private static final String VISIT_STUDY_FOLDER = "visitStudyFolder"; @@ -129,10 +135,6 @@ public class ClientAPITest extends BaseWebDriverTest public static final String TEST_DIV_NAME = "testDiv"; - private static final String GRIDTEST_GRIDTITLE = "ClientAPITest Grid Title"; - - private static final int PAGE_SIZE = 4; - public static final String SRC_PREFIX = "Loading XSS")); } + @Test // Issue 53699 + public void testLargeMultibytePayload() + { + // Arrange + int characterCount = 5_000; + String tooManyMultibyteCharacters = "\uD83D\uDC7E".repeat(characterCount); + + TestDataGenerator dataGenerator = new TestDataGenerator("lists", LIST_NAME, API_FOLDER_PATH) + .addCustomRow(Map.of("FirstName", tooManyMultibyteCharacters, "LastName", "Chaplin", "Age", 42)); + + // Act + // Prior to fix for Issue 53699 this would throw a BufferUnderflowException when processing the JSON payload + Exception exception = assertThrows(Exception.class, dataGenerator::insertRows); + + // Assert + // Prior to fix length validation incorrectly checked for string length rather than character code point length + String expectedPrefix = "Value is too long for column 'FirstName', a maximum length of 4000 is allowed."; + String expectedSuffix = String.format("was %d characters long.", characterCount); + + assertThat(exception.getMessage(), startsWith(expectedPrefix)); + assertThat(exception.getMessage(), endsWith(expectedSuffix)); + } + @Override public BrowserType bestBrowser() { From dbb2c9d2eb1cdc11218de6e9335e111103452a47 Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Thu, 14 Aug 2025 17:04:54 -0700 Subject: [PATCH 2/3] Test with multiple byte offsets to ensure everything works --- src/org/labkey/test/tests/ClientAPITest.java | 46 +++++++++++--------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/src/org/labkey/test/tests/ClientAPITest.java b/src/org/labkey/test/tests/ClientAPITest.java index 9ce6ebcbc9..f1dfddad62 100644 --- a/src/org/labkey/test/tests/ClientAPITest.java +++ b/src/org/labkey/test/tests/ClientAPITest.java @@ -213,7 +213,7 @@ public static void setupProject() throws Exception init._containerHelper.createSubfolder(PROJECT_NAME, API_FOLDER_NAME, SUBFOLDER_NAME, "None", null); - init.createStudies(); +// init.createStudies(); init.clickFolder(API_FOLDER_NAME); @@ -221,7 +221,7 @@ public static void setupProject() throws Exception init.createLists(); - init.createUsers(); +// init.createUsers(); } private static boolean dirtyList = false; @@ -1565,24 +1565,30 @@ public void testBootstrapButtonEncoding() @Test // Issue 53699 public void testLargeMultibytePayload() { - // Arrange - int characterCount = 5_000; - String tooManyMultibyteCharacters = "\uD83D\uDC7E".repeat(characterCount); - - TestDataGenerator dataGenerator = new TestDataGenerator("lists", LIST_NAME, API_FOLDER_PATH) - .addCustomRow(Map.of("FirstName", tooManyMultibyteCharacters, "LastName", "Chaplin", "Age", 42)); - - // Act - // Prior to fix for Issue 53699 this would throw a BufferUnderflowException when processing the JSON payload - Exception exception = assertThrows(Exception.class, dataGenerator::insertRows); - - // Assert - // Prior to fix length validation incorrectly checked for string length rather than character code point length - String expectedPrefix = "Value is too long for column 'FirstName', a maximum length of 4000 is allowed."; - String expectedSuffix = String.format("was %d characters long.", characterCount); - - assertThat(exception.getMessage(), startsWith(expectedPrefix)); - assertThat(exception.getMessage(), endsWith(expectedSuffix)); + for (int i = 0; i < 10; i++) + { + // Arrange + int characterCount = 5_000; + // We had problems fetching the JSON content server-side due to Tomcat's InputBuffer not dealing with + // characters that span multiple "pages" of the byte buffer. Test with different numbers of single-byte + // UTF-8 characters before the 4-byte characters to ensure we get different boundary conditions + String tooManyMultibyteCharacters = "a".repeat(i) + "\uD83D\uDC7E".repeat(characterCount); + + TestDataGenerator dataGenerator = new TestDataGenerator("lists", LIST_NAME, API_FOLDER_PATH) + .addCustomRow(Map.of("FirstName", tooManyMultibyteCharacters, "LastName", "Chaplin", "Age", 42)); + + // Act + // Prior to fix for Issue 53699 this would throw a BufferUnderflowException when processing the JSON payload + Exception exception = assertThrows(Exception.class, dataGenerator::insertRows); + + // Assert + // Prior to fix length validation incorrectly checked for string length rather than character code point length + String expectedPrefix = "Value is too long for column 'FirstName', a maximum length of 4000 is allowed."; + String expectedSuffix = String.format("was %d characters long.", characterCount + i); + + assertThat(exception.getMessage(), startsWith(expectedPrefix)); + assertThat(exception.getMessage(), endsWith(expectedSuffix)); + } } @Override From 2530099f633c1ce30c6fd1137af12a0137e98a5e Mon Sep 17 00:00:00 2001 From: labkey-jeckels Date: Thu, 14 Aug 2025 17:05:22 -0700 Subject: [PATCH 3/3] Restore full setup --- src/org/labkey/test/tests/ClientAPITest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/tests/ClientAPITest.java b/src/org/labkey/test/tests/ClientAPITest.java index f1dfddad62..3a6ceea1d1 100644 --- a/src/org/labkey/test/tests/ClientAPITest.java +++ b/src/org/labkey/test/tests/ClientAPITest.java @@ -213,7 +213,7 @@ public static void setupProject() throws Exception init._containerHelper.createSubfolder(PROJECT_NAME, API_FOLDER_NAME, SUBFOLDER_NAME, "None", null); -// init.createStudies(); + init.createStudies(); init.clickFolder(API_FOLDER_NAME); @@ -221,7 +221,7 @@ public static void setupProject() throws Exception init.createLists(); -// init.createUsers(); + init.createUsers(); } private static boolean dirtyList = false;