Repository navigation
deps(frontend): update dependency toml to v5 [security] - #8836
Open
flagsmith-engineering[bot] wants to merge 1 commit into
Open
flagsmith-engineering[bot] wants to merge 1 commit into
flagsmith-engineering[bot] wants to merge 1 commit into
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
flagsmith-engineering
Bot
requested review from
talissoncosta
and removed request for
a team
October 23, 2026 03:21
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Contributor
✅ oss · depot-ubuntu-latest-arm-16 — run #22664 (attempt 1)Playwright Test Results (oss - depot-ubuntu-latest-arm-16)Details
🗂️ Previous results✅ oss · depot-ubuntu-latest-16 — run #22664 (attempt 1)Playwright Test Results (oss - depot-ubuntu-latest-16)Details
|
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR contains the following updates:
^4.0.0→^5.0.0toml-node: Uncontrolled resource consumption (CPU DoS) via unbounded-length integer literals in toml.parse()
CVE-2026-114144 / GHSA-9qmh-m7c6-f92p
More information
Details
Summary
toml.parse()converts integer literals (hex, octal, binary, and decimal) toBigIntwithout first bounding the length of the digit sequence. SinceBigInt(string)construction from a very long numeric string runs in super-linear time, a single small, well-formed TOML document containing one long integer literal can pin a Node.js process's event loop at 100% CPU for several seconds — a denial of service for any application that parses attacker-supplied or third-party TOML. No authentication or complex crafting is required.Details
In
lib/parser.js, the grammar actions that build integer nodes callBigInt()directly on the matched digit text, with no upper bound on its length:lib/parser.js:326— hex:BigInt('0x' + stripUnderscores(digits))lib/parser.js:327— octal:BigInt('0o' + stripUnderscores(digits))lib/parser.js:328— binary:BigInt('0b' + stripUnderscores(digits))lib/parser.js:329— decimal:BigInt(stripUnderscores(text))The parser does enforce a nesting-depth limit (
MAX_DEPTH, default 500,lib/parser.js:4343) to guard against stack overflow from deeply nested arrays/inline tables (per GHSA-82x6-q7mm-w9cf). However, that guard only bounds structural nesting — it does not bound the length of a single scalar token.The subsequent 64-bit range check in
lib/compiler.js(reduceInteger, lines 143–150, comparing againstINT64_MIN/INT64_MAXdefined at lines 3–4) happens after the expensiveBigInt()conversion has already completed. It therefore provides no protection against this cost — the CPU time is already spent by the time the value is rejected as out of range.PoC
Measured results:
Growth is clearly super-linear. The same behavior reproduces for plain decimal literals (3,000,000
'9'digits → ~6.3 s).Impact
Any application that calls
toml.parse()on untrusted or third-party input — config upload endpoints, CI/CD tools ingesting user-supplied.tomlfiles, package managers, linters, editors/language servers — is exposed.During the conversion, the event loop is fully blocked. In a typical single-threaded Node.js server this stalls all concurrent request handling, not just the request containing the malicious payload. A single ~5 MB request is sufficient to cause several seconds of full CPU denial of service.
Suggested fix: Reject digit sequences above a reasonable length before calling
BigInt(), analogous to the existingmaxDepthoption. A 64-bit signed integer requires at most 20 decimal digits or 16 hex digits, so any digit sequence significantly longer than that is guaranteed to fail the existing range check anyway, and can be rejected with a cheap.lengthcomparison instead of paying for the fullBigIntconversion.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
toml-node: Quadratic-complexity DoS via long dotted keys or table paths in toml.parse()
CVE-2026-114143 / GHSA-9j65-hcg3-gc64
More information
Details
Summary
toml.parse()is vulnerable to a quadratic-complexity (O(n²)) denial-of-service via a singlelong dotted key, table header, or inline-table key. A ~100KB crafted document causes a
multi-second synchronous hang; a ~200KB document crashes the Node.js process outright with an
uncatchable out-of-memory error. This is a separate, unpatched code path from the previously
fixed stack-overflow DoS (
GHSA-82x6-q7mm-w9cf) — it requires no array/inline-table nesting atall, and is confirmed unaffected by the
maxDepthoption that fixed that earlier issue.Affected component / version
toml(npm), repositoryBinaryMuse/toml-nodeassign(),deepRef(),setNestedKey()inlib/compiler.js1b2ac40(2026-07-16), one commit pastthe
v5.0.0tag (a changelog-only change) — i.e. present inv5.0.0, the latest release.Not exhaustively verified against every prior release, but the affected functions are
unrelated to
v5.0.0's BigInt/Temporal changes (perCHANGELOG.md), so the defect likelypredates that release as well.
Details
toml.parse(input, options)(index.js:5-9) forwards the caller-supplied string toparser.parse()and thencompiler.compile()with no length or structural validation.Inside
lib/compiler.js, three separate functions each walk a dotted key's segments in aforloop and, on every iteration, recompute the full path-prefix string from scratch:assign()(lines 69-107, the sink at line 81):var intermediatePath = makeFullPath(keys.slice(0, i + 1));— reached for top-level dotted-key assignments likea.b.c = 1.deepRef()(lines 247-281, the sink at line 252): the identical pattern viapathKey(keys.slice(0, i + 1))— reached from table headers[a.b.c]and array-of-tables[[a.b.c]]viasetPath()/addTableArray().setNestedKey()(lines 184-202, the sink at line 187): the identical pattern — reached frominline-table dotted keys like
t = { a.b.c = 1 }.keys.slice(0, i + 1)allocates a new array of lengthi;pathKey()/makeFullPath()(lines 311-317) then does
.map(quoteDottedString).join("."), also O(i). Since this runs onevery one of the
nsegments of a dotted key, total work isO(1 + 2 + ... + n) = O(n²), notO(n). The string is computed unconditionally on the happy path (only ever used if an erroris about to be thrown), so there is no early exit that makes this cheap in practice.
No limit exists anywhere on dotted-key segment count:
dot_ended_table_key_part+andinline_dot_key_part+insrc/toml.pegjs(lines 122, 311) are unbounded+repetitions,parsed iteratively rather than through the grammar's
valuerule — so the existingMAX_DEPTH/maxDepthnesting guard (src/toml.pegjs:141-151, enforced in the generatedlib/parser.js:1022) does not apply to key length at all. We confirmed this directly: settingoptions.maxDepth: 1(the strictest possible value) against a 16,000-segment key produced nomeasurable timing difference from the unrestricted case.
Proof of Concept
Measured timings (Node v22, direct execution against this repo):
Doubling
nfrom 8,000→16,000 roughly quadruples runtime (~4.2x), the signature of O(n²)rather than the expected O(n) (~2x). Independently reproduced with the identical scaling
signature for the table-header variant (
[k0. ... .k15999], exercisingdeepRef()) and theinline-table variant (
t = { k0. ... .k15999 = 1 }, exercisingsetNestedKey()).We also generalized this into a property-based fuzzing harness (fast-check, run under Node)
that automatically tests the "doubling input size should not more than roughly double parse
time" invariant across 12 structural dimensions of the grammar/compiler; it independently
re-flags all three code paths above on every run, with no additional dimension confirmed
vulnerable after follow-up investigation. The harness is available on request.
Impact
Node.js is single-threaded — a synchronous multi-second-to-crashing
parse()call blocks theentire event loop of the process calling it, stalling every other request/operation that
process is handling, not just the attacker's own. At the upper end this is not merely a hang:
an uncaught, process-terminating out-of-memory crash from a ~200KB attacker-supplied string is
a full denial of service requiring no authentication — just the ability to get one string
parsed by an application using this library (config upload, API request body, imported
manifest, or any of this library's intended use cases).
Suggested remediation
Build the path prefix as an incrementally-extended string as the loop walks each segment
(append one already-quoted segment per iteration to a running string) instead of re-slicing an
array and re-joining the entire prefix from scratch on every iteration. For example, in
assign():The identical treatment applies to
deepRef()andsetNestedKey(). As defense-in-depth,consider also capping total key-segment count or overall input size, the same way
maxDepthalready caps nesting.
Credit
Discovered and reported by SecDim Security Research
secdim.com | @secdim | security@secdim.com
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
toml-node: Quadratic-complexity DoS via many array-of-tables headers in toml.parse()
CVE-2026-114141 / GHSA-4qx8-w7c3-pj3p
More information
Details
Summary
toml.parse()is vulnerable to a quadratic-complexity (O(n²)) denial-of-service via manyarray-of-tables (
[[name]]) headers.addTableArray()inlib/compiler.jsrescans twoentire bookkeeping Sets on every
[[name]]header without pruning unrelated or distinctentries, so a document containing n such headers costs O(n²) compiler work. A ~321KB document
of 20,000 distinct-named
[[name]]blocks takes ~10.3 seconds to parse — a completelyordinary-looking TOML shape (a flat settings block plus one or more array-of-tables sections),
not a contrived edge case. This is a separate, unpatched code path from the previously fixed
stack-overflow DoS (
GHSA-82x6-q7mm-w9cf) and requires no key nesting or dotted keys at all.Affected component / version
toml(npm), repositoryBinaryMuse/toml-nodeaddTableArray()inlib/compiler.js1b2ac40(2026-07-16), one commit pastthe
v5.0.0tag (a changelog-only change) — i.e. present inv5.0.0, the latest release.Not exhaustively verified against every prior release, but the affected function is
unrelated to
v5.0.0's BigInt/Temporal changes (perCHANGELOG.md), so the defect likelypredates that release as well.
Details
toml.parse(input, options)(index.js:5-9) forwards the caller-supplied string toparser.parse()and thencompiler.compile()with no length or structural validation. Every[[name]](array-of-tables) header is routed (reduce(),lib/compiler.js:43) toaddTableArray()(lib/compiler.js:218-245), which performs two full linear scans on everycall:
(
lib/compiler.js:228-233)assignedPathsandvalueAssignmentsaccumulate one entry per top-level key/table/array pathever assigned in the entire document. Each scan prunes only entries matching the currently
reopened path via
isSameOrSubPath()(lib/compiler.js:319-321:path === prefix || path.indexOf(prefix + ".") === 0); anything else — an unrelated top-level key, or simply adifferent array-of-tables name — is never removed and is rescanned in full on every
subsequent
[[name]]header for the rest of the parse.Set.prototype.forEachhas no earlyexit, so this is unconditionally O(current Set size) on every call, giving O(n²) total work
for n such headers.
Proof of Concept
The sharpest reproduction needs no unrelated keys at all — just N array-of-tables headers with
N distinct names, since nothing is ever pruned when no name repeats:
Measured timings (Node, direct execution against this repo):
[[name]]headers, no unrelated keys[[name]]headersThe ~130x gap between the distinct-name and same-name cases at identical N and comparable
input size isolates the mechanism precisely: the bug is unbounded Set growth from never-pruned
entries, not string length or any other confound. A product-law check (varying M and N
independently in the mixed variant) further confirmed genuine O(M·N) complexity.
Impact
Node.js is single-threaded — this synchronous, multi-second-to-tens-of-seconds compile blocks
the entire process event loop, standard uncontested Node.js behavior. The triggering document
shape — flat top-level config keys plus one or more array-of-tables sections (e.g. a
[db]settings block plus a
[[servers]]list) — is unremarkable, common real-world TOML, not anedge case. No authentication or special access is required; only the ability to get one TOML
string parsed by an application using this library.
Suggested remediation
Avoid scanning the entire
assignedPaths/valueAssignmentsSets on every[[name]]header.Index entries by their top-level path segment (e.g. a
Map<string, Set<string>>keyed by thefirst key component) so
addTableArray()only ever scans the bucket relevant to the pathbeing reopened, in time proportional to that bucket's size rather than the whole document's
accumulated path count:
As with the sibling finding, an input-size or table-count limit would also help as
defense-in-depth.
Credit
Discovered and reported by SecDim Security Research
secdim.com | @secdim | security@secdim.com
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by the GitHub Advisory Database (CC-BY 4.0).
toml-node: Quadratic-complexity DoS via many array-of-tables headers in toml.parse()
CVE-2026-114141 / GHSA-4qx8-w7c3-pj3p
More information
Details
Summary
toml.parse()is vulnerable to a quadratic-complexity (O(n²)) denial-of-service via manyarray-of-tables (
[[name]]) headers.addTableArray()inlib/compiler.jsrescans twoentire bookkeeping Sets on every
[[name]]header without pruning unrelated or distinctentries, so a document containing n such headers costs O(n²) compiler work. A ~321KB document
of 20,000 distinct-named
[[name]]blocks takes ~10.3 seconds to parse — a completelyordinary-looking TOML shape (a flat settings block plus one or more array-of-tables sections),
not a contrived edge case. This is a separate, unpatched code path from the previously fixed
stack-overflow DoS (
GHSA-82x6-q7mm-w9cf) and requires no key nesting or dotted keys at all.Affected component / version
toml(npm), repositoryBinaryMuse/toml-nodeaddTableArray()inlib/compiler.js1b2ac40(2026-07-16), one commit pastthe
v5.0.0tag (a changelog-only change) — i.e. present inv5.0.0, the latest release.Not exhaustively verified against every prior release, but the affected function is
unrelated to
v5.0.0's BigInt/Temporal changes (perCHANGELOG.md), so the defect likelypredates that release as well.
Details
toml.parse(input, options)(index.js:5-9) forwards the caller-supplied string toparser.parse()and thencompiler.compile()with no length or structural validation. Every[[name]](array-of-tables) header is routed (reduce(),lib/compiler.js:43) toaddTableArray()(lib/compiler.js:218-245), which performs two full linear scans on everycall:
(
lib/compiler.js:228-233)assignedPathsandvalueAssignmentsaccumulate one entry per top-level key/table/array pathever assigned in the entire document. Each scan prunes only entries matching the currently
reopened path via
isSameOrSubPath()(lib/compiler.js:319-321:path === prefix || path.indexOf(prefix + ".") === 0); anything else — an unrelated top-level key, or simply adifferent array-of-tables name — is never removed and is rescanned in full on every
subsequent
[[name]]header for the rest of the parse.Set.prototype.forEachhas no earlyexit, so this is unconditionally O(current Set size) on every call, giving O(n²) total work
for n such headers.
Proof of Concept
The sharpest reproduction needs no unrelated keys at all — just N array-of-tables headers with
N distinct names, since nothing is ever pruned when no name repeats:
Measured timings (Node, direct execution against this repo):
[[name]]headers, no unrelated keys[[name]]headersThe ~130x gap between the distinct-name and same-name cases at identical N and comparable
input size isolates the mechanism precisely: the bug is unbounded Set growth from never-pruned
entries, not string length or any other confound. A product-law check (varying M and N
independently in the mixed variant) further confirmed genuine O(M·N) complexity.
Impact
Node.js is single-threaded — this synchronous, multi-second-to-tens-of-seconds compile blocks
the entire process event loop, standard uncontested Node.js behavior. The triggering document
shape — flat top-level config keys plus one or more array-of-tables sections (e.g. a
[db]settings block plus a
[[servers]]list) — is unremarkable, common real-world TOML, not anedge case. No authentication or special access is required; only the ability to get one TOML
string parsed by an application using this library.
Suggested remediation
Avoid scanning the entire
assignedPaths/valueAssignmentsSets on every[[name]]header.Index entries by their top-level path segment (e.g. a
Map<string, Set<string>>keyed by thefirst key component) so
addTableArray()only ever scans the bucket relevant to the pathbeing reopened, in time proportional to that bucket's size rather than the whole document's
accumulated path count:
As with the sibling finding, an input-size or table-count limit would also help as
defense-in-depth.
Credit
Discovered and reported by SecDim Security Research
secdim.com | @secdim | security@secdim.com
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
toml-node: Quadratic-complexity DoS via long dotted keys or table paths in toml.parse()
CVE-2026-114143 / GHSA-9j65-hcg3-gc64
More information
Details
Summary
toml.parse()is vulnerable to a quadratic-complexity (O(n²)) denial-of-service via a singlelong dotted key, table header, or inline-table key. A ~100KB crafted document causes a
multi-second synchronous hang; a ~200KB document crashes the Node.js process outright with an
uncatchable out-of-memory error. This is a separate, unpatched code path from the previously
fixed stack-overflow DoS (
GHSA-82x6-q7mm-w9cf) — it requires no array/inline-table nesting atall, and is confirmed unaffected by the
maxDepthoption that fixed that earlier issue.Affected component / version
toml(npm), repositoryBinaryMuse/toml-nodeassign(),deepRef(),setNestedKey()inlib/compiler.js1b2ac40(2026-07-16), one commit pastthe
v5.0.0tag (a changelog-only change) — i.e. present inv5.0.0, the latest release.Not exhaustively verified against every prior release, but the affected functions are
unrelated to
v5.0.0's BigInt/Temporal changes (perCHANGELOG.md), so the defect likelypredates that release as well.
Details
toml.parse(input, options)(index.js:5-9) forwards the caller-supplied string toparser.parse()and thencompiler.compile()with no length or structural validation.Inside
lib/compiler.js, three separate functions each walk a dotted key's segments in aforloop and, on every iteration, recompute the full path-prefix string from scratch:assign()(lines 69-107, the sink at line 81):var intermediatePath = makeFullPath(keys.slice(0, i + 1));— reached for top-level dotted-key assignments likea.b.c = 1.deepRef()(lines 247-281, the sink at line 252): the identical pattern viapathKey(keys.slice(0, i + 1))— reached from table headers[a.b.c]and array-of-tables[[a.b.c]]viasetPath()/addTableArray().setNestedKey()(lines 184-202, the sink at line 187): the identical pattern — reached frominline-table dotted keys like
t = { a.b.c = 1 }.keys.slice(0, i + 1)allocates a new array of lengthi;pathKey()/makeFullPath()(lines 311-317) then does
.map(quoteDottedString).join("."), also O(i). Since this runs onevery one of the
nsegments of a dotted key, total work isO(1 + 2 + ... + n) = O(n²), notO(n). The string is computed unconditionally on the happy path (only ever used if an erroris about to be thrown), so there is no early exit that makes this cheap in practice.
No limit exists anywhere on dotted-key segment count:
dot_ended_table_key_part+andinline_dot_key_part+insrc/toml.pegjs(lines 122, 311) are unbounded+repetitions,parsed iteratively rather than through the grammar's
valuerule — so the existingMAX_DEPTH/maxDepthnesting guard (src/toml.pegjs:141-151, enforced in the generatedlib/parser.js:1022) does not apply to key length at all. We confirmed this directly: settingoptions.maxDepth: 1(the strictest possible value) against a 16,000-segment key produced nomeasurable timing difference from the unrestricted case.
Proof of Concept
Measured timings (Node v22, direct execution against this repo):
Doubling
nfrom 8,000→16,000 roughly quadruples runtime (~4.2x), the signature of O(n²)rather than the expected O(n) (~2x). Independently reproduced with the identical scaling
signature for the table-header variant (
[k0. ... .k15999], exercisingdeepRef()) and theinline-table variant (
t = { k0. ... .k15999 = 1 }, exercisingsetNestedKey()).We also generalized this into a property-based fuzzing harness (fast-check, run under Node)
that automatically tests the "doubling input size should not more than roughly double parse
time" invariant across 12 structural dimensions of the grammar/compiler; it independently
re-flags all three code paths above on every run, with no additional dimension confirmed
vulnerable after follow-up investigation. The harness is available on request.
Impact
Node.js is single-threaded — a synchronous multi-second-to-crashing
parse()call blocks theentire event loop of the process calling it, stalling every other request/operation that
process is handling, not just the attacker's own. At the upper end this is not merely a hang:
an uncaught, process-terminating out-of-memory crash from a ~200KB attacker-supplied string is
a full denial of service requiring no authentication — just the ability to get one string
parsed by an application using this library (config upload, API request body, imported
manifest, or any of this library's intended use cases).
Suggested remediation
Build the path prefix as an incrementally-extended string as the loop walks each segment
(append one already-quoted segment per iteration to a running string) instead of re-slicing an
array and re-joining the entire prefix from scratch on every iteration. For example, in
assign():The identical treatment applies to
deepRef()andsetNestedKey(). As defense-in-depth,consider also capping total key-segment count or overall input size, the same way
maxDepthalready caps nesting.
Credit
Discovered and reported by SecDim Security Research
secdim.com | @secdim | security@secdim.com
Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
toml-node: Uncontrolled resource consumption (CPU DoS) via unbounded-length integer literals in toml.parse()
CVE-2026-114144 / GHSA-9qmh-m7c6-f92p
More information
Details
Summary
toml.parse()converts integer literals (hex, octal, binary, and decimal) toBigIntwithout first bounding the length of the digit sequence. SinceBigInt(string)construction from a very long numeric string runs in super-linear time, a single small, well-formed TOML document containing one long integer literal can pin a Node.js process's event loop at 100% CPU for several seconds — a denial of service for any application that parses attacker-supplied or third-party TOML. No authentication or complex crafting is required.Details
In
lib/parser.js, the grammar actions that build integer nodes callBigInt()directly on the matched digit text, with no upper bound on its length:lib/parser.js:326— hex:BigInt('0x' + stripUnderscores(digits))lib/parser.js:327— octal:BigInt('0o' + stripUnderscores(digits))lib/parser.js:328— binary:BigInt('0b' + stripUnderscores(digits))lib/parser.js:329— decimal:BigInt(stripUnderscores(text))The parser does enforce a nesting-depth limit (
MAX_DEPTH, default 500,lib/parser.js:4343) to guard against stack overflow from deeply nested arrays/inline tables (per GHSA-82x6-q7mm-w9cf). However, that guard only bounds structural nesting — it does not bound the length of a single scalar token.The subsequent 64-bit range check in
lib/compiler.js(reduceInteger, lines 143–150, comparing againstINT64_MIN/INT64_MAXdefined at lines 3–4) happens after the expensiveBigInt()conversion has already completed. It therefore provides no protection against this cost — the CPU time is already spent by the time the value is rejected as out of range.PoC
Measured results:
Growth is clearly super-linear. The same behavior reproduces for plain decimal literals (3,000,000
'9'digits → ~6.3 s).Impact
Any application that calls
toml.parse()on untrusted or third-party input — config upload endpoints, CI/CD tools ingesting user-supplied.tomlfiles, package managers, linters, editors/language servers — is exposed.During the conversion, the event loop is fully blocked. In a typical single-threaded Node.js server this stalls all concurrent request handling, not just the request containing the malicious payload. A single ~5 MB request is sufficient to cause several seconds of full CPU denial of service.
Suggested fix: Reject digit sequences above a reasonable length before calling
BigInt(), analogous to the existingmaxDepthoption. A 64-bit signed integer requires at most 20 decimal digits or 16 hex digits, so any digit sequence significantly longer than that is guaranteed to fail the existing range check anyway, and can be rejected with a cheap.lengthcomparison instead of paying for the fullBigIntconversion.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:HReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
toml-node: Prototype pollution: parsing a TOML document can write to Object.prototype
CVE-2026-114142 / GHSA-h895-cgqg-jf52
More information
Details
Summary
CVE-2026-63376 described prototype pollution through proto key-path desynchronization and was fixed in 4.1.2 by rewriting the path bookkeeping and adding an ownedContainers WeakSet. The desynchronization is still reachable, spelled with a quote character instead of a dot, and the owned-container check is not applied on the path that matters.
A four-line TOML document parsed with toml.parse() writes a property onto Object.prototype, where it is visible to every object in the process — including objects produced by unrelated libraries.
Details
Three gaps compose. All line numbers are
lib/compiler.jsin 5.0.0.1. The path-key encoding is ambiguous.
quoteDottedString()quotes a segment only when it contains a., and never escapes a"inside the segment:So the key path
['x.y']and the key path['"x', 'y"']both render as the string"x.y". This is the same desynchronization CVE-2026-63376 named, with a quote in place of the dot.2. A table-array header can clear another key's guard.
addTableArray()walksassignedPaths/valueAssignmentswithisSameOrSubPath()over those ambiguous strings, so a header whose rendered path collides with an unrelated key deletes that key's entry.3. The descent in
assign()has no owned-container check.deepRef()appliesisOwnedContainer()only to intermediate segments (lib/compiler.js:265,:273) and returns the final container unchecked.assign()then walks it with only a shape test:A TOML datetime is a
Date. It satisfiestypeof target[k] === "object"and is not an array, so the walk descends into it — and aDatehas a live prototype chain toObject.prototype.Putting the three together, a document can make the current table a
Dateand then walk__proto__.__proto__toObject.prototype.Line 1 defines a key whose rendered path is
"b.c"and whose value is aDate.Line 2 is a table-array header whose rendered path collides with it, clearing its guard.
Line 3 re-enters that key, so the current table is now the
Date.Line 4 walks
Date→Date.prototype→Object.prototype.The payload sets a single harmless marker property. The last line shows the property is visible on an object this code never touched.
A useful control: the same document without line 2 (the guard-clearing header) is correctly rejected on 4.1.2 and later, which confirms the clearing step is what is load-bearing.
Impact
Any application that parses a TOML document from an untrusted source is affected — a configuration file uploaded by a user, a manifest from a third-party package or repository, a document posted to an API. Prototype pollution of this kind commonly escalates to authentication or authorization bypass, denial of service, and in applications with a suitable gadget, remote code execution, because every object in the process inherits the injected property.
Suggested fix: in
assign(), apply the same check the intermediate descent already uses — aftertarget = target[k], reject when!isOwnedContainer(target). Equivalently, applyisOwnedContainerto the valuedeepRef()returns. Separately,quoteDottedString()should escape"inside a segment so that distinct key paths cannot render to the same string.Severity
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:H/SC:N/SI:N/SA:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Release Notes
BinaryMuse/toml-node (toml)
v5.0.1Compare Source
=======================
Object.prototype. Two distinct key paths could render to the same bookkeeping string, letting an array-of-tables header clear the guard on an unrelated key, after which a dotted key could descend through a date value into its prototype chain. The compiler now records how each table was defined on the table itself rather than in string-keyed sets, so paths cannot collide and key paths can only descend through tables the compiler created.BigIntbefore any size check, so a multi-megabyte literal consumed super-linear CPU time before being rejected. Literals with more digits than any 64-bit integer are now rejected before conversion.v5.0.0Compare Source
=====================
Number.MAX_SAFE_INTEGER) now throw a parse error instead of silently returning a rounded value (#28). Opt in to lossless handling of the full 64-bit range withtoml.parse(input, { bigint: true }), which returns all integer values asBigInt.Configuration
📅 Schedule: (UTC)
🚦 Automerge: Disabled by config. Please merge this manually once you are satisfied.
♻ Rebasing: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.
🔕 Ignore: Close this PR and you won't be reminded about this update again.
This PR has been generated by Mend Renovate.