Skip to content

ScriptContext.user 是 unknown —— 沙箱接缝上没有任何类型把 dispatcher 的 user 形状钉住(observation) #5521

Description

@baozhoutao

Observation-class finding,来自 #5372 / PR #5518 的实施。今天没有用户会撞到,不影响任何运行行为 —— 记录的是一处"声明缺席"而非缺陷。

观察到的事实

ScriptContext(packages/runtime/src/sandbox/script-runner.ts:59)把交给 body 的调用者声明为:

user?: unknown;

而 ctx.user 在两个方向上都是有契约的:

也就是说值已经统一了,但类型系统对此一无所知:unknown 之下,第四个 dispatch 面明天再手搓一个 user 字面量,编译器不会说一句话 —— 而"三个 dispatcher 手搓出三种形状"正是 #5372 的成因。#5372 之所以能存在几个版本,部分原因就是没有任何声明可以违背。

为什么按 observation 归档而不是缺陷

可能的方向(未裁决)

  1. ScriptContext.user?: EvalUser(spec 契约,最小公分母),dispatch 面的传输键作为结构化扩展仍然合法;
  2. 在 runtime 侧导出 ActorUser 并声明 user?: ActorUser | EvalUser;
  3. 维持 unknown,改以闸门(而非类型)约束"user 形状只有一个生产者" —— 与 check:single-authz-resolver 同形。

需要先测 hook 面实际交付的形状再选。

会话:session_016FNvXhtSdnEGEfLEsMmvxh(仅记录,不认领)

Activity

  1. os-zhuang commented on Aug 5, 2026

    @os-zhuang
    Contributor

    发现分诊轮判级:持有(留 finding,补挂 domain:cli 缓存落点判断 —— packages/runtime 归 cli 车道)。为什么还留着:运行时形状已统一且有逐键 pin 测试兜底,今天无任何用户可撞面;类型收紧的前置(核实 hook 面 engineCtx.user 实际交付形状)未做,三个方向未裁。重启条件:出现第四个 dispatch 面、或 hook 面形状核实完成时晋级。下轮分诊轮复核。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  2. baozhoutao commented on Aug 6, 2026

    @baozhoutao
    ContributorAuthor

    相邻信息(不是认领):#5613 的 runtime 半边(PR #5991)把同一个接缝的姊妹字段 ScriptContext.session 从 unknown 收窄了,给本单的 user 字段留下了一个可直接照抄的模子和一份实测数据。

    收窄成什么:不是单一类型,而是两个真实生产者形状的联合,并导出成命名类型 —— export type ScriptSession = ActionSession | HookContext['session'](packages/runtime/src/sandbox/script-runner.ts)。理由是这个 seam 确实同时承载 hook body 与 action body,收成其中任一单型都会在另一边说谎。user 面大概率是同样的结构(dispatcher 侧的 ActorUser vs hook 侧的 user 形状),差别在于 ActorUser 今天只有 runtime 的 TS interface、没有 spec 契约(这一点单独记在 #6011)。

    #5697 当初留 unknown 的那句理由(「收窄会逼 seam 的每个消费者去判别 body 种类」)这次实测过,对 session 字段不成立:两个写入方(body-runner.ts 的 buildSandboxContext / buildActionSandboxContext)都从 any 引擎上下文赋值,唯一读取方(quickjs-runner.ts 的 installCtx → setObjectJson)收 unknown,全仓(objectstack / objectui / cloud)ScriptContext 没有 runtime 包外引用。全仓 turbo run typecheck 125/125 绿,收窄面止于 runtime 包内。同样的测法应当先对 user 字段跑一遍再动手 —— 结论可能不同(user 的读取方比 session 多)。

    本单仍未认领,finding 不变。


    Generated by Claude Code


    Generated by Claude Code

  3. claude commented on Aug 6, 2026

    @claude
    Contributor

    发现分诊轮(#4949 纪律):持有(留 finding),域维持 domain:cli。

    一、过时前提检查(origin/main @ 9e3709a)

    08-05 那版的两条重启条件逐条核过 —— 一条的前置模子已落地、测量本身仍未做,另一条未成立:

    • 「hook 面形状核实完成」:姊妹字段 session 的收窄已随 PR feat(runtime)!: action ctx.session 双发 positions(权威)+ roles(弃用别名) (#5613) #5991 于 2026-08-06 15:06Z MERGED。origin/main 上 packages/runtime/src/sandbox/script-runner.ts:54 现有 export type ScriptSession = ActionSession | HookContext['session'],而同一个 interface 的 :86 仍是 user?: unknown —— 模子与缺口相隔 33 行。测法也已由 14:46Z 那条相邻信息记全,但针对 user 面的那一遍测量至今没人跑。
    • 「出现第四个 dispatch 面」:未成立。origin/main 上仍恰好两个(非测试命中即此二处):body-runner.ts:307 buildSandboxContext / :326 buildActionSandboxContext。

    二、⛔ 本轮新事实 —— 一条硬串行,也是维持持有的主要理由

    #6011(priority:p0 + pm:queue + pm:dispatched + domain:cli + target:v17,在飞)正在处置 ActorUser 的 roles / positions 双发别名,落点 packages/runtime/src/security/actor-user.ts。而 ScriptContext.user 的两个写入方交付的正是这个形状:

    • body-runner.ts:315 — user: engineCtx?.user ?? engineCtx?.session?.user
    • body-runner.ts:340 — user: actionCtx?.user ?? actionCtx?.session?.user

    ⇒ 本单要收窄的目标类型,此刻正由 #6011 重新定形。现在晋级等于让 dev 在一个正在变的形状上钉类型,因此持有,不晋级。

    三、重启条件(重新锚定,替代 08-05 那版)

    1. [runtime] ctx.user 的 roles 别名(值是 positions)没有关闭日期 —— #5613 给 ctx.session 装了迁移窗口,同名同值的 ctx.user 面仍是无限期别名(observation) #6011 落地(其 PR 合入 origin/main)⇒ user 的生产者形状定形;
    2. 随后按 feat(runtime)!: action ctx.session 双发 positions(权威)+ roles(弃用别名) (#5613) #5991 的同一测法跑一遍 user 面:写入方两处(上列)、VM 侧唯一读取方 quickjs-runner.ts:489 的 setObjectJson(vm, ctxObj, 'user', ctx.user)、跨仓消费方。

    跨仓那一格本轮已顺带跑完:ScriptContext 在 objectui / cloud 零命中(阳性对照:两仓 package.json 均命中 objectstack,证伪「扫描器坏了/路径错了」)⇒ 收窄面止于 runtime 包内,与 session 当初的结论一致,这一格晋级时不必重跑。

    ⚠️ 晋级时须点名一处比 session 更复杂的差异:user 的两个写入方各带 ?? …session?.user 兜底链(session 字段当初没有),即形状来源比姊妹字段多一路。若测量结论是「联合类型要含第三支」,那已越出照抄 ScriptSession 模子的范围,需在派发令里作为必答项。

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

  4. claude commented on Aug 7, 2026

    @claude
    Contributor

    Findings triage (objectstack#4949 discipline) — restart condition 1 FIRED ⇒ PROMOTE to pm:queue. Domain unchanged: domain:cli.

    1. The trigger

    The previous verdict held on one blocker and named its exit:

    1. [runtime] ctx.user 的 roles 别名(值是 positions)没有关闭日期 —— #5613 给 ctx.session 装了迁移窗口,同名同值的 ctx.user 面仍是无限期别名(observation) #6011 落地(其 PR 合入 origin/main)⇒ user 的生产者形状定形

    #6011 closed 2026-08-07T01:25:53Z, via PR #6048 merged 2026-08-07T01:25:52Z (退役 ctx.user 的 roles 别名,positions 成为唯一拼法). The type this issue wants to pin is no longer being reshaped underneath it — that was the whole reason for the hold.

    2. Stale-premise check (origin/main @ ede5a8e) — the gap is unchanged

    3. What carries over, so the dispatch does not redo it

    • Restart condition 2 is the dispatch's own first step, not a gate: run the feat(runtime)!: action ctx.session 双发 positions(权威)+ roles(弃用别名) (#5613) #5991 measurement over the user face — the two writers (body-runner.ts:315 / :340), the single VM-side reader (quickjs-runner.ts:489, setObjectJson(vm, ctxObj, 'user', ctx.user)), and cross-repo consumers.
    • The cross-repo cell is already done and need not be re-run: ScriptContext has zero hits in objectui / cloud, with a positive control (both repos' package.json match objectstack) ruling out a broken scan. The narrowing surface stops inside packages/runtime.
    • One difference from session to carry into the dispatch: both writers carry a ?? …session?.user fallback chain, which the session field had no equivalent of — so the narrowed type has to admit whatever that chain can yield, not just the primary producer's shape.
    • "Fourth dispatch face" (the other half of the old condition) is still not met: exactly two on main, as before.

    4. Serial constraints

    No open PR touches packages/runtime/src/sandbox/** (all 5 open PRs enumerated this round). Re-pull origin/main at claim time regardless.

    ⛔ No target:<major>: a sandbox-seam type gap, not a shipped-surface defect.

    本评论来自分诊座位 Routine(#5474 试点),不构成认领。


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions