Refactor filevalidation.FileValidator constructor with options

Signed-off-by: Émile Ré <nemile.re@gmail.com>
This commit is contained in:
Émile Ré
2025-12-10 15:42:06 +01:00
parent 5e619664ac
commit aad86e08a3
4 changed files with 89 additions and 75 deletions

View File

@@ -39,20 +39,32 @@ type ContextType = {
};
const acceptedFileTypes = {
"application/csv": [".csv"],
"application/json": [".json"],
"application/msword": [".doc"],
"application/pdf": [".pdf"],
"application/vnd.ms-excel": [".xls"],
"application/vnd.ms-powerpoint": [".ppt"],
"application/vnd.oasis.opendocument.presentation": [".odp"],
"application/vnd.oasis.opendocument.spreadsheet": [".ods"],
"application/vnd.oasis.opendocument.text": [".odt"],
"application/vnd.openxmlformats-officedocument.presentationml.presentation": [".pptx"],
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": [".xlsx"],
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": [".docx"],
"application/yaml": [".yaml", ".yml"],
"image/gif": [".gif"],
"image/jpeg": [".jpeg", ".jpg"],
"image/png": [".png"],
"image/svg+xml": [".svg"],
"image/webp": [".webp"],
"text/csv": [".csv"],
"text/json": [".json"],
"text/markdown": [".md"],
"text/plain": [".txt"],
"text/uri-list; charset=utf-8": [".uri"],
"text/uri-list": [".uri"],
"text/x-log": [".log"],
"text/yaml": [".yaml", ".yml"],
}
export default function TrustCenterFilesTab() {

View File

@@ -105,16 +105,54 @@ type FileValidator struct {
Categories []string
}
type Opt func(v *FileValidator) *FileValidator
func WithCategories(categories ...string) Opt {
return func(v *FileValidator) *FileValidator {
v.Categories = categories
categoryMap := make(map[string]bool)
for _, category := range categories {
categoryMap[category] = true
}
for _, fileType := range FileTypes {
if categoryMap[fileType.Category] {
v.AllowedMimeTypes[fileType.MimeType] = true
for _, ext := range fileType.Extensions {
if v.AllowedExtensions[ext] == nil {
v.AllowedExtensions[ext] = []string{}
}
v.AllowedExtensions[ext] = append(v.AllowedExtensions[ext], fileType.MimeType)
}
}
}
return v
}
}
func WithMaxFileSize(maxFileSize int64) Opt {
return func(v *FileValidator) *FileValidator {
v.MaxFileSize = maxFileSize
return v
}
}
// NewValidator creates a new file validator using supported file types
func NewValidator(categories ...string) *FileValidator {
func NewValidator(opts ...Opt) *FileValidator {
v := &FileValidator{
MaxFileSize: DefaultMaxFileSize,
AllowedMimeTypes: make(map[string]bool),
AllowedExtensions: make(map[string][]string),
Categories: categories,
}
if len(categories) == 0 {
for _, opt := range opts {
v = opt(v)
}
if len(v.Categories) == 0 {
for _, fileType := range FileTypes {
v.AllowedMimeTypes[fileType.MimeType] = true
for _, ext := range fileType.Extensions {
@@ -124,24 +162,6 @@ func NewValidator(categories ...string) *FileValidator {
v.AllowedExtensions[ext] = append(v.AllowedExtensions[ext], fileType.MimeType)
}
}
return v
}
categoryMap := make(map[string]bool)
for _, category := range categories {
categoryMap[category] = true
}
for _, fileType := range FileTypes {
if categoryMap[fileType.Category] {
v.AllowedMimeTypes[fileType.MimeType] = true
for _, ext := range fileType.Extensions {
if v.AllowedExtensions[ext] == nil {
v.AllowedExtensions[ext] = []string{}
}
v.AllowedExtensions[ext] = append(v.AllowedExtensions[ext], fileType.MimeType)
}
}
}
return v

View File

@@ -60,7 +60,7 @@ func TestNewValidator(t *testing.T) {
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
v := NewValidator(tc.categories...)
v := NewValidator(WithCategories(tc.categories...))
// Test expected mime types are allowed
for _, mime := range tc.expectedMimes {
@@ -123,7 +123,7 @@ func TestValidate(t *testing.T) {
}{
{
name: "Valid PDF file",
validator: NewValidator(CategoryDocument),
validator: NewValidator(WithCategories(CategoryDocument)),
filename: "test.pdf",
contentType: "application/pdf",
fileSize: 1024 * 1024, // 1MB
@@ -140,7 +140,7 @@ func TestValidate(t *testing.T) {
},
{
name: "Disallowed content type",
validator: NewValidator(CategoryDocument),
validator: NewValidator(WithCategories(CategoryDocument)),
filename: "test.jpg",
contentType: "image/jpeg",
fileSize: 1024 * 1024,
@@ -158,7 +158,7 @@ func TestValidate(t *testing.T) {
},
{
name: "Disallowed file extension",
validator: NewValidator(CategoryDocument),
validator: NewValidator(WithCategories(CategoryDocument)),
filename: "test.exe",
contentType: "application/octet-stream",
fileSize: 1024,
@@ -176,7 +176,7 @@ func TestValidate(t *testing.T) {
},
{
name: "Valid image file",
validator: NewValidator(CategoryImage),
validator: NewValidator(WithCategories(CategoryImage)),
filename: "test.jpg",
contentType: "image/jpeg",
fileSize: 1024 * 1024,
@@ -184,7 +184,7 @@ func TestValidate(t *testing.T) {
},
{
name: "Valid with multiple categories",
validator: NewValidator(CategoryImage, CategoryDocument),
validator: NewValidator(WithCategories(CategoryImage, CategoryDocument)),
filename: "test.jpg",
contentType: "image/jpeg",
fileSize: 1024 * 1024,
@@ -315,7 +315,7 @@ func TestFileCategories(t *testing.T) {
for _, tc := range categoryTests {
t.Run(tc.category, func(t *testing.T) {
v := NewValidator(tc.category)
v := NewValidator(WithCategories(tc.category))
// Test that the validator accepts files of this category
err := v.Validate("test"+tc.validExt, tc.validMimeType, 1024)
@@ -381,7 +381,7 @@ func TestMultipleExtensionsPerMimeType(t *testing.T) {
// Test MIME types that have multiple allowed extensions
for _, fileType := range FileTypes {
if len(fileType.Extensions) > 1 {
v := NewValidator(fileType.Category)
v := NewValidator(WithCategories(fileType.Category))
// All extensions for this MIME type should be valid
for _, ext := range fileType.Extensions {
@@ -434,7 +434,7 @@ func BenchmarkValidate(b *testing.B) {
// BenchmarkNewValidator benchmarks the NewValidator function
func BenchmarkNewValidator(b *testing.B) {
for i := 0; i < b.N; i++ {
_ = NewValidator(CategoryDocument, CategoryImage)
_ = NewValidator(WithCategories(CategoryDocument, CategoryImage))
}
}

View File

@@ -180,13 +180,15 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
tenantService.Evidences = &EvidenceService{
svc: tenantService,
fileValidator: filevalidation.NewValidator(
filevalidation.CategoryDocument,
filevalidation.CategorySpreadsheet,
filevalidation.CategoryPresentation,
filevalidation.CategoryData,
filevalidation.CategoryText,
filevalidation.CategoryImage,
filevalidation.CategoryVideo,
filevalidation.WithCategories(
filevalidation.CategoryDocument,
filevalidation.CategorySpreadsheet,
filevalidation.CategoryPresentation,
filevalidation.CategoryData,
filevalidation.CategoryText,
filevalidation.CategoryImage,
filevalidation.CategoryVideo,
),
),
}
tenantService.Peoples = &PeopleService{svc: tenantService}
@@ -198,12 +200,17 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
tenantService.Organizations = &OrganizationService{
svc: tenantService,
fileValidator: filevalidation.NewValidator(
filevalidation.CategoryImage,
filevalidation.WithCategories(filevalidation.CategoryImage),
),
}
tenantService.Controls = &ControlService{svc: tenantService}
tenantService.Risks = &RiskService{svc: tenantService}
tenantService.VendorComplianceReports = &VendorComplianceReportService{svc: tenantService, fileValidator: filevalidation.NewValidator(filevalidation.CategoryDocument)}
tenantService.VendorComplianceReports = &VendorComplianceReportService{
svc: tenantService,
fileValidator: filevalidation.NewValidator(
filevalidation.WithCategories(filevalidation.CategoryDocument),
),
}
tenantService.VendorBusinessAssociateAgreements = &VendorBusinessAssociateAgreementService{svc: tenantService}
tenantService.VendorContacts = &VendorContactService{svc: tenantService}
tenantService.VendorDataPrivacyAgreements = &VendorDataPrivacyAgreementService{svc: tenantService}
@@ -219,42 +226,17 @@ func (s *Service) WithTenant(tenantID gid.TenantID) *TenantService {
tenantService.TrustCenterReferences = &TrustCenterReferenceService{svc: tenantService}
tenantService.TrustCenterFiles = &TrustCenterFileService{
svc: tenantService,
fileValidator: &filevalidation.FileValidator{
MaxFileSize: 10 * 1024 * 1024, // 10MB
AllowedMimeTypes: map[string]bool{
"application/json": true,
"application/msword": true,
"application/pdf": true,
"application/vnd.ms-excel": true,
"application/vnd.ms-powerpoint": true,
"application/vnd.openxmlformats-officedocument.presentationml.presentation": true,
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": true,
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": true,
"image/jpeg": true,
"image/png": true,
"image/svg+xml": true,
"image/webp": true,
"text/csv": true,
"text/markdown": true,
},
AllowedExtensions: map[string][]string{
".csv": {"text/csv"},
".doc": {"application/msword"},
".docx": {"application/vnd.openxmlformats-officedocument.wordprocessingml.document"},
".jpeg": {"image/jpeg"},
".jpg": {"image/jpeg"},
".json": {"application/json"},
".md": {"text/markdown"},
".pdf": {"application/pdf"},
".png": {"image/png"},
".ppt": {"application/vnd.ms-powerpoint"},
".pptx": {"application/vnd.openxmlformats-officedocument.presentationml.presentation"},
".svg": {"image/svg+xml"},
".webp": {"image/webp"},
".xls": {"application/vnd.ms-excel"},
".xlsx": {"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"},
},
},
fileValidator: filevalidation.NewValidator(
filevalidation.WithCategories(
filevalidation.CategoryData,
filevalidation.CategoryDocument,
filevalidation.CategoryImage,
filevalidation.CategoryPresentation,
filevalidation.CategorySpreadsheet,
filevalidation.CategoryText,
),
filevalidation.WithMaxFileSize(10*1024*1024), // 10MB
),
}
tenantService.Nonconformities = &NonconformityService{svc: tenantService}
tenantService.Obligations = &ObligationService{svc: tenantService}