diff --git a/go.sum b/go.sum index b2f893f..e430330 100644 --- a/go.sum +++ b/go.sum @@ -37,6 +37,8 @@ github.com/mattn/go-isatty v0.0.9 h1:d5US/mDsogSGW37IV293h//ZFaeajb69h+EHFsv2xGg github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ= github.com/mattn/go-isatty v0.0.11 h1:FxPOTFNqGkuDUGi3H/qkUbQO4ZiBa2brKq5r0l8TGeM= github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/microcosm-cc/bluemonday v1.0.2 h1:5lPfLTTAvAbtS0VqT+94yOtFnGfUWYyx0+iToC3Os3s= +github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= diff --git a/plugins/base/public/assets/style.css b/plugins/base/public/assets/style.css index 8f414f5..c27f10f 100644 --- a/plugins/base/public/assets/style.css +++ b/plugins/base/public/assets/style.css @@ -1 +1,5 @@ -/* TODO */ +iframe { + width: 100%; + height: 400px; + border: 0; +} diff --git a/plugins/base/public/message.html b/plugins/base/public/message.html index 89fd4dd..6ce633b 100644 --- a/plugins/base/public/message.html +++ b/plugins/base/public/message.html @@ -112,7 +112,12 @@ {{if .Body}}

Reply

-
{{.Body}}
+ {{if .IsHTML}} + + + {{else}} +
{{.Body}}
+ {{end}} {{else}}

Can't preview this message part.

Download diff --git a/plugins/base/routes.go b/plugins/base/routes.go index 293d313..02518cc 100644 --- a/plugins/base/routes.go +++ b/plugins/base/routes.go @@ -16,6 +16,7 @@ import ( "github.com/emersion/go-message" "github.com/emersion/go-smtp" "github.com/labstack/echo/v4" + "github.com/microcosm-cc/bluemonday" ) func registerRoutes(p *koushin.GoPlugin) { @@ -162,6 +163,7 @@ type MessageRenderData struct { Mailbox *imap.MailboxStatus Message *IMAPMessage Body string + IsHTML bool PartPath string MailboxPage int Flags map[string]bool @@ -216,6 +218,7 @@ func handleGetPart(ctx *koushin.Context, raw bool) error { // TODO: set Content-Length if possible + // Be careful not to serve types like text/html as inline if !strings.EqualFold(mimeType, "text/plain") || strings.EqualFold(disp, "attachment") { dispParams := make(map[string]string) if filename != "" { @@ -241,6 +244,13 @@ func handleGetPart(ctx *koushin.Context, raw bool) error { body = string(b) } + isHTML := false + if strings.EqualFold(mimeType, "text/html") { + p := bluemonday.UGCPolicy() + body = p.Sanitize(body) + isHTML = true + } + flags := make(map[string]bool) for _, f := range mbox.PermanentFlags { f = imap.CanonicalFlag(f) @@ -256,6 +266,7 @@ func handleGetPart(ctx *koushin.Context, raw bool) error { Mailbox: mbox, Message: msg, Body: body, + IsHTML: isHTML, PartPath: partPathString, MailboxPage: int(mbox.Messages-msg.SeqNum) / messagesPerPage, Flags: flags, diff --git a/server.go b/server.go index 5672a99..526db9f 100644 --- a/server.go +++ b/server.go @@ -302,7 +302,9 @@ func New(e *echo.Echo, options *Options) (*Server, error) { e.Use(func(next echo.HandlerFunc) echo.HandlerFunc { return func(ectx echo.Context) error { - ectx.Response().Header().Set("Content-Security-Policy", "default-src 'self'") + // `style-src 'unsafe-inline'` is required for e-mails with + // embedded stylesheets + ectx.Response().Header().Set("Content-Security-Policy", "default-src 'self'; style-src 'self' 'unsafe-inline'") return next(ectx) } })