GitHub Security Advisory: GHSA-4fcp-jxh7-23x8
Package: github.com/tomwright/dasel/v3 (GO)
Severity: MODERATE (CVSS 6.2)
CWE: CWE-674: Uncontrolled Recursion
Vulnerable versions: >= 3.0.0, < 3.3.2
Patched in: 3.3.2

Summary: Dasel has unbounded YAML alias expansion in dasel leads to CPU/memory denial of service

Details:
### Summary

`dasel`'s YAML reader allows an attacker who can supply YAML for processing to trigger extreme CPU and memory consumption. The issue is in the library's own `UnmarshalYAML` implementation, which manually resolves alias nodes by recursively following `yaml.Node.Alias` pointers without any expansion budget, bypassing go-yaml v4's built-in alias expansion limit.

The issue issue is on `v3.3.1` (`fba653c7f248aff10f2b89fca93929b64707dfc8`) and on the current default branch at commit `0dd6132e0c58edbd9b1a5f7ffd00dfab1e6085ad`. It is also verified the same code path is present in `v3.0.0` (`648f83baf070d9e00db8ff312febef857ec090a3`). A 342-byte payload did not complete within 5 seconds on the test system and exhibited unbounded resource growth.

### Details

In `v3.3.1` (`fba653c7f248aff10f2b89fca93929b64707dfc8`), the reachable call path is:

- The YAML reader is registered in [`parsing/yaml/yaml.go`](https://github.com/TomWright/dasel/blob/fba653c7f248aff10f2b89fca93929b64707dfc8/parsing/yaml/yaml.go) and exposed via `parsing.Format("yaml").NewReader()`
- `(*yamlReader).Read` in [`parsing/yaml/yaml_reader.go#L23-L48`](https://github.com/TomWright/dasel/blob/fba653c7f248aff10f2b89fca93929b64707dfc8/parsing/yaml/yaml_reader.go#L23-L48) uses `yaml.NewDecoder` to decode the input. Because `yamlValue` implements `UnmarshalYAML(*yaml.Node)`, the decoder passes the raw `*yaml.Node` tree to that custom unmarshaler
- `(*yamlValue).UnmarshalYAML` in [`parsing/yaml/yaml_reader.go#L57-L131`](https://github.com/TomWright/dasel/blob/fba653c7f248aff10f2b89fca93929b64707dfc8/parsing/yaml/yaml_reader.go#L57-L131) walks the Node tree
- When an `AliasNode` is encountered, the handler at [`parsing/yaml/yaml_reader.go#L119-L126`](https://github.com/TomWright/dasel/blob/fba653c7f248aff10f2b89fca93929b64707dfc8/parsing/yaml/yaml_reader.go#L119-L126) recursively calls `newVal.UnmarshalYAML(value.Alias)` without tracking expansion count

The root cause is that go-yaml v4 has two decoding paths:

1. **`Unmarshal` into Go values**: Tracks alias expansion count and rejects documents with excessive aliasing (`"yaml: document contains excessive aliasing"`).
2. **`Decode` into `yaml.Node` / custom `UnmarshalYAML`**: Passes a compact Node tree where alias nodes are pointers to their anchors. No expansion occurs at this level.

Dasel receives the compact Node tree via its `UnmarshalYAML(*yaml.Node)` hook and then recursively follows `value.Alias` pointers, re-expanding aliases without a budget:

```go
case yaml.AliasNode:
    newVal := &yamlValue{}
    if err := newVal.UnmarshalYAML(value.Alias); err != nil {
        return err
    }
    yv.value = newVal.value
    yv.value.SetMetadataValue("yaml-alias", value.Value)
```

With a 9-level alias bomb (each level referencing the previous 9 times), this produces hundreds of millions of recursive expansions from a 342-byte input.

Test environment:

- MacBook Air (Apple M2), macOS / Darwin `arm64`
- Go `1.26.1`
- dasel `v3.3.1