From f0a533eaf5c78078d9c517bd2ce86f5f41cb0e22 Mon Sep 17 00:00:00 2001 From: Rohil Surana Date: Mon, 17 Aug 2026 11:03:17 +0530 Subject: [PATCH 1/2] fix(reconcile): reject unknown top-level fields in desired-state documents --- internal/reconcile/reconcile.go | 5 +++++ internal/reconcile/reconcile_test.go | 11 +++++++++++ 2 files changed, 16 insertions(+) diff --git a/internal/reconcile/reconcile.go b/internal/reconcile/reconcile.go index aa5c70f56c..e136c41466 100644 --- a/internal/reconcile/reconcile.go +++ b/internal/reconcile/reconcile.go @@ -64,6 +64,11 @@ var kindDependencies = map[string][]string{ // Write `spec: []` to mean that on purpose. func parseDocuments(registry map[string]Reconciler, data []byte) ([]parsedDocument, error) { dec := yaml.NewDecoder(bytes.NewReader(data)) + // Reject unknown top-level keys (a stray "metadata:", a typo'd "apiVarsion") + // instead of ignoring them, the same way entry decoding does. The document's + // own spec content is decoded separately, so this only guards the outer + // apiVersion/kind/spec envelope. + dec.KnownFields(true) var docs []parsedDocument for { var doc document diff --git a/internal/reconcile/reconcile_test.go b/internal/reconcile/reconcile_test.go index 85865007c3..85552216e8 100644 --- a/internal/reconcile/reconcile_test.go +++ b/internal/reconcile/reconcile_test.go @@ -112,6 +112,17 @@ func TestRun_SpecHandling(t *testing.T) { assert.NoError(t, err) assert.Equal(t, 1, rec.called) }) + + t.Run("a document with an unknown top-level field is rejected", func(t *testing.T) { + rec := &fakeReconciler{} + reg := map[string]Reconciler{KindPlatformUser: rec} + + // A stray top-level key (here a typo of "spec") must fail the file rather + // than being ignored, the same way entry decoding rejects unknown fields. + _, err := Run(context.Background(), reg, []byte("kind: PlatformUser\nspec: []\nspce: oops\n"), false) + assert.ErrorContains(t, err, "field spce not found") + assert.Zero(t, rec.called) + }) } func TestExport_Errors(t *testing.T) { From 9ef21ae1e581715ef7e5567291a9ce4b2318dd21 Mon Sep 17 00:00:00 2001 From: Rohil Surana Date: Mon, 17 Aug 2026 13:02:23 +0530 Subject: [PATCH 2/2] test(reconcile): reject an unknown field in a later document --- internal/reconcile/reconcile_test.go | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/internal/reconcile/reconcile_test.go b/internal/reconcile/reconcile_test.go index 85552216e8..7b3833efd6 100644 --- a/internal/reconcile/reconcile_test.go +++ b/internal/reconcile/reconcile_test.go @@ -123,6 +123,18 @@ func TestRun_SpecHandling(t *testing.T) { assert.ErrorContains(t, err, "field spce not found") assert.Zero(t, rec.called) }) + + t.Run("an unknown field in a later document is rejected", func(t *testing.T) { + rec := &fakeReconciler{} + reg := map[string]Reconciler{KindPlatformUser: rec} + + // The stray key sits in the second document. The check must fire on every + // document, not only the first, which is the whole point of checking the file. + file := []byte("kind: PlatformUser\nspec: []\n---\nkind: PlatformUser\nspec: []\nspce: oops\n") + _, err := Run(context.Background(), reg, file, false) + assert.ErrorContains(t, err, "field spce not found") + assert.Zero(t, rec.called) + }) } func TestExport_Errors(t *testing.T) {