Skip to content

Commit bb0e212

Browse files
authored
Try to prevent autolinking of displaynames by email readers (#19169) (#19183)
Backport #19169 Unfortunately many email readers will (helpfully) detect url or url-like names and automatically create links to them, even in HTML emails. This is not ideal when usernames can have dots in them. This PR tries to prevent this behaviour by sticking ZWJ characters between dots and also set the meta tag to prevent format detection. Not every email template has been changed in this way - just the activation emails but it may be that we should be setting the above meta tag in all of our emails too. Signed-off-by: Andrew Thornton <[email protected]>
1 parent d21b7fd commit bb0e212

File tree

8 files changed

+38
-22
lines changed

8 files changed

+38
-22
lines changed

modules/templates/helper.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -633,6 +633,11 @@ func JSEscape(raw string) string {
633633
return template.JSEscapeString(raw)
634634
}
635635

636+
// DotEscape wraps a dots in names with ZWJ [U+200D] in order to prevent autolinkers from detecting these as urls
637+
func DotEscape(raw string) string {
638+
return strings.ReplaceAll(raw, ".", "\u200d.\u200d")
639+
}
640+
636641
// Sha1 returns sha1 sum of string
637642
func Sha1(str string) string {
638643
return base.EncodeSha1(str)

services/mailer/mail.go

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,9 @@ func sendUserMail(language string, u *user_model.User, tpl base.TplName, code, s
7777
"Code": code,
7878
"Language": locale.Language(),
7979
// helper
80-
"i18n": locale,
81-
"Str2html": templates.Str2html,
80+
"i18n": locale,
81+
"Str2html": templates.Str2html,
82+
"DotEscape": templates.DotEscape,
8283
}
8384

8485
var content bytes.Buffer
@@ -127,8 +128,9 @@ func SendActivateEmailMail(u *user_model.User, email *user_model.EmailAddress) {
127128
"Email": email.Email,
128129
"Language": locale.Language(),
129130
// helper
130-
"i18n": locale,
131-
"Str2html": templates.Str2html,
131+
"i18n": locale,
132+
"Str2html": templates.Str2html,
133+
"DotEscape": templates.DotEscape,
132134
}
133135

134136
var content bytes.Buffer
@@ -157,8 +159,9 @@ func SendRegisterNotifyMail(u *user_model.User) {
157159
"Username": u.Name,
158160
"Language": locale.Language(),
159161
// helper
160-
"i18n": locale,
161-
"Str2html": templates.Str2html,
162+
"i18n": locale,
163+
"Str2html": templates.Str2html,
164+
"DotEscape": templates.DotEscape,
162165
}
163166

164167
var content bytes.Buffer
@@ -190,8 +193,9 @@ func SendCollaboratorMail(u, doer *user_model.User, repo *repo_model.Repository)
190193
"Link": repo.HTMLURL(),
191194
"Language": locale.Language(),
192195
// helper
193-
"i18n": locale,
194-
"Str2html": templates.Str2html,
196+
"i18n": locale,
197+
"Str2html": templates.Str2html,
198+
"DotEscape": templates.DotEscape,
195199
}
196200

197201
var content bytes.Buffer
@@ -273,8 +277,9 @@ func composeIssueCommentMessages(ctx *mailCommentContext, lang string, recipient
273277
"ReviewComments": reviewComments,
274278
"Language": locale.Language(),
275279
// helper
276-
"i18n": locale,
277-
"Str2html": templates.Str2html,
280+
"i18n": locale,
281+
"Str2html": templates.Str2html,
282+
"DotEscape": templates.DotEscape,
278283
}
279284

280285
var mailSubject bytes.Buffer

services/mailer/mail_release.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,8 +74,9 @@ func mailNewRelease(lang string, tos []string, rel *models.Release) {
7474
"Subject": subject,
7575
"Language": locale.Language(),
7676
// helper
77-
"i18n": locale,
78-
"Str2html": templates.Str2html,
77+
"i18n": locale,
78+
"Str2html": templates.Str2html,
79+
"DotEscape": templates.DotEscape,
7980
}
8081

8182
var mailBody bytes.Buffer

services/mailer/mail_repo.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,9 @@ func sendRepoTransferNotifyMailPerLang(lang string, newOwner, doer *user_model.U
7373
"Language": locale.Language(),
7474
"Destination": destination,
7575
// helper
76-
"i18n": locale,
77-
"Str2html": templates.Str2html,
76+
"i18n": locale,
77+
"Str2html": templates.Str2html,
78+
"DotEscape": templates.DotEscape,
7879
}
7980

8081
if err := bodyTemplates.ExecuteTemplate(&content, string(mailRepoTransferNotify), data); err != nil {

templates/mail/auth/activate.tmpl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
<html>
33
<head>
44
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5-
<title>{{.i18n.Tr "mail.activate_account.title" .DisplayName}}</title>
5+
<meta name="format-detection" content="telephone=no,date=no,address=no,email=no,url=no"/>
6+
<title>{{.i18n.Tr "mail.activate_account.title" (.DisplayName|DotEscape)}}</title>
67
</head>
78

89
{{ $activate_url := printf "%suser/activate?code=%s" AppUrl (QueryEscape .Code)}}
910
<body>
10-
<p>{{.i18n.Tr "mail.activate_account.text_1" .DisplayName AppName | Str2html}}</p><br>
11+
<p>{{.i18n.Tr "mail.activate_account.text_1" (.DisplayName|DotEscape) AppName | Str2html}}</p><br>
1112
<p>{{.i18n.Tr "mail.activate_account.text_2" .ActiveCodeLives | Str2html}}</p><p><a href="{{$activate_url}}">{{$activate_url}}</a></p><br>
1213
<p>{{.i18n.Tr "mail.link_not_working_do_paste"}}</p>
1314

templates/mail/auth/activate_email.tmpl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
<html>
33
<head>
44
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5-
<title>{{.i18n.Tr "mail.activate_email.title" .DisplayName}}</title>
5+
<meta name="format-detection" content="telephone=no,date=no,address=no,email=no,url=no"/>
6+
<title>{{.i18n.Tr "mail.activate_email.title" (.DisplayName|DotEscape)}}</title>
67
</head>
78

89
{{ $activate_url := printf "%suser/activate_email?code=%s&email=%s" AppUrl (QueryEscape .Code) (QueryEscape .Email)}}
910
<body>
10-
<p>{{.i18n.Tr "mail.hi_user_x" .DisplayName | Str2html}}</p><br>
11+
<p>{{.i18n.Tr "mail.hi_user_x" (.DisplayName|DotEscape) | Str2html}}</p><br>
1112
<p>{{.i18n.Tr "mail.activate_email.text" .ActiveCodeLives | Str2html}}</p><p><a href="{{$activate_url}}">{{$activate_url}}</a></p><br>
1213
<p>{{.i18n.Tr "mail.link_not_working_do_paste"}}</p>
1314

templates/mail/auth/register_notify.tmpl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
<html>
33
<head>
44
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5-
<title>{{.i18n.Tr "mail.register_notify.title" .DisplayName AppName}}</title>
5+
<meta name="format-detection" content="telephone=no,date=no,address=no,email=no,url=no"/>
6+
<title>{{.i18n.Tr "mail.register_notify.title" (.DisplayName|DotEscape) AppName}}</title>
67
</head>
78

89
{{$set_pwd_url := printf "%[1]suser/forgot_password" AppUrl}}
910
<body>
10-
<p>{{.i18n.Tr "mail.hi_user_x" .DisplayName | Str2html}}</p><br>
11+
<p>{{.i18n.Tr "mail.hi_user_x" (.DisplayName|DotEscape) | Str2html}}</p><br>
1112
<p>{{.i18n.Tr "mail.register_notify.text_1" AppName}}</p><br>
1213
<p>{{.i18n.Tr "mail.register_notify.text_2" .Username}}</p><p><a href="{{AppUrl}}user/login">{{AppUrl}}user/login</a></p><br>
1314
<p>{{.i18n.Tr "mail.register_notify.text_3" ($set_pwd_url | Escape) | Str2html}}</p><br>

templates/mail/auth/reset_passwd.tmpl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@
22
<html>
33
<head>
44
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
5-
<title>{{.i18n.Tr "mail.reset_password.title" .DisplayName}}</title>
5+
<meta name="format-detection" content="telephone=no,date=no,address=no,email=no,url=no"/>
6+
<title>{{.i18n.Tr "mail.reset_password.title" (.DisplayName|DotEscape)}}</title>
67
</head>
78

89
{{ $recover_url := printf "%suser/recover_account?code=%s" AppUrl (QueryEscape .Code)}}
910
<body>
10-
<p>{{.i18n.Tr "mail.hi_user_x" .DisplayName | Str2html}}</p><br>
11+
<p>{{.i18n.Tr "mail.hi_user_x" (.DisplayName|DotEscape) | Str2html}}</p><br>
1112
<p>{{.i18n.Tr "mail.reset_password.text" .ResetPwdCodeLives | Str2html}}</p><p><a href="{{$recover_url}}">{{$recover_url}}</a></p><br>
1213
<p>{{.i18n.Tr "mail.link_not_working_do_paste"}}</p>
1314

0 commit comments

Comments
 (0)