music-metadata: uncatchable process crash parsing a crafted `.dsf` (residual of GHSA-v6c2-xwv6-8xf7)
What fixes it
- 11.15.0music-metadata, npmGitHub
npm install music-metadata@11.15.0
A fixed version on one release line is not the upgrade target for every line. Check your exact version and any vendor backports.
Description
Summary
DsfParser.parseChunks skips an unrecognised chunk's payload with an un-awaited call:
this.tokenizer.ignore(Number(chunkHeader.size) - ChunkHeader.len); // lib/dsf/DsfParser.js:51 — no await
ChunkHeader.len is 12. A crafted .dsf chunk with id != 'fmt ' and size in 0..11 makes the argument negative; strtok3 (≥ 10.3.5) throws RangeError on a negative ignore. Because the call is fire-and-forget, the rejection is **detached from the parseBuffer() promise chain → unhandled rejection → Node's default (≥ 15) crashes the process — after** parseBuffer() already resolved, so a caller's try/catch catches nothing and is still taken down.
Residual of GHSA-v6c2-xwv6-8xf7: the ASF site was fixed in 11.12.3 (size validation) and strtok3 now throws on negative ignore; the DSF site was never validated, and its missing await escalates that throw into an uncatchable crash.
Root cause (lib/dsf/DsfParser.js:34-56)
while (bytesRemaining >= ChunkHeader.len) { // ChunkHeader.len = 12
const chunkHeader = await this.tokenizer.readToken(ChunkHeader); // { id, size }
switch (chunkHeader.id) {
case 'fmt ': { ...; return; }
default: this.tokenizer.ignore(Number(chunkHeader.size) - ChunkHeader.len); break; // size<12 -> negative, no await
}
bytesRemaining -= chunkHeader.size;
}strtok3 AbstractTokenizer.ignore (L78-79): if (length < 0) throw new RangeError('ignore length must be ≥ 0 bytes');
Steps to reproduce
repro/ — public API only, Node's default unhandled-rejection mode, try/catch around the parse:
npm install && node poc.mjs
Confirmed on 11.14.0:
[app] parseBuffer() RESOLVED — the caller saw no error to catch.
RangeError: ignore length must be ≥ 0 bytes
at DsfParser.parseChunks (.../lib/dsf/DsfParser.js:51)
<process exits non-zero — the "process survived" line never prints>Impact
DoS: a single crafted .dsf (or any file with the DSD magic) crashes the Node process of any app parsing untrusted audio with music-metadata (2.2M weekly downloads). The crash bypasses the caller's error handling, so even apps that correctly try/catch per-file parsing are killed — one malicious upload can take down a shared server/worker.
Remediation
Add await on line 51 (makes the RangeError a catchable parse error), and validate chunkHeader.size >= ChunkHeader.len before the skip (as the ASF fix did; also guards the loop counter). Audit other parsers for un-awaited tokenizer.ignore()/readToken().
Scope / honesty
Requires the DSF path (a DSD -magic file — normal auto-detection). Relies on Node's default unhandled-rejection mode (throw, default since Node 15); the point is that the standard defensive per-parse try/catch does not protect against it. Crash (availability), not disclosure/RCE. Negatives confirmed alongside: ASF infinite loop fixed; negative-ignore infinite-loop class closed at strtok3; unbounded allocation bounded by strtok3's read bound-check.
Credits
Issue also reported by @ryu7eroo
Text from GitHub Advisory Database
Affected versions 3
- music-metadata, npm<= 11.14.0GitHub
- music-metadataaffected: < 11.15.0CVE List
- music-metadata, npmfrom first release, fixed in 11.15.0OSV
References 5
- github.com/Borewit/music-metadata/commit/e7fc27a96e789d41ece41fdac590fc7618274a41Fixvia CVE List
- github.com/Borewit/music-metadata/releases/tag/v11.15.0Fixvia CVE List
- github.com/advisories/GHSA-8j4c-6x6g-rq3jAdvisoryvia GitHub
- github.com/Borewit/music-metadata/security/advisories/GHSA-8j4c-6x6g-rq3jExploitvia CVE List
- github.com/Borewit/music-metadata/pull/2700Referencevia CVE List