using System.IO.Compression; using System.Text; namespace Admin.NET.Plugin.AiDOP.DataPlatform.FileImport; public static class MdpFileImportSecurity { public const long MaxBytes = 20 * 1024 * 1024; public const int MaxRows = 50000; public const int MaxColumns = 80; public const int MaxCellChars = 2000; public static void EnsureXlsx(string fileName, long length, Stream stream) { if (length <= 0) throw new InvalidOperationException("空文件"); if (length > MaxBytes) throw new InvalidOperationException("文件超过 20MB"); var ext = Path.GetExtension(fileName); if (!string.Equals(ext, ".xlsx", StringComparison.OrdinalIgnoreCase)) throw new InvalidOperationException("仅支持 .xlsx"); if (fileName.EndsWith(".xlsm", StringComparison.OrdinalIgnoreCase) || fileName.Contains("..")) throw new InvalidOperationException("不允许宏文件或路径穿越"); Span header = stackalloc byte[4]; var read = stream.Read(header); if (stream.CanSeek) stream.Position = 0; if (read < 4 || header[0] != (byte)'P' || header[1] != (byte)'K') throw new InvalidOperationException("不是合法 xlsx(ZIP)文件"); if (stream.CanSeek) { using var zip = new ZipArchive(stream, ZipArchiveMode.Read, leaveOpen: true, Encoding.UTF8); if (zip.Entries.Any(e => e.FullName.Contains("vbaProject", StringComparison.OrdinalIgnoreCase) || e.FullName.Contains("externalLink", StringComparison.OrdinalIgnoreCase))) throw new InvalidOperationException("拒绝含宏或外部链接的工作簿"); stream.Position = 0; } } public static void EnsureCell(string? value) { if (value != null && value.Length > MaxCellChars) throw new InvalidOperationException($"单元格超过 {MaxCellChars} 字符"); if (value != null && value.StartsWith('=')) throw new InvalidOperationException("不允许公式单元格"); } public static void StripClientTenant(IDictionary row) { var keys = row.Keys.Where(k => k.Equals("tenant_id", StringComparison.OrdinalIgnoreCase) || k.Equals("TenantId", StringComparison.OrdinalIgnoreCase)).ToList(); foreach (var k in keys) row.Remove(k); } }