45 lines
1.7 KiB
Diff
45 lines
1.7 KiB
Diff
# UNDF: UNDF-2026-000000822
|
||
# UNDF: (leave blank)
|
||
# CWE-407: Dedup processor Apply() uses GetField O(F) inside field loop → O(F²)
|
||
#
|
||
# In plugins/processors/dedup/dedup.go, the Apply() method compares each field
|
||
# of an incoming metric against the cached metric by calling m.GetField(f.Key)
|
||
# which performs a linear scan of the cached metric's field list. For each
|
||
# incoming metric, this is O(F_new × F_cached) ≈ O(F²).
|
||
#
|
||
# Fix: Build a map from the cached metric's fields for O(1) lookup, reducing
|
||
# the overall comparison to O(F).
|
||
#
|
||
# Severity: MEDIUM — Telegraf metrics typically have 10-50 fields, but
|
||
# system/CPU/disk metrics can exceed 100 fields. The dedup processor is a
|
||
# common pipeline component.
|
||
#
|
||
--- a/plugins/processors/dedup/dedup.go
|
||
+++ b/plugins/processors/dedup/dedup.go
|
||
@@ -31,6 +31,14 @@ func (d *Dedup) Apply(metrics ...telegraf.Metric) []telegraf.Metric {
|
||
idx := 0
|
||
for _, metric := range metrics {
|
||
id := metric.HashID()
|
||
m, ok := d.cache[id]
|
||
|
||
// If not in cache then just save it
|
||
@@ -53,8 +61,15 @@ func (d *Dedup) Apply(metrics ...telegraf.Metric) []telegraf.Metric {
|
||
|
||
// For each field compare value with the cached one
|
||
changed := false
|
||
added := false
|
||
sametime := metric.Time() == m.Time()
|
||
+
|
||
+ // Build a map of cached fields for O(1) lookup instead of
|
||
+ // calling m.GetField() which is O(F) per call
|
||
+ cachedFields := make(map[string]interface{}, len(m.FieldList()))
|
||
+ for _, cf := range m.FieldList() {
|
||
+ cachedFields[cf.Key] = cf.Value
|
||
+ }
|
||
+
|
||
for _, f := range metric.FieldList() {
|
||
- if value, ok := m.GetField(f.Key); ok {
|
||
+ if value, ok := cachedFields[f.Key]; ok {
|
||
if value != f.Value {
|
||
changed = true
|
||
break
|