Initial commit: Open sourcing all of the Maple Open Technologies code.
This commit is contained in:
commit
755d54a99d
2010 changed files with 448675 additions and 0 deletions
188
cloud/maplefile-backend/internal/service/file/file_validator.go
Normal file
188
cloud/maplefile-backend/internal/service/file/file_validator.go
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
package file
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
dom_collection "codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/domain/collection"
|
||||
)
|
||||
|
||||
// File size limits (in bytes)
|
||||
const (
|
||||
// MaxFileSizeGeneral is the maximum file size for general folders (500MB)
|
||||
MaxFileSizeGeneral = 500 * 1024 * 1024
|
||||
|
||||
// MaxFileSizeAlbum is the maximum file size for album uploads (100MB)
|
||||
// Albums are for photos/videos, so we use a more restrictive limit
|
||||
MaxFileSizeAlbum = 100 * 1024 * 1024
|
||||
|
||||
// MaxThumbnailSize is the maximum thumbnail size (10MB)
|
||||
MaxThumbnailSize = 10 * 1024 * 1024
|
||||
)
|
||||
|
||||
// Allowed content types for albums (photos and videos only)
|
||||
var AlbumAllowedContentTypes = []string{
|
||||
// Image formats
|
||||
"image/jpeg",
|
||||
"image/jpg",
|
||||
"image/png",
|
||||
"image/gif",
|
||||
"image/webp",
|
||||
"image/heic",
|
||||
"image/heif",
|
||||
"image/bmp",
|
||||
"image/tiff",
|
||||
"image/svg+xml",
|
||||
|
||||
// Video formats
|
||||
"video/mp4",
|
||||
"video/mpeg",
|
||||
"video/quicktime", // .mov files
|
||||
"video/x-msvideo", // .avi files
|
||||
"video/x-matroska", // .mkv files
|
||||
"video/webm",
|
||||
"video/3gpp",
|
||||
"video/x-flv",
|
||||
}
|
||||
|
||||
// FileValidator provides file upload validation based on collection type
|
||||
type FileValidator struct{}
|
||||
|
||||
// NewFileValidator creates a new file validator
|
||||
func NewFileValidator() *FileValidator {
|
||||
return &FileValidator{}
|
||||
}
|
||||
|
||||
// ValidateFileUpload validates a file upload based on collection type and file properties
|
||||
// CWE-434: Unrestricted Upload of File with Dangerous Type
|
||||
// OWASP A01:2021: Broken Access Control - File upload restrictions
|
||||
func (v *FileValidator) ValidateFileUpload(
|
||||
collectionType string,
|
||||
fileSize int64,
|
||||
thumbnailSize int64,
|
||||
contentType string,
|
||||
) error {
|
||||
// Validate file size based on collection type
|
||||
if err := v.validateFileSize(collectionType, fileSize); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Validate thumbnail size if provided
|
||||
if thumbnailSize > 0 {
|
||||
if err := v.validateThumbnailSize(thumbnailSize); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Validate content type for albums (photos/videos only)
|
||||
if collectionType == dom_collection.CollectionTypeAlbum {
|
||||
if err := v.validateContentType(contentType); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// For folders (non-albums), no content-type restrictions
|
||||
// Users can upload any file type to regular folders
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateFileSize checks if the file size is within limits
|
||||
func (v *FileValidator) validateFileSize(collectionType string, fileSize int64) error {
|
||||
if fileSize <= 0 {
|
||||
return fmt.Errorf("file size must be greater than 0")
|
||||
}
|
||||
|
||||
var maxSize int64
|
||||
var collectionTypeName string
|
||||
|
||||
if collectionType == dom_collection.CollectionTypeAlbum {
|
||||
maxSize = MaxFileSizeAlbum
|
||||
collectionTypeName = "album"
|
||||
} else {
|
||||
maxSize = MaxFileSizeGeneral
|
||||
collectionTypeName = "folder"
|
||||
}
|
||||
|
||||
if fileSize > maxSize {
|
||||
return fmt.Errorf(
|
||||
"file size (%s) exceeds maximum allowed size for %s (%s)",
|
||||
formatBytes(fileSize),
|
||||
collectionTypeName,
|
||||
formatBytes(maxSize),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateThumbnailSize checks if the thumbnail size is within limits
|
||||
func (v *FileValidator) validateThumbnailSize(thumbnailSize int64) error {
|
||||
if thumbnailSize <= 0 {
|
||||
return nil // Thumbnail is optional
|
||||
}
|
||||
|
||||
if thumbnailSize > MaxThumbnailSize {
|
||||
return fmt.Errorf(
|
||||
"thumbnail size (%s) exceeds maximum allowed size (%s)",
|
||||
formatBytes(thumbnailSize),
|
||||
formatBytes(MaxThumbnailSize),
|
||||
)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// validateContentType checks if the content type is allowed for albums
|
||||
func (v *FileValidator) validateContentType(contentType string) error {
|
||||
if contentType == "" {
|
||||
return fmt.Errorf("content type is required for album uploads")
|
||||
}
|
||||
|
||||
// Normalize content type (lowercase and trim)
|
||||
normalizedType := strings.ToLower(strings.TrimSpace(contentType))
|
||||
|
||||
// Check if content type is in allowed list
|
||||
for _, allowedType := range AlbumAllowedContentTypes {
|
||||
if normalizedType == allowedType {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Errorf(
|
||||
"content type '%s' is not allowed in albums. Only photos and videos are permitted",
|
||||
contentType,
|
||||
)
|
||||
}
|
||||
|
||||
// GetAllowedContentTypes returns the list of allowed content types for albums
|
||||
func (v *FileValidator) GetAllowedContentTypes() []string {
|
||||
return AlbumAllowedContentTypes
|
||||
}
|
||||
|
||||
// GetMaxFileSize returns the maximum file size for a collection type
|
||||
func (v *FileValidator) GetMaxFileSize(collectionType string) int64 {
|
||||
if collectionType == dom_collection.CollectionTypeAlbum {
|
||||
return MaxFileSizeAlbum
|
||||
}
|
||||
return MaxFileSizeGeneral
|
||||
}
|
||||
|
||||
// GetMaxThumbnailSize returns the maximum thumbnail size
|
||||
func (v *FileValidator) GetMaxThumbnailSize() int64 {
|
||||
return MaxThumbnailSize
|
||||
}
|
||||
|
||||
// formatBytes formats byte size into human-readable format
|
||||
func formatBytes(bytes int64) string {
|
||||
const unit = 1024
|
||||
if bytes < unit {
|
||||
return fmt.Sprintf("%d B", bytes)
|
||||
}
|
||||
div, exp := int64(unit), 0
|
||||
for n := bytes / unit; n >= unit; n /= unit {
|
||||
div *= unit
|
||||
exp++
|
||||
}
|
||||
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue