Files
summercms/modules/lagoon/attach/guard.go
Jakub Zych 19f4cf8232 feat(12.2-01): add deferred bindings, guarded upload store and purge
- deferred_bindings migration set under summercms.deferred with backend_user_id
- lagoon.DeferredBind/Unbind/Bindings/Forget/Slaves scoped by DeferredKey
- lagoon.PurgeDeferred with SKIP LOCKED batches and after-commit blob deletes
- attach.Store with the ported image guard, extension and MIME limits
- attach.Relation, attach.HasRelations, attach.BlobKeys, File.ThumbKey
- lagoon README and attachments docs
2026-10-02 17:36:43 +02:00

48 lines
1.5 KiB
Go

package attach
import (
"bytes"
"image"
"net/http"
"slices"
)
// AllowedImageMIMEs are the content types IsAllowedImage accepts, as
// sniffed from the bytes: JPEG, PNG, GIF and WebP, the formats the
// thumbnailer decodes.
var AllowedImageMIMEs = []string{"image/jpeg", "image/png", "image/gif", "image/webp"}
// MaxImagePixels is the largest image (width times height) IsAllowedImage
// accepts, the same ceiling File.Thumb applies before decoding, so every
// accepted image can be thumbnailed.
const MaxImagePixels = maxThumbSourcePixels
// IsAllowedImage reports whether data is a JPEG, PNG, GIF or WebP image:
// the bytes must sniff as one of AllowedImageMIMEs (http.DetectContentType,
// independent of any file name or client header), the header must decode
// through image.DecodeConfig as that format with a positive width and
// height, and the image must not exceed MaxImagePixels. Decoding the header
// rejects a polyglot whose first bytes alone look right. It fails closed:
// empty or unreadable content is refused. data may be a prefix of the file
// as long as it holds the image header.
func IsAllowedImage(data []byte) bool {
if len(data) == 0 {
return false
}
if !slices.Contains(AllowedImageMIMEs, http.DetectContentType(data)) {
return false
}
cfg, format, err := image.DecodeConfig(bytes.NewReader(data))
if err != nil || cfg.Width <= 0 || cfg.Height <= 0 {
return false
}
if int64(cfg.Width)*int64(cfg.Height) > MaxImagePixels {
return false
}
switch format {
case "jpeg", "png", "gif", "webp":
return true
}
return false
}