From 305ed25896bcc4530aa23138e407a786d9e8f602 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Philippe=20Qu=C3=A9m=C3=A9ner?= Date: Fri, 7 Nov 2025 15:32:17 +0100 Subject: [PATCH] fix(folders): add a circuit breaker to prevent infinite loops (#113596) --- pkg/services/folder/folderimpl/unifiedstore.go | 13 +++++++++++-- pkg/services/folder/folderimpl/unifiedstore_test.go | 2 +- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/pkg/services/folder/folderimpl/unifiedstore.go b/pkg/services/folder/folderimpl/unifiedstore.go index 28ced5f2321..8c30024eefb 100644 --- a/pkg/services/folder/folderimpl/unifiedstore.go +++ b/pkg/services/folder/folderimpl/unifiedstore.go @@ -388,7 +388,9 @@ func (ss *FolderUnifiedStoreImpl) GetFolders(ctx context.Context, q folder.GetFo } if (q.WithFullpath || q.WithFullpathUIDs) && f.Fullpath == "" { - buildFolderFullPaths(f, relations, folderMap) + if err := buildFolderFullPaths(f, relations, folderMap); err != nil { + return nil, err + } } hits = append(hits, f) @@ -559,15 +561,21 @@ func computeFullPath(parents []*folder.Folder) (string, string) { return strings.Join(fullpath, "/"), strings.Join(fullpathUIDs, "/") } -func buildFolderFullPaths(f *folder.Folder, relations map[string]string, folderMap map[string]*folder.Folder) { +func buildFolderFullPaths(f *folder.Folder, relations map[string]string, folderMap map[string]*folder.Folder) error { titles := make([]string, 0) uids := make([]string, 0) titles = append(titles, f.Title) uids = append(uids, f.UID) + i := 0 currentUID := f.UID for currentUID != "" { + // This is just a circuit breaker to prevent infinite loops. We should never reach this limit. + if i > 1000 { + return fmt.Errorf("folder depth exceeds the maximum allowed depth, You might have a circular reference") + } + i++ parentUID, exists := relations[currentUID] if !exists { break @@ -588,6 +596,7 @@ func buildFolderFullPaths(f *folder.Folder, relations map[string]string, folderM f.Fullpath = strings.Join(util.Reverse(titles), "/") f.FullpathUIDs = strings.Join(util.Reverse(uids), "/") + return nil } func shouldSkipFolder(f *folder.Folder, filterUIDs map[string]struct{}) bool { diff --git a/pkg/services/folder/folderimpl/unifiedstore_test.go b/pkg/services/folder/folderimpl/unifiedstore_test.go index a2bb1db024d..5ffc1d12947 100644 --- a/pkg/services/folder/folderimpl/unifiedstore_test.go +++ b/pkg/services/folder/folderimpl/unifiedstore_test.go @@ -881,7 +881,7 @@ func TestBuildFolderFullPaths(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - buildFolderFullPaths(tt.args.f, tt.args.relations, tt.args.folderMap) + require.NoError(t, buildFolderFullPaths(tt.args.f, tt.args.relations, tt.args.folderMap)) require.Equal(t, tt.want.Fullpath, tt.args.f.Fullpath, "BuildFolderFullPaths() = %v, want %v", tt.args.f.Fullpath, tt.want.Fullpath) require.Equal(t, tt.want.FullpathUIDs, tt.args.f.FullpathUIDs, "BuildFolderFullPaths() = %v, want %v", tt.args.f.FullpathUIDs, tt.want.FullpathUIDs) require.Equal(t, tt.want.Title, tt.args.f.Title, "BuildFolderFullPaths() = %v, want %v", tt.args.f.Title, tt.want.Title)