diff --git a/api4/file_test.go b/api4/file_test.go index 9f5b52e031..25ec9f7a7d 100644 --- a/api4/file_test.go +++ b/api4/file_test.go @@ -542,6 +542,20 @@ func TestUploadFiles(t *testing.T) { } }, }, + + { + title: "Error image too large", + names: []string{"test.png"}, + skipSuccessValidation: true, + checkResponse: CheckBadRequestStatus, + setupConfig: func(a *app.App) func(a *app.App) { + maxResSize := *a.Config().FileSettings.MaxImageResolution + a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxImageResolution = 90000 }) + return func(a *app.App) { + a.UpdateConfig(func(cfg *model.Config) { *cfg.FileSettings.MaxImageResolution = maxResSize }) + } + }, + }, } for _, useMultipart := range []bool{true, false} { diff --git a/app/brand.go b/app/brand.go index df23f5b55a..b543bbfff8 100644 --- a/app/brand.go +++ b/app/brand.go @@ -28,7 +28,7 @@ func (a *App) SaveBrandImage(imageData *multipart.FileHeader) *model.AppError { } defer file.Close() - if err = checkImageLimits(file); err != nil { + if err = checkImageLimits(file, *a.Config().FileSettings.MaxImageResolution); err != nil { return model.NewAppError("SaveBrandImage", "brand.save_brand_image.check_image_limits.app_error", nil, err.Error(), http.StatusBadRequest) } diff --git a/app/file.go b/app/file.go index 1d54bcf3dc..ce496fe824 100644 --- a/app/file.go +++ b/app/file.go @@ -38,7 +38,6 @@ import ( ) const ( - maxImageRes = int64(6048 * 4032) // 24 megapixels, up to ~196MB as a raw image imageThumbnailWidth = 120 imageThumbnailHeight = 100 imagePreviewWidth = 1920 @@ -642,6 +641,7 @@ type UploadFileTask struct { teeInput io.Reader fileinfo *model.FileInfo maxFileSize int64 + maxImageRes int64 // Cached image data that (may) get initialized in preprocessImage and // is used in postprocessImage @@ -702,6 +702,7 @@ func (a *App) UploadFileX(c *request.Context, channelID, name string, input io.R Name: filepath.Base(name), Input: input, maxFileSize: *a.Config().FileSettings.MaxFileSize, + maxImageRes: *a.Config().FileSettings.MaxImageResolution, imgDecoder: a.srv.imgDecoder, imgEncoder: a.srv.imgEncoder, } @@ -806,7 +807,7 @@ func (t *UploadFileTask) preprocessImage() *model.AppError { t.fileinfo.Width = w t.fileinfo.Height = h - if err = checkImageResolutionLimit(w, h); err != nil { + if err = checkImageResolutionLimit(w, h, t.maxImageRes); err != nil { return t.newAppError("api.file.upload_file.large_image_detailed.app_error", http.StatusBadRequest) } @@ -973,7 +974,7 @@ func (a *App) DoUploadFileExpectModification(c *request.Context, now time.Time, info.Path = pathPrefix + filename if info.IsImage() { - if limitErr := checkImageResolutionLimit(info.Width, info.Height); limitErr != nil { + if limitErr := checkImageResolutionLimit(info.Width, info.Height, *a.Config().FileSettings.MaxImageResolution); limitErr != nil { err := model.NewAppError("uploadFile", "api.file.upload_file.large_image.app_error", map[string]interface{}{"Filename": filename}, limitErr.Error(), http.StatusBadRequest) return nil, data, err } diff --git a/app/image.go b/app/image.go index 0ef51622a6..8ad2fc2ef5 100644 --- a/app/image.go +++ b/app/image.go @@ -10,23 +10,23 @@ import ( "github.com/mattermost/mattermost-server/v6/app/imaging" ) -func checkImageResolutionLimit(w, h int) error { +func checkImageResolutionLimit(w, h int, maxRes int64) error { // This casting is done to prevent overflow on 32 bit systems (not needed // in 64 bits systems because images can't have more than 32 bits height or // width) imageRes := int64(w) * int64(h) - if imageRes > maxImageRes { - return fmt.Errorf("image resolution is too high: %d, max allowed is %d", imageRes, maxImageRes) + if imageRes > maxRes { + return fmt.Errorf("image resolution is too high: %d, max allowed is %d", imageRes, maxRes) } return nil } -func checkImageLimits(imageData io.Reader) error { +func checkImageLimits(imageData io.Reader, maxRes int64) error { w, h, err := imaging.GetDimensions(imageData) if err != nil { return fmt.Errorf("failed to get image dimensions: %w", err) } - return checkImageResolutionLimit(w, h) + return checkImageResolutionLimit(w, h, maxRes) } diff --git a/app/team.go b/app/team.go index bb980ce05a..a5e1063ebc 100644 --- a/app/team.go +++ b/app/team.go @@ -1938,7 +1938,7 @@ func (a *App) SetTeamIconFromMultiPartFile(teamID string, file multipart.File) * return model.NewAppError("setTeamIcon", "api.team.set_team_icon.storage.app_error", nil, "", http.StatusNotImplemented) } - if limitErr := checkImageLimits(file); limitErr != nil { + if limitErr := checkImageLimits(file, *a.Config().FileSettings.MaxImageResolution); limitErr != nil { return model.NewAppError("SetTeamIcon", "api.team.set_team_icon.check_image_limits.app_error", nil, limitErr.Error(), http.StatusBadRequest) } diff --git a/app/upload.go b/app/upload.go index a8a3dce8ac..5470a64687 100644 --- a/app/upload.go +++ b/app/upload.go @@ -265,7 +265,7 @@ func (a *App) UploadData(c *request.Context, us *model.UploadSession, rd io.Read // image post-processing if info.IsImage() { - if limitErr := checkImageResolutionLimit(info.Width, info.Height); limitErr != nil { + if limitErr := checkImageResolutionLimit(info.Width, info.Height, *a.Config().FileSettings.MaxImageResolution); limitErr != nil { return nil, model.NewAppError("uploadData", "app.upload.upload_data.large_image.app_error", map[string]interface{}{"Filename": us.Filename, "Width": info.Width, "Height": info.Height}, "", http.StatusBadRequest) } diff --git a/app/user.go b/app/user.go index 6a5f6afb90..e9f3d0dbd7 100644 --- a/app/user.go +++ b/app/user.go @@ -753,7 +753,7 @@ func (a *App) SetProfileImage(userID string, imageData *multipart.FileHeader) *m } func (a *App) SetProfileImageFromMultiPartFile(userID string, file multipart.File) *model.AppError { - if limitErr := checkImageLimits(file); limitErr != nil { + if limitErr := checkImageLimits(file, *a.Config().FileSettings.MaxImageResolution); limitErr != nil { return model.NewAppError("SetProfileImage", "api.user.upload_profile_user.check_image_limits.app_error", nil, "", http.StatusBadRequest) } diff --git a/model/config.go b/model/config.go index 076da47a64..8c118bf65a 100644 --- a/model/config.go +++ b/model/config.go @@ -1403,6 +1403,7 @@ type FileSettings struct { EnableMobileUpload *bool `access:"site_file_sharing_and_downloads,cloud_restrictable"` EnableMobileDownload *bool `access:"site_file_sharing_and_downloads,cloud_restrictable"` MaxFileSize *int64 `access:"environment_file_storage,cloud_restrictable"` + MaxImageResolution *int64 `access:"environment_file_storage,cloud_restrictable"` DriverName *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"` Directory *string `access:"environment_file_storage,write_restrictable,cloud_restrictable"` EnablePublicLink *bool `access:"site_public_links,cloud_restrictable"` @@ -1436,7 +1437,11 @@ func (s *FileSettings) SetDefaults(isUpdate bool) { } if s.MaxFileSize == nil { - s.MaxFileSize = NewInt64(MB * 100) + s.MaxFileSize = NewInt64(100 * 1024 * 1024) // 100MB (IEC) + } + + if s.MaxImageResolution == nil { + s.MaxImageResolution = NewInt64(7680 * 4320) // 8K, ~33MPX } if s.DriverName == nil { diff --git a/model/utils.go b/model/utils.go index 501a64bf7f..66a383cc98 100644 --- a/model/utils.go +++ b/model/utils.go @@ -31,7 +31,6 @@ const ( UppercaseLetters = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" NUMBERS = "0123456789" SYMBOLS = " !\"\\#$%&'()*+,-./:;<=>?@[]^_`|~" - MB = 1 << 20 ) type StringInterface map[string]interface{} diff --git a/services/telemetry/telemetry.go b/services/telemetry/telemetry.go index a22c4be150..30d3a56a0d 100644 --- a/services/telemetry/telemetry.go +++ b/services/telemetry/telemetry.go @@ -557,6 +557,7 @@ func (ts *TelemetryService) trackConfig() { "amazon_s3_signv2": *cfg.FileSettings.AmazonS3SignV2, "amazon_s3_trace": *cfg.FileSettings.AmazonS3Trace, "max_file_size": *cfg.FileSettings.MaxFileSize, + "max_image_resolution": *cfg.FileSettings.MaxImageResolution, "enable_file_attachments": *cfg.FileSettings.EnableFileAttachments, "enable_mobile_upload": *cfg.FileSettings.EnableMobileUpload, "enable_mobile_download": *cfg.FileSettings.EnableMobileDownload, diff --git a/tests/test-config.json b/tests/test-config.json index 58920939c3..df132a5872 100644 --- a/tests/test-config.json +++ b/tests/test-config.json @@ -142,6 +142,7 @@ "EnableMobileUpload": true, "EnableMobileDownload": true, "MaxFileSize": 52428800, + "MaxImageResolution": 33177600, "DriverName": "local", "Directory": "./data/", "EnablePublicLink": false,