@@ -6,6 +6,7 @@ package mailer
66
77import (
88 "bytes"
9+ "context"
910 "encoding/base64"
1011 "fmt"
1112 "html/template"
@@ -16,9 +17,7 @@ import (
1617 "strings"
1718 texttmpl "text/template"
1819
19- access_model "code.gitea.io/gitea/models/perm/access"
2020 repo_model "code.gitea.io/gitea/models/repo"
21- "code.gitea.io/gitea/models/unit"
2221 user_model "code.gitea.io/gitea/models/user"
2322 "code.gitea.io/gitea/modules/log"
2423 "code.gitea.io/gitea/modules/setting"
@@ -54,11 +53,20 @@ func sanitizeSubject(subject string) string {
5453 return mime .QEncoding .Encode ("utf-8" , string (runes ))
5554}
5655
57- func Base64InlineImages (body string , ctx * mailCommentContext ) (string , error ) {
56+ type mailAttachmentBase64Embedder struct {
57+ doer * user_model.User
58+ repo * repo_model.Repository
59+ maxSize int64
60+ }
61+
62+ func newMailAttachmentBase64Embedder (doer * user_model.User , repo * repo_model.Repository , maxSize int64 ) * mailAttachmentBase64Embedder {
63+ return & mailAttachmentBase64Embedder {doer : doer , repo : repo , maxSize : maxSize }
64+ }
65+
66+ func (b64embedder * mailAttachmentBase64Embedder ) Base64InlineImages (ctx context.Context , body string ) (string , error ) {
5867 doc , err := html .Parse (strings .NewReader (body ))
5968 if err != nil {
60- log .Error ("Failed to parse HTML body: %v" , err )
61- return "" , err
69+ return "" , fmt .Errorf ("%w" , err )
6270 }
6371
6472 var totalEmbeddedImagesSize int64
@@ -70,7 +78,7 @@ func Base64InlineImages(body string, ctx *mailCommentContext) (string, error) {
7078 for i , attr := range n .Attr {
7179 if attr .Key == "src" {
7280 attachmentPath := attr .Val
73- dataURI , err := AttachmentSrcToBase64DataURI (attachmentPath , ctx , & totalEmbeddedImagesSize )
81+ dataURI , err := b64embedder . AttachmentSrcToBase64DataURI (ctx , attachmentPath , & totalEmbeddedImagesSize )
7482 if err != nil {
7583 log .Trace ("attachmentSrcToDataURI not possible: %v" , err ) // Not an error, just skip. This is probably an image from outside the gitea instance.
7684 continue
@@ -98,7 +106,7 @@ func Base64InlineImages(body string, ctx *mailCommentContext) (string, error) {
98106 return buf .String (), nil
99107}
100108
101- func AttachmentSrcToBase64DataURI (attachmentPath string , ctx * mailCommentContext , totalEmbeddedImagesSize * int64 ) (string , error ) {
109+ func ( b64embedder * mailAttachmentBase64Embedder ) AttachmentSrcToBase64DataURI (ctx context. Context , attachmentPath string , totalEmbeddedImagesSize * int64 ) (string , error ) {
102110 if ! strings .HasPrefix (attachmentPath , setting .AppURL ) { // external image
103111 return "" , fmt .Errorf ("external image" )
104112 }
@@ -113,14 +121,8 @@ func AttachmentSrcToBase64DataURI(attachmentPath string, ctx *mailCommentContext
113121 return "" , err
114122 }
115123
116- // "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.
117- // Therefore, we check the Doer, with which we counter leaking information as a Doer brute force attack on attachments would be possible.
118- perm , err := access_model .GetUserRepoPermission (ctx , ctx .Issue .Repo , ctx .Doer )
119- if err != nil {
120- return "" , err
121- }
122- if ! perm .CanRead (unit .TypeIssues ) {
123- return "" , fmt .Errorf ("no permission" )
124+ if attachment .RepoID != b64embedder .repo .ID {
125+ return "" , fmt .Errorf ("attachment does not belong to the repository" )
124126 }
125127
126128 fr , err := storage .Attachments .Open (attachment .RelativePath ())
@@ -129,15 +131,13 @@ func AttachmentSrcToBase64DataURI(attachmentPath string, ctx *mailCommentContext
129131 }
130132 defer fr .Close ()
131133
132- maxSize := setting .MailService .Base64EmbedImagesMaxSizePerEmail // at maximum read the whole available combined email size, to prevent maliciously large file reads
133-
134- lr := & io.LimitedReader {R : fr , N : maxSize + 1 }
134+ lr := & io.LimitedReader {R : fr , N : b64embedder .maxSize + 1 }
135135 content , err := io .ReadAll (lr )
136136 if err != nil {
137137 return "" , err
138138 }
139- if len (content ) > int ( maxSize ) {
140- return "" , fmt .Errorf ("file size exceeds the embedded image max limit \\ (%d bytes\\ )" , maxSize )
139+ if int64 ( len (content )) > b64embedder . maxSize {
140+ return "" , fmt .Errorf ("file size exceeds the embedded image max limit \\ (%d bytes\\ )" , b64embedder . maxSize )
141141 }
142142
143143 if * totalEmbeddedImagesSize + int64 (len (content )) > setting .MailService .Base64EmbedImagesMaxSizePerEmail {
0 commit comments