diff --git a/modules/markup/html_link.go b/modules/markup/html_link.go index 5fd38b63cd..fea82e50ab 100644 --- a/modules/markup/html_link.go +++ b/modules/markup/html_link.go @@ -9,6 +9,7 @@ import ( "strings" "code.gitea.io/gitea/modules/markup/common" + "code.gitea.io/gitea/modules/util" "golang.org/x/net/html" "golang.org/x/net/html/atom" @@ -171,6 +172,10 @@ func linkProcessor(ctx *RenderContext, node *html.Node) { } uri := node.Data[m[0]:m[1]] + remaining := node.Data[m[1]:] + if util.IsLikelySplitLeftPart(remaining) { + return + } replaceContent(node, m[0], m[1], createLink(ctx, uri, uri, "" /*link*/)) node = node.NextSibling.NextSibling } diff --git a/modules/markup/html_test.go b/modules/markup/html_test.go index 54bd91f3b3..f14fe4075c 100644 --- a/modules/markup/html_test.go +++ b/modules/markup/html_test.go @@ -206,6 +206,16 @@ func TestRender_links(t *testing.T) { test( "ftps://gitea.com", `

ftps://gitea.com

`) + + t.Run("LinkSplit", func(t *testing.T) { + input, _ := util.SplitStringAtByteN("http://10.1.2.3", 12) + assert.Equal(t, "http://10…", input) + test(input, "

http://10…

") + + input, _ = util.SplitStringAtByteN("http://10.1.2.3", 13) + assert.Equal(t, "http://10.…", input) + test(input, "

http://10.…

") + }) } func TestRender_email(t *testing.T) { diff --git a/modules/util/string.go b/modules/util/string.go index cf50f591c6..19cf75b8b3 100644 --- a/modules/util/string.go +++ b/modules/util/string.go @@ -3,7 +3,10 @@ package util -import "unsafe" +import ( + "strings" + "unsafe" +) func isSnakeCaseUpper(c byte) bool { return 'A' <= c && c <= 'Z' @@ -95,3 +98,15 @@ func UnsafeBytesToString(b []byte) string { func UnsafeStringToBytes(s string) []byte { return unsafe.Slice(unsafe.StringData(s), len(s)) } + +// SplitTrimSpace splits the string at given separator and trims leading and trailing space +func SplitTrimSpace(input, sep string) []string { + input = strings.TrimSpace(input) + var stringList []string + for _, s := range strings.Split(input, sep) { + if s = strings.TrimSpace(s); s != "" { + stringList = append(stringList, s) + } + } + return stringList +} diff --git a/modules/util/string_test.go b/modules/util/string_test.go index 0a4a8bbcfb..ff67b5c7d4 100644 --- a/modules/util/string_test.go +++ b/modules/util/string_test.go @@ -45,3 +45,8 @@ func TestToSnakeCase(t *testing.T) { assert.Equal(t, expected, ToSnakeCase(input)) } } + +func TestSplitTrimSpace(t *testing.T) { + assert.Equal(t, []string{"a", "b", "c"}, SplitTrimSpace("a\nb\nc", "\n")) + assert.Equal(t, []string{"a", "b"}, SplitTrimSpace("\r\na\n\r\nb\n\n", "\n")) +} diff --git a/modules/util/truncate.go b/modules/util/truncate.go index f2edbdc673..9f932facc9 100644 --- a/modules/util/truncate.go +++ b/modules/util/truncate.go @@ -14,6 +14,10 @@ const ( asciiEllipsis = "..." ) +func IsLikelySplitLeftPart(s string) bool { + return strings.HasSuffix(s, utf8Ellipsis) || strings.HasSuffix(s, asciiEllipsis) +} + // SplitStringAtByteN splits a string at byte n accounting for rune boundaries. (Combining characters are not accounted for.) func SplitStringAtByteN(input string, n int) (left, right string) { if len(input) <= n { @@ -38,19 +42,3 @@ func SplitStringAtByteN(input string, n int) (left, right string) { return input[:end] + utf8Ellipsis, utf8Ellipsis + input[end:] } - -// SplitTrimSpace splits the string at given separator and trims leading and trailing space -func SplitTrimSpace(input, sep string) []string { - // Trim initial leading & trailing space - input = strings.TrimSpace(input) - // replace CRLF with LF - input = strings.ReplaceAll(input, "\r\n", "\n") - - var stringList []string - for _, s := range strings.Split(input, sep) { - // trim leading and trailing space - stringList = append(stringList, strings.TrimSpace(s)) - } - - return stringList -}