[cmd] 优化可疑文件和进程确认流程,支持逐个确认;

改进白名单更新逻辑
This commit is contained in:
wuko233 2026-04-19 22:24:25 +08:00
parent 3adba7a09d
commit 5ffce43b21
2 changed files with 228 additions and 50 deletions

View File

@ -3,7 +3,6 @@ package safe
import (
"fmt"
"os"
"path/filepath"
"sysmonitord/internal/config"
"sysmonitord/internal/scanner/file"
"sysmonitord/internal/scanner/process"
@ -58,21 +57,19 @@ func interactiveSafe(cfg *config.Config) {
dubiousFiles, err := storage.LoadDubiousFiles(dataDir, cfg.Storage.DubiousFileListFile)
if err != nil {
fmt.Printf("无法读取可疑文件列表: %v\n", err)
return
dubiousFiles = nil
}
if len(dubiousFiles) == 0 {
fmt.Println("没有可疑文件需要处理。")
return
}
dubiousProcesses, err := storage.LoadDubiousProcesses(dataDir, cfg.Storage.DubiousProcessListFile)
if err != nil {
fmt.Printf("无法读取可疑进程列表: %v\n", err)
return
dubiousProcesses = nil
}
if len(dubiousProcesses) == 0 {
fmt.Println("没有可疑进程需要处理。")
return
}
fmt.Println("\n╔══════════════════════════════════════════════╗")
@ -93,10 +90,11 @@ func interactiveSafe(cfg *config.Config) {
fmt.Println("╚══════════════════════════════════════════════╝")
fmt.Println("\n请选择操作:")
fmt.Println("[1] 将以上可疑文件全部确认为安全 (移至白名单)")
fmt.Println("[2] 将以上可疑进程全部确认为安全 (移至白名单)")
fmt.Println("[3] 全部确认安全 (文件和进程)")
// Todo: 支持逐个确认
fmt.Println("[1] 逐个确认可疑文件")
fmt.Println("[2] 逐个确认可疑进程")
fmt.Println("[3] 将可疑文件全部确认为安全")
fmt.Println("[4] 将可疑进程全部确认为安全")
fmt.Println("[5] 全部确认安全 (文件和进程)")
fmt.Println("[ESC] 退出不处理")
fmt.Print("请输入选项: ")
@ -108,30 +106,54 @@ func interactiveSafe(cfg *config.Config) {
switch input {
case "1":
fmt.Println("正在处理...")
if err := confirmFilesAsSafe(cfg, dubiousFiles); err != nil {
fmt.Printf("处理失败: %v\n", err)
conformed, unconfirmed := confirmFiles(dubiousFiles)
if len(conformed) > 0 {
fmt.Println("正在处理可疑文件...")
if err := confirmFilesAsSafe(cfg, conformed); err != nil {
fmt.Printf("确认文件安全失败: %v\n", err)
} else {
fmt.Println("已将可疑文件移入白名单。")
fmt.Printf("已将 %d 个文件移入白名单,%d 个文件仍为可疑\n", len(conformed), len(unconfirmed))
}
} else {
fmt.Println("没有文件被确认安全,所有文件仍为可疑。")
}
case "2":
fmt.Println("正在处理...")
if err := confirmProcessesAsSafe(cfg, dubiousProcesses); err != nil {
fmt.Printf("处理失败: %v\n", err)
conformed, unconfirmed := confirmProcesses(dubiousProcesses)
if len(conformed) > 0 {
fmt.Println("正在处理可疑进程...")
if err := confirmProcessesAsSafe(cfg, conformed); err != nil {
fmt.Printf("确认进程安全失败: %v\n", err)
} else {
fmt.Println("已将可疑进程移入白名单。")
fmt.Printf("已将 %d 个进程移入白名单,%d 个进程仍为可疑\n", len(conformed), len(unconfirmed))
}
} else {
fmt.Println("没有进程被确认安全,所有进程仍为可疑。")
}
case "3":
fmt.Println("正在处理...")
fmt.Println("正在将所有可疑文件确认为安全...")
if err := confirmFilesAsSafe(cfg, dubiousFiles); err != nil {
fmt.Printf("处理失败: %v\n", err)
fmt.Printf("确认文件安全失败: %v\n", err)
} else {
fmt.Println("已将可疑文件移入白名单。")
fmt.Printf("已将 %d 个文件移入白名单\n", len(dubiousFiles))
}
case "4":
fmt.Println("正在将所有可疑进程确认为安全...")
if err := confirmProcessesAsSafe(cfg, dubiousProcesses); err != nil {
fmt.Printf("确认进程安全失败: %v\n", err)
} else {
fmt.Printf("已将 %d 个进程移入白名单\n", len(dubiousProcesses))
}
case "5":
fmt.Println("正在将所有可疑文件和进程确认为安全...")
if err := confirmFilesAsSafe(cfg, dubiousFiles); err != nil {
fmt.Printf("确认文件安全失败: %v\n", err)
} else {
fmt.Printf("已将 %d 个文件移入白名单\n", len(dubiousFiles))
}
if err := confirmProcessesAsSafe(cfg, dubiousProcesses); err != nil {
fmt.Printf("处理失败: %v\n", err)
fmt.Printf("确认进程安全失败: %v\n", err)
} else {
fmt.Println("已将可疑进程移入白名单。")
fmt.Printf("已将 %d 个进程移入白名单\n", len(dubiousProcesses))
}
case "ESC":
fmt.Println("已取消操作。")
@ -142,15 +164,11 @@ func interactiveSafe(cfg *config.Config) {
}
func confirmProcessesAsSafe(cfg *config.Config, processes []storage.DubiousProcessInfo) error {
dataDir := cfg.Storage.DataDir
whiteListPath := filepath.Join(dataDir, cfg.Storage.ProcessSystemFile)
// dubiousFile := filepath.Join(dataDir, cfg.Storage.DubiousProcessListFile)
f, err := os.OpenFile(whiteListPath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("无法打开白名单文件: %v", err)
if len(processes) == 0 {
return nil
}
defer f.Close()
dataDir := cfg.Storage.DataDir
var toWhitelist []process.ProcessInfo
for _, proc := range processes {
@ -162,30 +180,23 @@ func confirmProcessesAsSafe(cfg *config.Config, processes []storage.DubiousProce
}
if err := storage.AppendProcessToWhitelist(toWhitelist, dataDir, cfg.Storage.ProcessSystemFile); err != nil {
return fmt.Errorf("更新白名单失败: %v", err)
return fmt.Errorf("更新进程白名单失败: %v", err)
}
logger.Log.Debug("已将可疑进程移入白名单", zap.Int("count", len(toWhitelist)))
// Todo: 逐个删除条目
if err := storage.RemoveDubiousProcesses(dataDir, cfg.Storage.DubiousProcessListFile, []storage.DubiousProcessInfo{}); err != nil {
if err := storage.RemoveDubiousProcesses(dataDir, cfg.Storage.DubiousProcessListFile, processes); err != nil {
return fmt.Errorf("删除可疑进程列表失败: %v", err)
}
return nil
}
func confirmFilesAsSafe(cfg *config.Config, files []storage.DubiousFileInfo) error {
dataDir := cfg.Storage.DataDir
whiteListPath := filepath.Join(dataDir, cfg.Storage.FileSystemFile)
// dubiousFile := filepath.Join(dataDir, cfg.Storage.DubiousFileListFile)
f, err := os.OpenFile(whiteListPath, os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("无法打开白名单文件: %v", err)
if len(files) == 0 {
return nil
}
defer f.Close()
dataDir := cfg.Storage.DataDir
var toWhitelist []file.FileInfo
for _, f := range files {
@ -201,10 +212,113 @@ func confirmFilesAsSafe(cfg *config.Config, files []storage.DubiousFileInfo) err
logger.Log.Debug("已将可疑文件移入白名单", zap.Int("count", len(toWhitelist)))
// Todo: 逐个删除条目
if err := storage.RemoveDubiousFiles(dataDir, cfg.Storage.DubiousFileListFile, []storage.DubiousFileInfo{}); err != nil {
if err := storage.RemoveDubiousFiles(dataDir, cfg.Storage.DubiousFileListFile, files); err != nil {
return fmt.Errorf("删除可疑文件列表失败: %v", err)
}
return nil
}
func confirmFiles(files []storage.DubiousFileInfo) (confirmed []storage.DubiousFileInfo, unconfirmed []storage.DubiousFileInfo) {
if len(files) == 0 {
return nil, nil
}
fmt.Println("\n╔══════════════════════════════════════════════╗")
fmt.Println("║ 逐个确认可疑文件 ║")
fmt.Println("╠══════════════════════════════════════════════╣")
fmt.Println("║ [y] 确认安全 [n] 仍为可疑 [q] 退出 ║")
fmt.Println("╚══════════════════════════════════════════════╝")
for i, file := range files {
fmt.Printf("\n[%d/%d] 文件路径: %s\n", i+1, len(files), file.Path)
fmt.Printf(" 哈希值: %s\n", file.Hash)
fmt.Printf(" 发现时间: %s\n", file.DiscoveredAt)
fmt.Print(" 选择 [y/n/q]: ")
input, err := readKeyWithESC()
if err != nil {
fmt.Printf("读取输入失败: %v跳过此文件\n", err)
unconfirmed = append(unconfirmed, file)
continue
}
switch input {
case "y", "Y":
fmt.Println("已确认安全")
confirmed = append(confirmed, file)
case "n", "N":
fmt.Println("仍为可疑")
unconfirmed = append(unconfirmed, file)
case "q", "Q":
fmt.Println("已退出确认")
for j := i; j < len(files); j++ {
unconfirmed = append(unconfirmed, files[j])
}
return confirmed, unconfirmed
default:
fmt.Println("无效输入,默认仍为可疑")
unconfirmed = append(unconfirmed, file)
}
}
fmt.Printf("\n╔══════════════════════════════════════════════╗\n")
fmt.Printf("║ 文件确认完成 ║\n")
fmt.Printf("║ ✓ 确认安全: %d 个 ║\n", len(confirmed))
fmt.Printf("║ ✗ 保留可疑: %d 个 ║\n", len(unconfirmed))
fmt.Printf("╚══════════════════════════════════════════════╝\n")
return confirmed, unconfirmed
}
func confirmProcesses(processes []storage.DubiousProcessInfo) (confirmed []storage.DubiousProcessInfo, unconfirmed []storage.DubiousProcessInfo) {
if len(processes) == 0 {
return nil, nil
}
fmt.Println("\n╔══════════════════════════════════════════════╗")
fmt.Println("║ 逐个确认可疑进程 ║")
fmt.Println("╠══════════════════════════════════════════════╣")
fmt.Println("║ [y] 确认安全 [n] 仍为可疑 [q] 退出 ║")
fmt.Println("╚══════════════════════════════════════════════╝")
for i, proc := range processes {
fmt.Printf("\n[%d/%d] 进程名称: %s\n", i+1, len(processes), proc.Name)
fmt.Printf(" 路径: %s\n", proc.Path)
fmt.Printf(" 哈希值: %s\n", proc.FileHash)
fmt.Print(" 选择 [y/n/q]: ")
input, err := readKeyWithESC()
if err != nil {
fmt.Printf("读取输入失败: %v跳过此进程\n", err)
unconfirmed = append(unconfirmed, proc)
continue
}
switch input {
case "y", "Y":
fmt.Println("已确认安全")
confirmed = append(confirmed, proc)
case "n", "N":
fmt.Println("仍为可疑")
unconfirmed = append(unconfirmed, proc)
case "q", "Q":
fmt.Println("已退出确认")
for j := i; j < len(processes); j++ {
unconfirmed = append(unconfirmed, processes[j])
}
return confirmed, unconfirmed
default:
fmt.Println("无效输入,默认仍为可疑")
unconfirmed = append(unconfirmed, proc)
}
}
fmt.Printf("\n╔══════════════════════════════════════════════╗\n")
fmt.Printf("║ 进程确认完成 ║\n")
fmt.Printf("║ ✓ 确认安全: %d 个 ║\n", len(confirmed))
fmt.Printf("║ ✗ 保留可疑: %d 个 ║\n", len(unconfirmed))
fmt.Printf("╚══════════════════════════════════════════════╝\n")
return confirmed, unconfirmed
}

View File

@ -180,8 +180,28 @@ func AppendProcessToWhitelist(procs []process.ProcessInfo, dataDir string, proce
return writer.Flush()
}
func RemoveDubiousProcesses(dataDir string, dubiousProcessFile string, toKeep []DubiousProcessInfo) error {
func RemoveDubiousProcesses(dataDir string, dubiousProcessFile string, toRemove []DubiousProcessInfo) error {
filePath := filepath.Join(dataDir, dubiousProcessFile)
allProcs, err := LoadDubiousProcesses(dataDir, dubiousProcessFile)
if err != nil {
return fmt.Errorf("[storage]无法加载可疑进程记录文件%s: %w", filePath, err)
}
toRemoveMap := make(map[string]bool)
for _, proc := range toRemove {
key := fmt.Sprintf("%s:%s", proc.Name, proc.Path)
toRemoveMap[key] = true
}
var toKeep []DubiousProcessInfo
for _, proc := range allProcs {
key := fmt.Sprintf("%s:%d", proc.Name, proc.PID)
if !toRemoveMap[key] {
toKeep = append(toKeep, proc)
}
}
if len(toKeep) == 0 {
return os.Remove(filePath)
}
@ -194,6 +214,11 @@ func RemoveDubiousProcesses(dataDir string, dubiousProcessFile string, toKeep []
writer := bufio.NewWriter(f)
header := fmt.Sprintf("# 可疑进程记录 - 最后更新: %s\n", time.Now().Format("2006-01-02 15:04:05"))
if _, err := writer.WriteString(header); err != nil {
return err
}
for _, proc := range toKeep {
line := fmt.Sprintf("%s:%s:%s\n", proc.Name, proc.Path, proc.FileHash)
if _, err := writer.WriteString(line); err != nil {
@ -201,7 +226,15 @@ func RemoveDubiousProcesses(dataDir string, dubiousProcessFile string, toKeep []
}
}
return writer.Flush()
if err := writer.Flush(); err != nil {
return err
}
logger.Log.Debug("[storage]已从可疑进程列表删除条目",
zap.Int("deleted", len(toRemove)),
zap.Int("remaining", len(toKeep)))
return nil
}
func SaveDubiousFiles(files DubiousFileInfo, dataDir string, dubiousFileName string) error {
@ -243,8 +276,26 @@ func AppendFileToWhitelist(files []file.FileInfo, dataDir string, fileSystemFile
return writer.Flush()
}
func RemoveDubiousFiles(dataDir string, dubiousFileName string, toKeep []DubiousFileInfo) error {
func RemoveDubiousFiles(dataDir string, dubiousFileName string, toRemove []DubiousFileInfo) error {
filePath := filepath.Join(dataDir, dubiousFileName)
allFiles, err := LoadDubiousFiles(dataDir, dubiousFileName)
if err != nil {
return fmt.Errorf("[storage]无法加载可疑文件记录文件%s: %w", filePath, err)
}
toRemoveMap := make(map[string]bool)
for _, file := range toRemove {
toRemoveMap[file.Path] = true
}
var toKeep []DubiousFileInfo
for _, file := range allFiles {
if !toRemoveMap[file.Path] {
toKeep = append(toKeep, file)
}
}
if len(toKeep) == 0 {
return os.Remove(filePath)
}
@ -257,6 +308,11 @@ func RemoveDubiousFiles(dataDir string, dubiousFileName string, toKeep []Dubious
writer := bufio.NewWriter(f)
header := fmt.Sprintf("# 可疑文件记录 - 最后更新: %s\n", time.Now().Format("2006-01-02 15:04:05"))
if _, err := writer.WriteString(header); err != nil {
return err
}
for _, file := range toKeep {
line := fmt.Sprintf("%s:%s:%s\n", file.Path, file.Hash, file.DiscoveredAt)
if _, err := writer.WriteString(line); err != nil {
@ -264,7 +320,15 @@ func RemoveDubiousFiles(dataDir string, dubiousFileName string, toKeep []Dubious
}
}
return writer.Flush()
if err := writer.Flush(); err != nil {
return err
}
logger.Log.Debug("[storage]已从可疑文件列表删除条目",
zap.Int("deleted", len(toRemove)),
zap.Int("remaining", len(toKeep)))
return nil
}
func LoadDubiousFiles(dataDir string, dubiousFileName string) ([]DubiousFileInfo, error) {