This is an automated email from the ASF dual-hosted git repository.

reta pushed a commit to branch 3.5.x-fixes
in repository https://gitbox.apache.org/repos/asf/cxf.git


The following commit(s) were added to refs/heads/3.5.x-fixes by this push:
     new cea43ef018 CXF-8688: Accomodate some RFC 3986 checks to filter out 
invalid URIs (#935)
cea43ef018 is described below

commit cea43ef0184084b1068068ca9daaf8b144fcbec0
Author: Andriy Redko <[email protected]>
AuthorDate: Sat Apr 16 18:55:21 2022 -0400

    CXF-8688: Accomodate some RFC 3986 checks to filter out invalid URIs (#935)
    
    (cherry picked from commit fe512bc456d7d3ed2e24c1d378fa767e86951cbc)
---
 .../apache/cxf/jaxrs/impl/Rfc3986UriValidator.java | 70 ++++++++++++++++++++++
 .../org/apache/cxf/jaxrs/impl/UriBuilderImpl.java  |  8 ++-
 .../java/org/apache/cxf/jaxrs/utils/HttpUtils.java |  6 ++
 .../apache/cxf/jaxrs/impl/LinkBuilderImplTest.java |  7 +++
 .../apache/cxf/jaxrs/impl/UriBuilderImplTest.java  |  8 +++
 5 files changed, 98 insertions(+), 1 deletion(-)

diff --git 
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/Rfc3986UriValidator.java
 
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/Rfc3986UriValidator.java
new file mode 100644
index 0000000000..a7b27ffb43
--- /dev/null
+++ 
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/Rfc3986UriValidator.java
@@ -0,0 +1,70 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.jaxrs.impl;
+
+import java.net.URI;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+
+final class Rfc3986UriValidator {
+    private static final String SCHEME = "(?i)(http|https):";
+
+    private static final String USERINFO = "([^@\\[/?#]*)";
+
+    private static final String HOST = "([^/?#]*)";
+
+    private static final String PATH = "([^?#]*)";
+
+    private static final String QUERY = "([^#]*)";
+
+    private static final String LAST = "#(.*)";
+
+    private static final Pattern HTTP_URL = Pattern.compile("^" + SCHEME 
+        + "(//(" + USERINFO + "@)?" + HOST  + ")?" + PATH
+        + "(\\?" + QUERY + ")?" + "(" + LAST + ")?");
+
+    private Rfc3986UriValidator() {
+    }
+
+    /**
+     * Validate the HTTP URL according to 
https://datatracker.ietf.org/doc/html/rfc3986#appendix-B  
+     * @param uri HTTP schemed URI to validate
+     * @return "true" if URI matches RFC-3986 validation rules, "false" 
otherwise
+     */
+    public static boolean validate(final URI uri) {
+        // Only validate the HTTP(s) URIs
+        if (HttpUtils.isHttpScheme(uri.getScheme())) { 
+            final Matcher matcher = HTTP_URL.matcher(uri.toString());
+            if (matcher.matches()) {
+                final String host = matcher.group(5);
+                // There is no host component in the HTTP URI, it is required
+                return !(StringUtils.isEmpty(host));
+            } else {
+                return false;
+            }
+        } else {
+            // not HTTP URI, skipping
+            return true;
+        }
+    }
+}
diff --git 
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriBuilderImpl.java 
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriBuilderImpl.java
index c88062415c..5c7cc67e06 100644
--- 
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriBuilderImpl.java
+++ 
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/impl/UriBuilderImpl.java
@@ -124,7 +124,13 @@ public class UriBuilderImpl extends UriBuilder implements 
Cloneable {
 
         UriParts parts = doBuildUriParts(fromEncoded, encodePathSlash, false, 
values);
         try {
-            return buildURI(fromEncoded, parts.path, parts.query, 
parts.fragment);
+            final URI uri = buildURI(fromEncoded, parts.path, parts.query, 
parts.fragment);
+            
+            if (!Rfc3986UriValidator.validate(uri)) {
+                throw new UriBuilderException("[" + uri + "] is not a valid 
HTTP URL");
+            }
+            
+            return uri;
         } catch (URISyntaxException ex) {
             throw new UriBuilderException("URI can not be built", ex);
         }
diff --git 
a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java 
b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java
index 54a0a59130..735b32538a 100644
--- a/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java
+++ b/rt/frontend/jaxrs/src/main/java/org/apache/cxf/jaxrs/utils/HttpUtils.java
@@ -99,6 +99,8 @@ public final class HttpUtils {
         new HashSet<>(Arrays.asList(new String[]{"GET", "HEAD", "OPTIONS", 
"TRACE"}));
     private static final Set<String> KNOWN_HTTP_VERBS_WITH_NO_RESPONSE_CONTENT 
=
         new HashSet<>(Arrays.asList(new String[]{"HEAD", "OPTIONS"}));
+    
+    private static final Pattern HTTP_SCHEME_PATTERN = 
Pattern.compile("^(?i)(http|https)$");
 
     private HttpUtils() {
     }
@@ -703,4 +705,8 @@ public final class HttpUtils {
     public static boolean isMethodWithNoResponseContent(String method) {
         return KNOWN_HTTP_VERBS_WITH_NO_RESPONSE_CONTENT.contains(method);
     }
+    
+    public static boolean isHttpScheme(final String scheme) {
+        return scheme != null && HTTP_SCHEME_PATTERN.matcher(scheme).matches();
+    }
 }
diff --git 
a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java
 
b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java
index cbb3b941a7..7c4ac3fef3 100644
--- 
a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java
+++ 
b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/LinkBuilderImplTest.java
@@ -33,6 +33,7 @@ import org.junit.Test;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
@@ -140,6 +141,12 @@ public class LinkBuilderImplTest {
         }
     }
 
+    @Test
+    public void invalidUrlsNoHost() {
+        assertThrows(UriBuilderException.class, () -> 
Link.fromUri("http://@";).build());
+        assertThrows(UriBuilderException.class, () -> 
Link.fromUri("http://:@";).build());
+    }
+
     @Path("resource")
     public static class TestResource {
         @POST
diff --git 
a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java
 
b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java
index 923234498c..06ed0406db 100644
--- 
a/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java
+++ 
b/rt/frontend/jaxrs/src/test/java/org/apache/cxf/jaxrs/impl/UriBuilderImplTest.java
@@ -32,6 +32,7 @@ import javax.ws.rs.QueryParam;
 import javax.ws.rs.core.MultivaluedMap;
 import javax.ws.rs.core.Response;
 import javax.ws.rs.core.UriBuilder;
+import javax.ws.rs.core.UriBuilderException;
 
 import org.apache.cxf.jaxrs.resources.Book;
 import org.apache.cxf.jaxrs.resources.BookStore;
@@ -43,6 +44,7 @@ import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.fail;
 
 public class UriBuilderImplTest {
@@ -1975,4 +1977,10 @@ public class UriBuilderImplTest {
             .build();
         assertEquals("/%7B", uri.toString());
     }
+    
+    @Test
+    public void invalidUrlsNoHost() {
+        assertThrows(UriBuilderException.class, () -> 
UriBuilder.fromUri("http://@";).build());
+        assertThrows(UriBuilderException.class, () -> 
UriBuilder.fromUri("http://:@";).build());
+    }
 }
\ No newline at end of file

Reply via email to