HTML: Fully escape all required entities (#89)

Both when outputting HTML, and when highlighting code blocks within a
Markdown text, Splash now fully escapes all required character entities.
This commit is contained in:
John Sundell 2019-11-23 23:59:16 +00:00 committed by GitHub
parent 3a6c961de8
commit 3de0275b52
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 75 additions and 12 deletions

View File

@ -0,0 +1,24 @@
/**
* Splash
* Copyright (c) John Sundell 2019
* MIT license - see LICENSE.md
*/
import Foundation
internal extension StringProtocol {
func escapingHTMLEntities() -> String {
return String(flatMap { character -> String in
switch character {
case "&":
return "&"
case "<":
return "&lt;"
case ">":
return "&gt;"
default:
return String(character)
}
})
}
}

View File

@ -51,7 +51,7 @@ public extension HTMLOutputFormat {
public mutating func addPlainText(_ text: String) {
appendPending()
html.append(text.escaped)
html.append(text.escapingHTMLEntities())
}
public mutating func addWhitespace(_ whitespace: String) {
@ -70,7 +70,7 @@ public extension HTMLOutputFormat {
private mutating func appendPending() {
if let pending = pendingToken {
html.append("""
<span class="\(classPrefix)\(pending.type.string)">\(pending.string.escaped)</span>
<span class="\(classPrefix)\(pending.type.string)">\(pending.string.escapingHTMLEntities())</span>
""")
pendingToken = nil
@ -83,10 +83,3 @@ public extension HTMLOutputFormat {
}
}
}
private extension String {
var escaped: String {
return replacingOccurrences(of: "<", with: "&lt;")
.replacingOccurrences(of: ">", with: "&gt;")
}
}

View File

@ -36,7 +36,7 @@ public struct MarkdownDecorator {
if code.hasPrefix(skipHighlightingPrefix) {
let charactersToDrop = skipHighlightingPrefix + "\n"
code = String(code.dropFirst(charactersToDrop.count))
code = code.dropFirst(charactersToDrop.count).escapingHTMLEntities()
} else {
code = highlighter.highlight(code)
}

View File

@ -49,13 +49,57 @@ final class MarkdownTests: SplashTestCase {
struct Hello: Protocol {}
```
Text.
"""
let expectedResult = """
Text text.
<pre class="splash"><code>struct Hello: Protocol {}</code></pre>
Text.
"""
XCTAssertEqual(decorator.decorate(markdown), expectedResult)
}
func testEscapingSpecialCharactersWithinHighlightedCodeBlock() {
let markdown = """
Text text.
```
let a = "<Hello&World>"
```
Text.
"""
let expectedResult = """
Text text.
<pre class="splash"><code>struct Hello: Protocol {}</code></pre>
<pre class="splash"><code><span class="keyword">let</span> a = <span class="string">"&lt;Hello&amp;World&gt;"</span></code></pre>
Text.
"""
XCTAssertEqual(decorator.decorate(markdown), expectedResult)
}
func testEscapingSpecialCharactersWithinSkippedCodeBlock() {
let markdown = """
Text text.
```no-highlight
let a = "<Hello&World>"
```
Text.
"""
let expectedResult = """
Text text.
<pre class="splash"><code>let a = "&lt;Hello&amp;World&gt;"</code></pre>
Text.
"""
@ -72,7 +116,9 @@ extension MarkdownTests {
static var allTests: [(String, TestClosure<MarkdownTests>)] {
return [
("testConvertingCodeBlock", testConvertingCodeBlock),
("testSkippingHighlightingForCodeBlock", testSkippingHighlightingForCodeBlock)
("testSkippingHighlightingForCodeBlock", testSkippingHighlightingForCodeBlock),
("testEscapingSpecialCharactersWithinHighlightedCodeBlock", testEscapingSpecialCharactersWithinHighlightedCodeBlock),
("testEscapingSpecialCharactersWithinSkippedCodeBlock", testEscapingSpecialCharactersWithinSkippedCodeBlock)
]
}
}