{"record":{"id":"338594e2a5beb564","repo":"d2lang/d2","slug":"decoded-image-exceeds-maximum-size-of-d-bytes","errorCode":null,"errorMessage":"decoded image exceeds maximum size of %d bytes","messagePattern":"decoded image exceeds maximum size of (.+?) bytes","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"lib/imgbundler/imgbundler.go","lineNumber":304,"sourceCode":"}\n\nfunc inflate(buf []byte) ([]byte, error) {\n\tif zr, err := zlib.NewReader(bytes.NewReader(buf)); err == nil {\n\t\tdefer zr.Close()\n\t\treturn readDecoded(zr)\n\t}\n\tfr := flate.NewReader(bytes.NewReader(buf))\n\tdefer fr.Close()\n\treturn readDecoded(fr)\n}\n\nfunc readDecoded(r io.Reader) ([]byte, error) {\n\tbuf, err := io.ReadAll(io.LimitReader(r, maxImageSize+1))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif int64(len(buf)) > maxImageSize {\n\t\treturn nil, fmt.Errorf(\"decoded image exceeds maximum size of %d bytes\", maxImageSize)\n\t}\n\treturn buf, nil\n}\n\n// sniffMimeType sniffs the mime type of href based on its file extension and contents.\nfunc sniffMimeType(href, buf []byte, isRemote bool) string {\n\tp := string(href)\n\tif isRemote {\n\t\tu, err := url.Parse(html.UnescapeString(p))\n\t\tif err != nil {\n\t\t\tp = \"\"\n\t\t} else {\n\t\t\tp = u.Path\n\t\t}\n\t}\n\tmimeType := mime.TypeByExtension(path.Ext(p))\n\tif mimeType == \"\" {\n\t\tmimeType = http.DetectContentType(buf)","sourceCodeStart":286,"sourceCodeEnd":322,"githubUrl":"https://github.com/d2lang/d2/blob/0d69dca6f532ceaeacd615d35d1eaa41a238ffdb/lib/imgbundler/imgbundler.go#L286-L322","documentation":"readDecoded decompresses a response body while guarding against decompression bombs: it reads at most maxImageSize+1 bytes via LimitReader, and if the decoded output exceeds maxImageSize it returns this error. This protects the bundler from tiny compressed payloads that expand to enormous images.","triggerScenarios":"A fetched image's decompressed content (after gzip/brotli/deflate) is larger than maxImageSize — either a genuinely oversized image or a crafted/accidental decompression bomb.","commonSituations":"Pages embedding extremely large screenshots or uncompressed-format images behind compression; malicious pages deliberately serving zip bombs to exhaust bundler memory.","solutions":["Reduce the image size at the source (resize/optimize the asset) if you control it","Raise maxImageSize if your use case legitimately requires larger images and you have the memory headroom","Serve the asset uncompressed or pre-sized via CDN transforms so decoded size stays under the cap","Treat as malicious input if untrusted pages are bundled — keep the cap and reject the page"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"head, err := http.Head(imgURL)\nif err == nil && head.ContentLength > maxImageSize {\n\treturn fmt.Errorf(\"image %s too large: %d > %d\", imgURL, head.ContentLength, maxImageSize)\n}","typeGuard":null,"tryCatchPattern":"if err != nil {\n\tif strings.Contains(err.Error(), \"exceeds maximum size\") {\n\t\t// reject page or serve a downscaled variant\n\t}\n}","preventionTips":["Keep maxImageSize set deliberately and document it for users","Pre-resize/optimize large assets at the source","Treat oversized decoded payloads from untrusted pages as malicious (bomb) input","Monitor Content-Length and compression ratios of bundled assets"],"tags":["security","decompression-bomb","limits","go"],"backgroundTag":"decompression-bomb-detected","analyzedSha":"0d69dca6f532ceaeacd615d35d1eaa41a238ffdb","analyzedAt":"2026-08-31T12:19:21.182Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}