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
@@ -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)) {
@@ -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());
+ }
}