diff --git a/src/Web/Grand.Web/App_Data/appsettings.json b/src/Web/Grand.Web/App_Data/appsettings.json index 124777464..5861e41ae 100644 --- a/src/Web/Grand.Web/App_Data/appsettings.json +++ b/src/Web/Grand.Web/App_Data/appsettings.json @@ -14,8 +14,9 @@ //with other HTTP clients. "AllowNonAsciiCharInHeaders": false, //Gets or sets the maximum allowed size of any request body in bytes - //the default value is 30MB - "MaxRequestBodySize": null, + //null falls back to Kestrel's default of 30MB; set explicitly so anonymous storefront endpoints + //(contact/checkout/product attribute file uploads) can't be used to buffer oversized requests in memory + "MaxRequestBodySize": 10485760, //max 2147483648 //Gets or sets the value to enable a middleware for logging additional information about CurrentCustomer and CurrentStore diff --git a/src/Web/Grand.Web/Controllers/ContactController.cs b/src/Web/Grand.Web/Controllers/ContactController.cs index 6ed39bbcc..4a3a5bc81 100644 --- a/src/Web/Grand.Web/Controllers/ContactController.cs +++ b/src/Web/Grand.Web/Controllers/ContactController.cs @@ -136,25 +136,21 @@ public virtual async Task UploadFileContactAttribute(string attri var fileName = Path.GetFileName(file.FileName); var contentType = file.ContentType; var fileExtension = Path.GetExtension(fileName); - if (!string.IsNullOrEmpty(attribute.ValidationFileAllowedExtensions)) - { - var allowedFileExtensions = attribute.ValidationFileAllowedExtensions.Split(',', StringSplitOptions.RemoveEmptyEntries); - if (!allowedFileExtensions.IsAllowedMediaFileType(fileExtension)) - return Json(new - { - success = false, - message = _translationService.GetResource("ContactUs.ValidationFileAllowed"), - downloadGuid = Guid.Empty - }); - } - - var fileBinary = file.GetDownloadBits(); + //empty configuration must not mean "any extension allowed" - fall back to the safe default allow-list + var allowedFileExtensions = FileExtensions.GetAllowedMediaFileTypes(attribute.ValidationFileAllowedExtensions); + if (!allowedFileExtensions.IsAllowedMediaFileType(fileExtension)) + return Json(new + { + success = false, + message = _translationService.GetResource("ContactUs.ValidationFileAllowed"), + downloadGuid = Guid.Empty + }); if (attribute.ValidationFileMaximumSize.HasValue) { - //compare in bytes - var maxFileSizeBytes = attribute.ValidationFileMaximumSize.Value * 1024; - if (fileBinary.Length > maxFileSizeBytes) + //compare in bytes - check the size reported by the multipart headers before buffering the file into memory + var maxFileSizeBytes = attribute.ValidationFileMaximumSize.Value * 1024L; + if (file.Length > maxFileSizeBytes) //when returning JSON the mime-type must be set to text/plain //otherwise some browsers will pop-up a "Save As" dialog. return Json(new @@ -166,6 +162,8 @@ public virtual async Task UploadFileContactAttribute(string attri }); } + var fileBinary = file.GetDownloadBits(); + var download = new Download { DownloadGuid = Guid.NewGuid(), diff --git a/src/Web/Grand.Web/Controllers/ProductController.cs b/src/Web/Grand.Web/Controllers/ProductController.cs index c78d5f150..7e67a51ba 100644 --- a/src/Web/Grand.Web/Controllers/ProductController.cs +++ b/src/Web/Grand.Web/Controllers/ProductController.cs @@ -405,23 +405,20 @@ public virtual async Task UploadFileProductAttribute(string attri var contentType = file.ContentType; var fileExtension = Path.GetExtension(fileName); - if (!string.IsNullOrEmpty(attribute.ValidationFileAllowedExtensions)) - { - var allowedFileExtensions = attribute.ValidationFileAllowedExtensions.Split([','], StringSplitOptions.RemoveEmptyEntries); - if (!allowedFileExtensions.IsAllowedMediaFileType(fileExtension)) - return Json(new { - success = false, - message = _translationService.GetResource("ShoppingCart.ValidationFileAllowed"), - downloadGuid = Guid.Empty - }); - } - var fileBinary = file.GetDownloadBits(); + //empty configuration must not mean "any extension allowed" - fall back to the safe default allow-list + var allowedFileExtensions = FileExtensions.GetAllowedMediaFileTypes(attribute.ValidationFileAllowedExtensions); + if (!allowedFileExtensions.IsAllowedMediaFileType(fileExtension)) + return Json(new { + success = false, + message = _translationService.GetResource("ShoppingCart.ValidationFileAllowed"), + downloadGuid = Guid.Empty + }); if (attribute.ValidationFileMaximumSize.HasValue) { - //compare in bytes - var maxFileSizeBytes = attribute.ValidationFileMaximumSize.Value * 1024; - if (fileBinary.Length > maxFileSizeBytes) + //compare in bytes - check the size reported by the multipart headers before buffering the file into memory + var maxFileSizeBytes = attribute.ValidationFileMaximumSize.Value * 1024L; + if (file.Length > maxFileSizeBytes) //when returning JSON the mime-type must be set to text/plain //otherwise some browsers will pop-up a "Save As" dialog. return Json(new { @@ -432,6 +429,8 @@ public virtual async Task UploadFileProductAttribute(string attri }); } + var fileBinary = file.GetDownloadBits(); + var download = new Download { DownloadGuid = Guid.NewGuid(), CustomerId = _contextAccessor.WorkContext.CurrentCustomer.Id, diff --git a/src/Web/Grand.Web/Controllers/ShoppingCartController.cs b/src/Web/Grand.Web/Controllers/ShoppingCartController.cs index 07baa8ad1..1633fd25f 100644 --- a/src/Web/Grand.Web/Controllers/ShoppingCartController.cs +++ b/src/Web/Grand.Web/Controllers/ShoppingCartController.cs @@ -192,23 +192,20 @@ public virtual async Task UploadFileCheckoutAttribute(string attr var contentType = file.ContentType; var fileExtension = Path.GetExtension(fileName); - if (!string.IsNullOrEmpty(attribute.ValidationFileAllowedExtensions)) - { - var allowedFileExtensions = attribute.ValidationFileAllowedExtensions.Split([','], StringSplitOptions.RemoveEmptyEntries); - if (!allowedFileExtensions.IsAllowedMediaFileType(fileExtension)) - return Json(new { - success = false, - message = _translationService.GetResource("ShoppingCart.ValidationFileAllowed"), - downloadGuid = Guid.Empty - }); - } + //empty configuration must not mean "any extension allowed" - fall back to the safe default allow-list + var allowedFileExtensions = FileExtensions.GetAllowedMediaFileTypes(attribute.ValidationFileAllowedExtensions); + if (!allowedFileExtensions.IsAllowedMediaFileType(fileExtension)) + return Json(new { + success = false, + message = _translationService.GetResource("ShoppingCart.ValidationFileAllowed"), + downloadGuid = Guid.Empty + }); - var fileBinary = file.GetDownloadBits(); if (attribute.ValidationFileMaximumSize.HasValue) { - //compare in bytes - var maxFileSizeBytes = attribute.ValidationFileMaximumSize.Value * 1024; - if (fileBinary.Length > maxFileSizeBytes) + //compare in bytes - check the size reported by the multipart headers before buffering the file into memory + var maxFileSizeBytes = attribute.ValidationFileMaximumSize.Value * 1024L; + if (file.Length > maxFileSizeBytes) //when returning JSON the mime-type must be set to text/plain //otherwise some browsers will pop-up a "Save As" dialog. return Json(new { @@ -219,6 +216,8 @@ public virtual async Task UploadFileCheckoutAttribute(string attr }); } + var fileBinary = file.GetDownloadBits(); + var download = new Download { DownloadGuid = Guid.NewGuid(), CustomerId = _contextAccessor.WorkContext.CurrentCustomer.Id,