From ca4e1ffad3e70ba128bde536776ce16a909cf46b Mon Sep 17 00:00:00 2001 From: ZacharyZcR Date: Fri, 15 May 2026 22:47:39 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E5=A2=9E=E5=BC=BA=20systeminfo=20?= =?UTF-8?q?=E6=8F=92=E4=BB=B6=E5=B9=B6=E5=90=88=E5=B9=B6=20envinfo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - systeminfo 新增网卡信息、权限检测、补丁数量、杀软检测、 防火墙状态、敏感环境变量扫描等功能 - 合并 envinfo 到 systeminfo,删除独立的 envinfo 插件 - 修复本地插件通过 -m 指定时仍需 -h 参数的问题 - 通过回调机制解决 common/plugins 循环依赖 --- common/flag.go | 12 +- common/i18n/locales/en.yaml | 22 +++ common/i18n/locales/zh.yaml | 22 +++ core/scanner.go | 20 +-- plugins/init.go | 18 ++ plugins/local/envinfo.go | 131 -------------- plugins/local/systeminfo.go | 344 +++++++++++++++++++++--------------- 7 files changed, 277 insertions(+), 292 deletions(-) delete mode 100644 plugins/local/envinfo.go diff --git a/common/flag.go b/common/flag.go index 00ccabe..9af9bf9 100644 --- a/common/flag.go +++ b/common/flag.go @@ -15,6 +15,9 @@ import ( // ErrShowHelp 表示用户请求显示帮助(正常退出) var ErrShowHelp = errors.New("show help requested") +// IsLocalMode 由 plugins 包注册,判断 -m 指定的是否全是本地插件 +var IsLocalMode func(mode string) bool + // Banner 显示程序横幅信息 func Banner() { // 静默模式下完全跳过Banner显示 @@ -272,9 +275,14 @@ func shouldShowHelp(Info *HostInfo, fv *FlagVars) bool { // 检查是否提供了扫描目标 hasTarget := Info.Host != "" || fv.TargetURL != "" || fv.HostsFile != "" || fv.URLsFile != "" - // 本地模式需要指定插件才算有效目标 + // 本地模式不需要目标主机 if fv.LocalPlugin != "" { - hasTarget = true + return false + } + + // -m 指定的全是本地插件时也不需要目标 + if IsLocalMode != nil && IsLocalMode(fv.ScanMode) { + return false } // 如果没有提供任何扫描目标,则显示帮助 diff --git a/common/i18n/locales/en.yaml b/common/i18n/locales/en.yaml index 9d7c58c..f985528 100644 --- a/common/i18n/locales/en.yaml +++ b/common/i18n/locales/en.yaml @@ -693,6 +693,28 @@ systeminfo_distro_exists: other: "Distribution: /etc/os-release exists" systeminfo_whoami: other: "Current User (whoami): {{.Arg1}}" +systeminfo_iface: + other: "Interface {{.Arg1}}: {{.Arg2}} (MAC: {{.Arg3}})" +systeminfo_privilege: + other: "Privilege Level: {{.Arg1}}" +systeminfo_privilege_group: + other: "Admin Group: {{.Arg1}}" +systeminfo_id_info: + other: "User Info: {{.Arg1}}" +systeminfo_firewall: + other: "Firewall: {{.Arg1}}" +systeminfo_firewall_rules: + other: "Firewall Rules: {{.Arg1}}" +systeminfo_patches: + other: "Installed Patches: {{.Arg1}}" +systeminfo_antivirus: + other: "Antivirus Detected: {{.Arg1}}" +systeminfo_sudo: + other: "Sudo Privileges: {{.Arg1}}" +systeminfo_macos_detail: + other: "macOS: {{.Arg1}}" +systeminfo_sensitive_env: + other: "Sensitive Env Var: {{.Arg1}}={{.Arg2}}" # Windows scheduled task winschtask_success: diff --git a/common/i18n/locales/zh.yaml b/common/i18n/locales/zh.yaml index dfa506f..5a923da 100644 --- a/common/i18n/locales/zh.yaml +++ b/common/i18n/locales/zh.yaml @@ -693,6 +693,28 @@ systeminfo_distro_exists: other: "发行版: /etc/os-release 存在" systeminfo_whoami: other: "当前用户(whoami): {{.Arg1}}" +systeminfo_iface: + other: "网卡 {{.Arg1}}: {{.Arg2}} (MAC: {{.Arg3}})" +systeminfo_privilege: + other: "权限级别: {{.Arg1}}" +systeminfo_privilege_group: + other: "所属管理组: {{.Arg1}}" +systeminfo_id_info: + other: "用户信息: {{.Arg1}}" +systeminfo_firewall: + other: "防火墙: {{.Arg1}}" +systeminfo_firewall_rules: + other: "防火墙规则数: {{.Arg1}}" +systeminfo_patches: + other: "已安装补丁: {{.Arg1}}个" +systeminfo_antivirus: + other: "检测到杀软: {{.Arg1}}" +systeminfo_sudo: + other: "sudo权限: {{.Arg1}}" +systeminfo_macos_detail: + other: "macOS: {{.Arg1}}" +systeminfo_sensitive_env: + other: "敏感环境变量: {{.Arg1}}={{.Arg2}}" # Windows计划任务 winschtask_success: diff --git a/core/scanner.go b/core/scanner.go index 1813cfa..c97ce28 100644 --- a/core/scanner.go +++ b/core/scanner.go @@ -6,7 +6,6 @@ import ( "net/url" "os" "os/signal" - "strings" "sync" "syscall" "time" @@ -55,7 +54,7 @@ func determineScanMode(config *common.Config, state *common.State) ScanMode { return ScanModeAlive case config.LocalMode: return ScanModeLocal - case isAllLocalPlugins(config.Mode): + case common.IsLocalMode != nil && common.IsLocalMode(config.Mode): config.LocalMode = true config.LocalPlugin = config.Mode return ScanModeLocal @@ -66,23 +65,6 @@ func determineScanMode(config *common.Config, state *common.State) ScanMode { } } -// isAllLocalPlugins 检查 -m 指定的插件是否全部为 local 类型 -func isAllLocalPlugins(mode string) bool { - if mode == "" || mode == "all" { - return false - } - for _, name := range strings.Split(mode, ",") { - name = strings.TrimSpace(name) - if name == "" { - continue - } - if !plugins.HasType(name, plugins.PluginTypeLocal) { - return false - } - } - return true -} - // selectStrategy 根据扫描模式选择策略 func selectStrategy(config *common.Config, state *common.State, info common.HostInfo) ScanStrategy { mode := determineScanMode(config, state) diff --git a/plugins/init.go b/plugins/init.go index 23188ea..c6987ff 100644 --- a/plugins/init.go +++ b/plugins/init.go @@ -98,6 +98,24 @@ var ( mutex sync.RWMutex ) +func init() { + common.IsLocalMode = func(mode string) bool { + if mode == "" || mode == "all" { + return false + } + for _, name := range strings.Split(mode, ",") { + name = strings.TrimSpace(name) + if name == "" { + continue + } + if !HasType(name, PluginTypeLocal) { + return false + } + } + return true + } +} + // RegisterWithPorts 注册带端口信息的插件 func RegisterWithPorts(name string, factory func() Plugin, ports []int) { RegisterWithTypes(name, factory, ports, []string{PluginTypeService}) diff --git a/plugins/local/envinfo.go b/plugins/local/envinfo.go deleted file mode 100644 index 7c2c9d3..0000000 --- a/plugins/local/envinfo.go +++ /dev/null @@ -1,131 +0,0 @@ -//go:build (plugin_envinfo || !plugin_selective) && !no_local - -package local - -import ( - "context" - "fmt" - "os" - "strings" - - "github.com/shadow1ng/fscan/common" - "github.com/shadow1ng/fscan/common/i18n" - "github.com/shadow1ng/fscan/plugins" -) - -// EnvInfoPlugin 环境变量信息收集插件 -// 设计哲学:"做一件事并做好" -// - 专注于环境变量收集 -// - 过滤敏感信息关键词 -// - 简单有效的实现 -type EnvInfoPlugin struct { - plugins.BasePlugin -} - -// NewEnvInfoPlugin 创建环境变量信息插件 -func NewEnvInfoPlugin() *EnvInfoPlugin { - return &EnvInfoPlugin{ - BasePlugin: plugins.NewBasePlugin("envinfo"), - } -} - -// Scan 执行环境变量收集 - 直接、有效 -func (p *EnvInfoPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - var output strings.Builder - var sensitiveVars []string - - output.WriteString("=== 环境变量信息收集 ===\n") - - // 获取所有环境变量 - envs := os.Environ() - output.WriteString(fmt.Sprintf("总环境变量数: %d\n\n", len(envs))) - - // 敏感关键词 - 直接硬编码,简单有效 - sensitiveKeywords := []string{ - "password", "passwd", "pwd", "secret", "key", "token", - "auth", "credential", "api", "access", "session", - "密码", "令牌", "密钥", "认证", - } - - // 重要环境变量 - 系统相关 - importantVars := []string{ - "PATH", "HOME", "USER", "USERNAME", "USERPROFILE", "TEMP", "TMP", - "HOMEPATH", "COMPUTERNAME", "USERDOMAIN", "PROCESSOR_ARCHITECTURE", - } - - output.WriteString("=== 重要环境变量 ===\n") - for _, envVar := range importantVars { - if value := os.Getenv(envVar); value != "" { - // PATH特殊处理 - 只显示条目数 - if envVar == "PATH" { - paths := strings.Split(value, string(os.PathListSeparator)) - output.WriteString(fmt.Sprintf("%s: %d个路径\n", envVar, len(paths))) - } else { - output.WriteString(fmt.Sprintf("%s: %s\n", envVar, value)) - } - } - } - - // 扫描所有环境变量寻找敏感信息 - output.WriteString("\n=== 潜在敏感环境变量 ===\n") - for _, env := range envs { - parts := strings.SplitN(env, "=", 2) - if len(parts) != 2 { - continue - } - - envName := strings.ToLower(parts[0]) - envValue := parts[1] - - // 检查是否包含敏感关键词 - for _, keyword := range sensitiveKeywords { - if strings.Contains(envName, keyword) { - // 脱敏显示:只显示前几个字符 - displayValue := envValue - if len(envValue) > 10 { - displayValue = envValue[:10] + "..." - } - - sensitiveInfo := fmt.Sprintf("%s: %s", parts[0], displayValue) - sensitiveVars = append(sensitiveVars, sensitiveInfo) - output.WriteString(sensitiveInfo + "\n") - common.LogSuccess(i18n.Tr("envinfo_sensitive", parts[0])) - break - } - } - } - - if len(sensitiveVars) == 0 { - output.WriteString("未发现明显的敏感环境变量\n") - } - - // 统计信息 - output.WriteString("\n=== 统计结果 ===\n") - output.WriteString(fmt.Sprintf("总环境变量: %d个\n", len(envs))) - output.WriteString(fmt.Sprintf("潜在敏感变量: %d个\n", len(sensitiveVars))) - - // 按长度统计 - shortVars, longVars := 0, 0 - for _, env := range envs { - if len(env) < 50 { - shortVars++ - } else { - longVars++ - } - } - output.WriteString(fmt.Sprintf("短变量(<50字符): %d个\n", shortVars)) - output.WriteString(fmt.Sprintf("长变量(≥50字符): %d个\n", longVars)) - - return &plugins.Result{ - Success: len(sensitiveVars) > 0, - Output: output.String(), - Error: nil, - } -} - -// 注册插件 -func init() { - RegisterLocalPlugin("envinfo", func() Plugin { - return NewEnvInfoPlugin() - }) -} diff --git a/plugins/local/systeminfo.go b/plugins/local/systeminfo.go index 3eab92b..f86edfa 100644 --- a/plugins/local/systeminfo.go +++ b/plugins/local/systeminfo.go @@ -5,6 +5,7 @@ package local import ( "context" "fmt" + "net" "os" "os/exec" "os/user" @@ -16,184 +17,247 @@ import ( "github.com/shadow1ng/fscan/plugins" ) -// SystemInfoPlugin 系统信息收集插件 -// 设计哲学:纯信息收集,无攻击性功能 -// - 删除复杂的继承体系 -// - 收集基本系统信息 -// - 跨平台支持,运行时适配 type SystemInfoPlugin struct { plugins.BasePlugin + output strings.Builder } -// NewSystemInfoPlugin 创建系统信息插件 func NewSystemInfoPlugin() *SystemInfoPlugin { return &SystemInfoPlugin{ BasePlugin: plugins.NewBasePlugin("systeminfo"), } } -// Scan 执行系统信息收集 - 直接、简单、有效 -func (p *SystemInfoPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { - var output strings.Builder +func (p *SystemInfoPlugin) log(key string, args ...interface{}) { + msg := i18n.Tr(key, args...) + common.LogInfo(msg) + p.output.WriteString(msg + "\n") +} - output.WriteString("=== 系统信息收集 ===\n") +func (p *SystemInfoPlugin) logSuccess(key string, args ...interface{}) { + msg := i18n.Tr(key, args...) + common.LogSuccess(msg) + p.output.WriteString(msg + "\n") +} + +func (p *SystemInfoPlugin) Scan(ctx context.Context, info *common.HostInfo, session *common.ScanSession) *plugins.Result { common.LogSuccess(i18n.GetText("systeminfo_start")) - // 基本系统信息 - output.WriteString(fmt.Sprintf("操作系统: %s\n", runtime.GOOS)) - output.WriteString(fmt.Sprintf("架构: %s\n", runtime.GOARCH)) - output.WriteString(fmt.Sprintf("CPU核心数: %d\n", runtime.NumCPU())) - - common.LogInfo(i18n.Tr("systeminfo_os", runtime.GOOS)) - common.LogInfo(i18n.Tr("systeminfo_arch", runtime.GOARCH)) - common.LogInfo(i18n.Tr("systeminfo_cpu", runtime.NumCPU())) - - // 主机名 - if hostname, err := os.Hostname(); err == nil { - output.WriteString(fmt.Sprintf("主机名: %s\n", hostname)) - common.LogInfo(i18n.Tr("systeminfo_hostname", hostname)) - } - - // 当前用户 - if currentUser, err := user.Current(); err == nil { - output.WriteString(fmt.Sprintf("当前用户: %s\n", currentUser.Username)) - common.LogInfo(i18n.Tr("systeminfo_user", currentUser.Username)) - if currentUser.HomeDir != "" { - output.WriteString(fmt.Sprintf("用户目录: %s\n", currentUser.HomeDir)) - common.LogInfo(i18n.Tr("systeminfo_homedir", currentUser.HomeDir)) - } - } - - // 工作目录 - if workDir, err := os.Getwd(); err == nil { - output.WriteString(fmt.Sprintf("工作目录: %s\n", workDir)) - common.LogInfo(i18n.Tr("systeminfo_workdir", workDir)) - } - - // 临时目录 - output.WriteString(fmt.Sprintf("临时目录: %s\n", os.TempDir())) - common.LogInfo(i18n.Tr("systeminfo_tempdir", os.TempDir())) - - // 环境变量关键信息 - if path := os.Getenv("PATH"); path != "" { - pathCount := len(strings.Split(path, string(os.PathListSeparator))) - output.WriteString(fmt.Sprintf("PATH变量条目: %d个\n", pathCount)) - common.LogInfo(i18n.Tr("systeminfo_pathcount", pathCount)) - } - - // 平台特定信息 - platformInfo := p.getPlatformSpecificInfo() - if platformInfo != "" { - output.WriteString("\n=== 平台特定信息 ===\n") - output.WriteString(platformInfo) - // 输出平台特定信息到控制台 - p.logPlatformInfo() - } + p.collectBasicInfo() + p.collectNetworkInfo() + p.collectPrivilegeInfo() + p.collectPlatformInfo() + p.collectSensitiveEnvVars() return &plugins.Result{ Success: true, Type: plugins.ResultTypeService, - Output: output.String(), - Error: nil, + Output: p.output.String(), } } -// getPlatformSpecificInfo 获取平台特定信息 - 运行时适配,不做预检查 -func (p *SystemInfoPlugin) getPlatformSpecificInfo() string { - var info strings.Builder +func (p *SystemInfoPlugin) collectBasicInfo() { + p.log("systeminfo_os", runtime.GOOS) + p.log("systeminfo_arch", runtime.GOARCH) + p.log("systeminfo_cpu", runtime.NumCPU()) + if hostname, err := os.Hostname(); err == nil { + p.log("systeminfo_hostname", hostname) + } + if u, err := user.Current(); err == nil { + p.log("systeminfo_user", u.Username) + if u.HomeDir != "" { + p.log("systeminfo_homedir", u.HomeDir) + } + } + if wd, err := os.Getwd(); err == nil { + p.log("systeminfo_workdir", wd) + } + p.log("systeminfo_tempdir", os.TempDir()) +} + +func (p *SystemInfoPlugin) collectNetworkInfo() { + ifaces, err := net.Interfaces() + if err != nil { + return + } + for _, iface := range ifaces { + if iface.Flags&net.FlagLoopback != 0 || iface.Flags&net.FlagUp == 0 { + continue + } + addrs, err := iface.Addrs() + if err != nil || len(addrs) == 0 { + continue + } + var ips []string + for _, addr := range addrs { + ips = append(ips, addr.String()) + } + p.log("systeminfo_iface", iface.Name, strings.Join(ips, ", "), iface.HardwareAddr.String()) + } +} + +func (p *SystemInfoPlugin) collectPrivilegeInfo() { switch runtime.GOOS { case "windows": - // Windows版本信息 - if output, err := p.runCommand("cmd", "/c", "ver"); err == nil { - info.WriteString(i18n.Tr("systeminfo_winver", strings.TrimSpace(output)) + "\n") + if out, err := p.runCommand("net", "session"); err == nil { + _ = out + p.logSuccess("systeminfo_privilege", "Administrator") + } else { + p.log("systeminfo_privilege", "Normal User") } - - // 域信息 - if output, err := p.runCommand("cmd", "/c", "echo %USERDOMAIN%"); err == nil { - domain := strings.TrimSpace(output) - if domain != "" && domain != "%USERDOMAIN%" { - info.WriteString(i18n.Tr("systeminfo_domain", domain) + "\n") + if out, err := p.runCommand("whoami", "/groups"); err == nil { + if strings.Contains(out, "S-1-5-32-544") { + p.logSuccess("systeminfo_privilege_group", "Administrators") } } - case "linux", "darwin": - // Unix系统信息 - if output, err := p.runCommand("uname", "-a"); err == nil { - info.WriteString(i18n.Tr("systeminfo_kernel", strings.TrimSpace(output)) + "\n") + if uid := os.Getuid(); uid == 0 { + p.logSuccess("systeminfo_privilege", "root") + } else { + p.log("systeminfo_privilege", fmt.Sprintf("uid=%d", uid)) } - - // 发行版信息(Linux) - if runtime.GOOS == "linux" { - if output, err := p.runCommand("lsb_release", "-d"); err == nil { - info.WriteString(i18n.Tr("systeminfo_distro", strings.TrimSpace(output)) + "\n") - } else if p.fileExists("/etc/os-release") { - info.WriteString(i18n.GetText("systeminfo_distro_exists") + "\n") - } + if out, err := p.runCommand("id"); err == nil { + p.log("systeminfo_id_info", strings.TrimSpace(out)) } + } +} - // whoami - if output, err := p.runCommand("whoami"); err == nil { - info.WriteString(i18n.Tr("systeminfo_whoami", strings.TrimSpace(output)) + "\n") +func (p *SystemInfoPlugin) collectPlatformInfo() { + switch runtime.GOOS { + case "windows": + p.collectWindowsInfo() + case "linux": + p.collectLinuxInfo() + case "darwin": + p.collectDarwinInfo() + } +} + +func (p *SystemInfoPlugin) collectWindowsInfo() { + if out, err := p.runCommand("cmd", "/c", "ver"); err == nil { + p.log("systeminfo_winver", strings.TrimSpace(out)) + } + if out, err := p.runCommand("cmd", "/c", "echo %USERDOMAIN%"); err == nil { + domain := strings.TrimSpace(out) + if domain != "" && domain != "%USERDOMAIN%" { + p.log("systeminfo_domain", domain) } } - return info.String() + // 防火墙状态 + if out, err := p.runCommand("netsh", "advfirewall", "show", "allprofiles", "state"); err == nil { + for _, line := range strings.Split(out, "\n") { + line = strings.TrimSpace(line) + if strings.Contains(line, "ON") || strings.Contains(line, "OFF") { + p.log("systeminfo_firewall", line) + } + } + } + + // 已安装补丁 + if out, err := p.runCommand("wmic", "qfe", "get", "HotFixID,InstalledOn"); err == nil { + lines := strings.Split(strings.TrimSpace(out), "\n") + patches := 0 + for _, line := range lines { + if strings.HasPrefix(strings.TrimSpace(line), "KB") { + patches++ + } + } + if patches > 0 { + p.log("systeminfo_patches", patches) + } + } + + // 已安装的杀软 (WMI) + if out, err := p.runCommand("wmic", "/namespace:\\\\root\\SecurityCenter2", "path", "AntiVirusProduct", "get", "displayName"); err == nil { + for _, line := range strings.Split(out, "\n") { + line = strings.TrimSpace(line) + if line != "" && line != "displayName" { + p.logSuccess("systeminfo_antivirus", line) + } + } + } +} + +func (p *SystemInfoPlugin) collectLinuxInfo() { + if out, err := p.runCommand("uname", "-a"); err == nil { + p.log("systeminfo_kernel", strings.TrimSpace(out)) + } + if data, err := os.ReadFile("/etc/os-release"); err == nil { + for _, line := range strings.Split(string(data), "\n") { + if strings.HasPrefix(line, "PRETTY_NAME=") { + name := strings.Trim(strings.TrimPrefix(line, "PRETTY_NAME="), "\"") + p.log("systeminfo_distro", name) + break + } + } + } + + // 防火墙 + if out, err := p.runCommand("iptables", "-L", "-n", "--line-numbers"); err == nil { + ruleCount := 0 + for _, line := range strings.Split(out, "\n") { + if len(line) > 0 && line[0] >= '0' && line[0] <= '9' { + ruleCount++ + } + } + p.log("systeminfo_firewall_rules", ruleCount) + } + + // sudo 权限 + if out, err := p.runCommand("sudo", "-l", "-n"); err == nil { + if strings.Contains(out, "ALL") { + p.logSuccess("systeminfo_sudo", "ALL commands") + } else if strings.Contains(out, "NOPASSWD") { + p.logSuccess("systeminfo_sudo", "NOPASSWD entries found") + } + } +} + +func (p *SystemInfoPlugin) collectDarwinInfo() { + if out, err := p.runCommand("uname", "-a"); err == nil { + p.log("systeminfo_kernel", strings.TrimSpace(out)) + } + if out, err := p.runCommand("sw_vers"); err == nil { + for _, line := range strings.Split(out, "\n") { + line = strings.TrimSpace(line) + if line != "" { + p.log("systeminfo_macos_detail", line) + } + } + } +} + +func (p *SystemInfoPlugin) collectSensitiveEnvVars() { + keywords := []string{ + "password", "passwd", "secret", "key", "token", + "auth", "credential", "api_key", "access_key", + } + for _, env := range os.Environ() { + parts := strings.SplitN(env, "=", 2) + if len(parts) != 2 || parts[1] == "" { + continue + } + name := strings.ToLower(parts[0]) + for _, kw := range keywords { + if strings.Contains(name, kw) { + display := parts[1] + if len(display) > 8 { + display = display[:8] + "***" + } + p.logSuccess("systeminfo_sensitive_env", parts[0], display) + break + } + } + } } -// runCommand 执行命令 - 简单包装,无复杂错误处理 func (p *SystemInfoPlugin) runCommand(name string, args ...string) (string, error) { - cmd := exec.Command(name, args...) - output, err := cmd.Output() - return string(output), err + out, err := exec.Command(name, args...).Output() + return string(out), err } -// fileExists 检查文件是否存在 -func (p *SystemInfoPlugin) fileExists(path string) bool { - _, err := os.Stat(path) - return err == nil -} - -// logPlatformInfo 输出平台特定信息到控制台 -func (p *SystemInfoPlugin) logPlatformInfo() { - switch runtime.GOOS { - case "windows": - // Windows版本信息 - if output, err := p.runCommand("cmd", "/c", "ver"); err == nil { - common.LogInfo(i18n.Tr("systeminfo_winver", strings.TrimSpace(output))) - } - - // 域信息 - if output, err := p.runCommand("cmd", "/c", "echo %USERDOMAIN%"); err == nil { - domain := strings.TrimSpace(output) - if domain != "" && domain != "%USERDOMAIN%" { - common.LogInfo(i18n.Tr("systeminfo_domain", domain)) - } - } - - case "linux", "darwin": - // Unix系统信息 - if output, err := p.runCommand("uname", "-a"); err == nil { - common.LogInfo(i18n.Tr("systeminfo_kernel", strings.TrimSpace(output))) - } - - // 发行版信息(Linux) - if runtime.GOOS == "linux" { - if output, err := p.runCommand("lsb_release", "-d"); err == nil { - common.LogInfo(i18n.Tr("systeminfo_distro", strings.TrimSpace(output))) - } else if p.fileExists("/etc/os-release") { - common.LogInfo(i18n.GetText("systeminfo_distro_exists")) - } - } - - // whoami - if output, err := p.runCommand("whoami"); err == nil { - common.LogInfo(i18n.Tr("systeminfo_whoami", strings.TrimSpace(output))) - } - } -} - -// 注册插件 func init() { RegisterLocalPlugin("systeminfo", func() Plugin { return NewSystemInfoPlugin()