From 19f6e4b7c920d0ce6d5099c9823821520e01516d Mon Sep 17 00:00:00 2001 From: kolaente Date: Wed, 4 Feb 2026 19:37:50 +0100 Subject: [PATCH] fix(backgrounds): enforce max file size for unsplash downloads Check Content-Length and use io.LimitReader to prevent OOM from unexpectedly large unsplash responses before buffering into memory. --- pkg/modules/background/unsplash/unsplash.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pkg/modules/background/unsplash/unsplash.go b/pkg/modules/background/unsplash/unsplash.go index cb4e2f4a8..ca5432d87 100644 --- a/pkg/modules/background/unsplash/unsplash.go +++ b/pkg/modules/background/unsplash/unsplash.go @@ -280,11 +280,22 @@ func (p *Provider) Set(s *xorm.Session, image *background.Image, project *models } log.Debugf("Pinged unsplash download endpoint for photo %s", image.ID) - // Buffer the response body so we have a seekable reader for S3 uploads - bodyBytes, err := io.ReadAll(resp.Body) + // Enforce max file size to prevent OOM from unexpectedly large responses + maxSize := int64(config.GetMaxFileSizeInMBytes() * 1024 * 1024) + if resp.ContentLength > maxSize { + return files.ErrFileIsTooLarge{Size: uint64(resp.ContentLength)} + } + + // Buffer the response body so we have a seekable reader for S3 uploads. + // Use LimitReader as a safety net in case Content-Length was missing or inaccurate. + limitedReader := io.LimitReader(resp.Body, maxSize+1) + bodyBytes, err := io.ReadAll(limitedReader) if err != nil { return err } + if int64(len(bodyBytes)) > maxSize { + return files.ErrFileIsTooLarge{Size: uint64(len(bodyBytes))} + } // Save it as a file in vikunja file, err := files.Create(bytes.NewReader(bodyBytes), "", uint64(len(bodyBytes)), auth)