diff options
| author | test@dangofactory.cos <you@example.com> | 2026-08-20 16:57:18 +0000 |
|---|---|---|
| committer | test@dangofactory.cos <you@example.com> | 2026-08-20 16:57:18 +0000 |
| commit | 50ce5d5e6cd0d720ff437d35eace545816848428 (patch) | |
| tree | b59af206a36265cf081945e434900b32b0c43edd /app/html-cleaner/src/test/java | |
| download | monocles_mail-50ce5d5e6cd0d720ff437d35eace545816848428.tar.gz monocles_mail-50ce5d5e6cd0d720ff437d35eace545816848428.tar.bz2 monocles_mail-50ce5d5e6cd0d720ff437d35eace545816848428.zip | |
Inital
Diffstat (limited to 'app/html-cleaner/src/test/java')
| -rw-r--r-- | app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt | 516 |
1 files changed, 516 insertions, 0 deletions
diff --git a/app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt b/app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt new file mode 100644 index 0000000..f783e39 --- /dev/null +++ b/app/html-cleaner/src/test/java/app/k9mail/html/cleaner/HtmlSanitizerTest.kt @@ -0,0 +1,516 @@ +package app.k9mail.html.cleaner + +import assertk.assertThat +import assertk.assertions.isEqualTo +import org.jsoup.nodes.Document +import org.junit.Test + +class HtmlSanitizerTest { + private val htmlSanitizer = HtmlSanitizer() + + @Test + fun shouldRemoveMetaRefreshInHead() { + val html = + """ + <html> + <head><meta http-equiv="refresh" content="1; URL=http://example.com/"></head> + <body>Message</body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head></head><body>Message</body></html>") + } + + @Test + fun shouldRemoveMetaRefreshBetweenHeadAndBody() { + val html = + """ + <html> + <head></head> + <meta http-equiv="refresh" content="1; URL=http://example.com/"> + <body>Message</body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head></head><body>Message</body></html>") + } + + @Test + fun shouldRemoveMetaRefreshInBody() { + val html = + """ + <html> + <head></head> + <body><meta http-equiv="refresh" content="1; URL=http://example.com/">Message</body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head></head><body>Message</body></html>") + } + + @Test + fun shouldRemoveMetaRefreshWithUpperCaseAttributeValue() { + val html = + """ + <html> + <head><meta http-equiv="REFRESH" content="1; URL=http://example.com/"></head> + <body>Message</body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head></head><body>Message</body></html>") + } + + @Test + fun shouldRemoveMetaRefreshWithMixedCaseAttributeValue() { + val html = + """ + <html> + <head><meta http-equiv="Refresh" content="1; URL=http://example.com/"></head> + <body>Message</body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head></head><body>Message</body></html>") + } + + @Test + fun shouldRemoveMetaRefreshWithoutQuotesAroundAttributeValue() { + val html = + """ + <html> + <head><meta http-equiv=refresh content="1; URL=http://example.com/"></head> + <body>Message</body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head></head><body>Message</body></html>") + } + + @Test + fun shouldRemoveMetaRefreshWithSpacesInAttributeValue() { + val html = + """ + <html> + <head><meta http-equiv="refresh " content="1; URL=http://example.com/"></head> + <body>Message</body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head></head><body>Message</body></html>") + } + + @Test + fun shouldRemoveMultipleMetaRefreshTags() { + val html = + """ + <html> + <head><meta http-equiv="refresh" content="1; URL=http://example.com/"></head> + <body><meta http-equiv="refresh" content="1; URL=http://example.com/">Message</body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head></head><body>Message</body></html>") + } + + @Test + fun shouldRemoveMetaRefreshButKeepOtherMetaTags() { + val html = + """ + <html> + <head> + <meta http-equiv="content-type" content="text/html; charset=UTF-8"> + <meta http-equiv="refresh" content="1; URL=http://example.com/"> + </head> + <body>Message</body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo( + """ + <html> + <head><meta http-equiv="content-type" content="text/html; charset=UTF-8"></head> + <body>Message</body> + </html> + """.trimIndent().trimLineBreaks() + ) + } + + @Test + fun shouldProduceValidHtmlFromHtmlWithXmlDeclaration() { + val html = + """ + <?xml version="1.0" encoding="UTF-8"?> + <html> + <head></head> + <body></body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head></head><body></body></html>") + } + + @Test + fun shouldNormalizeTables() { + val html = "<html><head></head><body><table><tr><td></td><td></td></tr></table></body></html>" + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo( + "<html><head></head><body><table><tbody><tr><td></td><td></td></tr></tbody></table></body></html>" + ) + } + + @Test + fun shouldHtmlEncodeXmlDirectives() { + val html = + """ + <html> + <head></head> + <body> + <table><tr><td><!==><!==>Hmailserver service shutdown:</td><td><!==><!==>Ok</td></tr></table> + </body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo( + """ + <html> + <head></head> + <body><table><tbody><tr><td>Hmailserver service shutdown:</td><td>Ok</td></tr></tbody></table></body> + </html> + """.trimIndent().trimLineBreaks() + ) + } + + @Test + fun shouldKeepHrTags() { + val html = "<html><head></head><body>one<hr>two<hr />three</body></html>" + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head></head><body>one<hr>two<hr>three</body></html>") + } + + @Test + fun shouldKeepInsDelTags() { + val html = "<html><head></head><body><ins>Inserted</ins><del>Deleted</del></body></html>" + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo(html) + } + + @Test + fun shouldKeepMapAreaTags() { + val html = + """ + <html> + <head></head> + <body> + <map name="planetmap"> + <area shape="rect" coords="0,0,82,126" href="http://domain.com/sun.htm" alt="Sun"> + <area shape="circle" coords="90,58,3" href="http://domain.com/mercur.htm" alt="Mercury"> + <area shape="circle" coords="124,58,8" href="http://domain.com/venus.htm" alt="Venus"> + </map> + </body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo(html) + } + + @Test + fun shouldKeepImgUsemap() { + val html = + """ + <html> + <head></head> + <body><img src="http://domain.com/image.jpg" usemap="#planetmap"></body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo(html) + } + + @Test + fun shouldKeepAllowedElementsInHeadAndSkipTheRest() { + val html = + """ + <html> + <head> + <title>remove this</title> + <style>keep this</style> + <script>remove this</script> + </head> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head><style>keep this</style></head><body></body></html>") + } + + @Test + fun shouldRemoveIFrames() { + val html = """<html><body><iframe src="http://www.google.com" /></body></html>""" + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo("<html><head></head><body></body></html>") + } + + @Test + fun shouldKeepFormattingTags() { + val html = """<html><body><center><font face="Arial" color="red" size="12">A</font></center></body></html>""" + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo( + """ + <html> + <head></head> + <body><center><font face="Arial" color="red" size="12">A</font></center></body> + </html> + """.trimIndent().trimLineBreaks() + ) + } + + // This test will fail when jsoup updates its list of allowed "protocols" for the a.href attribute. + // When that happens, please adjust the removeProtocols("a", "href", …) line in BodyCleaner. + @Test + fun shouldKeepUris() { + val html = + """ + <html> + <body> + <a href="http://example.com/index.html">HTTP</a> + <a href="https://example.com/default.html">HTTPS</a> + <a href="mailto:user@example.com">Mailto</a> + <a href="tel:00442079460111">Telephone</a> + <a href="sms:00442079460111">SMS</a> + <a href="sip:user@example.com">SIP</a> + <a href="unknown:foobar">Unknown</a> + <a href="rtsp://example.com/media.mp4">RTSP</a> + </body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo( + """ + <html> + <head></head> + <body> + <a href="http://example.com/index.html">HTTP</a> + <a href="https://example.com/default.html">HTTPS</a> + <a href="mailto:user@example.com">Mailto</a> + <a href="tel:00442079460111">Telephone</a> + <a href="sms:00442079460111">SMS</a> + <a href="sip:user@example.com">SIP</a> + <a href="unknown:foobar">Unknown</a> + <a href="rtsp://example.com/media.mp4">RTSP</a> + </body> + </html> + """.trimIndent().trimLineBreaks() + ) + } + + @Test + fun shouldKeepDirAttribute() { + val html = + """ + <html> + <head></head> + <body><table><tbody><tr><td dir="rtl"></td></tr></tbody></table></body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo(html) + } + + @Test + fun shouldKeepAllowedBodyAttributes() { + val html = + """ + <html> + <body style="color: #fff" onload="alert()" class="body" id></body> + </html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo( + """ + <html> + <head></head> + <body style="color: #fff" class="body" id></body> + </html> + """.trimIndent().trimLineBreaks() + ) + } + + @Test + fun `should keep HTML 5 doctype`() { + val html = + """ + <!doctype html> + <html><head></head><body>text</body></html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo(html) + } + + @Test + fun `should keep HTML 4_01 doctype`() { + val html = + """ + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> + <html><head></head><body>text</body></html> + """.trimIndent().trimLineBreaks() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo( + """ + <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> + <html><head></head><body>text</body></html> + """.trimIndent().trimLineBreaks() + ) + } + + @Test + fun `should keep 'align' attribute on 'div' element`() { + val html = """<div align="center">text</div>""" + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo( + """ + <html> + <head></head> + <body> + <div align="center">text</div> + </body> + </html> + """.trimIndent().trimLineBreaks() + ) + } + + @Test + fun `should keep 'name' attribute on 'a' element`() { + val html = """<a name="something">""" + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo( + """ + <html> + <head></head> + <body> + <a name="something"></a> + </body> + </html> + """.trimIndent().trimLineBreaks() + ) + } + + @Test + fun `should keep 'tt' element`() { + assertTagsNotStripped("tt") + } + + @Test + fun `should keep 'kbd' element`() { + assertTagsNotStripped("kbd") + } + + @Test + fun `should keep 'samp' element`() { + assertTagsNotStripped("samp") + } + + @Test + fun `should keep 'var' element`() { + assertTagsNotStripped("var") + } + + @Test + fun `should keep 'base' element`() { + val html = + """ + <html> + <head> + <base href="https://domain.example/"> + </head> + <body> + <a href="relative">Link</a> + </body> + </html> + """.compactHtml() + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo(html) + } + + private fun assertTagsNotStripped(element: String) { + val html = """<$element>some text</$element>""" + + val result = htmlSanitizer.sanitize(html) + + assertThat(result.toCompactString()).isEqualTo( + """ + <html> + <head></head> + <body> + <$element>some text</$element> + </body> + </html> + """.trimIndent().trimLineBreaks() + ) + } + + private fun Document.toCompactString(): String { + outputSettings() + .prettyPrint(false) + .indentAmount(0) + + return html() + } + + private fun String.trimLineBreaks() = replace("\n", "") + + private fun String.compactHtml() = lines().joinToString(separator = "") { it.trim() } +} |
