diff --git a/src/main/java/org/apache/commons/validator/routines/UrlValidator.java b/src/main/java/org/apache/commons/validator/routines/UrlValidator.java index 44862bcc9..3ae6e82b9 100644 --- a/src/main/java/org/apache/commons/validator/routines/UrlValidator.java +++ b/src/main/java/org/apache/commons/validator/routines/UrlValidator.java @@ -474,6 +474,32 @@ protected boolean isValidFragment(final String fragment) { return isOff(NO_FRAGMENTS); } + private String decodePath(final String path) { + final StringBuilder sb = new StringBuilder(); + final int len = path.length(); + for (int i = 0; i < len; i++) { + final char c = path.charAt(i); + if (c == '%' && i + 2 < len) { + final char h1 = path.charAt(i + 1); + final char h2 = path.charAt(i + 2); + if (h1 == '2') { + if (h2 == 'e' || h2 == 'E') { + sb.append('.'); + i += 2; + continue; + } + if (h2 == 'f' || h2 == 'F') { + sb.append('/'); + i += 2; + continue; + } + } + } + sb.append(c); + } + return sb.toString(); + } + /** * Returns true if the path is valid. A {@code null} value is considered invalid. * @@ -487,7 +513,7 @@ protected boolean isValidPath(final String path) { try { // Don't omit host otherwise leading path may be taken as host if it starts with // - final URI uri = new URI(null, "localhost", path, null); + final URI uri = new URI(null, "localhost", decodePath(path), null); final String norm = uri.normalize().getPath(); if (norm.startsWith("/../") // Trying to go via the parent dir || norm.equals("/..")) { // Trying to go to the parent dir diff --git a/src/test/java/org/apache/commons/validator/routines/UrlValidatorTest.java b/src/test/java/org/apache/commons/validator/routines/UrlValidatorTest.java index 25e0b5dc5..7708f8842 100644 --- a/src/test/java/org/apache/commons/validator/routines/UrlValidatorTest.java +++ b/src/test/java/org/apache/commons/validator/routines/UrlValidatorTest.java @@ -524,6 +524,19 @@ void testValidator363() { assertTrue(urlValidator.isValid("http://www.example.org/.../..")); } + @Test + void testPathTraversalPercentEncoding() { + final UrlValidator urlValidator = new UrlValidator(); + assertFalse(urlValidator.isValid("http://www.example.org/..%2fworld")); + assertFalse(urlValidator.isValid("http://www.example.org/..%2Fworld")); + assertFalse(urlValidator.isValid("http://www.example.org/%2e%2e/world")); + assertFalse(urlValidator.isValid("http://www.example.org/%2e%2e%2fworld")); + assertFalse(urlValidator.isValid("http://www.example.org/%2E%2E%2Fworld")); + assertFalse(urlValidator.isValid("http://www.example.org/..%2f")); + assertFalse(urlValidator.isValid("http://www.example.org/%2e%2e")); + assertFalse(urlValidator.isValid("http://www.example.org/%2e%2e/")); + } + @Test void testValidator375() { final UrlValidator validator = new UrlValidator();