diff --git a/src/main/java/com/twilio/security/RequestValidator.java b/src/main/java/com/twilio/security/RequestValidator.java index 336291fcad..a9821bb9d5 100644 --- a/src/main/java/com/twilio/security/RequestValidator.java +++ b/src/main/java/com/twilio/security/RequestValidator.java @@ -136,14 +136,25 @@ private String addPort(String url) { private String updatePort(URI url, int newPort) { try { - return new URI( - url.getScheme(), - url.getUserInfo(), - url.getHost(), - newPort, - url.getPath(), - url.getQuery(), - url.getFragment()).toString(); + StringBuilder sb = new StringBuilder(); + sb.append(url.getScheme()).append("://"); + if (url.getRawUserInfo() != null) { + sb.append(url.getRawUserInfo()).append("@"); + } + sb.append(url.getHost()); + if (newPort != -1) { + sb.append(":").append(newPort); + } + if (url.getRawPath() != null) { + sb.append(url.getRawPath()); + } + if (url.getRawQuery() != null) { + sb.append("?").append(url.getRawQuery()); + } + if (url.getRawFragment() != null) { + sb.append("#").append(url.getRawFragment()); + } + return sb.toString(); } catch (Exception e) { return url.toString(); } diff --git a/src/test/java/com/twilio/security/RequestValidatorTest.java b/src/test/java/com/twilio/security/RequestValidatorTest.java index a97418277c..3126eaf585 100644 --- a/src/test/java/com/twilio/security/RequestValidatorTest.java +++ b/src/test/java/com/twilio/security/RequestValidatorTest.java @@ -114,4 +114,30 @@ public void testValidateAddsPortHttp() { Assert.assertTrue("Validator did not add port 80 to http url", isValid); } + @Test + public void testValidatePreservesUrlEncodingInQuery() { + // Test case for the specific issue: URLs with encoded characters in query string + String urlWithoutPort = "https://someurl.com/somepath?param1=client%3AAnonymous"; + String urlWithPort = "https://someurl.com:443/somepath?param1=client%3AAnonymous"; + RequestValidator validator = new RequestValidator("1234567890"); + + // Generate a signature for the URL without port + Map emptyParams = new HashMap<>(); + String signature = null; + try { + java.lang.reflect.Method method = RequestValidator.class.getDeclaredMethod("getValidationSignature", String.class, java.util.Map.class); + method.setAccessible(true); + signature = (String) method.invoke(validator, urlWithoutPort, emptyParams); + } catch (Exception e) { + Assert.fail("Could not generate signature: " + e.getMessage()); + } + + // Both URLs should validate with the same signature since they should be treated as equivalent + boolean validWithoutPort = validator.validate(urlWithoutPort, emptyParams, signature); + boolean validWithPort = validator.validate(urlWithPort, emptyParams, signature); + + Assert.assertTrue("URL without port should validate", validWithoutPort); + Assert.assertTrue("URL with port should validate (encoding preserved)", validWithPort); + } + }