---
title: "GHSA-r4xh-jqrq-34v2: smol-toml: Quadratic-time parse() from parseKey rescanning to end of document on each key line"
description: "Moderate severity. smol-toml: Quadratic-time parse() from parseKey rescanning to end of document on each key line. Affects smol-toml; fixed in 1.9.0."
url: https://inlinr.com/advisories/GHSA-r4xh-jqrq-34v2
lang: en
---

[Packages](https://inlinr.com/packages) / [smol-toml](https://inlinr.com/packages/smol-toml) / [Security](https://inlinr.com/packages/smol-toml/security)

Moderate Vulnerability GHSA-r4xh-jqrq-34v2 Published 5 Oct 2026

# smol-toml: Quadratic-time parse() from parseKey rescanning to end of document on each key line

Your version 

Affected releases · [smol-toml](https://inlinr.com/packages/smol-toml)

- 1.x 1.0.0 – 1.8.0 Fixed in 1.9.0
- 0.1.x 0.1.0 No fix on this line yet

On 1.x, the newest line:

```
npm install smol-toml@1.9.0
```

Every release on a timeline

1.x 

1. Affected

1.0.0 – 1.8.0  
27 releases
2. Fixed

1.9.0 – 1.9.1  
2 releases, latest

0.x 

1. Affected

0.1.0  
1 release

30 stable releases of smol-toml, oldest to newest, one row per major.

## What happens

Summary

`parse()` has a quadratic-time path in `parseKey`, reachable on default options with ordinary valid input. For every key line and table-header line, `parseKey` (dist/struct.js, lines 58 and 86) finds the dotted-key separator with `ctx.s.indexOf('.', ctx.p)`, where `ctx.s` is the whole document. When a key has no `.` ahead of it, that search runs all the way to the end of the input, and the result is then clamped back to the line terminator `endPtr` \- so everything scanned past the current line is wasted. `parseKey` runs once per line, so a document of N dot-free keys costs O(n^2).

The most ordinary TOML shape triggers it: a flat list of `key = value` lines, or a repeated `[[a]]` table. No dotted keys, no special options, valid input throughout.

Proof of concept

```
import { parse } from 'smol-toml'

let doc = ''
for (let i = 0; i < 256000; i++) doc += 'k' + i + ' = 1\n'

console.time('parse')
parse(doc) // ~2.8 MB of valid TOML, default options
console.timeEnd('parse')
```

Doubling the line count roughly quadruples the time:

| lines | size   | parse() |
| ----- | ------ | ------- |
| 32k   | 0.3 MB | 0.3 s   |
| 64k   | 0.7 MB | 1.0 s   |
| 128k  | 1.4 MB | 3.5 s   |
| 256k  | 2.8 MB | 14 s    |

Impact

Any service that runs `parse()` on attacker-supplied TOML can be stalled. The work is synchronous, so it blocks the whole event loop, and the quadratic is unbounded: a \~7 MB body pins a core for about a minute, larger bodies for several.

Patches

Version 1.9.0 uses a different implementation for parsing keys which is strictly linear.

Workarounds

Limit the maximum document size accepted when parsing arbitrary documents.

## How it is exploited

Exploitable over the network, with no account and no action from a user.

- **Confidentiality**: none
- **Integrity**: none
- **Availability**: low

CVSS:3.1/​AV:N/​AC:L/​PR:N/​UI:N/​S:U/​C:N/​I:N/​A:L

## References

- [Reference](https://github.com/squirrelchat/smol-toml/security/advisories/GHSA-r4xh-jqrq-34v2 "https://github.com/squirrelchat/smol-toml/security/advisories/GHSA-r4xh-jqrq-34v2")  
github.com/squirrelchat/smol-toml/security/advi…
- [Fix commit](https://github.com/squirrelchat/smol-toml/commit/99102aa57fc932f760ea9c15b4cf1c181f952d24 "https://github.com/squirrelchat/smol-toml/commit/99102aa57fc932f760ea9c15b4cf1c181f952d24")  
99102aa
- [Package](https://github.com/squirrelchat/smol-toml "https://github.com/squirrelchat/smol-toml")  
github.com/squirrelchat/smol-toml
- [Reference](https://github.com/squirrelchat/smol-toml/releases/tag/v1.9.0 "https://github.com/squirrelchat/smol-toml/releases/tag/v1.9.0")  
github.com/squirrelchat/smol-toml/releases/tag/…
