package whitelist import ( "crypto/sha256" "encoding/hex" "fmt" "io" "os" "path/filepath" "strings" "sync" "github.com/wuko233/sysmonitord/internal/config" ) type FileStatus string const ( StatusIgnored FileStatus = "IGNORED" StatusNonWhitelisted FileStatus = "NON_WHITELISTED" StatusHashMismatch FileStatus = "HASH_MISMATCH" StatusSafe FileStatus = "SAFE" ) type Manager struct { mu sync.RWMutex official config.OfficialConfig user config.UserConfig mergedIgnore []string } func NewManager(o config.OfficialConfig, u config.UserConfig) *Manager { m := &Manager{ official: o, user: u, } m.mergedIgnore = append([]string{}, m.official.IgnoredPaths...) m.mergedIgnore = append(m.mergedIgnore, m.user.IgnoredPaths...) return m } func (m *Manager) UpdateConfig(official config.OfficialConfig, user config.UserConfig) { m.mu.Lock() defer m.mu.Unlock() m.official = official m.user = user m.mergedIgnore = append([]string{}, m.official.IgnoredPaths...) m.mergedIgnore = append(m.mergedIgnore, m.user.IgnoredPaths...) } func (m *Manager) IsPathIgnored(path string) bool { m.mu.RLock() defer m.mu.RUnlock() return m.IsPathIgnoredUnsafe(path) } func (m *Manager) IsPathIgnoredUnsafe(path string) bool { path = filepath.Clean(path) for _, ignore := range m.mergedIgnore { if strings.HasPrefix(path, filepath.Clean(ignore)) { return true } } return false } // CheckFileStatus 检查文件状态 // 返回: isWhitelisted(是否在白名单), isValid(Hash是否匹配), err func (m *Manager) CheckFileStatus(path string) (bool, bool, error) { m.mu.RLock() defer m.mu.RUnlock() // 1. 首先检查是否在忽略列表中 if m.IsPathIgnoredUnsafe(path) { return true, true, nil } // 2. 合并官方和用户的白名单(并集策略) var allowedHashes []string // 添加官方白名单 Hash if officialHashes, exists := m.official.WhitelistFiles[path]; exists { allowedHashes = append(allowedHashes, officialHashes...) } // 添加用户补充白名单 Hash if userHashes, exists := m.user.SupplementFiles[path]; exists { allowedHashes = append(allowedHashes, userHashes...) } // 如果两个白名单都没有这个文件 if len(allowedHashes) == 0 { return false, false, nil } // 3. 计算当前文件 Hash fileHash, err := CalculateFileHash(path) if err != nil { return true, false, fmt.Errorf("计算文件哈希失败: %v", err) } // 4. 检查 Hash 是否在允许列表中 for _, h := range allowedHashes { // 支持 sha256:xxx 格式或纯 hash 格式 normalizedHash := h if strings.HasPrefix(h, "sha256:") { normalizedHash = h[7:] } if strings.EqualFold(normalizedHash, fileHash) { return true, true, nil } } // 在白名单中但 Hash 不匹配 return true, false, nil } func CalculateFileHash(filePath string) (string, error) { file, err := os.Open(filePath) if err != nil { return "", err } defer file.Close() hash := sha256.New() if _, err := io.Copy(hash, file); err != nil { return "", err } return hex.EncodeToString(hash.Sum(nil)), nil } func (m *Manager) IsProcessAllowed(procName string, cmdLine string) bool { m.mu.RLock() defer m.mu.RUnlock() // 检查官方白名单 for _, p := range m.official.WhitelistProcesses { if p == procName { return true } } // 检查用户补充白名单(数组形式) for _, p := range m.user.SupplementProcesses { if p == procName { return true } } return false } func (m *Manager) GetAuditServerUrl() string { m.mu.RLock() defer m.mu.RUnlock() return m.user.Connection.AuditServerURL } func (m *Manager) rebuildIgnoreList() { totalLen := len(m.official.IgnoredPaths) + len(m.user.IgnoredPaths) // 预分配容量以提高性能 m.mergedIgnore = make([]string, 0, totalLen) m.mergedIgnore = append(m.mergedIgnore, m.official.IgnoredPaths...) m.mergedIgnore = append(m.mergedIgnore, m.user.IgnoredPaths...) }