fix(findings): address Grok review r2 server batch (Codex gpt-6.1-sol)

Authored by Codex CLI (gpt-6.1-sol, reasoning high, first attempt). Historical
review task message follows the new suggest rule and treats the unified report
as best-effort; v3 tool refresh no longer re-adds a removed reporter binding.
This commit is contained in:
RuoJi6 committed 2026-10-24 23:17:26 +08:00
1 parent 5b342f9f78
commit 54f43fcd3f
4 files changed
+162 -4

No files matched your search

+1 -1
View File
@@ -34,7 +34,7 @@ func (s *Server) toolCompleteFindingCaseReview() actool.CoreTool {
"finding_id": idParam("所选独立漏洞记录 ID"), "finding_id": idParam("所选独立漏洞记录 ID"),
"verdict": map[string]any{"type": "string", "enum": []string{"independent", "merged", "suggested"}}, "verdict": map[string]any{"type": "string", "enum": []string{"independent", "merged", "suggested"}},
"reason": strParam("判断理由,引用已证据化的共同点或不同代码路径;仅类型、标题或资产相同不足以证明共同根因"), "reason": strParam("判断理由,引用已证据化的共同点或不同代码路径;仅类型、标题或资产相同不足以证明共同根因"),
"root_cause_evidence": strParam("原始报告/证据中支持实际共同或不同根因的具体材料;不确定则说明缺失证据及已保存建议")}, "finding_id", "verdict", "reason", "root_cause_evidence") "root_cause_evidence": strParam("原始报告/证据中支持实际共同或不同根因的具体材料;suggested 须说明具体共同点、缺失的关键证据及已保存分组;没有具体共同点应判 independent")}, "finding_id", "verdict", "reason", "root_cause_evidence")
return wrTool("complete_finding_case_review", "历史整理结束前提交每条所选记录的根因判断并验收:必须已读取全部记录,疑似必须已有待确认分组建议;归并只需已在活动文件夹中,统一报告可稍后补齐。验收失败不能宣布完成。", objSchema(map[string]any{"summary": strParam("整理结果总结"), "decisions": map[string]any{"type": "array", "items": decision}}, "summary", "decisions"), func(ctx context.Context, raw json.RawMessage) (actool.Result, error) { return wrTool("complete_finding_case_review", "历史整理结束前提交每条所选记录的根因判断并验收:必须已读取全部记录,疑似必须已有待确认分组建议;归并只需已在活动文件夹中,统一报告可稍后补齐。验收失败不能宣布完成。", objSchema(map[string]any{"summary": strParam("整理结果总结"), "decisions": map[string]any{"type": "array", "items": decision}}, "summary", "decisions"), func(ctx context.Context, raw json.RawMessage) (actool.Result, error) {
var a findingReviewConclusion var a findingReviewConclusion
if err := json.Unmarshal(raw, &a); err != nil { if err := json.Unmarshal(raw, &a); err != nil {
+36 -1
View File
@@ -4,11 +4,46 @@ import (
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/Autumn-27/artex/intercept"
"strings" "strings"
"testing" "testing"
"github.com/Autumn-27/artex/intercept"
) )
func TestFindingCaseReviewTaskMessagePolicy(t *testing.T) {
message := fmt.Sprintf(findingCaseReviewTaskMessage, `[101,102,103]`)
for _, want := range []string{
"finding_ids=[101,102,103]",
"根因明确相同则直接归并",
"相同端点、处理器/后端组件、参数或缺失检查",
"缺少确认共同根因的关键证据",
"一个覆盖2..20条同任务疑似成员的分组",
"不要拆成两两建议",
"仅类型或资产相同不足以建议",
"没有具体共同点时保持 independent",
"不同代码路径时可判 independent",
"尽力更新统一报告",
"统一报告暂缺或待更新不阻塞 complete_finding_case_review",
"不归并未选记录",
"不探测目标",
"遵守人工否决",
} {
if !strings.Contains(message, want) {
t.Errorf("task message missing policy: %s", want)
}
}
if strings.Contains(message, "不确定提交建议") {
t.Fatal("task message suggests solely because of uncertainty")
}
schema, err := json.Marshal((&Server{}).toolCompleteFindingCaseReview().InputSchema())
if err != nil {
t.Fatal(err)
}
if !strings.Contains(string(schema), "没有具体共同点应判 independent") || strings.Contains(string(schema), "不确定则说明缺失证据及已保存建议") {
t.Fatal("completion schema must not require suggestions solely because of uncertainty")
}
}
const completeFindingReviewReport = `# 同一权限缺陷 const completeFindingReviewReport = `# 同一权限缺陷
## 概述 ## 概述
同一对象权限检查缺失。 同一对象权限检查缺失。
+16 -2
View File
@@ -397,6 +397,8 @@ func (s *Server) seedFindingCaseTools() {
} }
names := []string{} names := []string{}
previous, _, _ := s.m.pg.GetSetting("finding_case_tools_v1") previous, _, _ := s.m.pg.GetSetting("finding_case_tools_v1")
// v3 refreshes descriptions and schemas without repeating the v2 binding migration.
previousReview, _, _ := s.m.pg.GetSetting("finding_case_tools_v2_review")
for _, t := range s.findingCaseTools() { for _, t := range s.findingCaseTools() {
schema, _ := json.Marshal(t.InputSchema()) schema, _ := json.Marshal(t.InputSchema())
if err := s.m.pg.RefreshToolDefaults(t.Name(), t.Description(), schema); err != nil { if err := s.m.pg.RefreshToolDefaults(t.Name(), t.Description(), schema); err != nil {
@@ -405,15 +407,27 @@ func (s *Server) seedFindingCaseTools() {
if err := s.m.pg.SeedTool(t.Name(), t.Description(), schema, json.RawMessage(`["reporter"]`)); err != nil { if err := s.m.pg.SeedTool(t.Name(), t.Description(), schema, json.RawMessage(`["reporter"]`)); err != nil {
return return
} }
if previous != "true" || t.Name() == "complete_finding_case_review" { needsBinding := previous != "true"
if t.Name() == "complete_finding_case_review" {
needsBinding = previousReview != "true"
}
if needsBinding {
names = append(names, t.Name()) names = append(names, t.Name())
} }
} }
if err := s.m.pg.AddAgentToToolBinding("reporter", names); err != nil { if err := s.m.pg.AddAgentToToolBinding("reporter", names); err != nil {
return return
} }
if previousReview != "true" {
if err := s.m.pg.SetSetting("finding_case_tools_v2_review", "true"); err != nil {
return
}
}
_ = s.m.pg.SetSetting(flag, "true") _ = s.m.pg.SetSetting(flag, "true")
} }
const findingCaseReviewTaskMessage = "人工选择整理(只读原始记录):本任务所选 finding_ids=%s。逐条读取已有报告,仅判断同一实际根因;根因明确相同则直接归并。只有证据指向相同端点、处理器/后端组件、参数或缺失检查等具体共同点,但缺少确认共同根因的关键证据时,才提交一个覆盖2..20条同任务疑似成员的分组,不要拆成两两建议;仅类型或资产相同不足以建议,没有具体共同点时保持 independent,不要因为不确定就提交建议。证据表明属于不同代码路径时可判 independent。已有或新建组应尽力更新统一报告;统一报告暂缺或待更新不阻塞 complete_finding_case_review,可由后续运行或人工补齐。禁止生成或覆盖原始报告、改评级状态、补绑证据,不归并未选记录,不探测目标,遵守人工否决。结束前调用 complete_finding_case_review 提交逐条判断并通过验收。"
func (s *Server) reviewFindingCases(w http.ResponseWriter, r *http.Request) { func (s *Server) reviewFindingCases(w http.ResponseWriter, r *http.Request) {
var a findingCaseRequest var a findingCaseRequest
if !decodeCaseRequest(w, r, &a) { if !decodeCaseRequest(w, r, &a) {
@@ -479,7 +493,7 @@ func (s *Server) reviewFindingCases(w http.ResponseWriter, r *http.Request) {
taskID, fids := reservation.TaskID, reservation.FindingIDs taskID, fids := reservation.TaskID, reservation.FindingIDs
taskContext := taskContexts[taskID] taskContext := taskContexts[taskID]
raw, _ := json.Marshal(fids) raw, _ := json.Marshal(fids)
msg := fmt.Sprintf("人工选择整理(只读原始记录):本任务所选 finding_ids=%s。逐条读取已有报告,仅判断同一实际根因,明确则归并,不确定提交建议;已有组更新统一报告。禁止生成或覆盖原始报告、改评级状态、补绑证据,不归并未选记录,不探测目标。结束前调用 complete_finding_case_review 提交逐条判断并通过验收。", raw) msg := fmt.Sprintf(findingCaseReviewTaskMessage, raw)
item := triggeredRun{agentKey: "reporter", title: "漏洞整理 · task#" + i64s(taskID), message: msg, taskID: taskID, taskDesc: taskContext.taskDesc, taskGoal: taskContext.taskGoal, conversationID: reservation.ConversationID} item := triggeredRun{agentKey: "reporter", title: "漏洞整理 · task#" + i64s(taskID), message: msg, taskID: taskID, taskDesc: taskContext.taskDesc, taskGoal: taskContext.taskGoal, conversationID: reservation.ConversationID}
cfg := s.readTriggerBehavior("reporter") cfg := s.readTriggerBehavior("reporter")
s.queueMu.Lock() s.queueMu.Lock()
+109
View File
@@ -42,6 +42,115 @@ func testCaseServer(t *testing.T) (*Server, int64, []int64) {
t.Cleanup(func() { _, _ = pg.Exec(`DELETE FROM findings WHERE task_id=$1`, task.ID); _ = pg.DeleteTask(task.ID) }) t.Cleanup(func() { _, _ = pg.Exec(`DELETE FROM findings WHERE task_id=$1`, task.ID); _ = pg.DeleteTask(task.ID) })
return &Server{m: &Manager{pg: pg}}, task.ID, ids return &Server{m: &Manager{pg: pg}}, task.ID, ids
} }
func TestFindingCaseToolMigrationPreservesBindings(t *testing.T) {
s, _, _ := testCaseServer(t)
pg := s.m.pg
for _, flag := range []string{"finding_case_tools_v1", "finding_case_tools_v2_review", "finding_case_tools_v3_review"} {
value, exists, err := pg.GetSetting(flag)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if exists {
if err := pg.SetSetting(flag, value); err != nil {
t.Error(err)
}
} else if _, err := pg.Exec(`DELETE FROM settings WHERE key=$1`, flag); err != nil {
t.Error(err)
}
})
}
for _, tool := range s.findingCaseTools() {
key := tool.Name()
old, err := pg.GetTool(key)
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() {
if old == nil {
if _, err := pg.Exec(`DELETE FROM tools WHERE key=$1`, key); err != nil {
t.Error(err)
}
return
}
bindings, _ := json.Marshal(old.Agents)
if err := pg.UpdateTool(key, old.Description, old.Schema, bindings, old.Enabled); err != nil {
t.Error(err)
}
})
if err := pg.SeedTool(key, tool.Description(), nil, nil); err != nil {
t.Fatal(err)
}
}
for _, tc := range []struct {
name string
v1, v2 bool
}{
{name: "initial"},
{name: "v1_to_v3", v1: true},
{name: "v2_to_v3", v1: true, v2: true},
{name: "v2_without_v1_flag", v2: true},
} {
t.Run(tc.name, func(t *testing.T) {
for flag, value := range map[string]bool{
"finding_case_tools_v1": tc.v1,
"finding_case_tools_v2_review": tc.v2,
"finding_case_tools_v3_review": false,
} {
if value {
if err := pg.SetSetting(flag, "true"); err != nil {
t.Fatal(err)
}
} else if _, err := pg.Exec(`DELETE FROM settings WHERE key=$1`, flag); err != nil {
t.Fatal(err)
}
}
for _, tool := range s.findingCaseTools() {
if err := pg.UpdateTool(tool.Name(), "old description", json.RawMessage(`{}`), json.RawMessage(`["custom-agent"]`), false); err != nil {
t.Fatal(err)
}
}
s.seedFindingCaseTools()
for _, tool := range s.findingCaseTools() {
row, err := pg.GetTool(tool.Name())
if err != nil || row == nil {
t.Fatal("missing tool", tool.Name(), err)
}
wantReporter := !tc.v1
if tool.Name() == "complete_finding_case_review" {
wantReporter = !tc.v2
}
if contains(row.Agents, "reporter") != wantReporter || !contains(row.Agents, "custom-agent") || row.Enabled {
t.Fatalf("binding/enabled configuration changed incorrectly: %+v", row)
}
if row.Description != tool.Description() || len(row.Schema) <= 2 {
t.Fatalf("description/schema not refreshed: %+v", row)
}
}
for _, flag := range []string{"finding_case_tools_v2_review", "finding_case_tools_v3_review"} {
if value, _, err := pg.GetSetting(flag); err != nil || value != "true" {
t.Fatalf("migration flag %s not saved: %q, %v", flag, value, err)
}
}
if err := pg.RemoveAgentFromTool("reporter", "complete_finding_case_review"); err != nil {
t.Fatal(err)
}
// A refresh retry must not replay a completed legacy binding migration.
if tc.v1 {
if err := pg.SetSetting("finding_case_tools_v3_review", "false"); err != nil {
t.Fatal(err)
}
}
s.seedFindingCaseTools()
row, err := pg.GetTool("complete_finding_case_review")
if err != nil || row == nil || contains(row.Agents, "reporter") {
t.Fatal("refresh restored an operator-removed binding", row, err)
}
})
}
}
func TestFindingCaseAPIPaginationAndTools(t *testing.T) { func TestFindingCaseAPIPaginationAndTools(t *testing.T) {
s, task, ids := testCaseServer(t) s, task, ids := testCaseServer(t)
ctx := context.Background() ctx := context.Background()