Decode peeks at the image header to enforce MaxPixels BEFORE allocating the full pixel buffer. image.DecodeConfig reads only the header bytes (a few hundred at most), so an attacker who uploads a "claimed 100k x 100k pixels" PNG can't OOM us — we reject before image.Decode allocates the row buffers.
(r io.Reader)
| 62 | // can replay it for the actual Decode. This costs a single io.ReadAll |
| 63 | // — bounded by the upload-handler's MaxBytesReader (25 MiB by default). |
| 64 | func (p *pureGoProcessor) Decode(r io.Reader) (image.Image, string, error) { |
| 65 | buf, err := io.ReadAll(r) |
| 66 | if err != nil { |
| 67 | return nil, "", fmt.Errorf("attachments: read image bytes: %w", err) |
| 68 | } |
| 69 | cfg, format, err := image.DecodeConfig(bytes.NewReader(buf)) |
| 70 | if err != nil { |
| 71 | // image.Decode would also fail; bail with a clearer error |
| 72 | // message that distinguishes "format unknown" from "format |
| 73 | // known, decoding failed mid-way" (a corrupt-bytes case). |
| 74 | return nil, "", fmt.Errorf("%w: %v", ErrUnsupportedFormat, err) |
| 75 | } |
| 76 | if !p.formatSupported(format) { |
| 77 | return nil, format, fmt.Errorf("%w: %s", ErrUnsupportedFormat, format) |
| 78 | } |
| 79 | if cfg.Width <= 0 || cfg.Height <= 0 { |
| 80 | return nil, format, fmt.Errorf("%w: zero dimension (%dx%d)", |
| 81 | ErrUnsupportedFormat, cfg.Width, cfg.Height) |
| 82 | } |
| 83 | if int64(cfg.Width)*int64(cfg.Height) > int64(p.caps.MaxPixels) { |
| 84 | return nil, format, fmt.Errorf("%w: %dx%d exceeds %d", |
| 85 | ErrImageTooLarge, cfg.Width, cfg.Height, p.caps.MaxPixels) |
| 86 | } |
| 87 | img, _, err := image.Decode(bytes.NewReader(buf)) |
| 88 | if err != nil { |
| 89 | return nil, format, fmt.Errorf("attachments: decode %s: %w", format, err) |
| 90 | } |
| 91 | return img, format, nil |
| 92 | } |
| 93 | |
| 94 | // Resize fits the longer edge to maxLong, preserving aspect ratio. The |
| 95 | // imaging package picks the right scale factor for whichever edge is |
nothing calls this directly
no test coverage detected