Skip to content

feat(configgen): yaml.v3 typed-struct config marshal (closes #126)#130

Merged
juev merged 1 commit into
forgekeep:mainfrom
ak2k:fix/configgen-yaml-marshal
May 21, 2026
Merged

feat(configgen): yaml.v3 typed-struct config marshal (closes #126)#130
juev merged 1 commit into
forgekeep:mainfrom
ak2k:fix/configgen-yaml-marshal

Conversation

@ak2k

@ak2k ak2k commented May 20, 2026

Copy link
Copy Markdown
Collaborator

Summary

Closes #126. Replaces internal/configgen/generator.go's text/template-based agent config generator with typed-struct marshalling through gopkg.in/yaml.v3. Defense-in-depth follow-up to GHSA-7hp6 — closes the entire YAML-injection class at the output side regardless of what upstream validators allow.

Public API (GeneratorInput, FirewallRule, LighthouseInfo, AdvancedUnsafeRoute) unchanged; only the implementation moves.

Changes

internal/configgen/marshal.go (new): typed nebulaConfig struct mirroring the previous template's shape. Inline PEM blocks use a literalString MarshalYAML to produce the | literal-block scalar form.

internal/configgen/generator.go: configTemplate, indentLines, and the template-based Generate are removed. Only public input types remain.

internal/configgen/generator_test.go:

  • Three multi-address tests (StaticHostMap_PerAddress, LighthouseHosts_AllAddresses, MultipleLighthousesEachMulti) converted from byte-equality on flow-style + quoted IPs to parse-then-compare via yaml.Unmarshal, per the suggestion in your issue body.
  • Added TestGenerate_LighthouseAndRelay covering the lighthouse+relay combo (both am_lighthouse: true and am_relay: true).
  • TestGenerate_Lighthouse now also asserts that static_host_map: {} is emitted explicitly — locks down the empty contract against a future omitempty refactor silently dropping the key.

internal/configgen/advanced_test.go: TestGenerate_TunDevice_StructuralBreakCharsAreQuoted (new) is the defense-in-depth acceptance criterion. A TunDevice value containing \r, \n, ENQ (0x05), \", :, #, or trailing whitespace round-trips through yaml.Unmarshal exactly, with no extra top-level keys appearing in the output (which would indicate YAML injection). Seven sub-cases, all pass.

Mobile bundle path (internal/mobilebundle/builder.go) uses the same GeneratorInput — no API change needed there.

Acceptance vs your issue body

Item Status
New `marshal.go` with typed struct + `Generate`
Delete `configTemplate`, `indentLines`, `template.FuncMap`
Update `advanced_test.go` + `generator_test.go` for parse-then-compare
Mobile bundle path: no API change
All existing `configgen` tests pass
Defense-in-depth test: TunDevice with `\r`, `\x05`, `"`, `:`, `#` round-trips as safe quoted scalar

Pre-flight

`make ci` green: vet, build, race tests, golangci-lint v2.12.0 (`0 issues.`). All existing configgen tests pass + the new `LighthouseAndRelay` test + the 7-case defense-in-depth subtest.

…p#126)

Replaces internal/configgen/generator.go's text/template-based agent
config generator with typed-struct marshalling through gopkg.in/yaml.v3.
Defense-in-depth follow-up to GHSA-7hp6-g3pq-3pc3 — closes the entire
YAML-injection class at the output side regardless of upstream
validators.

The public GeneratorInput / FirewallRule / LighthouseInfo /
AdvancedUnsafeRoute types are unchanged; only the implementation
moves from string-interpolation to yaml.v3 Encoder.

- internal/configgen/marshal.go (new): typed nebulaConfig struct
  mirroring the previous template's shape. Inline PEM blocks use a
  literalString MarshalYAML for the `|` literal-block scalar form.
- internal/configgen/generator.go: configTemplate, indentLines, and
  the template-based Generate are removed. Only public input types
  remain.
- internal/configgen/generator_test.go: three multi-address tests
  converted from byte-equality assertions on flow-style + quoted IPs
  to parse-then-compare via yaml.Unmarshal, per your issue body.
  Added TestGenerate_LighthouseAndRelay (lighthouse+relay combo)
  and an assertion in TestGenerate_Lighthouse that
  `static_host_map: {}` is emitted explicitly (locks down the empty
  contract against future omitempty drift).
- internal/configgen/advanced_test.go:
  TestGenerate_TunDevice_StructuralBreakCharsAreQuoted (new) is the
  defense-in-depth acceptance criterion: a TunDevice value containing
  \r, \n, ENQ (0x05), ", :, #, or trailing whitespace round-trips
  through yaml.Unmarshal exactly, and no extra top-level keys appear
  in the output (which would indicate YAML injection).

Mobile bundle path (internal/mobilebundle/builder.go) uses the same
GeneratorInput — no API change needed there.

make ci green: vet, build, race tests, golangci-lint v2.12.0
("0 issues."). All existing configgen tests pass plus the new
LighthouseAndRelay test plus the 7-case defense-in-depth subtest.
@juev juev force-pushed the fix/configgen-yaml-marshal branch from a9264b1 to 47670a3 Compare May 21, 2026 08:17
@juev juev merged commit c1506f7 into forgekeep:main May 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

configgen: marshal agent config via yaml.v3 typed struct (follow-up to GHSA-7hp6)

2 participants