Merge pull request 'Escape user-supplied values in HTML email templates' (#40) from fix/email-html-injection into main

Reviewed-on: https://git.ts.mattnite.net/mattnite/forgejo-tickets/pulls/40
This commit is contained in:
Matthew Knight 2026-02-18 00:10:34 +00:00
commit d33c138867
1 changed files with 10 additions and 7 deletions

View File

@ -1,6 +1,9 @@
package email package email
import "fmt" import (
"fmt"
"html"
)
func emailWrapper(content string) string { func emailWrapper(content string) string {
return fmt.Sprintf(`<!DOCTYPE html> return fmt.Sprintf(`<!DOCTYPE html>
@ -24,7 +27,7 @@ func renderVerificationEmail(name, verifyURL string) string {
</p> </p>
<p>Or copy and paste this link into your browser:</p> <p>Or copy and paste this link into your browser:</p>
<p style="word-break: break-all; color: #2563eb;">%s</p> <p style="word-break: break-all; color: #2563eb;">%s</p>
<p>This link expires in 24 hours.</p>`, name, verifyURL, verifyURL)) <p>This link expires in 24 hours.</p>`, html.EscapeString(name), verifyURL, verifyURL))
} }
func renderPasswordResetEmail(name, resetURL string) string { func renderPasswordResetEmail(name, resetURL string) string {
@ -37,7 +40,7 @@ func renderPasswordResetEmail(name, resetURL string) string {
</p> </p>
<p>Or copy and paste this link into your browser:</p> <p>Or copy and paste this link into your browser:</p>
<p style="word-break: break-all; color: #2563eb;">%s</p> <p style="word-break: break-all; color: #2563eb;">%s</p>
<p>This link expires in 1 hour. If you didn't request this, please ignore this email.</p>`, name, resetURL, resetURL)) <p>This link expires in 1 hour. If you didn't request this, please ignore this email.</p>`, html.EscapeString(name), resetURL, resetURL))
} }
func renderTicketClosedEmail(name, ticketTitle, ticketURL string) string { func renderTicketClosedEmail(name, ticketTitle, ticketURL string) string {
@ -48,7 +51,7 @@ func renderTicketClosedEmail(name, ticketTitle, ticketURL string) string {
<p style="margin: 30px 0;"> <p style="margin: 30px 0;">
<a href="%s" style="background: #2563eb; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: 500;">View Ticket</a> <a href="%s" style="background: #2563eb; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: 500;">View Ticket</a>
</p> </p>
<p>If you believe the issue is not fully resolved, you can add a comment on the ticket page.</p>`, name, ticketTitle, ticketURL)) <p>If you believe the issue is not fully resolved, you can add a comment on the ticket page.</p>`, html.EscapeString(name), html.EscapeString(ticketTitle), ticketURL))
} }
func renderTicketReplyEmail(name, ticketTitle, ticketURL string) string { func renderTicketReplyEmail(name, ticketTitle, ticketURL string) string {
@ -58,7 +61,7 @@ func renderTicketReplyEmail(name, ticketTitle, ticketURL string) string {
<p>There is a new reply on your ticket <strong>"%s"</strong>.</p> <p>There is a new reply on your ticket <strong>"%s"</strong>.</p>
<p style="margin: 30px 0;"> <p style="margin: 30px 0;">
<a href="%s" style="background: #2563eb; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: 500;">View Ticket</a> <a href="%s" style="background: #2563eb; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: 500;">View Ticket</a>
</p>`, name, ticketTitle, ticketURL)) </p>`, html.EscapeString(name), html.EscapeString(ticketTitle), ticketURL))
} }
func renderAccountApprovedEmail(name, loginURL string) string { func renderAccountApprovedEmail(name, loginURL string) string {
@ -68,7 +71,7 @@ func renderAccountApprovedEmail(name, loginURL string) string {
<p>Your account request has been approved. You can now log in and start creating tickets.</p> <p>Your account request has been approved. You can now log in and start creating tickets.</p>
<p style="margin: 30px 0;"> <p style="margin: 30px 0;">
<a href="%s" style="background: #2563eb; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: 500;">Log In</a> <a href="%s" style="background: #2563eb; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: 500;">Log In</a>
</p>`, name, loginURL)) </p>`, html.EscapeString(name), loginURL))
} }
func renderWelcomeEmail(name, email, tempPassword, loginURL string) string { func renderWelcomeEmail(name, email, tempPassword, loginURL string) string {
@ -83,5 +86,5 @@ func renderWelcomeEmail(name, email, tempPassword, loginURL string) string {
<p style="margin: 30px 0;"> <p style="margin: 30px 0;">
<a href="%s" style="background: #2563eb; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: 500;">Log In</a> <a href="%s" style="background: #2563eb; color: #fff; padding: 12px 24px; text-decoration: none; border-radius: 6px; font-weight: 500;">Log In</a>
</p> </p>
<p>Please change your password after logging in.</p>`, name, email, tempPassword, loginURL)) <p>Please change your password after logging in.</p>`, html.EscapeString(name), html.EscapeString(email), html.EscapeString(tempPassword), loginURL))
} }