test(base): 验证最后一种工具拒绝(角色不符),四种分支全部实测通过

分支 4 是最难构造的一种,两个前提缺一不可:

1. **工具的角色集合必须比 Agent 的更窄**。Agent 层的 validate_access(base.py:101)会先按
   AgentDefinition.allowed_roles 拦截,两者一致时永远进不到工具层的角色校验。所以把
   probe_alt 收窄为 ("risk_operator",),而 Agent 仍允许 admin。
2. **调用者必须有工具要求的权限**,否则会先命中权限分支。所以脚本临时给 admin 授
   probe:read,验证后撤销。

过程中又修掉一处自己写错的地方:探针的 handle 原先硬编码调用 PROBE_TOOL,导致分支 4
(需要调 probe_alt)与分支 2(需要调白名单之外的那一个)互相干扰——第一次跑出来的结果
是"工具不在当前意图白名单"。改为按消息里的 "alt" 选择要调的工具。

四种分支的实测结果,message 各自独立、指向不同处置动作:
- 白名单为空        → 该意图未配置工具白名单
- 工具不在白名单    → 工具不在当前意图白名单
- 缺少工具权限      → 缺少工具权限
- 角色不符          → 角色不能使用工具

目标的另一半也验证了:审计里是完整细节(reason = "角色 ['admin'] 与工具允许的角色
['risk_operator'] 无交集",并带 tool_name / intent / trace_id),而异常 message 只有
"角色不能使用工具"、不含角色集合。**内部配置只进审计,不进客户可见响应。**

环境复原:生效配置 9 条(与起点一致);sys_permission / sys_role_permission 中
probe:read 的行数为 0。
This commit is contained in:
2026-09-11 13:12:56 +08:00
parent 1eb8946552
commit 4e2e42c896
2 changed files with 12 additions and 4 deletions
@@ -66,9 +66,12 @@ class PlatformProbeAgent(BaseAgent):
)
async def handle(self, request: AgentRequest, context: RequestContext) -> CoreResult:
# 走公共工具链路:探针要验证的正是这条路上的白名单、权限与角色校验。
# 按消息里的 "alt" 决定调哪个探针工具。两个工具都必须可被指定调用:
# 分支 2(工具不在白名单)要调白名单之外的那个,分支 4(角色不符)要调角色更窄的
# 那个——硬编码其中一个的话,这两个分支会互相干扰(实测踩到过)。
tool = PROBE_ALT_TOOL if "alt" in request.message.lower() else PROBE_TOOL
output = await self.call_tool(
PROBE_TOOL,
tool,
{"note": request.message[:50]},
intent=INTENT_PROBE,
context=context,