Documentation
¶
Overview ¶
Package safeyaml provides secure YAML parsing with mandatory pre-validation.
Security Boundary ¶
This package is the ONLY authorized entry point for YAML parsing in the codebase. Direct use of gopkg.in/yaml.v3 is prohibited except in this package and the yamlpolicy package (which provides the validation logic).
An import guard test enforces this boundary by scanning all Go files for direct yaml.v3 imports.
Why This Exists ¶
YAML parsing is a known attack vector:
Alias bombs: Small YAML files can expand to huge in-memory structures via anchor/alias references (similar to XML billion-laughs attacks)
Memory exhaustion: Large YAML files can consume excessive memory during parsing before any application-level size checks
By centralizing YAML parsing through this package, we ensure:
- Size limits are enforced BEFORE parsing begins
- Alias bomb detection runs BEFORE the parser expands references
- Consistent error handling across all YAML parsing
Usage ¶
import (
"github.com/locktivity/epack/internal/limits"
"github.com/locktivity/epack/internal/safeyaml"
)
var config MyConfig
if err := safeyaml.Unmarshal(data, limits.ConfigFile, &config); err != nil {
return err
}
Package safeyaml provides secure YAML parsing with mandatory pre-validation.
Quick Start ¶
For parsing config files:
var config Config
if err := safeyaml.Unmarshal(data, limits.ConfigFile, &config); err != nil {
return err
}
For strict parsing (rejects unknown fields):
if err := safeyaml.UnmarshalStrict(data, limits.ConfigFile, &config); err != nil {
return err
}
Why Not gopkg.in/yaml.v3? ¶
This package wraps gopkg.in/yaml.v3 to ensure all YAML parsing goes through security validation BEFORE the actual parse. This prevents DoS attacks via:
- Large file parsing (memory exhaustion)
- YAML alias bombs (exponential expansion)
All packages needing to parse YAML should import this package instead of gopkg.in/yaml.v3 directly. An import guard test enforces this boundary.
Serialization ¶
For marshaling (serialization), use gopkg.in/yaml.v3 directly - there are no security concerns with serialization. This package provides NewEncoder for convenience, but you can also use yaml.Marshal directly.
Index ¶
Constants ¶
const ( DocumentNode = yaml.DocumentNode SequenceNode = yaml.SequenceNode MappingNode = yaml.MappingNode ScalarNode = yaml.ScalarNode AliasNode = yaml.AliasNode )
NodeKind re-exports yaml.Kind constants.
Variables ¶
This section is empty.
Functions ¶
func DecodeNode ¶
DecodeNode parses YAML into a yaml.Node tree with security validation.
SECURITY: Unlike Unmarshal to a Go type, DecodeNode preserves the node structure including anchors/aliases without expanding them. This is useful for inspection or transformation, but callers must be careful if they later convert nodes to Go types.
The yamlpolicy validation still runs to enforce size limits and detect alias bombs before the parse.
func Marshal ¶
Marshal serializes a Go value to YAML. This is a passthrough to yaml.Marshal - serialization has no security concerns. Provided for convenience so packages don't need to import yaml.v3 directly.
Types ¶
type Encoder ¶
type Encoder struct {
// contains filtered or unexported fields
}
Encoder wraps yaml.Encoder for streaming output. Use this for serialization when you need streaming writes.
func NewEncoder ¶
NewEncoder creates a new YAML encoder writing to w.