来源:#5859(producer 半边)实施时的越界发现,未在该 PR 修复。
事实(已核 origin/main)
resolveAuthzContext(@objectstack/core)被提取出来正是为了让两个 HTTP 入口不再漂移。
但从授权信封组装成 ExecutionContext 的那一步仍是两份手写副本:
packages/runtime/src/security/resolve-execution-context.ts(dispatcher / MCP 入口)packages/rest/src/rest-server.ts(execCtx = { … },os serve / dev 的数据与元数据路由)
两份的字段集已经不一致:runtime 那份按 ADR-0090 D9/D10 设置
ctx.principalKind(human / guest / agent)与 ctx.onBehalfOf,rest-server 那份
完全不设。
消费方是存在的,且在 enforcement 面:
plugin-security/src/explain-engine.ts:95 — if (!context?.userId || context?.principalKind === 'guest') return 'EXTERNAL';plugin-security/src/security-plugin.ts:2557 — const isAgent = context?.principalKind === 'agent';observability/src/perf-timing.ts:475 — service / system 的披露闸门explain-engine.ts:1122 — explain 输出里回显 principalKind
实测
在 dogfood showcase 全栈 boot(真实登录 + 真实 HTTP 请求)里插桩打印到达
plugin-sharing 的执行上下文,键集为:
userId, tenantId, email, positions, permissions, systemPermissions, posture,
isSystem, org_user_ids, accessible_org_ids, timezone, locale, __kernel, __readScope
__kernel 说明这是 rest-server 那条组装路径;principalKind不在键集里。
影响面(未评级 — 交分诊)
同一个请求经 dispatcher 与经 REST 会在这两个字段上得到不同上下文,
所以读 principalKind 的判断在 REST 面从不成立(guest 分支、agent 分支)。
是否有用户今天就撞到,取决于这些分支在 REST 面各自还有没有别的等效前置判断
(security-plugin.ts:950 明确写了「按委托 LINK 判断而非 principalKind 标签」,
提示至少 agent 那一路可能另有兜底)—— 请分诊评级,不要按本文的语气推断严重性。
建议方向(非裁决)
把「授权信封 → ExecutionContext」的组装收成一处共享函数(与
resolveAuthzContext 同一 package),两个传输各自只做传输特有的补充
(REST 的 __kernel / authGate,dispatcher 的 OAuth scopes)。#5859 是这一族在
下一跳的活标本:producer 读的 organizationId 是两份组装都没写过的键,
于是整条 DEPTH 租户隔离恒不生效,而两侧看起来都合规。
Refs:#5859、#5852、ADR-0090 D9/D10、ADR-0095 D2
来源:#5859(producer 半边)实施时的越界发现,未在该 PR 修复。
事实(已核 origin/main)
resolveAuthzContext(@objectstack/core)被提取出来正是为了让两个 HTTP 入口不再漂移。但从授权信封组装成
ExecutionContext的那一步仍是两份手写副本:packages/runtime/src/security/resolve-execution-context.ts(dispatcher / MCP 入口)packages/rest/src/rest-server.ts(execCtx = { … },os serve/dev的数据与元数据路由)两份的字段集已经不一致:runtime 那份按 ADR-0090 D9/D10 设置
ctx.principalKind(human/guest/agent)与ctx.onBehalfOf,rest-server 那份完全不设。
消费方是存在的,且在 enforcement 面:
plugin-security/src/explain-engine.ts:95—if (!context?.userId || context?.principalKind === 'guest') return 'EXTERNAL';plugin-security/src/security-plugin.ts:2557—const isAgent = context?.principalKind === 'agent';observability/src/perf-timing.ts:475—service/system的披露闸门explain-engine.ts:1122— explain 输出里回显principalKind实测
在 dogfood showcase 全栈 boot(真实登录 + 真实 HTTP 请求)里插桩打印到达
plugin-sharing的执行上下文,键集为:__kernel说明这是 rest-server 那条组装路径;principalKind不在键集里。影响面(未评级 — 交分诊)
同一个请求经 dispatcher 与经 REST 会在这两个字段上得到不同上下文,
所以读
principalKind的判断在 REST 面从不成立(guest 分支、agent 分支)。是否有用户今天就撞到,取决于这些分支在 REST 面各自还有没有别的等效前置判断
(
security-plugin.ts:950明确写了「按委托 LINK 判断而非principalKind标签」,提示至少 agent 那一路可能另有兜底)—— 请分诊评级,不要按本文的语气推断严重性。
建议方向(非裁决)
把「授权信封 → ExecutionContext」的组装收成一处共享函数(与
resolveAuthzContext同一 package),两个传输各自只做传输特有的补充(REST 的
__kernel/authGate,dispatcher 的 OAuth scopes)。#5859 是这一族在下一跳的活标本:producer 读的
organizationId是两份组装都没写过的键,于是整条 DEPTH 租户隔离恒不生效,而两侧看起来都合规。
Refs:#5859、#5852、ADR-0090 D9/D10、ADR-0095 D2