MdpFileImportSecurity.cs 2.4 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556
  1. using System.IO.Compression;
  2. using System.Text;
  3. namespace Admin.NET.Plugin.AiDOP.DataPlatform.FileImport;
  4. public static class MdpFileImportSecurity
  5. {
  6. public const long MaxBytes = 20 * 1024 * 1024;
  7. public const int MaxRows = 50000;
  8. public const int MaxColumns = 80;
  9. public const int MaxCellChars = 2000;
  10. public static void EnsureXlsx(string fileName, long length, Stream stream)
  11. {
  12. if (length <= 0)
  13. throw new InvalidOperationException("空文件");
  14. if (length > MaxBytes)
  15. throw new InvalidOperationException("文件超过 20MB");
  16. var ext = Path.GetExtension(fileName);
  17. if (!string.Equals(ext, ".xlsx", StringComparison.OrdinalIgnoreCase))
  18. throw new InvalidOperationException("仅支持 .xlsx");
  19. if (fileName.EndsWith(".xlsm", StringComparison.OrdinalIgnoreCase) || fileName.Contains(".."))
  20. throw new InvalidOperationException("不允许宏文件或路径穿越");
  21. Span<byte> header = stackalloc byte[4];
  22. var read = stream.Read(header);
  23. if (stream.CanSeek) stream.Position = 0;
  24. if (read < 4 || header[0] != (byte)'P' || header[1] != (byte)'K')
  25. throw new InvalidOperationException("不是合法 xlsx(ZIP)文件");
  26. if (stream.CanSeek)
  27. {
  28. using var zip = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: true, Encoding.UTF8);
  29. if (zip.Entries.Any(e => e.FullName.Contains("vbaProject", StringComparison.OrdinalIgnoreCase)
  30. || e.FullName.Contains("externalLink", StringComparison.OrdinalIgnoreCase)))
  31. throw new InvalidOperationException("拒绝含宏或外部链接的工作簿");
  32. stream.Position = 0;
  33. }
  34. }
  35. public static void EnsureCell(string? value)
  36. {
  37. if (value != null && value.Length > MaxCellChars)
  38. throw new InvalidOperationException($"单元格超过 {MaxCellChars} 字符");
  39. if (value != null && value.StartsWith('='))
  40. throw new InvalidOperationException("不允许公式单元格");
  41. }
  42. public static void StripClientTenant(IDictionary<string, object?> row)
  43. {
  44. var keys = row.Keys.Where(k => k.Equals("tenant_id", StringComparison.OrdinalIgnoreCase)
  45. || k.Equals("TenantId", StringComparison.OrdinalIgnoreCase)).ToList();
  46. foreach (var k in keys)
  47. row.Remove(k);
  48. }
  49. }