yamlutil

package
v0.23.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 21, 2026 License: MIT Imports: 5 Imported by: 0

Documentation

Overview

Package yamlutil provides safe YAML parsing and marshaling helpers.

All user-supplied content (config files, front matter, directive parameters) must pass through UnmarshalSafe or UnmarshalNodeSafe rather than calling yaml.Unmarshal directly. These wrappers call RejectYAMLAliases first, which prevents billion-laughs denial-of-service attacks by refusing any YAML that contains anchors or aliases before the alias expansion happens.

When to use each function:

  • UnmarshalSafe — unmarshal user content into a Go struct or map.
  • UnmarshalNodeSafe — unmarshal user content into a raw yaml.Node tree (needed when inspecting YAML structure before decoding into typed values).
  • Marshal — thin wrapper around yaml.Marshal for consistency; safe for output marshaling where data originates from trusted Go values.

See docs/security/2026-04-05-adversarial-markdown.md for threat model context.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Marshal

func Marshal(v any) ([]byte, error)

Marshal is a thin wrapper around yaml.Marshal for consistency with UnmarshalSafe. Safe for output marshaling where data originates from trusted Go values.

func RejectYAMLAliases

func RejectYAMLAliases(data []byte) error

RejectYAMLAliases decodes YAML into a node tree and returns an error if any anchor or alias is found. Decoding into yaml.Node does not expand aliases, so this is safe even for billion-laughs payloads. Non-anchor syntax errors return nil (handled by the caller's yaml.Unmarshal). This check must be called before yaml.Unmarshal on user-supplied content.

func TopLevelMappingLines added in v0.17.0

func TopLevelMappingLines(doc *yaml.Node, lineOffset int) map[string]int

TopLevelMappingLines walks the yaml.Node document produced by UnmarshalNodeSafe (or a direct yaml.Unmarshal into a yaml.Node) and returns a map from each top-level scalar mapping key to its source line, shifted by lineOffset.

yaml.v3 nests the user's mapping inside a single-document node; the helper skips past that layer to the content of interest. A nil node, missing content, or a non-mapping root all return nil so callers degrade to a "no per-key line known" fallback rather than panicking. Non-scalar mapping keys (YAML's explicit `?` syntax with a sequence or mapping as the key) are skipped silently — the remaining scalar keys still appear in the returned map, which may therefore be empty if every key is non-scalar.

Two callers want this: internal/schema parses a proto.md frontmatter and the requiredstructure rule parses its legacy schema frontmatter. Centralising the walk here means future YAML edge cases (documents, anchors, etc.) only need fixing in one place — see the Copilot review on PR #284.

func UnmarshalNodeSafe

func UnmarshalNodeSafe(data []byte) (yaml.Node, error)

UnmarshalNodeSafe rejects YAML anchors/aliases then unmarshals data into a yaml.Node document node. Use this when raw node inspection is needed before typed decoding (e.g. checking top-level key presence or tag types).

func UnmarshalSafe

func UnmarshalSafe(data []byte, v any) error

UnmarshalSafe rejects YAML anchors/aliases then unmarshals data into v. Use this for all user-supplied YAML content (config files, front matter, directive parameters).

Types

This section is empty.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL