Merge branch 'dont-linkify-invalid-urls' of https://git.secluded.site/cheogram-android

Stephen Paul Weber created

* 'dont-linkify-invalid-urls' of https://git.secluded.site/cheogram-android:
  Don't linkify invalid links

Change summary

src/main/java/de/gultsch/common/Linkify.java     | 14 ++++++++
src/test/java/de/gultsch/common/LinkifyTest.java | 31 +++++++++++++++++
2 files changed, 44 insertions(+), 1 deletion(-)

Detailed changes

src/main/java/de/gultsch/common/Linkify.java 🔗

@@ -45,6 +45,8 @@ import eu.siacs.conversations.entities.ListItem;
 import eu.siacs.conversations.utils.StylingHelper;
 import eu.siacs.conversations.utils.XmppUri;
 import eu.siacs.conversations.xmpp.Jid;
+import java.net.URI;
+import java.net.URISyntaxException;
 import java.util.Arrays;
 import java.util.List;
 import java.util.Objects;
@@ -59,6 +61,9 @@ public class Linkify {
         if (scheme == null) {
             return false;
         }
+        if (!isValidUri(match)) {
+            return false;
+        }
         return switch (scheme) {
             case "tel" -> Patterns.URI_TEL.matcher(match).matches();
             case "http", "https" -> Patterns.URI_HTTP.matcher(match).matches();
@@ -77,6 +82,15 @@ public class Linkify {
         };
     }
 
+    private static boolean isValidUri(final String match) {
+        try {
+            new URI(match);
+            return true;
+        } catch (final URISyntaxException e) {
+            return false;
+        }
+    }
+
     public static void addLinks(final Spannable body) {
         android.text.util.Linkify.addLinks(body, Patterns.URI_GENERIC, null, MATCH_FILTER, null);
         for (final URLSpan span : body.getSpans(0, body.length(), URLSpan.class)) {

src/test/java/de/gultsch/common/LinkifyTest.java 🔗

@@ -17,11 +17,40 @@ import org.robolectric.annotation.ConscryptMode;
 @Config(sdk = Build.VERSION_CODES.TIRAMISU, application = Conversations.class)
 @ConscryptMode(ConscryptMode.Mode.OFF)
 public class LinkifyTest {
-
     @Test
     public void addLinksDoesNotLinkifyInvalidUris() {
         final var text = new SpannableStringBuilder("https://example.com?q=%s");
         Linkify.addLinks(text);
         Assert.assertEquals(0, text.getSpans(0, text.length(), URLSpan.class).length);
     }
+
+    @Test
+    public void malformedEscapeIsRejected() {
+        final var links = Linkify.getLinks("https://example.com/?x=%");
+
+        Assert.assertTrue(links.isEmpty());
+    }
+
+    @Test
+    public void validEscapesStillProduceLinks() {
+        final var links = Linkify.getLinks("https://example.com/?x=%20");
+
+        Assert.assertEquals(1, links.size());
+        Assert.assertEquals("https://example.com/?x=%20", links.get(0).getRaw());
+    }
+
+    @Test
+    public void ipv6HostsStillProduceLinks() {
+        final var links = Linkify.getLinks("http://[::1]/foo");
+
+        Assert.assertEquals(1, links.size());
+        Assert.assertEquals("http://[::1]/foo", links.get(0).getRaw());
+    }
+
+    @Test
+    public void bracketsInPathAreRejected() {
+        final var links = Linkify.getLinks("http://example.com/foo[bar]");
+
+        Assert.assertTrue(links.isEmpty());
+    }
 }