GitHub Advisoryadvisory · Go
GHSA-cqxr-jxr2-85pq Dasel: Unbounded recursion in JSON and XML readers causes unrecoverable stack-overflow DoS
MEDIUM
Official page ↗
- Aliases
- CVE-2026-59168
- Ecosystem
- Go
- Packages
- github.com/tomwright/dasel/v3
- 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-674
- Reviewed
- 2026-09-22
Affected packages
• Go github.com/tomwright/dasel/v3: >= 3.0.0, < 3.11.1 → fixed in 3.11.1
Description
Summary
`dasel`'s JSON and XML readers parse nested structures with unbounded recursion, one
native stack frame per nesting level, with no depth guard. A small (sub-10 MB), deeply
nested document drives the Go runtime past its goroutine stack limit and triggers a
`fatal error: stack overflow`. This is unrecoverable: it is a runtime fatal error, not
a `panic`, so a consumer's `defer`/`recover` cannot intercept it, the entire process dies.
Both readers are affected; neither has a depth limit, and the JSON reader additionally has
no input-size cap (the XML reader caps size at 10 MB but not depth).
Affected Versions
`github.com/tomwright/dasel/v3` and all v3.x releases through v3.11.0 (current `main`,
commit `abc1e1d`). This vulnerability is fixed in 3.11.1. Pre-v3 is out of scope.
Description
JSON reader @ `parsing/json/json_reader.go`
`decodeValue` (line 54) dispatches to the mutually-recursive `decodeObject` (line 78) and
`decodeArray` (line 140). Each calls back into both for nested values
(`decodeArray`→`decodeArray` at line 151, `decodeObject` at 163; `decodeObject`→`decodeArray`
at 95, `decodeObject` at 111). Every `[` or `{` in the input adds one stack frame. There is
no depth counter and no `len(data)` cap anywhere in the reader.
XML reader @`parsing/xml/reader.go`
`parseElement` (line 172) recurses at line 211 for every `xml.StartElement`. The file
declares explicit DoS guards — `maxXMLSize = 10_000_000`, comment count/length — but these
bound size and comment volume, not nesting depth. The open tag `` is 3 bytes, so the
10 MB cap still permits ~3.3 M nesting levels, exhausting the stack long before the size
limit fires.
Reachability (both)
Both are on the primary public read path: `parsing.Format().NewReader(opts).Read(data)`,
with `data` fully attacker-controlled and no depth validation before the recursion. The same
path backs the `dasel` CLI (`dasel -r json` / `-r xml`). Default reader, no special options.
Go stack overflow is a `fatal error`, so `recover()` at the call site does not help.
Precedent in this codebase
DoS hardening on the readers is already an accepted concern here, which is why this is a gap
rather than a design choice: the XML reader has the `maxXMLSize` cap, and the YAML reader
already implements exactly the fix needed, `parsing/yaml/yaml_reader.go:31,90-93` returns
`ErrYamlExpansionDepthExceeded` once `expansionDepth > maxExpansionDepth`. The JSON and XML
readers simply lack the equivalent depth guard.
Proof of Concept
Single runnable program, public API only (`poc/main.go`, module wired to a local clone via
`replace`):
```go
package main
import (
"fmt"; "os"; "strings"
"github.com/tomwright/dasel/v3/parsing"
_ "github.com/tomwright/dasel/v3/parsing/json"
_ "github.com/tomwright/dasel/v3/parsing/xml"
)
func main() {
mode := "json"; if len(os.Args) > 1 { mode = os.Args[1] }
depth := 6_000_000; if mode == "xml" { depth = 3_200_000 }
var data []byte
if mode == "xml" {
data = []byte(strings.Repeat("", depth)) // ~9.6MB, under the 10MB cap
} else {
data = []byte(strings.Repeat("[", depth) + strings.Repeat("]", depth)) // ~12MB
}
defer func() { if r := recover(); r != nil { fmt.Println("recovered (NOT fatal):", r) } }()
r, _ := parsing.Format(mode).NewReader(parsing.DefaultReaderOptions())
v, err := r.Read(data)
fmt.Printf("Read returned WITHOUT crash: v=%v err=%v\n", v != nil, err)
}
```
```
go run . json # nested arrays -> fatal error: stack overflow ; ~85 decodeArray frames
go run . xml # nested -> fatal error: stack overflow ; ~93 parseElement frames
```
Observed (Go 1.26, default 1 GB goroutine stack):
• `json` depth 6 M (12 MB): `runtime: goroutine stack exceeds 1000000000-byte limit` →
`fatal error: stack overflow`; backtrace dominated by `json.(*jsonReader).decodeArray`.
`depth 2 M` completes (~0.83 s), confirming it is depth-driven, not a parse error.
• `xml` depth 3.2 M (9.6 MB, under the 10 MB `maxXMLSize` cap): same fatal overflow;
backtrace is an unbroken chain of `xml.(*xmlReader).parseElement` at `reader.go:211`.
• The deferred `recover()` never fires in either case — the process exits.
• CLI equivalents: `printf '%.0s' {1..3200000} | dasel -r xml`.
Impact
An attacker who controls JSON or XML passed to dasel — via the library `Read` API, the CLI,
or the `parse('json'|'xml', …)` selector function — crashes the host process with a single
small document. Because the failure is a Go `fatal error` rather than a recoverable panic, a
consumer that wraps parsing in `defer`/`recover` is still taken down: the whole process
terminates, killing every in-flight goroutine, not just the parse. Availability only — no
confidentiality or integrity impact. For a library consumer feeding network-sourced data to
`Read`, this is a remotely triggerable, unrecoverable DoS.
Suggested Fix
Add a recursion-depth guard to both readers, mirroring the YAML reader's existing
`maxExpansionDepth` / `ErrYamlExpansionDepthExceeded` pattern:
• JSON — thread a `depth int` through `decodeValue`/`decodeObject`/`decodeArray`,
increment on descent, return `ErrJSONMaxDepthExceeded` past a conservative bound
(e.g. 10 000). Optionally add a `maxJSONSize` cap matching `maxXMLSize` for parity.
• XML — add a `maxXMLDepth` constant to the existing `Security limits` block and thread
a `depth` through `parseElement`, returning a normal error past the bound.
Both return a clean `error` for pathological input instead of crashing the process,
consistent with how the comment-count, size, and YAML-expansion limits already behave. A
limit in the low thousands preserves all realistic legitimate documents.
Related
Alias · CVE (1)
- CVE-2026-59168 Dasel is a command-line tool and library for querying, modifying, and transforming data structures. vulnerability
Weakness · CWE (1)
- CWE-674 Uncontrolled Recursion weakness
References
- https://github.com/TomWright/dasel/security/advisories/GHSA-cqxr-jxr2-85pq web
- https://nvd.nist.gov/vuln/detail/CVE-2026-59168 advisory
- https://github.com/TomWright/dasel/commit/4c91d0d02dc59ce8404709b1bfed7a6fabe62f68 web
- https://github.com/TomWright/dasel package
- https://github.com/TomWright/dasel/releases/tag/v3.11.1 web