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().
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) } }
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. PassLimits(...)with your ownLimitSetto size them; start fromUntrustedLimits()and write the struct with field names. - A limit error is a
*LimitErrorwhoseLimitnames the bound, and it matchesoneyaml.ErrLimitwitherrors.Is. - Your own hooks (
UnmarshalYAMLand the like) run outside these bounds, and the compat packages have noUntrusted()mode. - No
unsafeand no cgo. A panic caused by input is treated as a security bug.
Every limit and its value is in Limits.