satellite/console,web/satellite: remove user input from welcome and invite emails

Emails should not contain user input that could be used by malicious
agents to deliver a message. Usernames have been removed from
account activation emails, and project names have been removed from
project invitation emails.

References storj-private#133

Change-Id: Ic05921149b409145df109c0966ea5dfd86d86eb1
This commit is contained in:
Jeremy Wharton 2023-01-31 15:22:17 -06:00 committed by Storj Robot
parent 15508d270c
commit 897de167a6
6 changed files with 16 additions and 29 deletions

View File

@ -349,18 +349,13 @@ func (a *Auth) Register(w http.ResponseWriter, r *http.Request) {
} }
link := a.ActivateAccountURL + "?token=" + token link := a.ActivateAccountURL + "?token=" + token
userName := user.ShortName
if user.ShortName == "" {
userName = user.FullName
}
a.mailService.SendRenderedAsync( a.mailService.SendRenderedAsync(
ctx, ctx,
[]post.Address{{Address: user.Email, Name: userName}}, []post.Address{{Address: user.Email}},
&console.AccountActivationEmail{ &console.AccountActivationEmail{
ActivationLink: link, ActivationLink: link,
Origin: a.ExternalAddress, Origin: a.ExternalAddress,
UserName: userName,
}, },
) )
} }
@ -684,24 +679,18 @@ func (a *Auth) ResendEmail(w http.ResponseWriter, r *http.Request) {
return return
} }
userName := user.ShortName
if user.ShortName == "" {
userName = user.FullName
}
link := a.ActivateAccountURL + "?token=" + token link := a.ActivateAccountURL + "?token=" + token
contactInfoURL := a.ContactInfoURL contactInfoURL := a.ContactInfoURL
termsAndConditionsURL := a.TermsAndConditionsURL termsAndConditionsURL := a.TermsAndConditionsURL
a.mailService.SendRenderedAsync( a.mailService.SendRenderedAsync(
ctx, ctx,
[]post.Address{{Address: user.Email, Name: userName}}, []post.Address{{Address: user.Email}},
&console.AccountActivationEmail{ &console.AccountActivationEmail{
Origin: a.ExternalAddress, Origin: a.ExternalAddress,
ActivationLink: link, ActivationLink: link,
TermsAndConditionsURL: termsAndConditionsURL, TermsAndConditionsURL: termsAndConditionsURL,
ContactInfoURL: contactInfoURL, ContactInfoURL: contactInfoURL,
UserName: userName,
}, },
) )
} }

View File

@ -139,6 +139,11 @@ func rootMutation(log *zap.Logger, service *console.Service, mailService *mailse
}, },
}, },
Resolve: func(p graphql.ResolveParams) (interface{}, error) { Resolve: func(p graphql.ResolveParams) (interface{}, error) {
inviter, err := console.GetUser(p.Context)
if err != nil {
return nil, err
}
pID, _ := p.Args[FieldProjectID].(string) pID, _ := p.Args[FieldProjectID].(string)
emails, _ := p.Args[FieldEmail].([]interface{}) emails, _ := p.Args[FieldEmail].([]interface{})
@ -182,7 +187,7 @@ func rootMutation(log *zap.Logger, service *console.Service, mailService *mailse
&console.ProjectInvitationEmail{ &console.ProjectInvitationEmail{
Origin: origin, Origin: origin,
UserName: userName, UserName: userName,
ProjectName: project.Name, InviterEmail: inviter.Email,
SignInLink: signIn, SignInLink: signIn,
LetUsKnowURL: letUsKnowURL, LetUsKnowURL: letUsKnowURL,
TermsAndConditionsURL: termsAndConditionsURL, TermsAndConditionsURL: termsAndConditionsURL,

View File

@ -90,20 +90,15 @@ func (chore *Chore) Run(ctx context.Context) (err error) {
authController := consoleapi.NewAuth(chore.log, nil, nil, nil, nil, nil, "", chore.address, "", "", "", "") authController := consoleapi.NewAuth(chore.log, nil, nil, nil, nil, nil, "", chore.address, "", "", "", "")
link := authController.ActivateAccountURL + "?token=" + token link := authController.ActivateAccountURL + "?token=" + token
userName := u.ShortName
if u.ShortName == "" {
userName = u.FullName
}
// blocking send allows us to verify that links are clicked in tests. // blocking send allows us to verify that links are clicked in tests.
if chore.useBlockingSend { if chore.useBlockingSend {
err = chore.mailService.SendRendered( err = chore.mailService.SendRendered(
ctx, ctx,
[]post.Address{{Address: u.Email, Name: userName}}, []post.Address{{Address: u.Email}},
&console.AccountActivationEmail{ &console.AccountActivationEmail{
ActivationLink: link, ActivationLink: link,
Origin: authController.ExternalAddress, Origin: authController.ExternalAddress,
UserName: userName,
}, },
) )
if err != nil { if err != nil {
@ -113,11 +108,10 @@ func (chore *Chore) Run(ctx context.Context) (err error) {
} else { } else {
chore.mailService.SendRenderedAsync( chore.mailService.SendRenderedAsync(
ctx, ctx,
[]post.Address{{Address: u.Email, Name: userName}}, []post.Address{{Address: u.Email}},
&console.AccountActivationEmail{ &console.AccountActivationEmail{
ActivationLink: link, ActivationLink: link,
Origin: authController.ExternalAddress, Origin: authController.ExternalAddress,
UserName: userName,
}, },
) )
} }

View File

@ -11,7 +11,6 @@ type AccountActivationEmail struct {
ActivationLink string ActivationLink string
ContactInfoURL string ContactInfoURL string
TermsAndConditionsURL string TermsAndConditionsURL string
UserName string
} }
// Template returns email template name. // Template returns email template name.
@ -41,7 +40,7 @@ func (*ForgotPasswordEmail) Subject() string { return "Password recovery request
type ProjectInvitationEmail struct { type ProjectInvitationEmail struct {
Origin string Origin string
UserName string UserName string
ProjectName string InviterEmail string
SignInLink string SignInLink string
LetUsKnowURL string LetUsKnowURL string
ContactInfoURL string ContactInfoURL string
@ -53,7 +52,7 @@ func (*ProjectInvitationEmail) Template() string { return "Invite" }
// Subject gets email subject. // Subject gets email subject.
func (email *ProjectInvitationEmail) Subject() string { func (email *ProjectInvitationEmail) Subject() string {
return "You were invited to join the Project " + email.ProjectName return "You were invited to join a project on Storj"
} }
// UnknownResetPasswordEmail is mailservice template with unknown password reset data. // UnknownResetPasswordEmail is mailservice template with unknown password reset data.

View File

@ -422,9 +422,9 @@
<div style="margin: 12px 20px;"> <div style="margin: 12px 20px;">
<div style="mso-line-height-rule: exactly;mso-text-raise: 4px;"> <div style="mso-line-height-rule: exactly;mso-text-raise: 4px;">
<p class="size-20" style="Margin-top: 0;Margin-bottom: 0;font-family: 'montserrat','dejavu sans','verdana',sans-serif;font-size: 17px;line-height: 26px;" lang="x-size-20"> <p class="size-20" style="Margin-top: 0;Margin-bottom: 0;font-family: 'montserrat','dejavu sans','verdana',sans-serif;font-size: 17px;line-height: 26px;" lang="x-size-20">
<span class="font-montserrat">You were invited to the <span class="font-montserrat">You were invited to join
<a href="{{ .Origin }}" style="color: #2683ff; text-decoration: none; font-weight: bold;">{{ .ProjectName }}</a> <a href="{{ .Origin }}" style="color: #2683ff; text-decoration: none; font-weight: bold;">a project</a>
on Storj on Storj by {{ .InviterEmail }}
</span> </span>
</p> </p>
<p class="size-20" <p class="size-20"

View File

@ -154,7 +154,7 @@
<div style="font-family: 'Roboto', 'Tahoma', 'Verdana', 'Segoe', sans-serif; <div style="font-family: 'Roboto', 'Tahoma', 'Verdana', 'Segoe', sans-serif;
line-height: 1.2; font-size: 12px; color: #000000; mso-line-height-alt: 14px;"> line-height: 1.2; font-size: 12px; color: #000000; mso-line-height-alt: 14px;">
<p style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px; margin: 0;"> <p style="font-size: 14px; line-height: 1.2; mso-line-height-alt: 17px; margin: 0;">
<span style="font-size: 18px;">Hi {{ .UserName }},</span> <span style="font-size: 18px;">Hi,</span>
</p> </p>
<p style="font-size: 12px; line-height: 1.2; mso-line-height-alt: 14px; margin: 0;"><br> <p style="font-size: 12px; line-height: 1.2; mso-line-height-alt: 14px; margin: 0;"><br>
<span style="font-size: 18px;">You created an account on Storj. <span style="font-size: 18px;">You created an account on Storj.