Vulnerability GHSA-qfx6-q44v-9qqw

High Risk
HIGH RISK
CVSS Score: 7.5
Score Range: 7.0–8.9
High severity vulnerabilities (CVSS 7.0–8.9). Serious vulnerabilities that should be prioritized soon after critical fixes.
2 hours ago
November 13, 2026 at 11:19 PM UTC
ag-psd can exhaust memory when parsing crafted PSD dimensions
<31.0.0
<31.0.0

Summary

ag-psd can exhaust memory when parsing crafted PSD dimensions

Details

Summary

readPsd() commits the image/layer buffers from the declared header geometry (width x height x channels x depth, and per-layer rectangles) before validating those values against the actual data. A tiny crafted PSD declaring huge geometry forces a multi-gigabyte allocation.

Impact

On ag-psd 30.2.0, Node 24, a 52-byte PSD makes readPsd(buf) commit ~3.17 GB (measured peak RSS) in ~0.45 s before throwing a catchable RangeError ("Offset is outside the bounds of the DataView") — about 61,000,000x amplification. Because the buffer is committed before validation, the allocation OOM-kills the process before the error on essentially any host (uncatchable), and concurrent requests exhaust memory. Any service reading untrusted PSDs with ag-psd is exposed to a remote DoS.

This is distinct from #39 and #206, which involve genuinely large PSDs (memory proportional to real data). Here the input is 52 bytes: the file declares enormous geometry that is never present, so the allocation is driven by an unvalidated header field, not by actual content, an uncontrolled-allocation denial-of-service vulnerability, not proportional memory use.

Steps to reproduce

The PoC is a 52-byte PSD (sha256 a1146a6fabdfef289472f99a56851249e7a46e8d19d4d8bf85fe833e2f0158b6). Reconstruct with:

base64 -d > poc.psd <<'EOF'
OEJQUwABAAAAAAAAAA0AADcuAAA58wAgAAMAAAAAAAAAAAAAAAAAAdEgNZv+i9bc9ygd2A==
EOF

Then (commits ~3.17 GB before throwing a catchable RangeError):

const { readPsd } = require('ag-psd');
const buf = require('fs').readFileSync('poc.psd');
readPsd(buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength));

Root cause

psdReader.js sizes the off-heap typed-array buffers from the declared header dimensions (and per-layer rectangles, a second independent lever) before validating against the available bytes.

Suggested fix

Validate the declared dimensions, channel count, depth, and per-layer rectangles against the actual input length (and a sane maximum) before allocating.

Impacted packages

Timeline

Published
2 hours ago
November 13, 2026 at 11:19 PM UTC
Fixed (31.0.0)
4 months ago
June 25, 2026 at 05:17 PM UTC
Last Modified
1 hour ago
November 13, 2026 at 11:30 PM UTC