Skip to content

Commit ed82f96

Browse files
authored
Email: Mark HTML comments as "safe" in email templates (grafana#64546)
1 parent 48f5825 commit ed82f96

File tree

13 files changed

+304
-255
lines changed

13 files changed

+304
-255
lines changed

emails/Makefile

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,18 @@
1-
build: build-html build-txt
1+
build: clean build-mjml build-grunt
22

3-
build-html:
3+
clean:
4+
rm -rf dist/
5+
mkdir dist/
6+
7+
build-mjml:
48
npx mjml \
59
--config.beautify true \
610
--config.minify false \
711
--config.validationLevel=strict \
812
--config.keepComments=false \
9-
./templates/*.mjml --output ../public/emails/
13+
./templates/*.mjml --output ./dist/
1014

11-
build-txt:
15+
build-grunt:
1216
npx grunt
1317

14-
.PHONY: build build-html build-txt
18+
.PHONY: clean build build-mjml build-grunt

emails/grunt/aliases.yaml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
default:
2-
- 'clean'
32
- 'assemble'
43
- 'replace'
54
- 'copy'

emails/grunt/clean.js

Lines changed: 0 additions & 5 deletions
This file was deleted.

emails/grunt/copy.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,11 @@ module.exports = function () {
77
src: ['**.txt'],
88
dest: '../public/emails/',
99
},
10+
html: {
11+
expand: true,
12+
cwd: 'dist',
13+
src: ['**.html'],
14+
dest: '../public/emails/',
15+
},
1016
};
1117
};

emails/grunt/replace.js

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,45 @@
1-
module.exports = {
2-
dist: {
3-
overwrite: true,
4-
src: ['dist/*.txt'],
5-
replacements: [
6-
{
7-
from: '[[',
8-
to: '{{',
9-
},
10-
{
11-
from: ']]',
12-
to: '}}',
13-
},
14-
],
15-
},
1+
module.exports = function () {
2+
'use strict';
3+
4+
return {
5+
txt,
6+
comments,
7+
};
8+
};
9+
10+
const txt = {
11+
overwrite: true,
12+
src: ['dist/*.txt'],
13+
replacements: [
14+
{
15+
from: '[[',
16+
to: '{{',
17+
},
18+
{
19+
from: ']]',
20+
to: '}}',
21+
},
22+
],
23+
};
24+
25+
/**
26+
* Replace all instances of HTML comments with {{ __dangerouslyInjectHTML "<!-- my comment -->" }}.
27+
*
28+
* MJML will output <!--[if !mso]><!--> comments which are specific to MS Outlook.
29+
*
30+
* Go's template/html package will strip all HTML comments and we need them to be preserved
31+
* to work with MS Outlook on the Desktop.
32+
*/
33+
const HTML_SAFE_FUNC = '__dangerouslyInjectHTML';
34+
const commentBlock = /(<!--[\s\S]*?-->)/g;
35+
36+
const comments = {
37+
overwrite: true,
38+
src: ['dist/*.html'],
39+
replacements: [
40+
{
41+
from: commentBlock,
42+
to: `{{ ${HTML_SAFE_FUNC} \`$1\` }}`,
43+
},
44+
],
1645
};

emails/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
"grunt": "1.5.3",
88
"grunt-assemble": "0.6.3",
99
"grunt-cli": "^1.4.3",
10-
"grunt-contrib-clean": "2.0.0",
1110
"grunt-contrib-copy": "^1.0.0",
1211
"grunt-contrib-watch": "1.1.0",
1312
"grunt-text-replace": "0.4.0",

pkg/services/notifications/notifications.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,9 @@ func ProvideService(bus bus.Bus, cfg *setting.Cfg, mailer Mailer, store TempUser
5454

5555
mailTemplates = template.New("name")
5656
mailTemplates.Funcs(template.FuncMap{
57-
"Subject": subjectTemplateFunc,
58-
"HiddenSubject": hiddenSubjectTemplateFunc,
57+
"Subject": subjectTemplateFunc,
58+
"HiddenSubject": hiddenSubjectTemplateFunc,
59+
"__dangerouslyInjectHTML": __dangerouslyInjectHTML,
5960
})
6061
mailTemplates.Funcs(sprig.FuncMap())
6162

@@ -174,6 +175,17 @@ func subjectTemplateFunc(obj map[string]interface{}, data map[string]interface{}
174175
return subj
175176
}
176177

178+
// __dangerouslyInjectHTML allows marking areas of am email template as HTML safe, this will _not_ sanitize the string and will allow HTML snippets to be rendered verbatim.
179+
// Use with absolute care as this _could_ allow for XSS attacks when used in an insecure context.
180+
//
181+
// It's safe to ignore gosec warning G203 when calling this function in an HTML template because we assume anyone who has write access
182+
// to the email templates folder is an administrator.
183+
//
184+
// nolint:gosec
185+
func __dangerouslyInjectHTML(s string) template.HTML {
186+
return template.HTML(s)
187+
}
188+
177189
func (ns *NotificationService) SendEmailCommandHandlerSync(ctx context.Context, cmd *SendEmailCommandSync) error {
178190
message, err := ns.buildEmailMessage(&SendEmailCommand{
179191
Data: cmd.Data,

public/emails/invited_to_org.html

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
<title>
66
{{ Subject .Subject .TemplateData "{{ .InvitedBy }} has added you to the {{ .OrgName }} organization" }}
77
</title>
8-
<!--[if !mso]><!-->
8+
{{ __dangerouslyInjectHTML `<!--[if !mso]><!-->` }}
99
<meta http-equiv="X-UA-Compatible" content="IE=edge">
10-
<!--<![endif]-->
10+
{{ __dangerouslyInjectHTML `<!--<![endif]-->` }}
1111
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
1212
<meta name="viewport" content="width=device-width, initial-scale=1">
1313
<style type="text/css">
@@ -44,7 +44,7 @@
4444
}
4545

4646
</style>
47-
<!--[if mso]>
47+
{{ __dangerouslyInjectHTML `<!--[if mso]>
4848
<noscript>
4949
<xml>
5050
<o:OfficeDocumentSettings>
@@ -53,19 +53,19 @@
5353
</o:OfficeDocumentSettings>
5454
</xml>
5555
</noscript>
56-
<![endif]-->
57-
<!--[if lte mso 11]>
56+
<![endif]-->` }}
57+
{{ __dangerouslyInjectHTML `<!--[if lte mso 11]>
5858
<style type="text/css">
5959
.mj-outlook-group-fix { width:100% !important; }
6060
</style>
61-
<![endif]-->
62-
<!--[if !mso]><!-->
61+
<![endif]-->` }}
62+
{{ __dangerouslyInjectHTML `<!--[if !mso]><!-->` }}
6363
<link href="https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700" rel="stylesheet" type="text/css">
6464
<style type="text/css">
6565
@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700);
6666

6767
</style>
68-
<!--<![endif]-->
68+
{{ __dangerouslyInjectHTML `<!--<![endif]-->` }}
6969
<style type="text/css">
7070
@media only screen and (min-width:480px) {
7171
.mj-column-per-100 {
@@ -100,13 +100,13 @@
100100

101101
<body style="word-spacing:normal;background-color:#111217;">
102102
<div style="background-color:#111217;">
103-
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
103+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->` }}
104104
<div style="margin:0px auto;max-width:600px;">
105105
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
106106
<tbody>
107107
<tr>
108108
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
109-
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
109+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->` }}
110110
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
111111
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="background-color:transparent;vertical-align:top;" width="100%">
112112
<tbody>
@@ -126,19 +126,19 @@
126126
</tbody>
127127
</table>
128128
</div>
129-
<!--[if mso | IE]></td></tr></table><![endif]-->
129+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
130130
</td>
131131
</tr>
132132
</tbody>
133133
</table>
134134
</div>
135-
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#22252b" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
135+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#22252b" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->` }}
136136
<div style="background:#22252b;background-color:#22252b;margin:0px auto;max-width:600px;">
137137
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#22252b;background-color:#22252b;width:100%;">
138138
<tbody>
139139
<tr>
140140
<td style="border:1px solid #2f3037;direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
141-
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->
141+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->` }}
142142
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
143143
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
144144
<tbody>
@@ -186,19 +186,19 @@ <h2>You have been added to {{ .OrgName }}</h2>
186186
</tbody>
187187
</table>
188188
</div>
189-
<!--[if mso | IE]></td></tr></table><![endif]-->
189+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
190190
</td>
191191
</tr>
192192
</tbody>
193193
</table>
194194
</div>
195-
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
195+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->` }}
196196
<div style="margin:0px auto;max-width:600px;">
197197
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
198198
<tbody>
199199
<tr>
200200
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
201-
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
201+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->` }}
202202
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
203203
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="background-color:transparent;vertical-align:top;" width="100%">
204204
<tbody>
@@ -210,13 +210,13 @@ <h2>You have been added to {{ .OrgName }}</h2>
210210
</tbody>
211211
</table>
212212
</div>
213-
<!--[if mso | IE]></td></tr></table><![endif]-->
213+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
214214
</td>
215215
</tr>
216216
</tbody>
217217
</table>
218218
</div>
219-
<!--[if mso | IE]></td></tr></table><![endif]-->
219+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
220220
</div>
221221
</body>
222222

public/emails/new_user_invite.html

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
<title>
66
{{ Subject .Subject .TemplateData "{{ .InvitedBy }} has invited you to join Grafana" }}
77
</title>
8-
<!--[if !mso]><!-->
8+
{{ __dangerouslyInjectHTML `<!--[if !mso]><!-->` }}
99
<meta http-equiv="X-UA-Compatible" content="IE=edge">
10-
<!--<![endif]-->
10+
{{ __dangerouslyInjectHTML `<!--<![endif]-->` }}
1111
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
1212
<meta name="viewport" content="width=device-width, initial-scale=1">
1313
<style type="text/css">
@@ -44,7 +44,7 @@
4444
}
4545

4646
</style>
47-
<!--[if mso]>
47+
{{ __dangerouslyInjectHTML `<!--[if mso]>
4848
<noscript>
4949
<xml>
5050
<o:OfficeDocumentSettings>
@@ -53,19 +53,19 @@
5353
</o:OfficeDocumentSettings>
5454
</xml>
5555
</noscript>
56-
<![endif]-->
57-
<!--[if lte mso 11]>
56+
<![endif]-->` }}
57+
{{ __dangerouslyInjectHTML `<!--[if lte mso 11]>
5858
<style type="text/css">
5959
.mj-outlook-group-fix { width:100% !important; }
6060
</style>
61-
<![endif]-->
62-
<!--[if !mso]><!-->
61+
<![endif]-->` }}
62+
{{ __dangerouslyInjectHTML `<!--[if !mso]><!-->` }}
6363
<link href="https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700" rel="stylesheet" type="text/css">
6464
<style type="text/css">
6565
@import url(https://fonts.googleapis.com/css?family=Ubuntu:300,400,500,700);
6666

6767
</style>
68-
<!--<![endif]-->
68+
{{ __dangerouslyInjectHTML `<!--<![endif]-->` }}
6969
<style type="text/css">
7070
@media only screen and (min-width:480px) {
7171
.mj-column-per-100 {
@@ -100,13 +100,13 @@
100100

101101
<body style="word-spacing:normal;background-color:#111217;">
102102
<div style="background-color:#111217;">
103-
<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
103+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->` }}
104104
<div style="margin:0px auto;max-width:600px;">
105105
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
106106
<tbody>
107107
<tr>
108108
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
109-
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
109+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->` }}
110110
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
111111
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="background-color:transparent;vertical-align:top;" width="100%">
112112
<tbody>
@@ -126,19 +126,19 @@
126126
</tbody>
127127
</table>
128128
</div>
129-
<!--[if mso | IE]></td></tr></table><![endif]-->
129+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
130130
</td>
131131
</tr>
132132
</tbody>
133133
</table>
134134
</div>
135-
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#22252b" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
135+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" bgcolor="#22252b" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->` }}
136136
<div style="background:#22252b;background-color:#22252b;margin:0px auto;max-width:600px;">
137137
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="background:#22252b;background-color:#22252b;width:100%;">
138138
<tbody>
139139
<tr>
140140
<td style="border:1px solid #2f3037;direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
141-
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->
141+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:598px;" ><![endif]-->` }}
142142
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
143143
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="vertical-align:top;" width="100%">
144144
<tbody>
@@ -180,19 +180,19 @@ <h2>You're invited to join {{ .OrgName }}</h2>
180180
</tbody>
181181
</table>
182182
</div>
183-
<!--[if mso | IE]></td></tr></table><![endif]-->
183+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
184184
</td>
185185
</tr>
186186
</tbody>
187187
</table>
188188
</div>
189-
<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->
189+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><table align="center" border="0" cellpadding="0" cellspacing="0" class="" role="presentation" style="width:600px;" width="600" ><tr><td style="line-height:0px;font-size:0px;mso-line-height-rule:exactly;"><![endif]-->` }}
190190
<div style="margin:0px auto;max-width:600px;">
191191
<table align="center" border="0" cellpadding="0" cellspacing="0" role="presentation" style="width:100%;">
192192
<tbody>
193193
<tr>
194194
<td style="direction:ltr;font-size:0px;padding:20px 0;text-align:center;">
195-
<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->
195+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]><table role="presentation" border="0" cellpadding="0" cellspacing="0"><tr><td class="" style="vertical-align:top;width:600px;" ><![endif]-->` }}
196196
<div class="mj-column-per-100 mj-outlook-group-fix" style="font-size:0px;text-align:left;direction:ltr;display:inline-block;vertical-align:top;width:100%;">
197197
<table border="0" cellpadding="0" cellspacing="0" role="presentation" style="background-color:transparent;vertical-align:top;" width="100%">
198198
<tbody>
@@ -204,13 +204,13 @@ <h2>You're invited to join {{ .OrgName }}</h2>
204204
</tbody>
205205
</table>
206206
</div>
207-
<!--[if mso | IE]></td></tr></table><![endif]-->
207+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
208208
</td>
209209
</tr>
210210
</tbody>
211211
</table>
212212
</div>
213-
<!--[if mso | IE]></td></tr></table><![endif]-->
213+
{{ __dangerouslyInjectHTML `<!--[if mso | IE]></td></tr></table><![endif]-->` }}
214214
</div>
215215
</body>
216216

0 commit comments

Comments
 (0)