Skip to content

Commit 08c7f69

Browse files
committed
Replaced grails markdown plugin with commonmark #3419
1 parent 54f84c8 commit 08c7f69

File tree

5 files changed

+97
-33
lines changed

5 files changed

+97
-33
lines changed

Diff for: grails-app/services/au/org/ala/merit/EmailService.groovy

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package au.org.ala.merit
22

33
import au.org.ala.merit.config.EmailTemplate
4+
import au.org.ala.merit.util.MarkdownUtils
45
import groovy.util.logging.Slf4j
56

67

@@ -17,7 +18,8 @@ class EmailService {
1718
def systemEmailAddress = grailsApplication.config.getProperty('fieldcapture.system.email.address')
1819
try {
1920
def subjectLine = settingService.getSettingText(mailSubjectTemplate, model)
20-
def body = settingService.getSettingText(mailTemplate, model).markdownToHtml()
21+
String bodyMarkdown = settingService.getSettingText(mailTemplate, model)
22+
String body = MarkdownUtils.markdownToHtmlAndSanitise(bodyMarkdown)
2123

2224
log.info("Sending email: ${subjectLine} to: ${recipient}, from: ${sender}, cc:${ccList}, body: ${body}")
2325
// This is to prevent spamming real users while testing.

Diff for: grails-app/taglib/au/org/ala/merit/FCTagLib.groovy

+3-28
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package au.org.ala.merit
22

33
import au.org.ala.cas.util.AuthenticationCookieUtils
44
import au.org.ala.merit.config.ProgramConfig
5+
import au.org.ala.merit.util.MarkdownUtils
56
import au.org.ala.web.AuthService
67
import bootstrap.Attribute
78
import grails.converters.JSON
@@ -11,12 +12,6 @@ import groovy.xml.MarkupBuilder
1112
import org.apache.commons.lang.WordUtils
1213
import org.grails.web.json.JSONArray
1314
import org.grails.web.json.JSONObject
14-
import org.owasp.html.HtmlChangeListener
15-
import org.owasp.html.HtmlPolicyBuilder
16-
import org.owasp.html.PolicyFactory
17-
import org.owasp.html.Sanitizers
18-
import org.commonmark.parser.Parser
19-
import org.commonmark.renderer.html.HtmlRenderer
2015

2116
@Slf4j
2217
class FCTagLib {
@@ -27,10 +22,6 @@ class FCTagLib {
2722
def userService
2823
def settingService
2924
AuthService authService
30-
MetadataService metadataService
31-
32-
/** Allow simple formatting, links and text within p and divs by default */
33-
def policy = (Sanitizers.FORMATTING & Sanitizers.LINKS & Sanitizers.BLOCKS) & new HtmlPolicyBuilder().allowTextIn("p", "div").toFactory()
3425

3526
def textField = { attrs ->
3627
def outerClass = attrs.remove 'outerClass'
@@ -1170,27 +1161,11 @@ class FCTagLib {
11701161
def markdownToHtml = { Map attrs, body ->
11711162
String text = attrs.text ?: body()
11721163

1173-
out << markdownToHtmlAndSanitise(text)
1164+
out << MarkdownUtils.markdownToHtmlAndSanitise(text)
11741165
}
11751166

11761167
private String markdownToHtmlAndSanitise(String text) {
1177-
Parser parser = Parser.builder().build()
1178-
org.commonmark.node.Node document = parser.parse(text)
1179-
HtmlRenderer renderer = HtmlRenderer.builder().build()
1180-
String html = renderer.render(document)
1181-
1182-
internalSanitise(policy, html)
1183-
}
1184-
1185-
private static String internalSanitise(PolicyFactory policyFactory, String input, String imageId = '', String metadataName = '') {
1186-
policyFactory.sanitize(input, new HtmlChangeListener<Object>() {
1187-
void discardedTag(Object context, String elementName) {
1188-
log.warn("Dropping element $elementName in $imageId.$metadataName")
1189-
}
1190-
void discardedAttributes(Object context, String tagName, String... attributeNames) {
1191-
log.warn("Dropping attributes $attributeNames from $tagName in $imageId.$metadataName")
1192-
}
1193-
}, null)
1168+
MarkdownUtils.markdownToHtmlAndSanitise(text)
11941169
}
11951170

11961171
private static String getScoreLabels(def scoreIds, ProgramConfig config, Boolean includeService) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package au.org.ala.merit.util
2+
3+
import groovy.transform.CompileStatic
4+
import groovy.util.logging.Slf4j
5+
import org.commonmark.parser.Parser
6+
import org.commonmark.renderer.html.HtmlRenderer
7+
import org.owasp.html.HtmlChangeListener
8+
import org.owasp.html.HtmlPolicyBuilder
9+
import org.owasp.html.PolicyFactory
10+
import org.owasp.html.Sanitizers
11+
12+
@CompileStatic
13+
@Slf4j
14+
class MarkdownUtils {
15+
16+
/** Allow simple formatting, links and text within p and divs by default */
17+
static PolicyFactory policy = (Sanitizers.FORMATTING & Sanitizers.LINKS & Sanitizers.BLOCKS) & new HtmlPolicyBuilder().allowTextIn("p", "div").toFactory()
18+
19+
static String markdownToHtmlAndSanitise(String text) {
20+
Parser parser = Parser.builder().build()
21+
org.commonmark.node.Node document = parser.parse(text)
22+
HtmlRenderer renderer = HtmlRenderer.builder().build()
23+
String html = renderer.render(document)
24+
25+
internalSanitise(policy, html)
26+
}
27+
28+
private static String internalSanitise(PolicyFactory policyFactory, String input, String imageId = '', String metadataName = '') {
29+
policyFactory.sanitize(input, new HtmlChangeListener<Object>() {
30+
void discardedTag(Object context, String elementName) {
31+
log.warn("Dropping element $elementName in $imageId.$metadataName")
32+
}
33+
void discardedAttributes(Object context, String tagName, String... attributeNames) {
34+
log.warn("Dropping attributes $attributeNames from $tagName in $imageId.$metadataName")
35+
}
36+
}, null)
37+
}
38+
39+
}

Diff for: src/test/groovy/au/org/ala/merit/EmailServiceSpec.groovy

+2-4
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ class EmailServiceSpec extends Specification implements AutowiredTest{
5454
List usersAndRoles = [admin1, grantManager1, editor]
5555
EmailTemplate emailTemplate = EmailTemplate.DEFAULT_PLAN_SUBMITTED_EMAIL_TEMPLATE
5656
String body = "body"
57-
body.metaClass.markdownToHtml = { "Body" }
5857
EmailParams email
5958

6059
when:
@@ -69,7 +68,7 @@ class EmailServiceSpec extends Specification implements AutowiredTest{
6968
email.params.from == "merit@ala.org.au"
7069
email.params.replyTo == "merituser1@test.com"
7170
email.params.subject == "Subject"
72-
email.params.html == "Body"
71+
email.params.html == "<p>body</p>\n"
7372
}
7473

7574

@@ -88,7 +87,6 @@ class EmailServiceSpec extends Specification implements AutowiredTest{
8887
List usersAndRoles = [admin1, admin2, editor]
8988
EmailTemplate emailTemplate = EmailTemplate.DEFAULT_PLAN_APPROVED_EMAIL_TEMPLATE
9089
String body = "body"
91-
body.metaClass.markdownToHtml = { "Body" }
9290
EmailParams email
9391

9492
when:
@@ -102,7 +100,7 @@ class EmailServiceSpec extends Specification implements AutowiredTest{
102100
email.params.from == "merit@ala.org.au"
103101
email.params.replyTo == "merituser1@test.com"
104102
email.params.subject == "Subject"
105-
email.params.html == "Body"
103+
email.params.html == "<p>body</p>\n"
106104

107105
}
108106

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
package au.org.ala.merit.util
2+
3+
import spock.lang.Specification
4+
5+
class MarkdownUtilsSpec extends Specification {
6+
7+
def "markdownToHtmlAndSanitise should convert markdown to HTML and sanitize it"() {
8+
given:
9+
String markdown = "# Heading\n\nThis is a [link](http://example.com)."
10+
11+
when:
12+
String result = MarkdownUtils.markdownToHtmlAndSanitise(markdown)
13+
14+
then:
15+
result == "<h1>Heading</h1>\n<p>This is a <a href=\"http://example.com\" rel=\"nofollow\">link</a>.</p>\n"
16+
}
17+
18+
def "markdownToHtmlAndSanitise should remove disallowed tags"() {
19+
given:
20+
String markdown = "<script>alert('XSS');</script>"
21+
22+
when:
23+
String result = MarkdownUtils.markdownToHtmlAndSanitise(markdown)
24+
25+
then:
26+
result == "\n"
27+
}
28+
29+
def "markdownToHtmlAndSanitise should allow simple formatting"() {
30+
given:
31+
String markdown = "**bold** *italic*"
32+
33+
when:
34+
String result = MarkdownUtils.markdownToHtmlAndSanitise(markdown)
35+
36+
then:
37+
result == "<p><strong>bold</strong> <em>italic</em></p>\n"
38+
}
39+
40+
def "markdownToHtmlAndSanitise should allow text within p and div tags"() {
41+
given:
42+
String markdown = "<p>Paragraph</p><div>Division</div>"
43+
44+
when:
45+
String result = MarkdownUtils.markdownToHtmlAndSanitise(markdown)
46+
47+
then:
48+
result == "<p>Paragraph</p><div>Division</div>\n"
49+
}
50+
}

0 commit comments

Comments
 (0)