Skip to content

[REQ-128][CODE] 绑定 package 授权、认证与事务 admission #713

Closed

Description

@jinjunnn

⚠️ 本期范围裁决(2026-07-30/31,owner)—— 优先于下方原文与基线

REQ-128 分四相,本期只做 Phase 1、只支持单组件 package。
本票是 Phase 1 的最后一张 CODE,也是让 alpha.secret-prerequisite.v1
真正兑现的那张票。

一、上游全部已交付(都可在本机读到)

产物 位置
host contract(#694) packages/ui-mac/src/shared/host-extension-package-contract/
secret prerequisite(#696) packages/ui-mac/src/shared/package-secret-prerequisite.ts
唯一 evaluator(#702) packages/ui-mac/src/main/package-installability.ts
详情页(#695) 与本票并行,勿动其文件
producer 侧密钥消费闸(alpha-web#101) 已合并

二、被删除的范围(不实现、不写测试、不留插槽)

  • MCP OAuth 与 Alpha Connection 的编排(#703/#704,Phase 2)——
    Required sequence 里的 required OAuth/Connection ready 一步本期不做;
  • graph digest —— 单组件 package 没有 graph;authorization 绑定的是
    snapshot / envelope / item / capability 四组,不是五组;
  • Development plan 第 3 条的 optional未连接进入installed-unavailable
    —— 单组件无 optional child;
  • Development plan 第 6 条 V3 repository rollout gate ——
    owner 已推翻基线 §2.9 的自缚:单组件没有 graph、没有共享 claim,
    现有 InstallRecordV2 + 现有单装事务已满足全旧或全新;
  • Dependencies 里的 V3 repository、Bundle builder/probe enabling、
    MCP OAuth prerequisite、Alpha Connection prerequisite —— 均已移出本期。

本期的 required sequence 收敛为:

signed snapshot + Envelope refs
→ 不含 secret 值的完整 plan preview
→ capability authorization(绑定 snapshot/envelope/item/capability 四组 digest)
→ 瞬时 secret 采集
→ main 重取并逐条重验
→ 现有 transaction authorization 终闸
→ runExtensionTransaction(prepared populate → probe → switch → 记账)

三、本票必须给出的一条端到端证据(Phase 1 收尾判据)

#696 交付的 secret prerequisite 模块目前零生产调用者,
alpha.secret-prerequisite.v1 在运行期尚未兑现 —— 这一点已在 #696 与
docs/contracts/extension-capability-authorization.md 如实记录为「尚未接线的声明形状」。

本票的退出门必须包含一条端到端证据,证明该 token 从已签 Envelope 一路走到
真实的 secret 采集与写盘。这条证据落地后,才可以把那份契约文档里的
「声明形状」措辞改回「当前有效行为」。不要跳过这一步,也不要提前改那段文档。

四、从 #702 R1/R2 转记过来的三处覆盖缺口(本票负责补)

这三处在 #702 时今天不可达,而本票正是让它们上路的那张票:

  1. package-installability.ts 的 package-payload-unavailable 与
    package-prerequisite-invalid 两个 reason code 只有表、没有行为断言
    (换成 package-invalid 全绿);
  2. fetchPackagePayload 整函数零覆盖(字节上限 ×2、redirect:"error"、HTTP 状态
    四条逐条拆全绿)—— 所有现有用例都注入 fetchPayload,默认实现从没被执行过;
  3. preflight 的值校验零覆盖(scope / attemptId / grants 白名单三条改成放行全绿)
    —— 本票会真的在这些值上执行。

五、从前八票十二轮审计带进来的五条硬要求

  1. 断言返回值,不要断言源码文本。
  2. 闸门要测我们真走的那条路(第零问) —— 真实 Catalog 里今天 0 个 package,
    夹具必然是构造的,显式标注,不要假装在跑。
  3. 闸门要测生产入口,不是你自己拼的等价链。(#702 R1 Blocker、#695 R1 Blocker
    都是这一条:闸门在,但它守的不是生产路径。)
  4. 绿的理由必须是你声称的那个。(#702 R2、#695 R1 M-3 都栽在这里:
    夹具的前后两次结果逐字节相同,于是没有任何断言能区分"用了新的"和"用了旧的"。)
  5. 不要只测最退化的形状。(alpha-web#101 R1:负向用例只用了"整个为空",
    于是把判定放松成子串匹配仍全绿 —— 而放松后放行的正是作者最常见的笔误。)

六、反过度工程(owner 直令)

复用现有 capability authorization ledger、现有 runExtensionTransaction、
现有 alpha-mcp-secrets 受限 store、现有 withRecoveredWrite 串行写通道。
不新建第二套同意系统 / 授权账本 / 通用状态机 / 抽象层;不引入新依赖;
不为 Phase 2/3/4 预留插槽、空接口、TODO 钩子;不做预防性抽象;
不要为了让测试挂得上而改生产入口结构。
发现超范围缺陷写回票,不顺手修。

Covers

alpha-work#49 AC3–AC5:建立唯一 main-owned PackageAdmissionCoordinator,把能力授权、
MCP OAuth/Alpha Connection、瞬时 secret、Catalog重验与现有 transaction authorization
绑定到同一 signed package/graph identity。

Solution baseline

REQ-128 accepted baseline
§2.7。

Required sequence

signed snapshot + Envelope refs
→ full value-free plan preview
→ capability authorization bound to snapshot/envelope/graph/item/capability digests
→ required OAuth/Connection ready
→ transient secret collection
→ main re-fetch and exact revalidation
→ existing transaction authorization final check
→ transaction prepared populate/probe/switch/V3 mutation

Development plan

  1. 定义 opaque attempt record、digest binding、state/reason codes 与 expiry/replay规则。
  2. 复用现有 capability authorization ledger;不新建第二套同意系统,但 confirmation对象
    不再只是 capability名称。
  3. required prerequisite未ready时 transaction=0;optional未连接进入installed-unavailable。
  4. Catalog/snapshot/graph/item/capability任一漂移即废弃attempt、清空瞬时secret、回preview。
  5. renderer只传 attempt ID与瞬时 grants;main重取所有权威事实。
  6. package install capability只有 V3 repository rollout gate开启后才广告。

Acceptance and named gates

  • package-admission.wiring.test.ts 对时序逐步断言生产调用,任何越序失败。
  • authorization精确绑定五组digest/capability facts;同能力新代码不能复用旧确认。
  • OAuth成功后本地失败保留connection并诚实显示;未授权前无外部consent/secret写。
  • cancel/stale/replay/tamper/TOCTOU全部零未授权安装副作用。

Dependencies

Blocked by single evaluator、V3 repository、Bundle builder/probe enabling、alpha-code#696、
MCP OAuth prerequisite 与 Alpha Connection prerequisite。

Out of scope

新授权账本、provider-specific wizard、post-commit rollback history。

Exit condition

Merged PR + security review + production sequence/digest-binding tests + docs同步。

Activity

  1. self-assigned this
    on Jul 30, 2026
  2. added
    type:featureNew user or system capability
    prio:P1High-value near-term delivery
    area:securitySecurity or access control
    cross-repoRequires coordinated work in multiple repositories
    on Jul 30, 2026
  3. jinjunnn commented on Jul 30, 2026

    @jinjunnn
    OwnerAuthor

    从 #694 R1 审计转记的一条

    #694 的 decodeMcpLocalPayload 不校验 command[0] 是否可执行/在白名单内。
    审计判定契约票不做执行语义,转到本票(admission 才是执行面权威)。

    要求:本票的 admission 时序里,local MCP 的 command 必须在授权面被当作执行请求对待
    (用户可见、进入 capability 授权、不得靠 decoder 的形状校验冒充安全边界)。
    #694 的 decoder 只保证形状合法,不保证可执行安全 —— 别把它当闸门用。

  4. added 2 commits that reference this issue on Jul 30, 2026
  5. jinjunnn commented on Jul 31, 2026

    @jinjunnn
    OwnerAuthor

    R1 跨模型对抗审计记录(2026-07-31)

    实现方 codex,审计方 Opus(只读)。预算 L 级 ≤3 轮(开审前定死,非中途加码) ——
    理由:这是 Phase 1 里唯一决定「未授权安装能不能发生」的票,票面退出条件本身写着 security review。

    生产行为没被绕过去

    四类攻击全部被拦,且拦在事务与写盘之前:授权前送密钥、四组 digest 逐个篡改、
    capability 相同但 payload 变(TOCTOU)、replay / cancel 后续用。
    审计还多试了一个实现方没测的轴 —— 重验时服务端换 payload 字节而 envelope 不动 ——
    被 package-payload-integrity 拒住,tx=0、零密钥。

    ext-transaction.ts / catalog-channels.ts / remote-catalog.ts 三处改动经判定为
    接线必需,非「为让测试挂得上而改生产结构」;既有语义未削弱,45 条既有行为门全绿。

    44 处 mutation、32 处零红存活(73%)。问题全在闸门覆盖。

    采纳(已回传实现方修复)

    # 判定
    B-1(Blocker) #702 转记的「preflight 值校验」补在了生产不走的那条路上:测试不传 installPackage,而生产恒传 ⇒ package 意图被路由到 admission,永不到达被测解码器;同 commit 收紧的 ATTEMPT_ID 正则对该路径已是死码。生产真正执行的 decodePackageAdmissionIntent 及其 helper 致残 11 处全部零红。而 gate-files.tsv 那行写着「守 preflight 值白名单」—— 登记簿在替一道不存在的闸背书 修(把表的目标换成 coordinator + 修正 guarantee 文本)
    B-2(Blocker) 契约文档把「当前运行行为」卖过头:renderer 发的意图没有 attemptId,用户可达路径进不了 admission 修文档措辞 + owner 裁决补开 #732
    M-1 密钥失败路径三处卫生零红(unhealthy probe 判定 / populate 异常吞掉 / 失败后清理)—— 契约 §9 只把残留窗口记成「崩溃时」,即隐含承诺非崩溃失败会清理,而那条承诺无断言 修
    M-2 MCP live-config 收养闸零红 —— 用户手写的 mcp.<name> 会被 package 安装静默覆盖 修
    m-2 fetchPackagePayload 最终 URL 校验零红(票面只点名四条,这是第五条,属枚举漏) 修
    m-4 #695 转记的四条本 diff 未动 —— 这是对的(本票被要求勿动 #695 文件,且属 renderer 半场),但需留痕由 #732 承接 留痕

    已知不修 + 理由(留痕)

    finding 不修理由
    binding 的 envelopeDigest / capabilityDigest / itemDigest-payload 三处常量化零红;attempt catalogId 比对与 verdict 复核零红 审计逐条推证结构上不可达:snapshot 钉死 catalog 字节 ⇒ envelope 漂移必带 snapshot 漂移;accepted 回调只在 compatibleView 前一行触发 ⇒ verdict 复核恒真。给不可达状态造夹具是明确要拒的形状
    snapshotDigest 在 cache / LKG / v1 三条源上零断言(5 处) 后果被 envelope/item 另外三组 digest 兜住,不是漏洞;属覆盖不足
    skill/agent + asset 半场约 90 行、零 producer、零 catalog 实例、零测试 今天不可达(corpus 只有 expected.mcp-remote.*)。它的去留是 owner 裁决,本轮不处理 —— 属「不为后续 Phase 预留插槽」直令的边缘品
    package 安装不过 advisory / 撤销闸 CANONICAL_ENTRY_ID_RE 结构上不接受 package: 前缀,发布侧今天无法签发针对 package 的公示 ⇒ 第零问不通过。属 Phase 2 真实缺口,写回票即可,不在本票开轮
    MAX_ATTEMPTS=64 FIFO 淘汰可被 renderer 冲掉他人挂起的 attempt 单用户桌面,冲掉只导致重新 preview,无权限后果
    每次 preview/confirm 重拉 payload 纯性能,非本票 AC
    preload/types.ts 联合类型加宽 类型层不是安全边界(as any 可绕);生产判据在 decodePackageAdmissionIntent,审计已实测拒得住
    package-admission.wiring.test.ts:36 把另一测试文件起子进程重跑一遍 纯重复约 1 秒,无正确性影响

    第零问的判定(审计结论,编排者认可)

    真实 Catalog 今天 0 个 package,全部夹具都是构造的 —— 两个 cases 文件头部已显式标注。
    signed-catalog cases 用真 Ed25519 + 真 refreshRemoteCatalog,wiring cases 用真注册 IPC,
    两条各覆盖一半、并集诚实,不按「单条不全」报。

  6. added a commit that references this issue on Jul 31, 2026
  7. jinjunnn commented on Jul 31, 2026

    @jinjunnn
    OwnerAuthor

    R2 对抗审计记录(Opus,只读)

    预算:本票开审前定为 L ≤3;实际用掉 2 轮(R1 全量 → 修 → R2 只判闭合 + 修复 diff)。

    R2 结论:RECOMMEND: MERGE —— R1 全部 finding 判 FIXED,修复 diff 内无新 Blocker/Major。

    本轮最有价值的一件事:自报数字被独立复核推翻

    R2 的派发指令里写死了一句:「实现方的自报数字不作数,你自己数一遍。」

    修复方自报「R1 所述 11 处零红存活 → 降为 0」。审计独立复核的结果是 5 处仍零红。

    差异不是谎报,是指代不同:

    • 按 R1 原文所指的那 11 处(decoder 及其 helper)数 ⇒ 5 处仍零红;
    • 按「本次修复实际命中的 11 处」(B-1 的 6 + M-1 的 3 + M-2 的 2)数 ⇒ 确实全红。

    两种读法都不算错,但只有一种是 R1 问的那个问题。commit message 已按准确表述改写,
    不保留「降为 0」这句 —— 交付记录不该比事实宽。剩余 5 处的性质与 R1 记录一致
    (decoder 内部 helper 的冗余守卫,由更外层的 named gate 覆盖),不再单独开轮。

    编排者拒掉的一条 minor

    R2 提出「契约担保面宽于断言面」:installs.json / grants.json / journal 三个持久面
    只断言了存在,没扫 canary。

    核实后判定不成立 —— 审计查的是 ext-package-detail-wiring.cases.ts,
    而这三个面的 canary 扫描在 test-component/package-admission.signed-catalog.cases.ts:232/233/236。
    担保面与断言面是齐的,不加冗余断言。

    合并前本机门(实跑)

    门 结果
    bun test src 3314 pass / 0 fail
    bash scripts/assert-gate-files.sh ✓ 60 个闸门文件在册且真被执行
    bunx tsgo -b 202,与 base 202 相同;改动文件内 0 条
    git diff --check 干净

    合并:PR #734,squash 为 edaab603。

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

Metadata

Metadata

Assignees

Labels

area:securitySecurity or access controlcross-repoRequires coordinated work in multiple repositoriesprio:P1High-value near-term deliverytype:featureNew user or system capability

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions