Files
heicode/heicode/model/agent_swarm_query_test.go
T
chenchenandClaude Opus 4.8 b9d9eddf7b fix(swarm): address #45 review — payload redaction, user-scoped events, no fake stop
回应 Fasthei 复审(PR #53 CHANGES_REQUESTED):
1. 事件 payload 脱敏:swarmEventView 经 sanitizeSwarmPayload —— 递归剔除
   secret_ref/credentials/token/api_key/private_key/access_key/password 及 plan/payload/
   permission_manifest/env 大字段,再跑 RedactText 兜底。绝不下发 azkv:// secret_ref 或
   sk-/Bearer(approval.requested 等 envelope 携带的凭据引用)。加 TestSanitizeSwarmPayload_*。
2. user 作用域:model.ListSwarmCallbackEventsAfter 增加 userID 参数 + WHERE user_id,
   controller 传入当前用户;防 runtime_swarm_id/deployment_id 碰撞或误写导致跨用户事件泄漏。
   测试补 user 隔离用例。
3. stop 不伪造成功:移除「开关打开返回 accepted:true」路径;未启用→POLICY_REJECTED,
   启用也→NOT_IMPLEMENTED(未转发运行时),直到 agent_swarm#2 冻结接上真实 stop。

文档 §5.2 同步(脱敏 / user 作用域 / stop 语义)。go build/vet 干净,controller+model 全回归通过。

Affects: Manager only(只读查询脱敏 + 写端点安全语义)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 14:23:58 +08:00

51 lines
1.8 KiB
Go

package model
import (
"testing"
"github.com/stretchr/testify/require"
)
// #45: events?after 游标 + user 作用域 —— 按 deployment_id/swarm_id 过滤,id>after 增量,oldest-first;
// 传入 userID 时按 user_id 收口(防跨用户泄漏,复审 #3)。
func TestListSwarmCallbackEventsAfter(t *testing.T) {
require.NoError(t, LOG_DB.Where("1 = 1").Delete(&AgentCallbackEvent{}).Error)
mk := func(eventID, uid, dep, swarm, etype string) {
_, err := InsertAgentCallbackEvent(&AgentCallbackEvent{
EventID: eventID, UserID: uid, DeploymentID: dep, SwarmID: swarm, EventType: etype,
})
require.NoError(t, err)
}
mk("e1", "7", "dep_A", "swarm_A", "deployment.status_changed")
mk("e2", "7", "dep_A", "swarm_A", "task.created")
mk("e3", "7", "dep_A", "swarm_A", "artifact.produced")
mk("e4", "7", "dep_B", "swarm_B", "task.created") // 另一个 run
mk("e5", "9", "dep_A", "swarm_A", "task.created") // 同 dep/swarm 但别的用户 → 不应泄漏给 user 7
// user 7 + dep_A:3 条(e5 属 user 9,被排除),oldest-first
all, err := ListSwarmCallbackEventsAfter("7", "dep_A", "swarm_A", 0, 100)
require.NoError(t, err)
require.Len(t, all, 3)
require.Equal(t, "e1", all[0].EventID)
for _, e := range all {
require.NotEqual(t, "e5", e.EventID, "不得返回别的用户的事件")
}
// 游标:after = 第一条 id → 其后 2 条
rest, err := ListSwarmCallbackEventsAfter("7", "dep_A", "swarm_A", all[0].Id, 100)
require.NoError(t, err)
require.Len(t, rest, 2)
require.Equal(t, "e2", rest[0].EventID)
// user 9 只看到自己的 e5
u9, err := ListSwarmCallbackEventsAfter("9", "dep_A", "swarm_A", 0, 100)
require.NoError(t, err)
require.Len(t, u9, 1)
require.Equal(t, "e5", u9[0].EventID)
// 空标识 → 空
none, err := ListSwarmCallbackEventsAfter("7", "", "", 0, 100)
require.NoError(t, err)
require.Empty(t, none)
}