tcp.ac/img.go

121 lines
2.9 KiB
Go
Raw Normal View History

2021-02-15 20:52:35 +00:00
package main
import (
"bytes"
2022-01-21 12:58:22 +00:00
"errors"
2022-07-18 10:41:29 +00:00
"image"
2022-01-21 12:58:22 +00:00
_ "image/gif"
_ "image/jpeg"
_ "image/png"
"io"
"net/http"
"strings"
2022-07-14 07:50:26 +00:00
_ "git.tcp.direct/kayos/common"
2021-02-15 20:52:35 +00:00
"github.com/gin-gonic/gin"
"github.com/rs/zerolog/log"
exifremove "github.com/scottleedavis/go-exif-remove"
)
2022-07-18 10:41:29 +00:00
type imageValidator struct{}
2021-02-15 20:52:35 +00:00
2022-07-18 10:41:29 +00:00
func (i imageValidator) finalize(data []byte) ([]byte, error) {
return data, nil
}
2022-01-21 12:58:22 +00:00
2022-07-18 10:41:29 +00:00
func readAndScrubImage(c any, readHead io.ReadSeeker) (scrubbed []byte, err error) {
cg := c.(*gin.Context)
imageFormat, err := checkImage(readHead)
2022-01-21 12:58:22 +00:00
if err != nil {
2021-02-15 20:52:35 +00:00
return
}
2022-07-18 10:41:29 +00:00
cg.Set("real.extension", imageFormat)
// dump this into a byte object and scrub it
// TO-DO: Write our own function for scrubbing exif
fbytes, err := io.ReadAll(readHead)
if err != nil {
2021-02-15 20:52:35 +00:00
return
}
2022-07-18 10:41:29 +00:00
scrubbed = fbytes
if imageFormat == "gif" {
2021-02-15 20:52:35 +00:00
return
}
2022-07-18 10:41:29 +00:00
scrubbed, err = exifremove.Remove(fbytes)
2021-02-15 20:52:35 +00:00
if err != nil {
return
}
2022-07-18 10:41:29 +00:00
return
}
2021-02-15 20:52:35 +00:00
2022-07-18 10:41:29 +00:00
func (i imageValidator) getContentType(c *gin.Context) (string, error) {
imageType, ok := c.Get("real.extension")
if !ok {
return "", errors.New("no filetype in context")
2021-02-15 20:52:35 +00:00
}
2022-07-18 10:41:29 +00:00
return "image/" + imageType.(string), nil
2021-02-15 20:52:35 +00:00
}
2022-07-18 10:41:29 +00:00
func (i imageValidator) checkURL(c *gin.Context) error {
2022-07-09 19:39:29 +00:00
sUID := strings.Split(c.Param("uid"), ".")
2022-07-18 10:41:29 +00:00
var fExt string
2022-07-09 19:39:29 +00:00
if len(sUID) > 1 {
fExt = strings.ToLower(sUID[1])
2022-07-18 10:41:29 +00:00
log.Trace().Str("caller", c.Request.RequestURI).Str("ext", fExt).Msg("detected file extension")
if fExt != "png" && fExt != "jpg" && fExt != "jpeg" && fExt != "gif" && fExt != "webm" {
return errors.New("bad file extension")
2021-02-15 20:52:35 +00:00
}
2022-07-18 10:41:29 +00:00
c.Set("url.extension", fExt)
2021-02-15 20:52:35 +00:00
}
2022-07-18 10:41:29 +00:00
return nil
2022-07-14 07:50:26 +00:00
}
2022-07-18 10:41:29 +00:00
func (i imageValidator) checkContent(c *gin.Context, data []byte) error {
readHead := bytes.NewReader(data)
var err error
_, err = readAndScrubImage(c, readHead)
2022-07-14 07:50:26 +00:00
if err != nil {
return err
}
2022-07-18 10:41:29 +00:00
urlExt, uExists := c.Get("url.extension")
bytExt, bExists := c.Get("real.extension")
if uExists && bExists && urlExt != bytExt {
return errors.New("bad file extension")
2022-01-21 12:58:22 +00:00
}
2022-07-18 10:41:29 +00:00
return nil
2022-01-21 12:58:22 +00:00
}
2022-07-18 10:41:29 +00:00
func (i imageValidator) checkAndScrubPost(c any) ([]byte, error) {
cg := c.(*gin.Context)
2022-01-21 12:58:22 +00:00
slog := log.With().Str("caller", "imgPost").
2022-07-18 10:41:29 +00:00
Str("User-Agent", cg.GetHeader("User-Agent")).
Str("RemoteAddr", cg.ClientIP()).Logger()
2021-02-15 20:52:35 +00:00
// check if incoming POST data is invalid
2022-07-18 10:41:29 +00:00
f, err := cg.FormFile("upload")
2022-01-21 12:58:22 +00:00
if err != nil || f == nil {
2022-07-14 07:50:26 +00:00
return nil, err
2021-02-15 20:52:35 +00:00
}
2021-07-28 07:31:34 +00:00
slog.Debug().Str("filename", f.Filename).Msg("[+] New upload")
2021-02-15 20:52:35 +00:00
// read the incoming file into an io file reader
file, err := f.Open()
if err != nil {
2022-07-18 10:41:29 +00:00
errThrow(cg, http.StatusInternalServerError, err, message500)
2022-07-14 07:50:26 +00:00
return nil, err
2021-02-15 20:52:35 +00:00
}
2022-07-18 10:41:29 +00:00
scrubbed, err := readAndScrubImage(c, file)
2022-01-21 12:58:22 +00:00
if err != nil {
2022-07-18 10:41:29 +00:00
errThrow(cg, http.StatusBadRequest, err, message400)
2022-07-14 07:50:26 +00:00
return nil, err
2021-02-15 20:52:35 +00:00
}
2022-07-14 07:50:26 +00:00
return scrubbed, nil
}
2021-02-15 20:52:35 +00:00
2022-07-18 10:41:29 +00:00
func checkImage(r io.ReadSeeker) (fmt string, err error) {
// in theory this makes sure the file is an image via magic bytes
_, fmt, err = image.Decode(r)
2021-02-15 20:52:35 +00:00
if err != nil {
2022-07-18 10:41:29 +00:00
return
2021-02-15 20:52:35 +00:00
}
2022-07-18 10:41:29 +00:00
_, err = r.Seek(0, 0)
return
2021-02-15 20:52:35 +00:00
}