From dfa9cda71fda74657f2950e0856f50efd57a1b87 Mon Sep 17 00:00:00 2001 From: Luis Rubiera Date: Thu, 13 Feb 2025 10:30:12 +0100 Subject: [PATCH] fix(independent-projects): throw IllegalArgumentException on failed URL decoding Previously, URLUtils.decode threw a RuntimeException when encountering invalid percent-encoded values. Now, it throws an IllegalArgumentException, ensuring that malformed input is correctly recognized as a client error. Test coverage added for: - Invalid percent encoding (e.g., %zz, %2) - Gray-area invalid UTF-8 cases (e.g., %80) - Properly encoded values (e.g., %20, form-encoded +, Japanese characters) Fixes #46197 (cherry picked from commit ed6ec4b6d5d1726275515d9892498da5d0081df1) --- .../reactive/common/util/URLUtils.java | 4 +- .../reactive/common/util/URLUtilsTest.java | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 independent-projects/resteasy-reactive/common/runtime/src/test/java/org/jboss/resteasy/reactive/common/util/URLUtilsTest.java diff --git a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/URLUtils.java b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/URLUtils.java index cc4b407b4aca5..c8376149220e1 100644 --- a/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/URLUtils.java +++ b/independent-projects/resteasy-reactive/common/runtime/src/main/java/org/jboss/resteasy/reactive/common/util/URLUtils.java @@ -217,8 +217,8 @@ public static String decode(String s, Charset enc, boolean decodeSlash, boolean return s; } - private static RuntimeException failedToDecodeURL(String s, Charset enc, Throwable o) { - return new RuntimeException("Failed to decode URL " + s + " to " + enc, o); + private static IllegalArgumentException failedToDecodeURL(String s, Charset enc, Throwable o) { + return new IllegalArgumentException("Failed to decode URL " + s + " to " + enc, o); } private static byte[] expandBytes(byte[] bytes) { diff --git a/independent-projects/resteasy-reactive/common/runtime/src/test/java/org/jboss/resteasy/reactive/common/util/URLUtilsTest.java b/independent-projects/resteasy-reactive/common/runtime/src/test/java/org/jboss/resteasy/reactive/common/util/URLUtilsTest.java new file mode 100644 index 0000000000000..66e252f89e08e --- /dev/null +++ b/independent-projects/resteasy-reactive/common/runtime/src/test/java/org/jboss/resteasy/reactive/common/util/URLUtilsTest.java @@ -0,0 +1,50 @@ +package org.jboss.resteasy.reactive.common.util; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; + +import java.nio.charset.StandardCharsets; + +import org.junit.jupiter.api.Test; + +class URLUtilsTest { + @Test + void decodeInvalidPercentEncoding() { + String incomplete = "invalid%2"; + String invalidHex = "invalid%zz"; + + assertThrows(IllegalArgumentException.class, + () -> URLUtils.decode(incomplete, StandardCharsets.UTF_8, true, new StringBuilder())); + assertThrows(IllegalArgumentException.class, + () -> URLUtils.decode(invalidHex, StandardCharsets.UTF_8, true, new StringBuilder())); + } + + @Test + void decodeGrayAreaInvalidUtf8() { + String invalidUtf8 = "invalid%80"; + + // This is a gray area: %80 is not valid in UTF-8 as a standalone byte, + // but Java's default decoding behavior does not throw an exception. + // Instead, it replaces it with a special character (�). + // + // To enforce strict decoding, CharsetDecoder with CodingErrorAction.REPORT + // should be used inside URLUtils.decode. + String decoded = URLUtils.decode(invalidUtf8, StandardCharsets.UTF_8, true, new StringBuilder()); + + assertEquals("invalid�", decoded); // Note: This may vary depending on the JVM. + } + + @Test + void decodeValidValues() { + String path = "test%20path"; + String formEncoded = "test+path"; + String japanese = "%E3%83%86%E3%82%B9%E3%83%88"; // テスト + + assertEquals("test path", + URLUtils.decode(path, StandardCharsets.UTF_8, true, new StringBuilder())); + assertEquals("test path", + URLUtils.decode(formEncoded, StandardCharsets.UTF_8, true, true, new StringBuilder())); + assertEquals("テスト", + URLUtils.decode(japanese, StandardCharsets.UTF_8, true, new StringBuilder())); + } +}