max size and check whether doer is allowed

This commit is contained in:
Felix Sommer 2024-10-13 15:26:14 +02:00
parent 13190ebb9f
commit 79ce159262
3 changed files with 38 additions and 11 deletions

View File

@ -1707,6 +1707,9 @@ LEVEL = Info
;; ;;
;; convert links of attached images to inline images. Only for images hosted in this gitea instance. ;; convert links of attached images to inline images. Only for images hosted in this gitea instance.
;BASE64_EMBED_IMAGES = false ;BASE64_EMBED_IMAGES = false
;;
;; The maximum size of an image attachment to be embedded in the email.
;BASE64_EMBED_IMAGES_MAX_SIZE = 5242880
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

View File

@ -13,7 +13,7 @@ import (
"code.gitea.io/gitea/modules/log" "code.gitea.io/gitea/modules/log"
shellquote "github.com/kballard/go-shellquote" "github.com/kballard/go-shellquote"
) )
// Mailer represents mail service. // Mailer represents mail service.
@ -29,6 +29,7 @@ type Mailer struct {
SubjectPrefix string `ini:"SUBJECT_PREFIX"` SubjectPrefix string `ini:"SUBJECT_PREFIX"`
OverrideHeader map[string][]string `ini:"-"` OverrideHeader map[string][]string `ini:"-"`
Base64EmbedImages bool `ini:"BASE64_EMBED_IMAGES"` Base64EmbedImages bool `ini:"BASE64_EMBED_IMAGES"`
Base64EmbedImagesMaxSizePerAttachment int64 `ini:"BASE64_EMBED_IMAGES_MAX_SIZE_PER_ATTACHMENT"`
// SMTP sender // SMTP sender
Protocol string `ini:"PROTOCOL"` Protocol string `ini:"PROTOCOL"`
@ -152,6 +153,7 @@ func loadMailerFrom(rootCfg ConfigProvider) {
sec.Key("SENDMAIL_CONVERT_CRLF").MustBool(true) sec.Key("SENDMAIL_CONVERT_CRLF").MustBool(true)
sec.Key("FROM").MustString(sec.Key("USER").String()) sec.Key("FROM").MustString(sec.Key("USER").String())
sec.Key("BASE64_EMBED_IMAGES").MustBool(false) sec.Key("BASE64_EMBED_IMAGES").MustBool(false)
sec.Key("BASE64_EMBED_IMAGES_MAX_SIZE_PER_ATTACHMENT").MustInt64(5 * 1024 * 1024)
// Now map the values on to the MailService // Now map the values on to the MailService
MailService = &Mailer{} MailService = &Mailer{}

View File

@ -21,7 +21,9 @@ import (
activities_model "code.gitea.io/gitea/models/activities" activities_model "code.gitea.io/gitea/models/activities"
issues_model "code.gitea.io/gitea/models/issues" issues_model "code.gitea.io/gitea/models/issues"
access_model "code.gitea.io/gitea/models/perm/access"
repo_model "code.gitea.io/gitea/models/repo" repo_model "code.gitea.io/gitea/models/repo"
"code.gitea.io/gitea/models/unit"
user_model "code.gitea.io/gitea/models/user" user_model "code.gitea.io/gitea/models/user"
"code.gitea.io/gitea/modules/base" "code.gitea.io/gitea/modules/base"
"code.gitea.io/gitea/modules/emoji" "code.gitea.io/gitea/modules/emoji"
@ -421,6 +423,7 @@ func Base64InlineImages(body string, ctx *MailCommentContext) (string, error) {
} }
func AttachmentSrcToBase64DataURI(attachmentPath string, ctx *MailCommentContext) (string, error) { func AttachmentSrcToBase64DataURI(attachmentPath string, ctx *MailCommentContext) (string, error) {
maxSizePerImageAttachment := setting.MailService.Base64EmbedImagesMaxSizePerAttachment
if !strings.HasPrefix(attachmentPath, setting.AppURL) { // external image if !strings.HasPrefix(attachmentPath, setting.AppURL) { // external image
return "", fmt.Errorf("external image") return "", fmt.Errorf("external image")
} }
@ -435,6 +438,16 @@ func AttachmentSrcToBase64DataURI(attachmentPath string, ctx *MailCommentContext
return "", err return "", err
} }
// "Doer" is theoretically not the correct permission check (as Doer created the action on which to send), but as this is batch processed the receipants can't be accessed.
// Therefore we check the Doer, with which we counter leaking information as a Doer brute force attack on attachments would be possible.
perm, err := access_model.GetUserRepoPermission(ctx, ctx.Issue.Repo, ctx.Doer)
if err != nil {
return "", err
}
if !perm.CanRead(unit.TypeIssues) {
return "", fmt.Errorf("no permission")
}
fr, err := storage.Attachments.Open(attachment.RelativePath()) fr, err := storage.Attachments.Open(attachment.RelativePath())
if err != nil { if err != nil {
return "", err return "", err
@ -446,7 +459,16 @@ func AttachmentSrcToBase64DataURI(attachmentPath string, ctx *MailCommentContext
return "", err return "", err
} }
if len(content) > int(maxSizePerImageAttachment) {
return "", fmt.Errorf("image too large (%d bytes) of max %d bytes", len(content), maxSizePerImageAttachment)
}
mimeType := http.DetectContentType(content) mimeType := http.DetectContentType(content)
if !strings.HasPrefix(mimeType, "image/") {
return "", fmt.Errorf("not an image")
}
encoded := base64.StdEncoding.EncodeToString(content) encoded := base64.StdEncoding.EncodeToString(content)
dataURI := fmt.Sprintf("data:%s;base64,%s", mimeType, encoded) dataURI := fmt.Sprintf("data:%s;base64,%s", mimeType, encoded)