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 ¶
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 ¶
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
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 ¶
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 ¶
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.