327 lines
6.8 KiB
Go
327 lines
6.8 KiB
Go
package audit
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"log/slog"
|
|
"os"
|
|
"os/exec"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/shirou/gopsutil/v4/process"
|
|
)
|
|
|
|
// Logger 日志接口
|
|
type Logger interface {
|
|
Debug(format string, args ...interface{})
|
|
Info(format string, args ...interface{})
|
|
Warn(format string, args ...interface{})
|
|
Error(format string, args ...interface{})
|
|
}
|
|
|
|
// defaultLogger 默认日志实现
|
|
type defaultLogger struct{}
|
|
|
|
func (l *defaultLogger) Debug(format string, args ...interface{}) {
|
|
slog.Debug(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
func (l *defaultLogger) Info(format string, args ...interface{}) {
|
|
slog.Info(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
func (l *defaultLogger) Warn(format string, args ...interface{}) {
|
|
slog.Warn(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
func (l *defaultLogger) Error(format string, args ...interface{}) {
|
|
slog.Error(fmt.Sprintf(format, args...))
|
|
}
|
|
|
|
var globalLogger Logger = &defaultLogger{}
|
|
|
|
// SetLogger 设置全局日志器
|
|
func SetLogger(logger Logger) {
|
|
if logger != nil {
|
|
globalLogger = logger
|
|
}
|
|
}
|
|
|
|
// ProcessCache 进程缓存
|
|
type ProcessCache struct {
|
|
processes []*process.Process
|
|
timestamp time.Time
|
|
mu sync.RWMutex
|
|
ttl time.Duration
|
|
}
|
|
|
|
// NewProcessCache 创建进程缓存
|
|
func NewProcessCache(ttl time.Duration) *ProcessCache {
|
|
return &ProcessCache{
|
|
ttl: ttl,
|
|
}
|
|
}
|
|
|
|
// Get 获取进程列表(带缓存)
|
|
func (pc *ProcessCache) Get() ([]*process.Process, error) {
|
|
pc.mu.RLock()
|
|
if time.Since(pc.timestamp) < pc.ttl && pc.processes != nil {
|
|
processes := pc.processes
|
|
pc.mu.RUnlock()
|
|
return processes, nil
|
|
}
|
|
pc.mu.RUnlock()
|
|
|
|
// 缓存过期,重新获取
|
|
pc.mu.Lock()
|
|
defer pc.mu.Unlock()
|
|
|
|
// 双重检查
|
|
if time.Since(pc.timestamp) < pc.ttl && pc.processes != nil {
|
|
return pc.processes, nil
|
|
}
|
|
|
|
processes, err := process.Processes()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
pc.processes = processes
|
|
pc.timestamp = time.Now()
|
|
globalLogger.Debug("进程缓存已更新,共 %d 个进程", len(processes))
|
|
|
|
return processes, nil
|
|
}
|
|
|
|
// Clear 清除缓存
|
|
func (pc *ProcessCache) Clear() {
|
|
pc.mu.Lock()
|
|
defer pc.mu.Unlock()
|
|
pc.processes = nil
|
|
pc.timestamp = time.Time{}
|
|
}
|
|
|
|
// CommandExecutor 命令执行器
|
|
type CommandExecutor struct {
|
|
timeout time.Duration
|
|
}
|
|
|
|
// NewCommandExecutor 创建命令执行器
|
|
func NewCommandExecutor(timeout time.Duration) *CommandExecutor {
|
|
return &CommandExecutor{
|
|
timeout: timeout,
|
|
}
|
|
}
|
|
|
|
// Execute 执行命令
|
|
func (ce *CommandExecutor) Execute(name string, args ...string) (string, error) {
|
|
ctx, cancel := context.WithTimeout(context.Background(), ce.timeout)
|
|
defer cancel()
|
|
|
|
cmd := exec.CommandContext(ctx, name, args...)
|
|
var stdout bytes.Buffer
|
|
var stderr bytes.Buffer
|
|
cmd.Stdout = &stdout
|
|
cmd.Stderr = &stderr
|
|
|
|
err := cmd.Run()
|
|
if err != nil {
|
|
// 检查是否超时
|
|
if ctx.Err() == context.DeadlineExceeded {
|
|
globalLogger.Warn("命令执行超时(%v): %s %v", ce.timeout, name, args)
|
|
return "", fmt.Errorf("命令执行超时(%v): %s", ce.timeout, name)
|
|
}
|
|
|
|
// 记录错误但返回输出
|
|
if stderr.Len() > 0 {
|
|
globalLogger.Debug("命令执行失败: %s %v, stderr: %s", name, args, stderr.String())
|
|
}
|
|
return stdout.String(), err
|
|
}
|
|
|
|
return stdout.String(), nil
|
|
}
|
|
|
|
// FileHashCache 文件哈希缓存
|
|
type FileHashCache struct {
|
|
cache map[string]cachedHash
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
type cachedHash struct {
|
|
hash string
|
|
modTime time.Time
|
|
}
|
|
|
|
// NewFileHashCache 创建文件哈希缓存
|
|
func NewFileHashCache() *FileHashCache {
|
|
return &FileHashCache{
|
|
cache: make(map[string]cachedHash),
|
|
}
|
|
}
|
|
|
|
// GetSHA256 获取文件 SHA256 哈希(带缓存)
|
|
func (fhc *FileHashCache) GetSHA256(filePath string) string {
|
|
info, err := os.Stat(filePath)
|
|
if err != nil {
|
|
globalLogger.Debug("无法获取文件信息: %s, err: %v", filePath, err)
|
|
return ""
|
|
}
|
|
|
|
modTime := info.ModTime()
|
|
|
|
// 检查缓存
|
|
fhc.mu.RLock()
|
|
if cached, ok := fhc.cache[filePath]; ok {
|
|
if cached.modTime.Equal(modTime) {
|
|
fhc.mu.RUnlock()
|
|
return cached.hash
|
|
}
|
|
}
|
|
fhc.mu.RUnlock()
|
|
|
|
// 计算哈希
|
|
hash := calculateSHA256(filePath)
|
|
if hash == "" {
|
|
return ""
|
|
}
|
|
|
|
// 更新缓存
|
|
fhc.mu.Lock()
|
|
fhc.cache[filePath] = cachedHash{
|
|
hash: hash,
|
|
modTime: modTime,
|
|
}
|
|
fhc.mu.Unlock()
|
|
|
|
return hash
|
|
}
|
|
|
|
// calculateSHA256 计算文件 SHA256 哈希
|
|
func calculateSHA256(filePath string) string {
|
|
file, err := os.Open(filePath)
|
|
if err != nil {
|
|
return ""
|
|
}
|
|
defer file.Close()
|
|
|
|
hash := sha256.New()
|
|
if _, err := io.Copy(hash, file); err != nil {
|
|
return ""
|
|
}
|
|
|
|
return hex.EncodeToString(hash.Sum(nil))
|
|
}
|
|
|
|
// StringUtils 字符串工具
|
|
type StringUtils struct{}
|
|
|
|
// TruncateString 截断字符串
|
|
func (su *StringUtils) Truncate(s string, maxLen int) string {
|
|
if len(s) <= maxLen {
|
|
return s
|
|
}
|
|
return s[:maxLen]
|
|
}
|
|
|
|
// ContainsAny 检查字符串是否包含任意关键词
|
|
func (su *StringUtils) ContainsAny(s string, keywords []string) bool {
|
|
lower := strings.ToLower(s)
|
|
for _, keyword := range keywords {
|
|
if strings.Contains(lower, strings.ToLower(keyword)) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// IsLocalIP 判断是否是本地 IP
|
|
func IsLocalIP(ip string) bool {
|
|
if ip == "127.0.0.1" || ip == "::1" || ip == "localhost" {
|
|
return true
|
|
}
|
|
|
|
// 简化版本,实际应该使用 net.ParseIP
|
|
privateRanges := []string{
|
|
"10.", "192.168.", "172.16.", "172.17.", "172.18.", "172.19.",
|
|
"172.20.", "172.21.", "172.22.", "172.23.", "172.24.", "172.25.",
|
|
"172.26.", "172.27.", "172.28.", "172.29.", "172.30.", "172.31.",
|
|
}
|
|
|
|
for _, prefix := range privateRanges {
|
|
if strings.HasPrefix(ip, prefix) {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// BatchProcessor 批处理器
|
|
type BatchProcessor struct {
|
|
batchSize int
|
|
}
|
|
|
|
// NewBatchProcessor 创建批处理器
|
|
func NewBatchProcessor(batchSize int) *BatchProcessor {
|
|
return &BatchProcessor{
|
|
batchSize: batchSize,
|
|
}
|
|
}
|
|
|
|
// Process 批量处理
|
|
func (bp *BatchProcessor) Process(items []string, handler func(batch []string) error) error {
|
|
for i := 0; i < len(items); i += bp.batchSize {
|
|
end := i + bp.batchSize
|
|
if end > len(items) {
|
|
end = len(items)
|
|
}
|
|
|
|
batch := items[i:end]
|
|
if err := handler(batch); err != nil {
|
|
return fmt.Errorf("批处理失败 (批次 %d-%d): %w", i, end, err)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// RetryExecutor 重试执行器
|
|
type RetryExecutor struct {
|
|
maxRetries int
|
|
delay time.Duration
|
|
}
|
|
|
|
// NewRetryExecutor 创建重试执行器
|
|
func NewRetryExecutor(maxRetries int, delay time.Duration) *RetryExecutor {
|
|
return &RetryExecutor{
|
|
maxRetries: maxRetries,
|
|
delay: delay,
|
|
}
|
|
}
|
|
|
|
// Execute 执行函数(带重试)
|
|
func (re *RetryExecutor) Execute(fn func() error) error {
|
|
var lastErr error
|
|
for i := 0; i <= re.maxRetries; i++ {
|
|
if i > 0 {
|
|
globalLogger.Debug("重试第 %d 次...", i)
|
|
time.Sleep(re.delay)
|
|
}
|
|
|
|
if err := fn(); err != nil {
|
|
lastErr = err
|
|
continue
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
return fmt.Errorf("重试 %d 次后仍失败: %w", re.maxRetries, lastErr)
|
|
}
|