fix(plugins): 全插件审查修复(qq/a2a/memo/calendar/rss/browser)

17 个生产插件全量审查:编译/vet 全过、无硬编码密钥、内核
executeToolCall 有 panic recover + 超时兜底。发现并修复:

P1 qq: downloadURL 裸 http.Get 无超时 → 120s client(挂起泄漏)
P2 a2a: inbound http.Server 无超时 → Read 30s/Write 120s/Idle 60s
   (慢速连接占用 goroutine)
P5 memo/calendar/rss: 数据持久化直写 → atomicWriteJSON temp+rename
   (崩溃截断 JSON 丢全部数据)
P6 qq: 3 处后台 goroutine(已读标记/rcon转发/下载任务)加 recover
   (工具调用外 panic 会带崩 homed 进程)
P7 browser: dump-dom failback Kill 后补 wait 回收僵尸进程

已知可接受项:bili output_dir 用户可控(本机单用户)、recoverydiag
db_path 可读任意 sqlite(诊断工具固有权限,argv 传参无注入)。

全部经 plugindev 重打包 v+0.1 安装验证 config_kept=true。
This commit is contained in:
JianFeeeee
2026-08-26 16:46:06 +08:00
parent ddef1956b5
commit 75447aa7f8
12 changed files with 2791 additions and 2 deletions

View File

@ -819,6 +819,7 @@ func (p *Plugin) handleWebhook(w http.ResponseWriter, r *http.Request) {
if evt.GroupID == rule.GroupID {
mcMsg := fmt.Sprintf("%s 说 %s", nickname, text)
go func(r ForwardRule, msg string) {
defer func() { _ = recover() }()
if err := rconSend(r.Host, r.Port, r.Password, "say "+msg); err != nil {
log.Printf("[qq] rcon forward to %s:%d: %v", r.Host, r.Port, err)
}
@ -989,6 +990,7 @@ func (p *Plugin) handleGetMessage(args map[string]interface{}) (interface{}, err
// 异步标记已读
go func() {
defer func() { _ = recover() }() // 后台任务不允许 panic 冒泡带崩进程
if d.MessageType == "group" && d.GroupID > 0 {
p.napcat("mark_group_msg_as_read", map[string]interface{}{"group_id": d.GroupID})
} else if d.UserID > 0 {
@ -1659,7 +1661,8 @@ func (p *Plugin) handleGetGroupFiles(args map[string]interface{}) (interface{},
return resp, nil
}
dlURL := parsed.Data.URL
httpResp, err := http.Get(dlURL)
client := &http.Client{Timeout: 120 * time.Second}
httpResp, err := client.Get(dlURL)
if err != nil {
return nil, fmt.Errorf("download: %w", err)
}
@ -1714,6 +1717,11 @@ func (p *Plugin) handleDownloadFile(args map[string]interface{}) (interface{}, e
task := p.addDownloadTask(fileID, filename)
go func(t *DownloadTask, fid, fname, furl string, gid, uid int64) {
defer func() {
if r := recover(); r != nil {
log.Printf("[qq] download task %s panic: %v", fid, r)
}
}()
savePath := ""
errMsg := ""
if furl != "" {