grafana/pkg/services/store/validate.go
Artur Wierzbicki cc4473faf3
Storage: validation and sanitization stubs (#50523)
* add `IsPathValidationError` util to fs api

* refactor storage.Upload method

* remove unused struct

* extract `RootUpload` constant

* move file validation outside of the service

* Make UploadErrorToStatusCode exported

* validation/sanitization

* refactor pathValidationError check

* refactor, rename sanitize to transform

* add a todo

* refactor

* transform -> sanitize

* lint fix

* #50608: fix jpg/jpeg

Co-authored-by: Tania B <yalyna.ts@gmail.com>
Co-authored-by: Ryan McKinley <ryantxu@gmail.com>
2022-06-15 12:32:29 +04:00

91 lines
2.2 KiB
Go

package store
import (
"context"
"encoding/json"
"path/filepath"
"github.com/grafana/grafana/pkg/infra/filestorage"
"github.com/grafana/grafana/pkg/models"
)
var (
allowedImageExtensions = map[string]bool{
".jpg": true,
".jpeg": 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},
}
)
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 *models.SignedInUser, uploadRequest *UploadRequest) string {
// TODO: implement a spoofing-proof MimeType detection based on the contents
return uploadRequest.MimeType
}
func (s *standardStorageService) validateImage(ctx context.Context, user *models.SignedInUser, uploadRequest *UploadRequest) validationResult {
ext := filepath.Ext(uploadRequest.Path)
if !allowedImageExtensions[ext] {
return fail("unsupported extension")
}
mimeType := s.detectMimeType(ctx, user, uploadRequest)
if !imageExtensionsToMatchingMimeTypes[ext][mimeType] {
return fail("mismatched extension and file contents")
}
return success()
}
func (s *standardStorageService) validateUploadRequest(ctx context.Context, user *models.SignedInUser, req *UploadRequest, storagePath string) validationResult {
// TODO: validateSize
// TODO: validateProperties
if err := filestorage.ValidatePath(storagePath); err != nil {
return fail("path validation failed: " + err.Error())
}
switch req.EntityType {
case EntityTypeFolder:
fallthrough
case EntityTypeDashboard:
// TODO: add proper validation
var something interface{}
if err := json.Unmarshal(req.Contents, &something); err != nil {
return fail(err.Error())
}
return success()
case EntityTypeImage:
return s.validateImage(ctx, user, req)
default:
return fail("unknown entity")
}
}