Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

public static class FileExtensions
{
/// <summary>
/// Hard upper bound (in KB) for a single file-upload attribute (contact/checkout/product attribute uploads),
/// enforced regardless of the per-attribute ValidationFileMaximumSize configuration. Must be checked against
/// IFormFile.Length before the request body is buffered into memory.
/// </summary>
public const int MaxAttributeUploadFileSizeKb = 10 * 1024; // 10 MB

public static IList<string> GetAllowedMediaFileTypes(string allowedFileTypes)
{
if (string.IsNullOrEmpty(allowedFileTypes))
Expand Down
51 changes: 24 additions & 27 deletions src/Web/Grand.Web/Controllers/ContactController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,35 +136,32 @@ public virtual async Task<IActionResult> 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
});
}
//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
});

var fileBinary = file.GetDownloadBits();
//enforce a hard cap regardless of attribute configuration, and check it against the size reported by the
//multipart headers before buffering the file into memory
var maxFileSizeKb = attribute.ValidationFileMaximumSize.HasValue
? Math.Min(attribute.ValidationFileMaximumSize.Value, FileExtensions.MaxAttributeUploadFileSizeKb)
: FileExtensions.MaxAttributeUploadFileSizeKb;
if (file.Length > maxFileSizeKb * 1024L)
//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
{
success = false,
message = string.Format(_translationService.GetResource("ContactUs.MaximumUploadedFileSize"), maxFileSizeKb),
downloadGuid = Guid.Empty
});

if (attribute.ValidationFileMaximumSize.HasValue)
{
//compare in bytes
var maxFileSizeBytes = attribute.ValidationFileMaximumSize.Value * 1024;
if (fileBinary.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
{
success = false,
message = string.Format(_translationService.GetResource("ContactUs.MaximumUploadedFileSize"),
attribute.ValidationFileMaximumSize.Value),
downloadGuid = Guid.Empty
});
}
var fileBinary = file.GetDownloadBits();

var download = new Download
{
Expand Down
48 changes: 23 additions & 25 deletions src/Web/Grand.Web/Controllers/ProductController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -405,32 +405,30 @@ public virtual async Task<IActionResult> 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)
//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 {
success = false,
message = string.Format(_translationService.GetResource("ShoppingCart.MaximumUploadedFileSize"),
attribute.ValidationFileMaximumSize.Value),
downloadGuid = Guid.Empty
});
}
//enforce a hard cap regardless of attribute configuration, and check it against the size reported by the
//multipart headers before buffering the file into memory
var maxFileSizeKb = attribute.ValidationFileMaximumSize.HasValue
? Math.Min(attribute.ValidationFileMaximumSize.Value, FileExtensions.MaxAttributeUploadFileSizeKb)
: FileExtensions.MaxAttributeUploadFileSizeKb;
if (file.Length > maxFileSizeKb * 1024L)
//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 {
success = false,
message = string.Format(_translationService.GetResource("ShoppingCart.MaximumUploadedFileSize"), maxFileSizeKb),
downloadGuid = Guid.Empty
});

var fileBinary = file.GetDownloadBits();

var download = new Download {
DownloadGuid = Guid.NewGuid(),
Expand Down
46 changes: 22 additions & 24 deletions src/Web/Grand.Web/Controllers/ShoppingCartController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -192,32 +192,30 @@ public virtual async Task<IActionResult> 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
});

//enforce a hard cap regardless of attribute configuration, and check it against the size reported by the
//multipart headers before buffering the file into memory
var maxFileSizeKb = attribute.ValidationFileMaximumSize.HasValue
? Math.Min(attribute.ValidationFileMaximumSize.Value, FileExtensions.MaxAttributeUploadFileSizeKb)
: FileExtensions.MaxAttributeUploadFileSizeKb;
if (file.Length > maxFileSizeKb * 1024L)
//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 {
success = false,
message = string.Format(_translationService.GetResource("ShoppingCart.MaximumUploadedFileSize"), maxFileSizeKb),
downloadGuid = Guid.Empty
});

var fileBinary = file.GetDownloadBits();
if (attribute.ValidationFileMaximumSize.HasValue)
{
//compare in bytes
var maxFileSizeBytes = attribute.ValidationFileMaximumSize.Value * 1024;
if (fileBinary.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 {
success = false,
message = string.Format(_translationService.GetResource("ShoppingCart.MaximumUploadedFileSize"),
attribute.ValidationFileMaximumSize.Value),
downloadGuid = Guid.Empty
});
}

var download = new Download {
DownloadGuid = Guid.NewGuid(),
Expand Down
Loading