| id | 2606022122 |
|---|---|
| title | Review and centralize YAML handling |
| status | ✅ |
| summary | Audit all YAML parsing/marshaling, unify under a central package, ensure consistent security checks |
| model | sonnet |
Centralize YAML operations under one package. Reduce duplication. Ensure consistent security checks prevent DoS attacks.
YAML handling is scattered across 9 packages with 13+ unmarshal
sites. Each site must manually call RejectYAMLAliases() before
yaml.Unmarshal() to prevent billion-laughs attacks. This
pattern is duplicated at every call site.
File: internal/lint/yamlsafe.go
- Defines
RejectYAMLAliases()for DoS protection
Files: internal/config/{load,convention,config}.go
- Load config files, validate conventions
- Define
RuleCfgcustom marshal/unmarshal
File: internal/lint/frontmatter.go
- Parse front-matter
kinds:field
Files in internal/archetype/gensection/, internal/rules/:
- Parse YAML parameters for multiple directive types
- Files:
parse.go,catalog/rule.go,requiredstructure/rule.go
Files: internal/kindsout/kindsout.go, cmd/mdsmith/main.go
- Marshal kind bodies and config
File: internal/corpus/config.go
- Load corpus config
- Create
internal/yamlutilpackage with:
UnmarshalSafe(data []byte, v any) error— combines alias rejection + unmarshalUnmarshalNodeSafe(data []byte) (yaml.Node, error)— for node-based parsingMarshal(v any) ([]byte, error)— thin wrapper for consistency- Move
RejectYAMLAliasesfrominternal/linttointernal/yamlutil
- Update all 13+
yaml.Unmarshalcall sites to useyamlutil.UnmarshalSafe:
internal/config/load.go(2 sites: load, topLevelKeySet; validateConventionScalar uses plain yaml.Unmarshal since node parsing does not expand aliases)internal/lint/frontmatter.go(1 site)internal/archetype/gensection/parse.go(1 site)internal/rules/catalog/rule.go(1 site)internal/rules/requiredstructure/rule.go(3 sites via UnmarshalSafe: require, include, schema; front-matter read uses explicit RejectYAMLAliases + yaml.Unmarshal to preserve distinct error messages per error type)internal/corpus/config.go(2 sites)cmd/mdsmith/main.go(check if front-matter parsing needs update)
- Update all
yaml.Marshalcall sites to useyamlutil.Marshal:
internal/kindsout/kindsout.gocmd/mdsmith/main.gointernal/config/config.go(RuleCfg.MarshalYAML — keep custom logic, but consider if any standardization helps)
- Update import statements across all affected files
- Add godoc to
internal/yamlutilpackage explaining:
- Why alias rejection is mandatory for user content
- When to use
UnmarshalSafevs directyamlpackage - Link to adversarial-markdown security doc
- Update tests:
- Add
internal/yamlutil/yamlutil_test.gowith tests for new wrapper functions and alias rejection - Repurpose
internal/lint/yamlsafe_test.goto verify alias rejection through the front-matter parsing path - Ensure coverage of error paths
- Run full test suite:
go test ./... - Run linter:
go tool golangci-lint run
- New
internal/yamlutilpackage exists with documented safe-unmarshal wrappers -
RejectYAMLAliasesmoved frominternal/linttointernal/yamlutil - All user-content unmarshal sites use
yamlutil.UnmarshalSafe(no directyaml.Unmarshalon user data) - All marshal sites use
yamlutil.Marshalor keep well-documented custom logic in place -
internal/yamlutilhas comprehensive godoc - All tests pass:
go test ./... -
go tool golangci-lint runreports no issues - No direct calls to
lint.RejectYAMLAliasesoutsideyamlutilpackage