loading up the forgejo repo on tangled to test page performance
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Escaping specific markdown in commit messages on Discord-type embeds #3664 (#5811) Co-authored-by: Marco De Araujo <marco.araujo.junior@gmail.com> Co-committed-by: Marco De Araujo <marco.araujo.junior@gmail.com>

authored by

Marco De Araujo
Marco De Araujo
and committed by
Earl Warren
1f4be5ba cff77547

+145
+41
services/webhook/discord.go
··· 10 10 "html/template" 11 11 "net/http" 12 12 "net/url" 13 + "regexp" 13 14 "strconv" 14 15 "strings" 15 16 "unicode/utf8" ··· 202 203 // limit the commit message display to just the summary, otherwise it would be hard to read 203 204 message := strings.TrimRight(strings.SplitN(commit.Message, "\n", 1)[0], "\r") 204 205 206 + // Escaping markdown character 207 + message = escapeMarkdown(message) 208 + 205 209 // a limit of 50 is set because GitHub does the same 206 210 if utf8.RuneCountInString(message) > 50 { 207 211 message = fmt.Sprintf("%.47s...", message) ··· 365 369 }, 366 370 } 367 371 } 372 + 373 + var orderedListPattern = regexp.MustCompile(`(\d+)\.`) 374 + 375 + var markdownPatterns = map[string]*regexp.Regexp{ 376 + "~": regexp.MustCompile(`\~(.*?)\~`), 377 + "*": regexp.MustCompile(`\*(.*?)\*`), 378 + "_": regexp.MustCompile(`\_(.*?)\_`), 379 + } 380 + 381 + var markdownToEscape = strings.NewReplacer( 382 + "* ", "\\* ", 383 + "`", "\\`", 384 + "[", "\\[", 385 + "]", "\\]", 386 + "(", "\\(", 387 + ")", "\\)", 388 + "#", "\\#", 389 + "+ ", "\\+ ", 390 + "- ", "\\- ", 391 + "---", "\\---", 392 + "!", "\\!", 393 + "|", "\\|", 394 + "<", "\\<", 395 + ">", "\\>", 396 + ) 397 + 398 + // Escape Markdown characters 399 + func escapeMarkdown(input string) string { 400 + // Escaping ordered list 401 + output := orderedListPattern.ReplaceAllString(input, "$1\\.") 402 + 403 + for char, pattern := range markdownPatterns { 404 + output = pattern.ReplaceAllString(output, fmt.Sprintf(`\%s$1\%s`, char, char)) 405 + } 406 + 407 + return markdownToEscape.Replace(output) 408 + }
+100
services/webhook/discord_test.go
··· 94 94 assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) 95 95 }) 96 96 97 + t.Run("PushWithMarkdownCharactersInCommitMessage", func(t *testing.T) { 98 + p := pushTestEscapeCommitMessagePayload() 99 + 100 + pl, err := dc.Push(p) 101 + require.NoError(t, err) 102 + 103 + assert.Len(t, pl.Embeds, 1) 104 + assert.Equal(t, "[test/repo:test] 2 new commits", pl.Embeds[0].Title) 105 + assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) \\# conflicts\n\\# \\- some/conflicting/file.txt - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) \\# conflicts\n\\# \\- some/conflicting/file.txt - user1", pl.Embeds[0].Description) 106 + assert.Equal(t, p.Sender.UserName, pl.Embeds[0].Author.Name) 107 + assert.Equal(t, setting.AppURL+p.Sender.UserName, pl.Embeds[0].Author.URL) 108 + assert.Equal(t, p.Sender.AvatarURL, pl.Embeds[0].Author.IconURL) 109 + }) 110 + 97 111 t.Run("Issue", func(t *testing.T) { 98 112 p := issueTestPayload() 99 113 ··· 346 360 require.NoError(t, err) 347 361 assert.Equal(t, "[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1\n[2020558](http://localhost:3000/test/repo/commit/2020558fe2e34debb818a514715839cabd25e778) commit message - user1", body.Embeds[0].Description) 348 362 } 363 + 364 + var escapedMarkdownTests = map[string]struct { 365 + input string 366 + expected string 367 + }{ 368 + "Escape heading level 1": { 369 + input: "# Heading level 1", 370 + expected: "\\# Heading level 1", 371 + }, 372 + "Escape heading level 2": { 373 + input: "## Heading level 2", 374 + expected: "\\#\\# Heading level 2", 375 + }, 376 + "Escape heading level 3": { 377 + input: "### Heading level 3", 378 + expected: "\\#\\#\\# Heading level 3", 379 + }, 380 + "Escape bold text": { 381 + input: "**bold text**", 382 + expected: "\\*\\*bold text\\*\\*", 383 + }, 384 + "Escape italic text": { 385 + input: "*italic text*", 386 + expected: "\\*italic text\\*", 387 + }, 388 + "Escape italic text underline": { 389 + input: "_italic text_", 390 + expected: "\\_italic text\\_", 391 + }, 392 + "Escape strikethrough": { 393 + input: "~~strikethrough~~", 394 + expected: "\\~\\~strikethrough\\~\\~", 395 + }, 396 + "Escape Ordered list item": { 397 + input: "1. Ordered list item\n2. Second ordered list item\n999999999999. 999999999999 ordered list item", 398 + expected: "1\\. Ordered list item\n2\\. Second ordered list item\n999999999999\\. 999999999999 ordered list item", 399 + }, 400 + "Escape Unordered list item": { 401 + input: "- Unordered list\n + using plus", 402 + expected: "\\- Unordered list\n \\+ using plus", 403 + }, 404 + "Escape bullet list item": { 405 + input: "* Bullet list item", 406 + expected: "\\* Bullet list item", 407 + }, 408 + "Escape table": { 409 + input: "| Table | Example |\n|-|-|\n| Lorem | Ipsum |", 410 + expected: "\\| Table \\| Example \\|\n\\|-\\|-\\|\n\\| Lorem \\| Ipsum \\|", 411 + }, 412 + "Escape link": { 413 + input: "[Link to Forgejo](https://forgejo.org/)", 414 + expected: "\\[Link to Forgejo\\]\\(https://forgejo.org/\\)", 415 + }, 416 + "Escape Alt text for an image": { 417 + input: "![Alt text for an image](https://forgejo.org/_astro/mascot-dark.1omhhgvT_Zm0N2n.webp)", 418 + expected: "\\!\\[Alt text for an image\\]\\(https://forgejo.org/\\_astro/mascot-dark.1omhhgvT\\_Zm0N2n.webp\\)", 419 + }, 420 + "Escape URL if it has markdown character": { 421 + input: "https://forgejo.org/_astro/mascot-dark.1omhhgvT_Zm0N2n.webp", 422 + expected: "https://forgejo.org/\\_astro/mascot-dark.1omhhgvT\\_Zm0N2n.webp", 423 + }, 424 + "Escape blockquote text": { 425 + input: "> Blockquote text.", 426 + expected: "\\> Blockquote text.", 427 + }, 428 + "Escape inline code": { 429 + input: "`Inline code`", 430 + expected: "\\`Inline code\\`", 431 + }, 432 + "Escape multiple code": { 433 + input: "```\nCode block\nwith multiple lines\n```\n", 434 + expected: "\\`\\`\\`\nCode block\nwith multiple lines\n\\`\\`\\`\n", 435 + }, 436 + "Escape horizontal rule": { 437 + input: "---", 438 + expected: "\\---", 439 + }, 440 + } 441 + 442 + func TestEscapeMarkdownChar(t *testing.T) { 443 + for name, test := range escapedMarkdownTests { 444 + t.Run(name, func(t *testing.T) { 445 + assert.Equal(t, test.expected, escapeMarkdown(test.input)) 446 + }) 447 + } 448 + }
+4
services/webhook/general_test.go
··· 72 72 return pushTestPayloadWithCommitMessage("This is a commit summary ⚠️⚠️⚠️⚠️ containing 你好 ⚠️⚠️️\n\nThis is the message body.") 73 73 } 74 74 75 + func pushTestEscapeCommitMessagePayload() *api.PushPayload { 76 + return pushTestPayloadWithCommitMessage("# conflicts\n# - some/conflicting/file.txt") 77 + } 78 + 75 79 func pushTestPayloadWithCommitMessage(message string) *api.PushPayload { 76 80 commit := &api.PayloadCommit{ 77 81 ID: "2020558fe2e34debb818a514715839cabd25e778",