fscan/plugins/local/fileinfo.go
ZacharyZcR 8f54702c02 refactor: 精准修复插件系统三个设计问题
经Linus式架构审计,发现并修复插件系统中的具体问题:

## 核心修复

### 1. 消除local插件GetPorts()方法冗余
- 删除21个local插件中无意义的GetPorts()方法
- 简化local.Plugin接口:移除端口概念
- 理由:本地插件不涉及网络,端口概念完全多余

### 2. 消除web插件GetPorts()方法冗余
- 删除2个web插件中无用的GetPorts()方法
- 简化web.WebPlugin接口:专注智能HTTP检测
- 理由:Web插件使用动态HTTP检测,预定义端口无价值

### 3. 统一插件命名规范
- 统一所有插件接口使用Name()方法(符合Go惯例)
- 消除GetName()与Name()不一致问题
- 简化适配器:不再需要方法名转换

## 技术改进

接口精简:
- local插件:GetName() + GetPorts() → Name()
- web插件:GetName() + GetPorts() → Name()
- services插件:GetName() → Name()(保留GetPorts(),业务必需)

代码减少:
- 删除23个无用GetPorts()方法
- 重命名52个Name()方法
- 简化3个插件接口定义

## 影响范围

修改文件:55个插件文件
代码变更:-155行 +61行(净减少94行)
功能影响:零破坏性,保持所有业务逻辑不变

这是基于业务需求分析的精准重构,消除真正多余的部分,
保持系统架构合理性和向后兼容性。
2025-08-26 20:38:39 +08:00

185 lines
4.4 KiB
Go

package local
import (
"context"
"fmt"
"os"
"path/filepath"
"runtime"
"strings"
"github.com/shadow1ng/fscan/common"
)
// FileInfoPlugin 文件信息收集插件 - Linus式简化版本
//
// 设计哲学:删除所有不必要的复杂性
// - 没有继承体系
// - 没有权限检查(让系统告诉我们)
// - 没有平台检查(运行时错误更清晰)
// - 没有复杂配置(直接硬编码关键路径)
type FileInfoPlugin struct {
name string
}
// NewFileInfoPlugin 创建文件信息插件
func NewFileInfoPlugin() *FileInfoPlugin {
return &FileInfoPlugin{
name: "fileinfo",
}
}
// GetName 实现Plugin接口
func (p *FileInfoPlugin) Name() string {
return p.name
}
// Scan 执行本地文件扫描 - 直接、简单、有效
func (p *FileInfoPlugin) Scan(ctx context.Context, info *common.HostInfo) *ScanResult {
var foundFiles []string
// 扫描关键敏感文件位置 - 删除复杂的配置系统
sensitiveFiles := p.getSensitiveFiles()
for _, file := range sensitiveFiles {
if p.fileExists(file) {
foundFiles = append(foundFiles, file)
common.LogSuccess(fmt.Sprintf("发现敏感文件: %s", file))
}
}
// 搜索用户目录下的敏感文件 - 简化搜索逻辑
userFiles := p.searchUserFiles()
foundFiles = append(foundFiles, userFiles...)
// 构建结果
output := fmt.Sprintf("文件扫描完成 - 发现 %d 个敏感文件", len(foundFiles))
if len(foundFiles) > 0 {
output += "\n发现的文件:"
for _, file := range foundFiles {
output += "\n " + file
}
}
return &ScanResult{
Success: len(foundFiles) > 0,
Output: output,
Error: nil,
}
}
// getSensitiveFiles 获取关键敏感文件列表 - 删除复杂的初始化逻辑
func (p *FileInfoPlugin) getSensitiveFiles() []string {
var files []string
switch runtime.GOOS {
case "windows":
files = []string{
"C:\\boot.ini",
"C:\\Windows\\System32\\config\\SAM",
"C:\\Windows\\repair\\sam",
}
// 添加用户相关路径
if homeDir, err := os.UserHomeDir(); err == nil {
files = append(files, []string{
filepath.Join(homeDir, ".ssh", "id_rsa"),
filepath.Join(homeDir, ".aws", "credentials"),
filepath.Join(homeDir, ".azure", "accessTokens.json"),
}...)
}
case "linux", "darwin":
files = []string{
"/etc/passwd",
"/etc/shadow",
"/root/.ssh/id_rsa",
"/root/.ssh/authorized_keys",
"/root/.bash_history",
"/etc/nginx/nginx.conf",
"/etc/apache2/apache2.conf",
}
// 添加用户相关路径
if homeDir, err := os.UserHomeDir(); err == nil {
files = append(files, []string{
filepath.Join(homeDir, ".ssh", "id_rsa"),
filepath.Join(homeDir, ".aws", "credentials"),
filepath.Join(homeDir, ".bash_history"),
}...)
}
}
return files
}
// searchUserFiles 搜索用户目录敏感文件 - 简化搜索逻辑
func (p *FileInfoPlugin) searchUserFiles() []string {
var foundFiles []string
homeDir, err := os.UserHomeDir()
if err != nil {
return foundFiles
}
// 关键目录 - 删除复杂的目录配置
searchDirs := []string{
filepath.Join(homeDir, "Desktop"),
filepath.Join(homeDir, "Documents"),
filepath.Join(homeDir, ".ssh"),
filepath.Join(homeDir, ".aws"),
}
// 敏感文件关键词 - 删除复杂的白名单系统
keywords := []string{"password", "key", "secret", "token", "credential", "passwd"}
for _, dir := range searchDirs {
if !p.dirExists(dir) {
continue
}
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil
}
// 限制深度和大小 - 简单有效
if info.IsDir() || info.Size() > 1024*1024 { // 1MB
return nil
}
// 检查文件名是否包含敏感关键词
filename := strings.ToLower(filepath.Base(path))
for _, keyword := range keywords {
if strings.Contains(filename, keyword) {
foundFiles = append(foundFiles, path)
common.LogSuccess(fmt.Sprintf("发现潜在敏感文件: %s", path))
break
}
}
return nil
})
}
return foundFiles
}
// fileExists 检查文件是否存在
func (p *FileInfoPlugin) fileExists(path string) bool {
_, err := os.Stat(path)
return err == nil
}
// dirExists 检查目录是否存在
func (p *FileInfoPlugin) dirExists(path string) bool {
info, err := os.Stat(path)
return err == nil && info.IsDir()
}
// 注册插件
func init() {
RegisterLocalPlugin("fileinfo", func() Plugin {
return NewFileInfoPlugin()
})
}