- POST /_feedback/api/v1/{key}/submit validates as PHP, refuses non-image screenshots (ImageContentGuard copy) and stores the submission, its public screenshot and the G15Office job in one transaction
- PUT /_feedback/api/v1/me/hidden (jwt.auth) stores the caller's preference; feedback_widget_hidden joins every golem15.user payload
- preflights stay with the framework CORS layer
60 lines
1.7 KiB
Go
60 lines
1.7 KiB
Go
// Package classes holds the golem15.feedback services: the screenshot
|
|
// content guard, the G15Office client and the job that files a submission
|
|
// as a G15Office task.
|
|
package classes
|
|
|
|
import (
|
|
"bytes"
|
|
"image"
|
|
"net/http"
|
|
|
|
// The decoders image.DecodeConfig needs for the allowed formats; the
|
|
// webp decoder is registered by lagoon/attach.
|
|
_ "image/gif"
|
|
_ "image/jpeg"
|
|
_ "image/png"
|
|
|
|
_ "git.golem15.com/golem15/summercms/modules/lagoon/attach"
|
|
)
|
|
|
|
// AllowedImageMIMEs is ImageContentGuard::ALLOWED_IMAGE_MIMES.
|
|
var AllowedImageMIMEs = []string{"image/jpeg", "image/png", "image/gif", "image/webp"}
|
|
|
|
// SniffImageMIME is the finfo magic-byte sniff of ImageContentGuard: the
|
|
// content type the bytes themselves declare, independent of any file name
|
|
// or client header.
|
|
func SniffImageMIME(data []byte) string {
|
|
return http.DetectContentType(data)
|
|
}
|
|
|
|
// IsAllowedImage ports ImageContentGuard::isAllowedImage: the bytes must
|
|
// sniff as JPEG, PNG, GIF or WebP and must decode as that image
|
|
// (getimagesize), which rejects a polyglot whose header alone looks right.
|
|
// It fails closed: empty or unreadable content is refused. The WinterCMS
|
|
// plugins keep one copy of the guard each, and so do their ports.
|
|
func IsAllowedImage(data []byte) bool {
|
|
if len(data) == 0 {
|
|
return false
|
|
}
|
|
sniffed := SniffImageMIME(data)
|
|
allowed := false
|
|
for _, m := range AllowedImageMIMEs {
|
|
if sniffed == m {
|
|
allowed = true
|
|
break
|
|
}
|
|
}
|
|
if !allowed {
|
|
return false
|
|
}
|
|
cfg, format, err := image.DecodeConfig(bytes.NewReader(data))
|
|
if err != nil || cfg.Width <= 0 || cfg.Height <= 0 {
|
|
return false
|
|
}
|
|
switch format {
|
|
case "jpeg", "png", "gif", "webp":
|
|
return true
|
|
}
|
|
return false
|
|
}
|