Repository navigation
[REQ-128][CODE] 绑定 package 授权、认证与事务 admission #713
Description
⚠️ 本期范围裁决(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 采集与写盘。这条证据落地后,才可以把那份契约文档里的
「声明形状」措辞改回「当前有效行为」。不要跳过这一步,也不要提前改那段文档。四、从
#702R1/R2 转记过来的三处覆盖缺口(本票负责补)这三处在
#702时今天不可达,而本票正是让它们上路的那张票:
package-installability.ts的package-payload-unavailable与
package-prerequisite-invalid两个 reason code 只有表、没有行为断言
(换成package-invalid全绿);fetchPackagePayload整函数零覆盖(字节上限 ×2、redirect:"error"、HTTP 状态
四条逐条拆全绿)—— 所有现有用例都注入fetchPayload,默认实现从没被执行过;- preflight 的值校验零覆盖(
scope/attemptId/grants白名单三条改成放行全绿)
—— 本票会真的在这些值上执行。五、从前八票十二轮审计带进来的五条硬要求
- 断言返回值,不要断言源码文本。
- 闸门要测我们真走的那条路(第零问) —— 真实 Catalog 里今天 0 个 package,
夹具必然是构造的,显式标注,不要假装在跑。- 闸门要测生产入口,不是你自己拼的等价链。(
#702R1 Blocker、#695R1 Blocker
都是这一条:闸门在,但它守的不是生产路径。)- 绿的理由必须是你声称的那个。(
#702R2、#695R1 M-3 都栽在这里:
夹具的前后两次结果逐字节相同,于是没有任何断言能区分"用了新的"和"用了旧的"。)- 不要只测最退化的形状。(
alpha-web#101R1:负向用例只用了"整个为空",
于是把判定放松成子串匹配仍全绿 —— 而放松后放行的正是作者最常见的笔误。)六、反过度工程(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
- 定义 opaque attempt record、digest binding、state/reason codes 与 expiry/replay规则。
- 复用现有 capability authorization ledger;不新建第二套同意系统,但 confirmation对象
不再只是 capability名称。 - required prerequisite未ready时 transaction=0;optional未连接进入installed-unavailable。
- Catalog/snapshot/graph/item/capability任一漂移即废弃attempt、清空瞬时secret、回preview。
- renderer只传 attempt ID与瞬时 grants;main重取所有权威事实。
- 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
jinjunnn commentedon Jul 30, 2026
从 #694 R1 审计转记的一条
#694的decodeMcpLocalPayload不校验command[0]是否可执行/在白名单内。审计判定契约票不做执行语义,转到本票(admission 才是执行面权威)。
要求:本票的 admission 时序里,local MCP 的
command必须在授权面被当作执行请求对待(用户可见、进入 capability 授权、不得靠 decoder 的形状校验冒充安全边界)。
#694的 decoder 只保证形状合法,不保证可执行安全 —— 别把它当闸门用。[#696][CODE] secret prerequisite 消费 host Envelope + 删除 renderer 明文回显 (#…
[#695][CODE] 通用详情页呈现 package 兼容、组件与 prerequisite (#730)
jinjunnn commentedon Jul 31, 2026
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%)。问题全在闸门覆盖。
采纳(已回传实现方修复)
#702转记的「preflight 值校验」补在了生产不走的那条路上:测试不传installPackage,而生产恒传 ⇒ package 意图被路由到 admission,永不到达被测解码器;同 commit 收紧的 ATTEMPT_ID 正则对该路径已是死码。生产真正执行的decodePackageAdmissionIntent及其 helper 致残 11 处全部零红。而gate-files.tsv那行写着「守 preflight 值白名单」—— 登记簿在替一道不存在的闸背书attemptId,用户可达路径进不了 admission#732mcp.<name>会被 package 安装静默覆盖fetchPackagePayload最终 URL 校验零红(票面只点名四条,这是第五条,属枚举漏)#695转记的四条本 diff 未动 —— 这是对的(本票被要求勿动#695文件,且属 renderer 半场),但需留痕由#732承接已知不修 + 理由(留痕)
binding的 envelopeDigest / capabilityDigest / itemDigest-payload 三处常量化零红;attempt catalogId 比对与 verdict 复核零红accepted回调只在 compatibleView 前一行触发 ⇒ verdict 复核恒真。给不可达状态造夹具是明确要拒的形状expected.mcp-remote.*)。它的去留是 owner 裁决,本轮不处理 —— 属「不为后续 Phase 预留插槽」直令的边缘品CANONICAL_ENTRY_ID_RE结构上不接受package:前缀,发布侧今天无法签发针对 package 的公示 ⇒ 第零问不通过。属 Phase 2 真实缺口,写回票即可,不在本票开轮MAX_ATTEMPTS=64FIFO 淘汰可被 renderer 冲掉他人挂起的 attemptpreload/types.ts联合类型加宽as any可绕);生产判据在decodePackageAdmissionIntent,审计已实测拒得住package-admission.wiring.test.ts:36把另一测试文件起子进程重跑一遍第零问的判定(审计结论,编排者认可)
真实 Catalog 今天 0 个 package,全部夹具都是构造的 —— 两个 cases 文件头部已显式标注。
signed-catalog cases 用真 Ed25519 + 真
refreshRemoteCatalog,wiring cases 用真注册 IPC,两条各覆盖一半、并集诚实,不按「单条不全」报。
[#713][CODE] package admission:digest 绑定、重取重验与端到端 secret 兑现 (#734)
jinjunnn commentedon Jul 31, 2026
R2 对抗审计记录(Opus,只读)
预算:本票开审前定为 L ≤3;实际用掉 2 轮(R1 全量 → 修 → R2 只判闭合 + 修复 diff)。
R2 结论:
RECOMMEND: MERGE—— R1 全部 finding 判 FIXED,修复 diff 内无新 Blocker/Major。本轮最有价值的一件事:自报数字被独立复核推翻
R2 的派发指令里写死了一句:「实现方的自报数字不作数,你自己数一遍。」
修复方自报「R1 所述 11 处零红存活 → 降为 0」。审计独立复核的结果是 5 处仍零红。
差异不是谎报,是指代不同:
两种读法都不算错,但只有一种是 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 srcbash scripts/assert-gate-files.shbunx tsgo -bgit diff --check合并:PR #734,squash 为
edaab603。[REQ-128][CODE] MCP 密钥版本作为类型化 prepared resource 进 journal 与恢复 (#756)