GHSA-8J4C-6X6G-RQ3J
Vulnerability from github – Published: 2026-10-08 19:40 – Updated: 2026-10-08 19:40Summary
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
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 11.14.0"
},
"package": {
"ecosystem": "npm",
"name": "music-metadata"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "11.15.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-107392"
],
"database_specific": {
"cwe_ids": [
"CWE-248",
"CWE-400"
],
"github_reviewed": true,
"github_reviewed_at": "2026-10-08T19:40:45Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "## Summary\n`DsfParser.parseChunks` skips an unrecognised chunk\u0027s payload with an **un-awaited** call:\n```js\nthis.tokenizer.ignore(Number(chunkHeader.size) - ChunkHeader.len); // lib/dsf/DsfParser.js:51 \u2014 no await\n```\n`ChunkHeader.len` is 12. A crafted `.dsf` chunk with `id != \u0027fmt \u0027` and `size` in `0..11` makes the\nargument negative; strtok3 (\u2265 10.3.5) throws `RangeError` on a negative `ignore`. Because the call\nis fire-and-forget, the rejection is **detached from the `parseBuffer()` promise chain** \u2192 unhandled\nrejection \u2192 Node\u0027s default (\u2265 15) **crashes the process** \u2014 **after** `parseBuffer()` already\nresolved, so a caller\u0027s `try/catch` catches nothing and is still taken down.\n\nResidual of GHSA-v6c2-xwv6-8xf7: the ASF site was fixed in 11.12.3 (size validation) and strtok3\nnow throws on negative `ignore`; the DSF site was never validated, and its missing `await`\nescalates that throw into an **uncatchable** crash.\n\n## Root cause (`lib/dsf/DsfParser.js:34-56`)\n```js\nwhile (bytesRemaining \u003e= ChunkHeader.len) { // ChunkHeader.len = 12\n const chunkHeader = await this.tokenizer.readToken(ChunkHeader); // { id, size }\n switch (chunkHeader.id) {\n case \u0027fmt \u0027: { ...; return; }\n default: this.tokenizer.ignore(Number(chunkHeader.size) - ChunkHeader.len); break; // size\u003c12 -\u003e negative, no await\n }\n bytesRemaining -= chunkHeader.size;\n}\n```\nstrtok3 `AbstractTokenizer.ignore` (L78-79): `if (length \u003c 0) throw new RangeError(\u0027ignore length must be \u2265 0 bytes\u0027);`\n\n## Steps to reproduce\n`repro/` \u2014 public API only, Node\u0027s default unhandled-rejection mode, `try/catch` around the parse:\n```\nnpm install \u0026\u0026 node poc.mjs\n```\nConfirmed on 11.14.0:\n```\n[app] parseBuffer() RESOLVED \u2014 the caller saw no error to catch.\nRangeError: ignore length must be \u2265 0 bytes\n at DsfParser.parseChunks (.../lib/dsf/DsfParser.js:51)\n \u003cprocess exits non-zero \u2014 the \"process survived\" line never prints\u003e\n```\n\n## Impact\nDoS: a single crafted `.dsf` (or any file with the `DSD ` magic) crashes the Node process of any\napp parsing untrusted audio with music-metadata (2.2M weekly downloads). The crash bypasses the\ncaller\u0027s error handling, so even apps that correctly `try/catch` per-file parsing are killed \u2014 one\nmalicious upload can take down a shared server/worker.\n\n## Remediation\nAdd `await` on line 51 (makes the `RangeError` a catchable parse error), and validate\n`chunkHeader.size \u003e= ChunkHeader.len` before the skip (as the ASF fix did; also guards the loop\ncounter). Audit other parsers for un-awaited `tokenizer.ignore()`/`readToken()`.\n\n## Scope / honesty\nRequires the DSF path (a `DSD `-magic file \u2014 normal auto-detection). Relies on Node\u0027s default\nunhandled-rejection mode (`throw`, default since Node 15); the point is that the standard defensive\nper-parse `try/catch` does not protect against it. Crash (availability), not disclosure/RCE.\nNegatives confirmed alongside: ASF infinite loop fixed; negative-`ignore` infinite-loop class\nclosed at strtok3; unbounded allocation bounded by strtok3\u0027s read bound-check.\n\n## Credits\nIssue also reported by @ryu7eroo",
"id": "GHSA-8j4c-6x6g-rq3j",
"modified": "2026-10-08T19:40:45Z",
"published": "2026-10-08T19:40:45Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/Borewit/music-metadata/security/advisories/GHSA-8j4c-6x6g-rq3j"
},
{
"type": "WEB",
"url": "https://github.com/Borewit/music-metadata/pull/2700"
},
{
"type": "WEB",
"url": "https://github.com/Borewit/music-metadata/commit/e7fc27a96e789d41ece41fdac590fc7618274a41"
},
{
"type": "PACKAGE",
"url": "https://github.com/Borewit/music-metadata"
},
{
"type": "WEB",
"url": "https://github.com/Borewit/music-metadata/releases/tag/v11.15.0"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
"type": "CVSS_V3"
}
],
"summary": "music-metadata: uncatchable process crash parsing a crafted `.dsf` (residual of GHSA-v6c2-xwv6-8xf7)"
}
Drag nodes to arrange them. Double-click a related vulnerability to open it, or a source to visit its link. The Physics button on the left tunes the layout and the simulation; the legend at the bottom left hides or shows nodes by kind and by sighting type.
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.
Vulnerability timeline
Sightings and recorded dates, grouped by day · UTC
Loading timeline…
The approach is described in our paper Mapping CVEs to MITRE ATT&CK Techniques: A Curated Gold-Set Classifier and the Limits of LLM-Assisted Label Expansion.
Browse all ATT&CK techniques and the vulnerabilities related to each.
Related by attack behaviour
Vulnerabilities whose description is nearest to this one in the vector space of the CIRCL/vulnerability-attack-technique-biencoder model. This is a similarity search over the bi-encoder space (plain cosine), not a classification, and it has no measured accuracy.