Files
bee/audit/internal/collector/dmesg_events.go
T
Mikhail ChusavitinandClaude Sonnet 5 56d12b1f3c fix(collector): use word-boundary matching for dmesg severity
dmesgSeverity classified a kernel log line as Critical via plain
strings.Contains, without word boundaries, so common words that merely
contain a keyword as a substring false-positive — "disabled by
default" was flagged Critical because "default" contains "fault"
(de-fault). Found in a support bundle where 140/238 event_logs entries
came back Critical, most of them harmless boot messages (module load
notices, "... is initialized", "disabled by default"), drowning out
genuinely critical entries (Xid, AER, ECC) in the same list.

Switch to the same \b-bounded regexes already used for the capture
patterns above it in this file.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-24 17:41:28 +03:00

140 lines
3.7 KiB
Go

package collector
import (
"bee/audit/internal/schema"
"log/slog"
"os/exec"
"regexp"
"strings"
"time"
)
// dmesg -T output: [Thu Jun 18 14:23:45 2026] message
// dmesg without -T: [ 123.456789] message
var dmesgTimestampRE = regexp.MustCompile(`^\[([^\]]+)\]\s*(.*)$`)
// Keywords that indicate an error or hardware problem worth capturing.
var dmesgErrorPatterns = []*regexp.Regexp{
regexp.MustCompile(`(?i)\berr(or)?\b`),
regexp.MustCompile(`(?i)\bfail(ed|ure)?\b`),
regexp.MustCompile(`(?i)\bfault\b`),
regexp.MustCompile(`(?i)\bwarn(ing)?\b`),
regexp.MustCompile(`(?i)\bAER\b`),
regexp.MustCompile(`(?i)\bXid\b`),
regexp.MustCompile(`(?i)\bNVRM\b`),
regexp.MustCompile(`(?i)\bpanic\b`),
regexp.MustCompile(`(?i)\bcorrected\b`),
regexp.MustCompile(`(?i)\buncorrect`),
regexp.MustCompile(`(?i)\bECC\b`),
regexp.MustCompile(`(?i)\btimeout\b`),
regexp.MustCompile(`(?i)\breset\b`),
regexp.MustCompile(`(?i)\bdead\b`),
regexp.MustCompile(`(?i)\bhang\b`),
regexp.MustCompile(`(?i)\bstall\b`),
regexp.MustCompile(`(?i)\bdisabled\b`),
}
// collectDmesgErrors runs `dmesg -T` (or `dmesg` without -T on failure) and
// returns only lines that match known error/warning patterns.
func collectDmesgErrors() []schema.HardwareEventLog {
out, err := exec.Command("dmesg", "-T").Output()
if err != nil || len(out) == 0 {
// Fallback: dmesg without human-readable timestamps
out, err = exec.Command("dmesg").Output()
if err != nil || len(out) == 0 {
return nil
}
}
entries := parseDmesgErrors(string(out))
if len(entries) == 0 {
return nil
}
slog.Info("dmesg: collected error entries", "count", len(entries))
return entries
}
func parseDmesgErrors(output string) []schema.HardwareEventLog {
var entries []schema.HardwareEventLog
collectedAt := time.Now().UTC().Format(time.RFC3339)
for _, line := range strings.Split(output, "\n") {
line = strings.TrimSpace(line)
if line == "" {
continue
}
var timestamp, message string
if m := dmesgTimestampRE.FindStringSubmatch(line); m != nil {
timestamp = strings.TrimSpace(m[1])
message = strings.TrimSpace(m[2])
} else {
message = line
}
if message == "" {
continue
}
if !matchesAny(message, dmesgErrorPatterns) {
continue
}
severity := dmesgSeverity(message)
source := "dmesg"
var eventTime *string
if timestamp != "" {
t := timestamp
eventTime = &t
} else {
eventTime = &collectedAt
}
entries = append(entries, schema.HardwareEventLog{
Source: source,
EventTime: eventTime,
Severity: &severity,
Message: message,
})
}
return entries
}
func matchesAny(s string, patterns []*regexp.Regexp) bool {
for _, p := range patterns {
if p.MatchString(s) {
return true
}
}
return false
}
// dmesgSeverityCriticalPatterns use \b word boundaries, unlike a plain
// strings.Contains check, so common words that merely contain a keyword as a
// substring don't false-positive — e.g. "disabled by default" contains
// "fault" (de-fault), and "undead" would contain "dead".
var dmesgSeverityCriticalPatterns = []*regexp.Regexp{
regexp.MustCompile(`(?i)\bpanic\b`),
regexp.MustCompile(`(?i)\baer\b`),
regexp.MustCompile(`(?i)\buncorrect`),
regexp.MustCompile(`(?i)\bxid\b`),
regexp.MustCompile(`(?i)\bnvrm\b`),
regexp.MustCompile(`(?i)\berror\b`),
regexp.MustCompile(`(?i)\bfault\b`),
regexp.MustCompile(`(?i)\bfail(ed|ure)?\b`),
regexp.MustCompile(`(?i)\bdead\b`),
regexp.MustCompile(`(?i)\bhang\b`),
}
func dmesgSeverity(msg string) string {
if sev, ok := XidSeverity(msg); ok {
if sev == "critical" {
return statusCritical
}
return statusWarning
}
if matchesAny(msg, dmesgSeverityCriticalPatterns) {
return statusCritical
}
return statusWarning
}