// SPDX-License-Identifier: GPL-3.0-or-later package correlation // LineageOf walks each starting PID through parentOf, excluding pid 0/init so // unrelated process trees never correlate merely because they share init. func LineageOf(pids []int, parentOf func(int) int, depth int) map[int]bool { seen := make(map[int]bool) for _, start := range pids { current := start steps := 0 for current != 0 && current != 1 && steps <= depth { if seen[current] { break } seen[current] = true current = parentOf(current) steps++ } } return seen } type IndexIncident struct { ID string `json:"id"` Host string `json:"host"` LastTimestamp float64 `json:"last_ts"` Lineage []int `json:"lineage"` } // Assign returns the most recently active incident compatible with this host // and lineage. Empty-lineage evidence may join any open incident on the host, // reproducing Python's fallback for FIM/package evidence without a live PID. func Assign(index []IndexIncident, lineage map[int]bool, when float64, host string, window int) string { best := "" bestTimestamp := -1.0 for _, incident := range index { if incident.Host != host || when-incident.LastTimestamp > float64(window) { continue } if len(lineage) > 0 && !intersects(incident.Lineage, lineage) { continue } if incident.LastTimestamp > bestTimestamp { best, bestTimestamp = incident.ID, incident.LastTimestamp } } return best } func MaxSeverity(left, right string) string { if severityRank(left) >= severityRank(right) { return left } return right } func severityRank(value string) int { switch value { case "MEDIUM": return 1 case "HIGH": return 2 case "CRITICAL": return 3 default: return 0 } } func intersects(values []int, wanted map[int]bool) bool { for _, value := range values { if wanted[value] { return true } } return false }