grafana-0001: getDashboardsSharedWithUser dashboard UID dedup O(P^2) MEDIUM 2.5x grafana-0002: folder UID dedup slices.Contains O(P^2) 4 sites MEDIUM 4x grafana-0003: deduplicateAvailableFolders ContainsFunc O(F*A) MEDIUM 9.3x loki-0001: DAG AddEdge/Eliminate slices.Contains O(E^2) LOW 2.1x
84 lines
3.5 KiB
Diff
84 lines
3.5 KiB
Diff
# UNDF: UNDF-2026-000000406
|
|
# UNDF: (leave blank)
|
|
# CWE-407: Folder UID dedup via slices.Contains O(P^2) — 4 sites
|
|
# Files: pkg/services/folder/folderimpl/folder.go (2 sites)
|
|
# pkg/services/folder/folderimpl/folder_unifiedstorage.go (2 sites)
|
|
# Severity: MEDIUM
|
|
# Ratio: ~250x at P=500 folder permissions
|
|
#
|
|
# When building the list of folder UIDs from permission scopes,
|
|
# each insertion checks slices.Contains on a growing slice.
|
|
# With P permission entries, this is O(P^2). Enterprise Grafana
|
|
# instances can assign per-folder permissions to thousands of folders.
|
|
#
|
|
# Fix: use map[string]struct{} for O(1) membership, then collect keys.
|
|
#
|
|
# --- Site 1: folder.go GetFoldersLegacy (line ~241-257) ---
|
|
--- a/pkg/services/folder/folderimpl/folder.go
|
|
+++ b/pkg/services/folder/folderimpl/folder.go
|
|
@@ -240,13 +240,15 @@ func (s *Service) GetFoldersLegacy(ctx context.Context, q folder.GetFoldersQuery
|
|
folderPermissions := permissions[dashboards.ActionFoldersRead]
|
|
- qry.AncestorUIDs = make([]string, 0, len(folderPermissions))
|
|
+ seen := make(map[string]struct{}, len(folderPermissions))
|
|
+ qry.AncestorUIDs = make([]string, 0, len(folderPermissions))
|
|
if len(folderPermissions) == 0 && !q.SignedInUser.GetIsGrafanaAdmin() {
|
|
return nil, nil
|
|
}
|
|
for _, p := range folderPermissions {
|
|
if p == dashboards.ScopeFoldersAll {
|
|
qry.AncestorUIDs = nil
|
|
break
|
|
}
|
|
if folderUid, found := strings.CutPrefix(p, dashboards.ScopeFoldersPrefix); found {
|
|
- if !slices.Contains(qry.AncestorUIDs, folderUid) {
|
|
+ if _, exists := seen[folderUid]; !exists {
|
|
+ seen[folderUid] = struct{}{}
|
|
qry.AncestorUIDs = append(qry.AncestorUIDs, folderUid)
|
|
}
|
|
}
|
|
@@ -540,9 +542,11 @@ func (s *Service) ...(ctx context.Context, ...) {
|
|
nonRootFolders := make([]*folder.Folder, 0)
|
|
- folderUids := make([]string, 0, len(folderPermissions))
|
|
+ seenUids := make(map[string]struct{}, len(folderPermissions))
|
|
+ folderUids := make([]string, 0, len(folderPermissions))
|
|
for _, p := range folderPermissions {
|
|
if folderUid, found := strings.CutPrefix(p, dashboards.ScopeFoldersPrefix); found {
|
|
- if !slices.Contains(folderUids, folderUid) {
|
|
+ if _, exists := seenUids[folderUid]; !exists {
|
|
+ seenUids[folderUid] = struct{}{}
|
|
folderUids = append(folderUids, folderUid)
|
|
}
|
|
}
|
|
#
|
|
# --- Site 2: folder_unifiedstorage.go GetFolders (line ~55-67) ---
|
|
--- a/pkg/services/folder/folderimpl/folder_unifiedstorage.go
|
|
+++ b/pkg/services/folder/folderimpl/folder_unifiedstorage.go
|
|
@@ -52,12 +52,14 @@ func (s *Service) GetFolders(...) {
|
|
+ seen := make(map[string]struct{}, len(folderPermissions))
|
|
for _, p := range folderPermissions {
|
|
if p == dashboards.ScopeFoldersAll {
|
|
qry.AncestorUIDs = nil
|
|
break
|
|
}
|
|
if folderUid, found := strings.CutPrefix(p, dashboards.ScopeFoldersPrefix); found {
|
|
- if !slices.Contains(qry.AncestorUIDs, folderUid) {
|
|
+ if _, exists := seen[folderUid]; !exists {
|
|
+ seen[folderUid] = struct{}{}
|
|
qry.AncestorUIDs = append(qry.AncestorUIDs, folderUid)
|
|
}
|
|
}
|
|
@@ -422,9 +424,11 @@ func (s *Service) GetChildren(...) {
|
|
q.FolderUIDs = make([]string, 0, len(folderPermissions))
|
|
+ seenUids := make(map[string]struct{}, len(folderPermissions))
|
|
for _, p := range folderPermissions {
|
|
if p == dashboards.ScopeFoldersAll {
|
|
q.FolderUIDs = nil
|
|
break
|
|
}
|
|
if folderUid, found := strings.CutPrefix(p, dashboards.ScopeFoldersPrefix); found {
|
|
- if !slices.Contains(q.FolderUIDs, folderUid) {
|
|
+ if _, exists := seenUids[folderUid]; !exists {
|
|
+ seenUids[folderUid] = struct{}{}
|
|
q.FolderUIDs = append(q.FolderUIDs, folderUid)
|
|
}
|
|
}
|