package store import ( "context" "encoding/json" "fmt" "net/http" "path/filepath" "strings" "github.com/grafana/grafana/pkg/infra/filestorage" "github.com/grafana/grafana/pkg/services/user" "github.com/grafana/grafana/pkg/util" ) var ( allowedImageExtensions = map[string]bool{ ".jpg": true, ".jpeg": true, ".svg": true, ".gif": true, ".png": true, ".webp": true, } imageExtensionsToMatchingMimeTypes = map[string]map[string]bool{ ".jpg": {"image/jpg": true, "image/jpeg": true}, ".jpeg": {"image/jpg": true, "image/jpeg": true}, ".gif": {"image/gif": true}, ".png": {"image/png": true}, ".webp": {"image/webp": true}, ".svg": {"image/svg+xml": true}, } ) type validationResult struct { ok bool reason string } func success() validationResult { return validationResult{ ok: true, } } func fail(reason string) validationResult { return validationResult{ ok: false, reason: reason, } } func (s *standardStorageService) detectMimeType(ctx context.Context, user *user.SignedInUser, uploadRequest *UploadRequest) string { if strings.HasSuffix(uploadRequest.Path, ".svg") { if util.IsSVG(uploadRequest.Contents) { return "image/svg+xml" } } return http.DetectContentType(uploadRequest.Contents) } func (s *standardStorageService) validateImage(ctx context.Context, user *user.SignedInUser, uploadRequest *UploadRequest) validationResult { ext := filepath.Ext(uploadRequest.Path) if !allowedImageExtensions[ext] { return fail(fmt.Sprintf("unsupported extension: %s", ext)) } mimeType := s.detectMimeType(ctx, user, uploadRequest) if !imageExtensionsToMatchingMimeTypes[ext][mimeType] { return fail(fmt.Sprintf("extension '%s' does not match the detected MimeType: %s", ext, mimeType)) } return success() } func (s *standardStorageService) validateUploadRequest(ctx context.Context, user *user.SignedInUser, req *UploadRequest, storagePath string) validationResult { // TODO: validateSize // TODO: validateProperties if err := filestorage.ValidatePath(storagePath); err != nil { return fail(fmt.Sprintf("path validation failed. error: %s. path: %s", err.Error(), storagePath)) } switch req.EntityType { case EntityTypeJSON: fallthrough case EntityTypeFolder: fallthrough case EntityTypeDashboard: // TODO: add proper validation if !json.Valid(req.Contents) { return fail("invalid json") } return success() case EntityTypeImage: return s.validateImage(ctx, user, req) default: return fail("unknown entity") } }