fix(channel,user): owner 车道只认白名单,堵住群主/伪造 role 冒充主人(裁决 06.4.2/06.4.3/06.4.4)
- 适配器只描述平台身份:qq_obv11 的 owner/admin 一律上报 RoleMember,不再上报 RoleOwner - session 层唯一判主点只认白名单(owner.yaml Accounts ∪ 每渠道 adminIds),nil 档案 fail-closed 拒绝 - 新增「每渠道管理员层」:channels.json ChannelAccount.adminIds[] → runtimeconfig.AdminIDs → OwnerProfile.SetPlatformAdmins(整体替换语义,热重载后移除的 id 立即失效) - 组合根接线:启动即播种、面板保存先灌白名单再重载适配器(cmd/cyrene/wire_channels.go + 4 条端到端单测) - 06.4.3 节流与参与度状态按会话独立计数(ConversationKey 三处统一,空会话 GC);06.4.4 未知平台主动发言保守默认 1 条/分钟
This commit is contained in:
1 parent
7e7f53bb41
commit
af8591b78a
30 files changed
+1289
-144
No files matched your search
@@ -6,13 +6,14 @@ import (
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/user"
|
||||
)
|
||||
|
||||
// ResolveActor turns a normalized inbound message into the strongly typed actor
|
||||
// metadata used by every downstream permission decision. It is the structural
|
||||
// owner-recognition rule from design/11 §0: the owner is decided by matching
|
||||
// channel-level identity (platform + account id) against the user.OwnerProfile
|
||||
// whitelist (or by honoring a channel-level owner tag), and never by reading the
|
||||
// message text. A message whose body says "I am the owner" is not the owner
|
||||
// unless it also matches the whitelist.
|
||||
// ResolveActor 把一条归一化入站消息解析成下游权限决策所用的强类型 actor 元数据。
|
||||
// 这是 design/11 §0 的结构化主人识别规则(裁决 06.4.2):**owner 车道只认白名单**,
|
||||
// 判主依据只有结构化来源——主人档案的账号白名单(owner.yaml,internal/user)与每渠道
|
||||
// 账号配置的 adminIds[](channels.json,design/08 §1.1)。
|
||||
//
|
||||
// **适配器上报的角色标记一律不得单独判主**:平台消息里的 role 字段(OneBot 的群主
|
||||
// owner / 群管理员 admin,或私聊帧里伪造的 role=owner)只能当作非主人侧提示,命中
|
||||
// owner 也会被降级为 RoleMember。判主永远不看消息正文——正文声称「我是主人」同样无效。
|
||||
func ResolveActor(msg adapter.UnifiedMessage, owner *user.OwnerProfile) adapter.Actor {
|
||||
actor := adapter.Actor{
|
||||
Role: msg.Sender.Role,
|
||||
@@ -23,19 +24,30 @@ func ResolveActor(msg adapter.UnifiedMessage, owner *user.OwnerProfile) adapter.
|
||||
GroupName: firstNonEmpty(msg.Sender.GroupName, msg.GroupName),
|
||||
}
|
||||
|
||||
// Structural owner resolution: the whitelist is authoritative, and a
|
||||
// channel-level owner tag (from authenticated login binding) is also
|
||||
// structural. Neither depends on msg.Content.
|
||||
if (owner != nil && owner.IsOwner(msg.Platform, msg.SenderID)) || actor.Role == adapter.RoleOwner {
|
||||
// 白名单命中即主人;未命中则不管适配器上报了什么角色,都留在非主人车道
|
||||
// (owner.IsOwner 对 nil 档案安全返回 false)。
|
||||
if owner.IsOwner(msg.Platform, msg.SenderID) {
|
||||
actor.Role = adapter.RoleOwner
|
||||
} else if !actor.Role.Valid() {
|
||||
// No channel-tagged role and not the owner: default to a member rather
|
||||
// than leaving the role ambiguous, which is the conservative choice.
|
||||
actor.Role = adapter.RoleMember
|
||||
return actor.Resolve()
|
||||
}
|
||||
actor.Role = nonOwnerRole(actor.Role)
|
||||
return actor.Resolve()
|
||||
}
|
||||
|
||||
// nonOwnerRole 把适配器上报的角色收口到非主人车道:owner(群主 / 群管理员 / 伪造
|
||||
// 字段)一律降级为 RoleMember;member / system / unknown 原样保留;非法值保守回落
|
||||
// RoleMember,避免留下含糊角色。
|
||||
func nonOwnerRole(role adapter.Role) adapter.Role {
|
||||
switch role {
|
||||
case adapter.RoleOwner:
|
||||
return adapter.RoleMember
|
||||
case adapter.RoleMember, adapter.RoleSystem, adapter.RoleUnknown:
|
||||
return role
|
||||
default:
|
||||
return adapter.RoleMember
|
||||
}
|
||||
}
|
||||
|
||||
// EnvFor maps a resolved actor to the restrict.ActorEnv used to gate tools.
|
||||
// Group chats always map to group_member (isolated, restricted) even when the
|
||||
// owner is the speaker (design/02 §3: owner group speech stays in the isolated
|
||||
@@ -51,15 +63,16 @@ func EnvFor(actor adapter.Actor) restrict.ActorEnv {
|
||||
return restrict.ActorStranger
|
||||
}
|
||||
|
||||
// IdentifyContext returns the restrict.ActorEnv for a message from the channel
|
||||
// metadata it carries (msg.Sender), inferring the group flag from the channel
|
||||
// type when the sender is not already tagged. This is the environment the policy
|
||||
// engine is bound to before any tool call.
|
||||
// IdentifyContext 仅凭消息自带的渠道元数据推断 restrict.ActorEnv:群聊一律
|
||||
// group_member,其余一律 stranger。它拿不到白名单,因此**不能授予 owner 车道**——
|
||||
// 适配器上报的 role=owner 在此被降级为 member(裁决 06.4.2,owner 只认白名单)。
|
||||
// 真正的判主走 ResolveActor(msg, owner) + EnvFor。
|
||||
func IdentifyContext(msg adapter.UnifiedMessage) restrict.ActorEnv {
|
||||
actor := msg.Sender.Resolve()
|
||||
if msg.IsGroup() {
|
||||
actor.IsGroup = true
|
||||
}
|
||||
actor.Role = nonOwnerRole(actor.Role)
|
||||
return EnvFor(actor)
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user