Summary
@jimp/js-bmp delegates BMP decoding entirely to the bmp-ts npm package with no validation of its own. bmp-ts's decoder allocates its output pixel buffer directly from the BMP header's width/height fields with no upper bound and no comparison against the actual input size:
// bmp-ts@1.0.9 dist/commonjs/decoder.js:153
this.data = Buffer.alloc(this.width * this.height * 4);
For RLE4/RLE8-compressed BMPs, the very next thing that happens is:
// decoder.js:201 (bit4) and :279 (bit8)
this.data.fill(0);
Buffer.fill() forces real physical memory to be committed across the whole buffer (unlike Buffer.alloc's lazily-zero-mapped pages), and this happens before any actual pixel bytes are read from the file, so a tiny file with a big declared width/height is enough to force a large real memory allocation.
Proof of Concept
A crafted 1078-byte BMP (valid BM magic + 40-byte BITMAPINFOHEADER, bitPP=8, compression=1 i.e. BI_RLE8, width=10000, height=10000, a 256-entry zero palette, and no actual RLE pixel data after the palette) causes roughly 405MB of RSS growth in a single Jimp.read() call, confirmed against the real published jimp@1.6.1 (which bundles bmp-ts@1.0.9):
input size 1078 bytes rss before ~67.7MB
Jimp.read(buffer) -> eventually throws RangeError (only AFTER the .fill(0) commits memory)
time 278ms rss after ~472.4MB (~405MB forced commit from a 1KB file)
Scaling width/height further is unbounded — nothing in bmp-ts or in @jimp/js-bmp's wrapper (plugins/js-bmp/src/index.ts, which calls BMP.decode() directly with no width/height sanity check before delegating) caps the allocation. A handful of concurrent uploads of ~1KB crafted files could force multi-GB memory growth or an allocator OOM in any service that calls Jimp.read() on user-uploaded images (a very common pattern — avatar/image upload handling).
This is a memory-exhaustion availability issue (CWE-400/789), not memory corruption — Node's Buffer/TypedArray bounds checks prevent any actual out-of-bounds read/write.
Suggested Fix
Before allocating the output buffer, validate that width * height * bytesPerPixel is consistent with the actual input buffer size (or with a reasonable configurable maximum), and reject the file early if not. This applies both in bmp-ts's decoder.js (root cause) and as a defense-in-depth check in @jimp/js-bmp's wrapper before it delegates to bmp-ts, since jimp is the package most people actually depend on directly.
Disclosure note
Checked for existing reports first: no GHSA/CVE/OSV advisory exists for bmp-ts, jimp, or @jimp/js-bmp. Neither repo has SECURITY.md or GitHub private vulnerability reporting enabled, so filing this as a plain public issue rather than a private advisory — severity is DoS-only (not RCE/memory-corruption), so this seemed reasonable to disclose directly; happy to move to a private channel if one gets set up.
Summary
@jimp/js-bmpdelegates BMP decoding entirely to thebmp-tsnpm package with no validation of its own.bmp-ts's decoder allocates its output pixel buffer directly from the BMP header'swidth/heightfields with no upper bound and no comparison against the actual input size:For RLE4/RLE8-compressed BMPs, the very next thing that happens is:
Buffer.fill()forces real physical memory to be committed across the whole buffer (unlikeBuffer.alloc's lazily-zero-mapped pages), and this happens before any actual pixel bytes are read from the file, so a tiny file with a big declared width/height is enough to force a large real memory allocation.Proof of Concept
A crafted 1078-byte BMP (valid
BMmagic + 40-byte BITMAPINFOHEADER,bitPP=8,compression=1i.e. BI_RLE8,width=10000, height=10000, a 256-entry zero palette, and no actual RLE pixel data after the palette) causes roughly 405MB of RSS growth in a singleJimp.read()call, confirmed against the real publishedjimp@1.6.1(which bundlesbmp-ts@1.0.9):Scaling
width/heightfurther is unbounded — nothing inbmp-tsor in@jimp/js-bmp's wrapper (plugins/js-bmp/src/index.ts, which callsBMP.decode()directly with no width/height sanity check before delegating) caps the allocation. A handful of concurrent uploads of ~1KB crafted files could force multi-GB memory growth or an allocator OOM in any service that callsJimp.read()on user-uploaded images (a very common pattern — avatar/image upload handling).This is a memory-exhaustion availability issue (CWE-400/789), not memory corruption — Node's
Buffer/TypedArray bounds checks prevent any actual out-of-bounds read/write.Suggested Fix
Before allocating the output buffer, validate that
width * height * bytesPerPixelis consistent with the actual input buffer size (or with a reasonable configurable maximum), and reject the file early if not. This applies both inbmp-ts'sdecoder.js(root cause) and as a defense-in-depth check in@jimp/js-bmp's wrapper before it delegates tobmp-ts, since jimp is the package most people actually depend on directly.Disclosure note
Checked for existing reports first: no GHSA/CVE/OSV advisory exists for
bmp-ts,jimp, or@jimp/js-bmp. Neither repo has SECURITY.md or GitHub private vulnerability reporting enabled, so filing this as a plain public issue rather than a private advisory — severity is DoS-only (not RCE/memory-corruption), so this seemed reasonable to disclose directly; happy to move to a private channel if one gets set up.