From 7f45ef53d8439b1e3841e6dfbcab56fcb4d175ef Mon Sep 17 00:00:00 2001 From: Amolith Date: Sat, 4 Apr 2026 20:47:30 -0600 Subject: [PATCH] Don't linkify invalid links --- src/main/java/de/gultsch/common/Linkify.java | 14 +++++++ .../java/de/gultsch/common/LinkifyTest.java | 37 +++++++++++++++++++ 2 files changed, 51 insertions(+) create mode 100644 src/test/java/de/gultsch/common/LinkifyTest.java diff --git a/src/main/java/de/gultsch/common/Linkify.java b/src/main/java/de/gultsch/common/Linkify.java index 408b23c49c7d6285f28cf2fad81c122c0ba49e26..586a833cbf45273342fe351b83faf7287046b72a 100644 --- a/src/main/java/de/gultsch/common/Linkify.java +++ b/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); } diff --git a/src/test/java/de/gultsch/common/LinkifyTest.java b/src/test/java/de/gultsch/common/LinkifyTest.java new file mode 100644 index 0000000000000000000000000000000000000000..8db288ca060a80721d94cc3d123f26a67fd3a368 --- /dev/null +++ b/src/test/java/de/gultsch/common/LinkifyTest.java @@ -0,0 +1,37 @@ +package de.gultsch.common; + +import org.junit.Assert; +import org.junit.Test; + +public class LinkifyTest { + + @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()); + } +}