SLING-7814 - URLs with JCR namespaces can get double encoded by XSSAPI.getValidHref

* decode the potentially encoded mangledPath before reconstructing the URI
diff --git a/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java b/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java
index 546eb0c..e9d8afc 100644
--- a/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java
+++ b/src/main/java/org/apache/sling/xss/impl/XSSAPIImpl.java
@@ -19,7 +19,7 @@
 import java.io.StringReader;
 import java.io.StringWriter;
 import java.net.URI;
-import java.net.URISyntaxException;
+import java.net.URLDecoder;
 import java.util.HashMap;
 import java.util.Map;
 import java.util.regex.Matcher;
@@ -202,28 +202,25 @@
                 }
             }
             if (mangledPath != null) {
-                try {
-                    URI mangledURI = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(), mangledPath,
-                            uri.getRawQuery(), uri.getRawFragment());
-                    StringBuilder uriBuilder = new StringBuilder();
-                    if (StringUtils.isNotEmpty(mangledURI.getScheme()) && StringUtils.isNotEmpty(mangledURI.getAuthority())) {
-                        uriBuilder.append(mangledURI.getScheme()).append("://").append(mangledURI.getRawAuthority());
-                    }
-                    if (StringUtils.isNotEmpty(mangledURI.getPath())) {
-                        uriBuilder.append(mangledURI.getRawPath());
-                    }
-                    if (StringUtils.isNotEmpty(mangledURI.getQuery())) {
-                        uriBuilder.append("?").append(mangledURI.getRawQuery());
-                    }
-                    if (StringUtils.isNotEmpty(mangledURI.getFragment())) {
-                        uriBuilder.append("#").append(mangledURI.getRawFragment());
-                    }
-                    return uriBuilder.toString();
-                } catch (URISyntaxException e) {
-                    LOGGER.warn("Invalid URI.", e);
+                URI mangledURI = new URI(uri.getScheme(), uri.getUserInfo(), uri.getHost(), uri.getPort(),
+                        URLDecoder.decode(mangledPath, "UTF-8"),
+                        uri.getRawQuery(), uri.getRawFragment());
+                StringBuilder uriBuilder = new StringBuilder();
+                if (StringUtils.isNotEmpty(mangledURI.getScheme()) && StringUtils.isNotEmpty(mangledURI.getAuthority())) {
+                    uriBuilder.append(mangledURI.getScheme()).append("://").append(mangledURI.getRawAuthority());
                 }
+                if (StringUtils.isNotEmpty(mangledURI.getPath())) {
+                    uriBuilder.append(mangledURI.getRawPath());
+                }
+                if (StringUtils.isNotEmpty(mangledURI.getQuery())) {
+                    uriBuilder.append("?").append(mangledURI.getRawQuery());
+                }
+                if (StringUtils.isNotEmpty(mangledURI.getFragment())) {
+                    uriBuilder.append("#").append(mangledURI.getRawFragment());
+                }
+                return uriBuilder.toString();
             }
-        } catch (URISyntaxException e) {
+        } catch (Exception e) {
             LOGGER.warn("Invalid URI.", e);
         }
         return absPath;
diff --git a/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java b/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java
index 76c302d..5d1737b 100644
--- a/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java
+++ b/src/test/java/org/apache/sling/xss/impl/XSSAPIImplTest.java
@@ -235,6 +235,8 @@
                 // JCR namespaces:
                 {"my/page/jcr:content.feed", "my/page/_jcr_content.feed"},
                 {"my/jcr:content/page/jcr:content", "my/_jcr_content/page/_jcr_content"},
+                {"my/jcr:content/encoded%20spaces", "my/_jcr_content/encoded%20spaces"},
+                {"my/jcr:content/this path has spaces", "my/_jcr_content/this%20path%20has%20spaces"},
 
                 {"\" onClick=ugly", "%22%20onClick=ugly"},
                 {"javascript:ugly", ""},
@@ -272,6 +274,14 @@
                         "/test/jcr:content/search.html?0_tag:id=test",
                         "/test/_jcr_content/search.html?0_tag:id=test"
                 },
+                { // JCR namespaces and colons in query string plus encoded path
+                        "/test%20with%20encoded%20spaces/jcr:content/search.html?0_tag:id=test",
+                        "/test%20with%20encoded%20spaces/_jcr_content/search.html?0_tag:id=test"
+                },
+                { // JCR namespaces and colons in query string plus spaces in path
+                        "/test with spaces/jcr:content/search.html?0_tag:id=test",
+                        "/test%20with%20spaces/_jcr_content/search.html?0_tag:id=test"
+                },
                 { // ? in query string
                         "/test/search.html?0_tag:id=test?ing&1_tag:id=abc",
                         "/test/search.html?0_tag:id=test?ing&1_tag:id=abc",