feat(go): enrich snapshots asynchronously
This commit is contained in:
parent
1ab4add205
commit
eff31b3a82
7 changed files with 270 additions and 14 deletions
|
|
@ -6,9 +6,14 @@
|
|||
package enrich
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/model"
|
||||
"codeberg.org/anassaeneroi/enodia-sentinal/go-agent/internal/netutil"
|
||||
|
|
@ -18,6 +23,7 @@ const (
|
|||
maxAlerts = 128
|
||||
maxProcesses = 64
|
||||
maxIndicators = 128
|
||||
MaxHashBytes = int64(8 << 20)
|
||||
)
|
||||
|
||||
var (
|
||||
|
|
@ -33,6 +39,58 @@ type Process struct {
|
|||
PPIDComm string
|
||||
}
|
||||
|
||||
// SlowResult contains bounded filesystem enrichment collected away from the
|
||||
// alert-emission path. A file larger than MaxHashBytes is described but not
|
||||
// hashed, avoiding unbounded worker time or memory use.
|
||||
type SlowResult struct {
|
||||
Processes map[int]map[string]any
|
||||
Files map[string]map[string]any
|
||||
}
|
||||
|
||||
func CollectSlow(processes []Process, paths []string) SlowResult {
|
||||
result := SlowResult{Processes: map[int]map[string]any{}, Files: map[string]map[string]any{}}
|
||||
for _, process := range processes {
|
||||
exe := cleanPath(process.Exe)
|
||||
if exe == "" {
|
||||
continue
|
||||
}
|
||||
result.Processes[process.PID] = map[string]any{"exe_sha256": boundedHash(exe)}
|
||||
}
|
||||
for _, path := range paths {
|
||||
result.Files[path] = fileMetadata(path)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func boundedHash(path string) any {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil || !info.Mode().IsRegular() || info.Size() > MaxHashBytes {
|
||||
return nil
|
||||
}
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
defer file.Close()
|
||||
hash := sha256.New()
|
||||
if _, err := io.Copy(hash, io.LimitReader(file, MaxHashBytes+1)); err != nil {
|
||||
return nil
|
||||
}
|
||||
return hex.EncodeToString(hash.Sum(nil))
|
||||
}
|
||||
|
||||
func fileMetadata(path string) map[string]any {
|
||||
info, err := os.Lstat(path)
|
||||
if err != nil {
|
||||
return map[string]any{"exists": false, "mode": nil, "uid": nil, "gid": nil}
|
||||
}
|
||||
mode, uid, gid := int(info.Mode().Perm()), -1, -1
|
||||
if stat, ok := info.Sys().(*syscall.Stat_t); ok {
|
||||
uid, gid = int(stat.Uid), int(stat.Gid)
|
||||
}
|
||||
return map[string]any{"exists": true, "mode": mode, "uid": uid, "gid": gid}
|
||||
}
|
||||
|
||||
// Build returns the stable Python enrichment keys without invoking any slow
|
||||
// collectors. Input and indicator caps keep attacker-controlled details from
|
||||
// causing unbounded snapshot work or output.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue