shoc-backend/SeaHaven.Services/Helpers/WorkOrderCompletionDocFileRules.cs

71 lines
2.6 KiB
C#
Raw Normal View History

using Microsoft.AspNetCore.Http;
namespace SeaHaven.Services.Helpers
{
/// <summary>SH-337 file type allowlist for work-order completion documents.</summary>
public static class WorkOrderCompletionDocFileRules
{
private const string PdfContentType = "application/pdf";
private static readonly HashSet<string> AllowedExtensions =
new(StringComparer.OrdinalIgnoreCase) { ".pdf" };
// A browser sets the multipart part's Content-Type from File.type, which the OS
// sometimes leaves empty for a PDF. The completion-doc dialog already accepts
// that case on the client (a .pdf name with no type passes), so rejecting it
// here would fail uploads that work today. The %PDF- signature is the real gate.
private static readonly HashSet<string> UndeterminedContentTypes =
new(StringComparer.OrdinalIgnoreCase) { "application/octet-stream" };
public static bool IsAllowed(IFormFile file)
{
if (file == null || file.Length <= 0)
return false;
var declaredType = (file.ContentType ?? string.Empty).Trim();
if (!string.IsNullOrWhiteSpace(declaredType)
&& !declaredType.Equals(PdfContentType, StringComparison.OrdinalIgnoreCase)
&& !UndeterminedContentTypes.Contains(declaredType))
{
return false;
}
var extension = Path.GetExtension(file.FileName ?? string.Empty);
if (string.IsNullOrWhiteSpace(extension) || !AllowedExtensions.Contains(extension))
return false;
try
{
using var stream = file.OpenReadStream();
var headerLength = (int)Math.Min(Math.Max(file.Length, 0), 64);
if (headerLength == 0)
return false;
var header = new byte[headerLength];
var read = stream.Read(header, 0, header.Length);
if (read <= 0)
return false;
if (read < header.Length)
Array.Resize(ref header, read);
return WorkOrderMediaFileRules.MatchesSignature(PdfContentType, header);
}
catch
{
return false;
}
}
public static void EnsureAllowed(IFormFile file)
{
if (!IsAllowed(file))
{
throw new Exceptions.WorkOrderBoardValidationException(
"UnsupportedMediaType",
"The completion document must be a PDF file.");
}
}
}
}