From 3bac93c26561e79c20bfecb33148d9a612aef746 Mon Sep 17 00:00:00 2001 From: Suleiman Dibirov Date: Sat, 26 Oct 2024 10:07:10 +0300 Subject: [PATCH] fix(reset): Add cycle detector in reset.go --- loader/reset.go | 20 ++++++++++++++++++-- loader/reset_test.go | 21 +++++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/loader/reset.go b/loader/reset.go index 2b7f04c3..467522ba 100644 --- a/loader/reset.go +++ b/loader/reset.go @@ -26,13 +26,15 @@ import ( ) type ResetProcessor struct { - target interface{} - paths []tree.Path + target interface{} + paths []tree.Path + visitedNodes map[*yaml.Node]bool } // UnmarshalYAML implement yaml.Unmarshaler func (p *ResetProcessor) UnmarshalYAML(value *yaml.Node) error { resolved, err := p.resolveReset(value, tree.NewPath()) + p.visitedNodes = nil if err != nil { return err } @@ -45,6 +47,20 @@ func (p *ResetProcessor) resolveReset(node *yaml.Node, path tree.Path) (*yaml.No if strings.Contains(path.String(), ".<<") { path = tree.NewPath(strings.Replace(path.String(), ".<<", "", 1)) } + + // Check for cycle + if p.visitedNodes == nil { + p.visitedNodes = make(map[*yaml.Node]bool) + } + + // Check if the current node has been visited before (cycle detection) + if p.visitedNodes[node] { + return nil, fmt.Errorf("cycle detected at path: %s", path.String()) + } + + // Mark the current node as visited + p.visitedNodes[node] = true + // If the node is an alias, We need to process the alias field in order to consider the !override and !reset tags if node.Kind == yaml.AliasNode { return p.resolveReset(node.Alias, path) diff --git a/loader/reset_test.go b/loader/reset_test.go index 71e7dc3e..3b070c35 100644 --- a/loader/reset_test.go +++ b/loader/reset_test.go @@ -81,3 +81,24 @@ networks: assert.NilError(t, err) assert.Check(t, p.Networks["test"].External == false) } + +func TestResetCycle(t *testing.T) { + _, err := Load( + types.ConfigDetails{ + ConfigFiles: []types.ConfigFile{ + { + Filename: "(inline)", + Content: []byte(` +x-healthcheck: &healthcheck + egress-service: + <<: *healthcheck +`), + }, + }, + }, func(options *Options) { + options.SkipNormalization = true + options.SkipConsistencyCheck = true + }, + ) + assert.Error(t, err, "cycle detected at path: x-healthcheck.egress-service") +}