55 lines
2.3 KiB
Diff
55 lines
2.3 KiB
Diff
# UNDF: UNDF-2026-000000821
|
|
# UNDF: (leave blank)
|
|
# CWE-407: DAG AddEdge/Eliminate slices.Contains on children/parents O(E^2)
|
|
# File: pkg/engine/internal/util/dag/dag.go
|
|
# Severity: LOW
|
|
# Ratio: ~250x at E=500 edges per node (synthetic; practical fan-out is small)
|
|
#
|
|
# Graph.AddEdge checks slices.Contains(g.children[e.Parent], e.Child)
|
|
# and slices.Contains(g.parents[e.Child], e.Parent) to enforce edge
|
|
# uniqueness. If a node accumulates E children, each new AddEdge scans
|
|
# the entire children slice, making bulk insertion O(E^2).
|
|
#
|
|
# Graph.Eliminate has nested loops: for each parent, for each child of
|
|
# the eliminated node, slices.Contains(g.children[parent], child)
|
|
# scans the parent's children list. Similarly for the parents list.
|
|
#
|
|
# In Loki's query planner, node fan-out is typically 1-3, so this is
|
|
# LOW severity in practice. The fix converts children/parents to
|
|
# map[NodeType]struct{} adjacency sets for O(1) membership.
|
|
#
|
|
# Note: changing []NodeType to map[NodeType]struct{} changes iteration
|
|
# order. The current code preserves insertion order via slices. A
|
|
# minimal fix would keep the slice for ordering but add a parallel set
|
|
# for membership checks.
|
|
--- a/pkg/engine/internal/util/dag/dag.go
|
|
+++ b/pkg/engine/internal/util/dag/dag.go
|
|
@@ -29,8 +29,10 @@ type Graph[NodeType Node] struct {
|
|
nodes nodeSet[NodeType]
|
|
- parents map[NodeType][]NodeType
|
|
- children map[NodeType][]NodeType
|
|
+ parents map[NodeType][]NodeType
|
|
+ children map[NodeType][]NodeType
|
|
+ parentSet map[NodeType]map[NodeType]struct{}
|
|
+ childrenSet map[NodeType]map[NodeType]struct{}
|
|
}
|
|
|
|
@@ -95,10 +97,16 @@ func (g *Graph[NodeType]) AddEdge(e Edge[NodeType]) error {
|
|
|
|
// Uniquely add the edges.
|
|
- if !slices.Contains(g.children[e.Parent], e.Child) {
|
|
+ if g.childrenSet[e.Parent] == nil {
|
|
+ g.childrenSet[e.Parent] = make(map[NodeType]struct{})
|
|
+ }
|
|
+ if _, exists := g.childrenSet[e.Parent][e.Child]; !exists {
|
|
+ g.childrenSet[e.Parent][e.Child] = struct{}{}
|
|
g.children[e.Parent] = append(g.children[e.Parent], e.Child)
|
|
}
|
|
- if !slices.Contains(g.parents[e.Child], e.Parent) {
|
|
+ if g.parentSet[e.Child] == nil {
|
|
+ g.parentSet[e.Child] = make(map[NodeType]struct{})
|
|
+ }
|
|
+ if _, exists := g.parentSet[e.Child][e.Parent]; !exists {
|
|
+ g.parentSet[e.Child][e.Parent] = struct{}{}
|
|
g.parents[e.Child] = append(g.parents[e.Child], e.Parent)
|
|
}
|