diff --git a/server/channels/app/post_metadata_test.go b/server/channels/app/post_metadata_test.go index da86ee7ca5..3719a1f2c1 100644 --- a/server/channels/app/post_metadata_test.go +++ b/server/channels/app/post_metadata_test.go @@ -1727,12 +1727,27 @@ func TestGetFirstLinkAndImages(t *testing.T) { ExpectedFirstLink: "", ExpectedImages: []string{}, }, + "http link in angle brackets": { + Input: "this is a ", + ExpectedFirstLink: "http://example.com", + ExpectedImages: []string{}, + }, + "http link with only opening angle bracket": { + Input: "this is a ", + ExpectedFirstLink: "http://example.com", + ExpectedImages: []string{}, + }, } { t.Run(name, func(t *testing.T) { firstLink, images := th.App.getFirstLinkAndImages(th.Context, testCase.Input) - assert.Equal(t, firstLink, testCase.ExpectedFirstLink) - assert.Equal(t, images, testCase.ExpectedImages) + assert.Equal(t, testCase.ExpectedFirstLink, firstLink) + assert.Equal(t, testCase.ExpectedImages, images) }) } diff --git a/server/public/shared/markdown/autolink.go b/server/public/shared/markdown/autolink.go index 2eb05d9040..c7a9345f5d 100644 --- a/server/public/shared/markdown/autolink.go +++ b/server/public/shared/markdown/autolink.go @@ -58,7 +58,7 @@ func parseWWWAutolink(data string, position int) (Range, bool) { func isAllowedBeforeWWWLink(c byte) bool { switch c { - case '*', '_', '~', ')': + case '*', '_', '~', ')', '<', '(', '>': return true } return false @@ -174,9 +174,9 @@ func trimTrailingCharactersFromLink(markdown string, start int, end int) int { runes := []rune(markdown[start:end]) linkEnd := len(runes) - // Cut off the link before an open angle bracket if it contains one + // Cut off the link before an angle bracket if it contains one for i, c := range runes { - if c == '<' { + if c == '<' || c == '>' { linkEnd = i break } diff --git a/server/public/shared/markdown/autolink_test.go b/server/public/shared/markdown/autolink_test.go index 2ab7dc1587..9026e4b85d 100644 --- a/server/public/shared/markdown/autolink_test.go +++ b/server/public/shared/markdown/autolink_test.go @@ -125,7 +125,7 @@ func TestParseURLAutolink(t *testing.T) { Expected: "https://example.com", }, { - Description: "link with angle brackets", + Description: "link in html tags", Input: "We use http://example.com", Position: 14, Expected: "http://example.com", @@ -142,6 +142,138 @@ func TestParseURLAutolink(t *testing.T) { Position: 1000, Expected: "", }, + { + Description: "no link with angle brackets", + Input: "This is an <:emoji:>", + Position: 15, + Expected: "", + }, + { + Description: "link with http in angle brackets", + Input: " and some text", + Position: 5, + Expected: "http://example.com", + }, + { + Description: "link with https in angle brackets", + Input: " and some text", + Position: 6, + Expected: "https://example.com", + }, + { + Description: "link with ftp in angle brackets", + Input: " and some text", + Position: 4, + Expected: "ftp://example.com", + }, + { + Description: "link with a path in angle brackets", + Input: " and some text", + Position: 6, + Expected: "https://example.com/abcd", + }, + { + Description: "link with parameters in angle brackets", + Input: " and some text", + Position: 4, + Expected: "ftp://example.com/abcd?foo=bar", + }, + { + Description: "link, not at start in angle brackets", + Input: "This is and some text", + Position: 14, + Expected: "https://example.com", + }, + { + Description: "link with a path, not at start in angle brackets", + Input: "This is also and some text", + Position: 18, + Expected: "http://www.example.com/abcd", + }, + { + Description: "link with parameters, not at start in angle brackets", + Input: "These are and some text", + Position: 16, + Expected: "https://www.example.com/abcd?foo=bar", + }, + { + Description: "link with trailing characters in angle brackets", + Input: "This is ", + Position: 12, + Expected: "ftp://www.example.com", + }, + { + Description: "multiple links in angle brackets", + Input: "This is and ", + Position: 14, + Expected: "https://example.com/abcd", + }, + { + Description: "second of multiple links in angle brackets", + Input: "This is and ", + Position: 43, + Expected: "ftp://www.example.com/1234", + }, + { + Description: "multiple links with first one in angle brackets", + Input: "This is and ftp://www.example.com/1234", + Position: 14, + Expected: "https://example.com/abcd", + }, + { + Description: "second of multiple links with first one in angle brackets", + Input: "This is and ftp://www.example.com/1234", + Position: 42, + Expected: "ftp://www.example.com/1234", + }, + { + Description: "multiple links with second one in angle brackets", + Input: "This is https://example.com/abcd and ", + Position: 13, + Expected: "https://example.com/abcd", + }, + { + Description: "second of multiple links with second one in angle brackets", + Input: "This is https://example.com/abcd and ", + Position: 41, + Expected: "ftp://www.example.com/1234", + }, + { + Description: "link with brackets wrapped in angle brackets", + Input: "Go to and some text", + Position: 10, + Expected: "ftp://www.example.com/my/page_(disambiguation)", + }, + { + Description: "link with angle brackets in parenthesis", + Input: "()", + Position: 7, + Expected: "https://www.example.com/foo/bar", + }, + { + Description: "link with angle brackets in underscores", + Input: "__", + Position: 6, + Expected: "http://www.example.com", + }, + { + Description: "link with angle brackets in asterisks", + Input: "This is ****", + Position: 14, + Expected: "ftp://example.com", + }, + { + Description: "link with angle brackets in strikethrough", + Input: "Those were ~~~~", + Position: 19, + Expected: "https://example.com", + }, + { + Description: "link with angle brackets in html tags", + Input: "We use ", + Position: 15, + Expected: "http://example.com", + }, } for _, testCase := range testCases { @@ -275,11 +407,221 @@ func TestParseWWWAutolink(t *testing.T) { Expected: "www1.example.com/foo", }, { - Description: "link with angle brackets", + Description: "link in html tags", Input: "We use www2.example.com", Position: 10, Expected: "www2.example.com", }, + { + Description: "no link, text with angle brackets", + Input: "This is some <> text", + Position: 0, + Expected: "", + }, + { + Description: "link with angle brackets", + Input: " and some text", + Position: 1, + Expected: "www.example.com", + }, + { + Description: "link with a path in angle brackets", + Input: " and some text", + Position: 1, + Expected: "www.example.com/abcd", + }, + { + Description: "link with parameters in angle brackets", + Input: " and some text", + Position: 1, + Expected: "www.example.com/abcd?foo=bar", + }, + { + Description: "link in angle brackets, not at start", + Input: "This is and some text", + Position: len("This is <"), + Expected: "www.example.com", + }, + { + Description: "link with a path in angle brackets, not at start", + Input: "This is also and some text", + Position: len("This is also <"), + Expected: "www.example.com/abcd", + }, + { + Description: "link with parameters in angle brackets, not at start", + Input: "These are and some text", + Position: len("These are <"), + Expected: "www.example.com/abcd?foo=bar", + }, + { + Description: "link with trailing characters in angle brackets", + Input: "This is ", + Position: len("This is <"), + Expected: "www.example.com", + }, + { + Description: "link in angle brackets after current position", + Input: "This is some text and ", + Position: 0, + Expected: "", + }, + { + Description: "multiple links with angle brackets", + Input: "This is and ", + Position: len("This is <"), + Expected: "www.example.com/abcd", + }, + { + Description: "multiple links 2 with angle brackets", + Input: "This is and ", + Position: len("This is and <"), + Expected: "www.example.com/1234", + }, + { + Description: "multiple links, last one with angle brackets", + Input: "This is www.example.com/abcd and ", + Position: len("This is "), + Expected: "www.example.com/abcd", + }, + { + Description: "multiple links, last one with angle brackets 2", + Input: "This is www.example.com/abcd and ", + Position: len("This is www.example.com/abcd and <"), + Expected: "www.example.com/1234", + }, + { + Description: "multiple links, first one with angle brackets", + Input: "This is and www.example.com/1234", + Position: len("This is <"), + Expected: "www.example.com/abcd", + }, + { + Description: "multiple links, first one with angle brackets 2", + Input: "This is and www.example.com/1234", + Position: len("This is and "), + Expected: "www.example.com/1234", + }, + { + Description: "link with brackets in angle brackets", + Input: "Go to and some text", + Position: 7, + Expected: "www.example.com/my/page_(disambiguation)", + }, + { + Description: "link in angle brackets following other letters", + Input: "aaa and some text", + Position: 4, + Expected: "www.example.com", + }, + { + Description: "link with angle brackets in parentheses", + Input: "()", + Position: 2, + Expected: "www.example.com", + }, + { + Description: "link with angle brackets in underscores", + Input: "__", + Position: 2, + Expected: "www.example.com", + }, + { + Description: "link with angle brackets in asterisks", + Input: "This is ****", + Position: len("This is **<"), + Expected: "www.example.com", + }, + { + Description: "link with angle brackets in strikethrough", + Input: "Those were ~~~~", + Position: len("Those were ~~<"), + Expected: "www.example.com", + }, + { + Description: "link using www1 in angle brackets", + Input: "Our backup site is at ", + Position: len("Our backup site is at <"), + Expected: "www1.example.com/foo", + }, + { + Description: "link with angle brackets in html tags", + Input: "We use ", + Position: len("We use <"), + Expected: "www2.example.com", + }, + { + Description: "link with only opening angle bracket", + Input: " and some text", + Position: 0, + Expected: "www.example.com", + }, + { + Description: "link with only opening bracket", + Input: "(www.example.com", + Position: 1, + Expected: "www.example.com", + }, + { + Description: "link with only closing bracket", + Input: "www.example.com)", + Position: 0, + Expected: "www.example.com", + }, + { + Description: "link with underscore at only start", + Input: "_www.example.com", + Position: 1, + Expected: "www.example.com", + }, + { + Description: "link with underscore at only end", + Input: "www.example.com_", + Position: 0, + Expected: "www.example.com", + }, + { + Description: "link with asterisks at only start", + Input: "This is **www.example.com", + Position: 10, + Expected: "www.example.com", + }, + { + Description: "link with asterisks at only end", + Input: "This is www.example.com**", + Position: 8, + Expected: "www.example.com", + }, + { + Description: "link with tildes at only start", + Input: "Those were ~~www.example.com", + Position: 13, + Expected: "www.example.com", + }, + { + Description: "link with tildes at only end", + Input: "Those were www.example.com~~", + Position: 11, + Expected: "www.example.com", + }, + { + Description: "link with only opening html tag", + Input: "We use www2.example.com", + Position: 10, + Expected: "www2.example.com", + }, + { + Description: "link with only closing html tag", + Input: "We use www2.example.com", + Position: 7, + Expected: "www2.example.com", + }, } for _, testCase := range testCases { @@ -422,6 +764,28 @@ func TestTrimTrailingCharactersFromLink(t *testing.T) { End: len("http://🍄.ga/ http://x🍄.ga/"), ExpectedEnd: len("http://🍄.ga/ http://x🍄.ga/"), }, + { + Input: "", + Start: 1, + ExpectedEnd: 23, + }, + { + Input: "", + Start: 1, + ExpectedEnd: 23, + }, + { + Input: "this is a sentence containing in it", + Start: len("this is a sentence containing <"), + End: len("this is a sentence containing "), + ExpectedEnd: 53, + }, + { + Input: "this is a sentence containing ", + Start: len("this is a sentence containing <"), + End: len("this is a sentence containing "), + ExpectedEnd: 53, + }, } for _, testCase := range testCases { @@ -699,3 +1063,90 @@ func TestAutolinking(t *testing.T) { }) } } + +func Test_isAllowedBeforeWWWLink(t *testing.T) { + type args struct { + c byte + } + tests := []struct { + name string + args args + want bool + }{ + { + name: "empty space", + args: args{ + c: byte(' '), + }, + want: false, + }, + { + name: "asterisk", + args: args{ + c: byte('*'), + }, + want: true, + }, + { + name: "underscore", + args: args{ + c: byte('_'), + }, + want: true, + }, + { + name: "open parenthesis", + args: args{ + c: byte('('), + }, + want: true, + }, + { + name: "close parenthesis", + args: args{ + c: byte(')'), + }, + want: true, + }, + { + name: "tilde", + args: args{ + c: byte('~'), + }, + want: true, + }, + { + name: "open angle bracket", + args: args{ + c: byte('<'), + }, + want: true, + }, + { + name: "close angle bracket", + args: args{ + c: byte('>'), + }, + want: true, + }, + { + name: "alphabet", + args: args{ + c: byte('c'), + }, + want: false, + }, + { + name: "number", + args: args{ + c: byte('4'), + }, + want: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + assert.Equalf(t, tt.want, isAllowedBeforeWWWLink(tt.args.c), "isAllowedBeforeWWWLink(%v)", tt.args.c) + }) + } +}