---
title: "Read untrusted YAML safely"
url: https://recursiveai.org/oneyaml/go/cookbook/untrusted-input/
description: "Read YAML from the network in Go with oneyaml’s Untrusted() mode, refuse billion-laughs alias bombs and deep nesting, and size the limits yourself."
version: "1.0.0"
---

# Read untrusted YAML safely

YAML from a request body, an upload or another tenant can be built to exhaust memory or the stack. oneyaml bounds nesting depth, alias expansion, number size and patch work by default; for input you don’t control, add `Untrusted()`.

```go
package main

import (
	"errors"
	"fmt"
	"strings"

	oneyaml "oneyaml.dev/go"
)

func main() {
	// A normal request body reads as usual.
	var ok map[string]any
	fmt.Println("ok:", oneyaml.Unmarshal([]byte("name: web\nports: [80]\n"), &ok, oneyaml.Untrusted()))

	// "Billion laughs": nine levels of aliases, each repeated ten times.
	var b strings.Builder
	b.WriteString("l0: &l0 [x, x, x, x, x, x, x, x, x, x]\n")
	for i := 1; i < 9; i++ {
		refs := strings.TrimSuffix(strings.Repeat(fmt.Sprintf("*l%d, ", i-1), 10), ", ")
		fmt.Fprintf(&b, "l%d: &l%d [%s]\n", i, i, refs)
	}
	var v any
	err := oneyaml.Unmarshal([]byte(b.String()), &v, oneyaml.Untrusted())
	var le *oneyaml.LimitError
	if errors.As(err, &le) {
		fmt.Println("refused:", le.Limit, errors.Is(err, oneyaml.ErrLimit))
	}

	// Deep nesting is refused too, without overflowing the stack.
	err = oneyaml.Unmarshal([]byte(strings.Repeat("[", 100_000)), &v, oneyaml.Untrusted())
	if errors.As(err, &le) {
		fmt.Println("deep:", le.Limit)
	}

	// Size the limits yourself: start from UntrustedLimits and change fields.
	limits := oneyaml.UntrustedLimits()
	limits.MaxInputBytes = 1 << 10
	err = oneyaml.Unmarshal([]byte(strings.Repeat("a: b\n", 1000)), &v, oneyaml.Limits(limits))
	if errors.As(err, &le) {
		fmt.Println("too big:", le.Limit)
	}
}
```

```text
ok: <nil>
refused: MaxAliasNodes true
deep: MaxDepth
too big: MaxInputBytes
```

## Good to know

- **Bounded by default:** nesting depth, alias expansion, the digits of a number and the work of a patch, unless you pass `NoLimits()`.
- **Bounded only with `Untrusted()`:** input size, documents per stream and scalar length. Pass `Limits(...)` with your own `LimitSet` to size them; start from `UntrustedLimits()` and write the struct with field names.
- A limit error is a `*LimitError` whose `Limit` names the bound, and it matches `oneyaml.ErrLimit` with `errors.Is`.
- Your own hooks (`UnmarshalYAML` and the like) run outside these bounds, and the compat packages have no `Untrusted()` mode.
- No `unsafe` and no cgo. A panic caused by input is treated as a security bug.

Every limit and its value is in [Limits](https://recursiveai.org/oneyaml/go/docs/limits/).
