Bugfix 1459 - Escape ~ character as markdown (#1561)

* Add simple test for escaping markdown content in statuses

* Add ~ as markdown character to be escaped in statuses

The ~ isn't documented in the original markdown syntax docs but is
commonly used (including by AttributedString) to surround text
formatted with a strikethrough.
This commit is contained in:
Grant McSheffrey 2023-08-24 03:58:29 -04:00 committed by GitHub
parent 30f9da06c8
commit 077b0d269d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 33 additions and 9 deletions

View file

@ -37,10 +37,12 @@ public struct HTMLString: Codable, Equatable, Hashable, @unchecked Sendable {
if !alreadyDecoded { if !alreadyDecoded {
// https://daringfireball.net/projects/markdown/syntax // https://daringfireball.net/projects/markdown/syntax
// Pre-escape \ ` _ * and [ as these are the only // Pre-escape \ ` _ * ~ and [ as these are the only
// characters the markdown parser used picks up // characters the markdown parser uses when it renders
// when it renders to attributed text // to attributed text. Note that ~ for strikethrough is
main_regex = try? NSRegularExpression(pattern: "([\\*\\`\\[\\\\])", options: .caseInsensitive) // not documented in the syntax docs but is used by
// AttributedString.
main_regex = try? NSRegularExpression(pattern: "([\\*\\`\\~\\[\\\\])", options: .caseInsensitive)
// don't escape underscores that are between colons, they are most likely custom emoji // don't escape underscores that are between colons, they are most likely custom emoji
underscore_regex = try? NSRegularExpression(pattern: "(?!\\B:[^:]*)(_)(?![^:]*:\\B)", options: .caseInsensitive) underscore_regex = try? NSRegularExpression(pattern: "(?!\\B:[^:]*)(_)(?![^:]*:\\B)", options: .caseInsensitive)

View file

@ -66,4 +66,26 @@ final class HTMLStringTests: XCTestCase {
XCTAssertEqual("https://test.com/go%C3%9F%C3%AB%C3%B1a", htmlString.links[0].url.absoluteString) XCTAssertEqual("https://test.com/go%C3%9F%C3%AB%C3%B1a", htmlString.links[0].url.absoluteString)
XCTAssertEqual("test", htmlString.links[0].displayString) XCTAssertEqual("test", htmlString.links[0].displayString)
} }
func testHTMLStringInit_markdownEscaping() throws {
let decoder = JSONDecoder()
let stdMarkdownContent = "\"<p>This [*is*] `a`\\n**test**</p>\""
var htmlString = try decoder.decode(HTMLString.self, from: Data(stdMarkdownContent.utf8))
XCTAssertEqual("This [*is*] `a`\n**test**", htmlString.asRawText)
XCTAssertEqual("<p>This [*is*] `a`\n**test**</p>", htmlString.htmlValue)
XCTAssertEqual("This \\[\\*is\\*] \\`a\\` \\*\\*test\\*\\*", htmlString.asMarkdown)
let underscoreContent = "\"<p>This _is_ an :emoji_maybe:</p>\""
htmlString = try decoder.decode(HTMLString.self, from: Data(underscoreContent.utf8))
XCTAssertEqual("This _is_ an :emoji_maybe:", htmlString.asRawText)
XCTAssertEqual("<p>This _is_ an :emoji_maybe:</p>", htmlString.htmlValue)
XCTAssertEqual("This \\_is\\_ an :emoji_maybe:", htmlString.asMarkdown)
let strikeContent = "\"<p>This ~is~ a\\n`test`</p>\""
htmlString = try decoder.decode(HTMLString.self, from: Data(strikeContent.utf8))
XCTAssertEqual("This ~is~ a\n`test`", htmlString.asRawText)
XCTAssertEqual("<p>This ~is~ a\n`test`</p>", htmlString.htmlValue)
XCTAssertEqual("This \\~is\\~ a \\`test\\`", htmlString.asMarkdown)
}
} }