mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 01:18:08 +00:00
安全修复(客户端鉴权): - remotedevice 服务端移除授权状态存储(authorized map/SetAuthorized/handleDeviceAuth) - DeviceMeta.Authorized 改为设备 hello 自报,服务端仅透传展示 - device_ctl_* 工具移除服务端授权检查,无条件转发,设备端自行决定是否执行 - 共享设备桥库 Bridge 新增本地 authorized 状态,未授权收到 cmd 直接拒绝 - waiter: --device-authorized / device_authorized 配置控制本地授权 - GUI: 授权存 gui-prefs 本地文件;设备页仅本机可切换开关 - webui /device/auth 旧路径返回 410 Gone - 根因:agent 可经 config_set 篡改服务端授权配置自行授权设备 插件管理强化: - 内置插件禁止卸载(IsBuiltinPlugin + 409),外部插件卸载即时生效 - 卸载不存在插件返回 404;移除误导性 reload_required 提示 - webui 插件路由:名称白名单校验防路径穿越、保留字路径保护
121 lines
3.0 KiB
Go
121 lines
3.0 KiB
Go
package client
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"strings"
|
||
"sync"
|
||
)
|
||
|
||
// CmdRouter 命令路由器,支持按命令前缀分发到不同 handler。
|
||
// 用于 CLI 和 GUI 根据能力类型注册不同的执行函数。
|
||
type CmdRouter struct {
|
||
mu sync.RWMutex
|
||
prefixes map[string]CmdHandler
|
||
default_ CmdHandler
|
||
}
|
||
|
||
// NewCmdRouter 创建命令路由器。
|
||
func NewCmdRouter() *CmdRouter {
|
||
return &CmdRouter{
|
||
prefixes: make(map[string]CmdHandler),
|
||
}
|
||
}
|
||
|
||
// Handle 注册匹配指定前缀的命令处理器。
|
||
// 例如 Handle("homeagent-", homeagentHandler) 会处理所有 homeagent-* 命令。
|
||
func (r *CmdRouter) Handle(prefix string, handler CmdHandler) {
|
||
r.mu.Lock()
|
||
defer r.mu.Unlock()
|
||
r.prefixes[prefix] = handler
|
||
}
|
||
|
||
// HandleDefault 注册默认命令处理器(无前缀匹配时使用)。
|
||
func (r *CmdRouter) HandleDefault(handler CmdHandler) {
|
||
r.mu.Lock()
|
||
defer r.mu.Unlock()
|
||
r.default_ = handler
|
||
}
|
||
|
||
// Dispatch 分发命令到匹配的处理器。
|
||
// 返回 true 表示已处理,false 表示无匹配。
|
||
func (r *CmdRouter) Dispatch(reqID, command string) bool {
|
||
r.mu.RLock()
|
||
defer r.mu.RUnlock()
|
||
|
||
// 先按前缀匹配
|
||
for prefix, handler := range r.prefixes {
|
||
if strings.HasPrefix(command, prefix) {
|
||
handler(reqID, command)
|
||
return true
|
||
}
|
||
}
|
||
|
||
// 无前缀匹配,使用默认
|
||
if r.default_ != nil {
|
||
r.default_(reqID, command)
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
// ===== 能力解析辅助 =====
|
||
|
||
// ParseHomeagentCmd 解析 homeagent-* 命令,返回能力名和参数。
|
||
// 例如 "homeagent-screensue 5 你好" → ("screensue", "5 你好")
|
||
// 也支持 "screensue 5 你好"(无前缀)
|
||
func ParseHomeagentCmd(command string) (capability, args string) {
|
||
cmd := strings.TrimSpace(command)
|
||
// 去掉 homeagent- 前缀
|
||
cmd = strings.TrimPrefix(cmd, "homeagent-")
|
||
parts := strings.SplitN(cmd, " ", 2)
|
||
capability = parts[0]
|
||
if len(parts) > 1 {
|
||
args = parts[1]
|
||
}
|
||
return
|
||
}
|
||
|
||
// ParseJSONCmd 解析 JSON 格式的命令参数。
|
||
// 例如 "computeruse {\"x\":100,\"y\":200,\"action\":\"click\"}"
|
||
// 返回动作名和参数 map。
|
||
func ParseJSONCmd(command string) (action string, params map[string]interface{}, err error) {
|
||
cmd := strings.TrimSpace(command)
|
||
// 去掉 homeagent- 前缀
|
||
cmd = strings.TrimPrefix(cmd, "homeagent-")
|
||
|
||
idx := strings.IndexByte(cmd, '{')
|
||
if idx < 0 {
|
||
action = cmd
|
||
return
|
||
}
|
||
action = strings.TrimSpace(cmd[:idx])
|
||
jsonStr := cmd[idx:]
|
||
if err = json.Unmarshal([]byte(jsonStr), ¶ms); err != nil {
|
||
err = fmt.Errorf("parse json params: %w", err)
|
||
}
|
||
return
|
||
}
|
||
|
||
// BaseResult 构造基础命令结果。
|
||
func BaseResult(reqID, status, output, errMsg string) map[string]interface{} {
|
||
res := map[string]interface{}{
|
||
"op": "cmd_result",
|
||
"req_id": reqID,
|
||
"status": status,
|
||
}
|
||
if output != "" {
|
||
res["output"] = output
|
||
}
|
||
if errMsg != "" {
|
||
res["error"] = errMsg
|
||
}
|
||
return res
|
||
}
|
||
|
||
// ResultJSON 序列化结果 map 为 JSON。
|
||
func ResultJSON(res map[string]interface{}) string {
|
||
b, _ := json.Marshal(res)
|
||
return string(b)
|
||
}
|