Prevent image decode panics, handle errors gracefully
Some checks failed
Run Integration Tests / test (push) Failing after 44s

This commit is contained in:
partisan 2025-06-27 08:29:50 +02:00
parent fa266ec993
commit 40f322ef01
2 changed files with 34 additions and 47 deletions

View file

@ -142,26 +142,12 @@ func cacheImage(imageURL, imageID string, imageType string) (string, bool, error
return cachedImagePath, true, nil
}
// Decode the image based on the content type
var img image.Image
switch contentType {
case "image/x-icon", "image/vnd.microsoft.icon":
img, err = ico.Decode(bytes.NewReader(data))
case "image/jpeg":
img, err = jpeg.Decode(bytes.NewReader(data))
case "image/png":
img, err = png.Decode(bytes.NewReader(data))
case "image/gif":
img, err = gif.Decode(bytes.NewReader(data))
case "image/webp":
img, err = webp.Decode(bytes.NewReader(data))
case "image/bmp":
img, err = bmp.Decode(bytes.NewReader(data))
case "image/tiff":
img, err = tiff.Decode(bytes.NewReader(data))
default:
// Decode image
img, err := safeDecodeImage(contentType, data)
if err != nil {
printErr("Failed to decode favicon: %s [%s] (%v)", imageURL, imageID, err)
recordInvalidImageID(imageID)
return "", false, fmt.Errorf("unsupported image type: %s", contentType)
return "", false, err
}
if err != nil {
@ -522,6 +508,33 @@ func cleanupCache() {
}
}
func safeDecodeImage(contentType string, data []byte) (img image.Image, err error) {
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("image decode panic: %v", r)
}
}()
switch contentType {
case "image/x-icon", "image/vnd.microsoft.icon":
img, err = ico.Decode(bytes.NewReader(data))
case "image/jpeg":
img, err = jpeg.Decode(bytes.NewReader(data))
case "image/png":
img, err = png.Decode(bytes.NewReader(data))
case "image/gif":
img, err = gif.Decode(bytes.NewReader(data))
case "image/webp":
img, err = webp.Decode(bytes.NewReader(data))
case "image/bmp":
img, err = bmp.Decode(bytes.NewReader(data))
case "image/tiff":
img, err = tiff.Decode(bytes.NewReader(data))
default:
err = fmt.Errorf("unsupported image type: %s", contentType)
}
return
}
// Serve missing.svg
func serveMissingImage(w http.ResponseWriter, r *http.Request) {
missingImagePath := filepath.Join("static", "images", "missing.svg")

View file

@ -1,16 +1,12 @@
package main
import (
"bytes"
"crypto/md5"
"crypto/tls"
"encoding/base64"
"encoding/hex"
"fmt"
"image"
"image/gif"
"image/jpeg"
"image/png"
"io"
"net/http"
"net/url"
@ -21,10 +17,7 @@ import (
"time"
"github.com/chai2010/webp"
"github.com/fyne-io/image/ico"
"golang.org/x/image/bmp"
"golang.org/x/image/draw"
"golang.org/x/image/tiff"
"golang.org/x/net/html"
)
@ -504,28 +497,9 @@ func cacheFavicon(imageURL, imageID string) (string, bool, error) {
}
// Decode image
var img image.Image
var err error
switch contentType {
case "image/x-icon", "image/vnd.microsoft.icon":
img, err = ico.Decode(bytes.NewReader(data))
case "image/jpeg":
img, err = jpeg.Decode(bytes.NewReader(data))
case "image/png":
img, err = png.Decode(bytes.NewReader(data))
case "image/gif":
img, err = gif.Decode(bytes.NewReader(data))
case "image/webp":
img, err = webp.Decode(bytes.NewReader(data))
case "image/bmp":
img, err = bmp.Decode(bytes.NewReader(data))
case "image/tiff":
img, err = tiff.Decode(bytes.NewReader(data))
default:
recordInvalidImageID(imageID)
return "", false, fmt.Errorf("unsupported image type: %s", contentType)
}
img, err := safeDecodeImage(contentType, data)
if err != nil {
printErr("Failed to decode favicon: %s [%s] (%v)", imageURL, imageID, err)
recordInvalidImageID(imageID)
return "", false, err
}