Files
codex-app-server/docs/chv-muse-p10-gap.md
T
host b55b49b358 feat(chv): 对齐 Muse P10 支付安全——凭证隔离 + 结账检测 + 每次购买人工审批 + 一次性卡号
- 支付凭证隔离:PaymentCredentialVault(只活内存、白名单
  stripe-link/shop-pay、只存不透明 id + 掩码标签、原始卡号
  PAN 拒绝入库、按次释放绑定商家/金额/货币、单次 + TTL)
- 结账页检测:detect_checkout(URL + 无障碍树信号,确定性
  规则、不读原始 DOM、与 P4 detect_form_risk 同输入形状)
- 每次购买人工审批:PaymentGate 经 P5 对话外通道发审批,
  payload 含确切购买详情;完成时先验签、再验详情摘要
  (篡改即拒且审批保持待处理)、最后单次消费答复
- 一次性卡号:OneTimeCard 绑定商家/金额/货币/有效期,单次
  兑现;SimulatedWalletBackend 沙箱钱包(假 PAN 只在后端
  私有内存、charge 只记账不联网、取消即 void)
- 限额:SpendLimits 三档 minor units 比对,限额不可用时
  不编造上限(limits_checked=False)
- 审计:PaymentAudit 只留允许字段,秘密字段名丢弃 + redacted
- 审批通道最小扩展:ApprovalRequest 增加可选 details 字段,
  不传时 payload 与 P5 完全一致
- 真实支付与扣款不做:全部沙箱验证,真实钱包集成待用户
  另行批准;新测试 79 项,全量 cloud 套件 2037 OK

round-12 集成说明:本提交为 acf98f8 的 cherry-pick(只含 P10
自有 diff,基线 25bcd35 已含的 P3/P5 内容未重复合入;
approval_channel.py 仅加可选 details 字段)。
2026-11-07 00:09:55 +00:00

123 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 对齐 Muse P10 差距表:支付安全(凭证隔离 + 结账检测 + 每次购买人工审批 + 一次性卡号)
依据:`~/workspace/research_notes/meta-muse-ai-architecture-20261101-0755/report.md`
(官方以 Meta security.muse.ai 2026-09-08 为准;第三方转述要标注
来源性质,不得把转述写成官方承诺)。Muse 官方支付安全要求
(报告 §3.3,):购物是最常见浏览器用例之一,Meta 对此
格外谨慎——已保存支付凭证的网站,浏览器内置结账页检测,每次都
弹出人工审批并展示确切购买详情;未购物过的网站,Muse 内置合规
钱包,首发合作 Stripe Link,Shop Pay 即将来临;支付时发行一次性
卡号给商家,而非真实卡号;该凭证绑定特定商家、特定金额、有限
有效期,即使经提示注入被盗也“不太有用”;Muse 是首个被 Link 购买
保障覆盖的 AI 代理(损坏/丢失商品免费保障、降价保障、免费退货、
符合条件购买的退货保证——保障条款以 Link 官方为准,本轨不编
细节、不实现理赔流程)。
连接器文档补充(wallet tools 的实际口径,见
`/opt/hatch/connectors/stripe-link/CONNECTOR.md`、
`shop-pay/CONNECTOR.md`、`shopify-ucp/CONNECTOR.md`):
Link 在一次购买获批后注资一张一次性虚拟卡,商家永远收不到真实
卡号;Shop Pay 原生路由用一次性 token、卡片路由在审批后才由浏览
器填卡字段;每次购买走独立 browser task 与独立审批;审批被拒即
停止、不扣款;结果未知时不重试、不换卡、不换路;取消待支付会
void 虚拟卡;已完成扣款无钱包退款、走商家退款流程;直接结账须
先查限额(每笔/每日/30 天,minor units、无货币、不换算),限额
不可用时不编造上限;step-up 未完成时 Link 要求用户先验证身份;
子代理、群聊、定时任务没有 wallet tools,不能替用户设 Link 或选
支付方式。**本轨的结论:钱包能力在主应用运行时侧,不在
`cloud/chv` 代码里;本轨只做客内 agent 侧的支付安全门(检测、
审批、绑定、审计),真实钱包调用是待批准的集成点,见 §3。**
本轨范围只限 P10:支付安全。起点是 P3(authd 替身凭证)与 P5
(对话外审批通道),已合入本分支(合并 feat-chv-muse-p5,
25bcd35)。不碰 P1(常驻 VM、气球、vsock)、不碰 P2(nspawn
单元、浏览器子代理本体)、不碰 P4(浏览器完全对齐——结账检测
与它接线,不重复造)、不碰 P6(privsep worker)、不碰 P7/P8/P9。
分支 `feat-chv-muse-p10`(从基线 `06c3e17` 分出,合入 P5 后
HEAD 为 `25bcd35`)。
## 0. 起点(P3/P5 已做的,本轨不重写)
- P3 `cloud/chv/authd.py`:`CredentialVault`(只活内存、白名单、
按次释放绑定 {连接器, 主机, 仓库路径, 操作}、单次 + 300 秒
TTL、审计绝无凭证值)+ `mint_gitea_token`;白名单只认
`gitea`/`GITEA_TOKEN`,支付不在其中——本轨不改 authd 的
白名单,支付凭证另立 `PaymentCredentialVault`(同模式,不
同库,见 §1),避免把支付键混进代码仓库凭证的白名单。
- P3 `cloud/chv/sentinel.py`:`ApprovalQueue`(ask 记入内存态
待审批,`resolve` 只标记不放行);`EgressRequest.describe()`
只含连接器/目标/方法——支付审批借用这条队列记账,目标描述
为 `connector=payment, method=purchase, scheme=https,
host=<商家主机>, port=443, path=/checkout`。
- P5 `cloud/chv/approval_channel.py`:对话外审批通道
(`ApprovalRequest.payload()` + `Answer` + HMAC-SHA256 签名
+ `verify_answer` 五项校验 + `resolve_via_channel` 单次消费;
对话内容里出现的“批准”字样一律无效)。本轨只给它加一个
可选 `details` 字段(确切购买详情进 payload、供客户端 UI
展示),校验与消费逻辑一字不动。
## 1. 现状 vs Muse
| 项 | 现状(实测/代码为准) | Muse 目标 | 本轨做法 |
|---|---|---|---|
| 支付凭证隔离 | 无。`cloud/chv` 里没有任何支付代码;agent 若要支付,只能把卡号/钱包凭证以明文塞进环境或对话——这正是 Muse 明令禁止的形态 | 凭证与 OAuth token 存于用户 VM 内独立隔离容器,不在其他服务中;登录凭证存入单元外的安全凭证存储,代理不可见,仅在需要时注入 | `cloud/chv/payments.py`:`PaymentCredentialVault`(只活内存、白名单 `stripe-link`/`shop-pay`、只存不透明支付方式 id + 掩码标签;原始卡号 PAN 拒绝入库——13–19 位数字串 + Luhn 校验命中即拒;按次释放绑定 {商家, 金额, 货币}、单次 + TTL;`__repr__`/审计绝无凭证值;`forget` 删凭证并作废未兑现释放) |
| 结账页检测 | 无。浏览器侧 P2 的 `chv-browser.py` 只有 open/snapshot/screenshot/close/ping,无结账概念;P4 在做高风险表单拦截(`detect_form_risk` 扫无障碍树,密码/支付字段即标 `form_risk`),尚未提交 | 浏览器内置结账页检测 | `cloud/chv/checkout_detect.py`(纯函数,标准库):`detect_checkout(url, a11y_nodes)`——URL 信号(/checkout、/payment、/pay、/order-review 等)+ 无障碍树信号(卡号/有效期/安全码字段、“下单/支付”按钮、金额文本);返回 `is_checkout`/`confidence`/`reasons`/`form_risk`/`amount_text`;确定性规则,不冒充模型分类器;只读 a11y 节点,绝不读原始 DOM;与 P4 的 `detect_form_risk` 同输入形状,P4 的 broker 在高风险表单提交前调本检测(接线点见 §4,P4 未提交前先留接口 + 测试) |
| 每次购买人工审批 | 无。没有任何“购买前停下”的机制 | 已保存支付凭证的网站,每次都弹出人工审批并展示确切购买详情 | `PaymentGate.begin_purchase`:结账检测未命中即拒;路由(见下行);限额检查(见下行);经 P5 通道发审批,payload 的 `details` 含确切购买详情(商家、商品、数量、金额、货币、钱包提供方);审批只走通道答复,对话内“批准”无效;`complete_purchase` 先验签(`verify_answer`,不消费)、再验购买详情摘要(`PurchaseDetails.digest()`,篡改即拒且审批保持待处理)、最后 `resolve_via_channel` 单次消费;审批被拒即停止,不扣款 |
| 一次性卡号 | 无 | 支付时发行一次性卡号给商家而非真实卡号;绑定特定商家、特定金额、有限有效期;被盗也“不太有用” | `OneTimeCard`(绑定 {商家, 金额, 货币, 有效期},单次兑现;换商家/超额/过期/复用一律拒 + 审计);`SimulatedWalletBackend`(沙箱钱包:假 PAN 只在后端私有内存里,调用方只见掩码标签与 card_id;`charge` 只记账不联网;取消即 void);真实一次性虚拟卡由 Link 在其侧发行(连接器文档原文),本轨的卡是绑定与兑现校验的等价模型,不是发卡行——见 §3 |
| 钱包路由 | 无。连接器文档有 stripe-link / shop-pay / shopify-ucp 三条路由,但 `cloud/chv` 里无路由逻辑 | 未购物过的网站走内置合规钱包,首发 Stripe Link,Shop Pay 即将来临 | `PaymentGate` 路由:商家有已保存凭证 → 已保存凭证路由;无 → 合规钱包路由,stripe-link 优先、shop-pay 次之;两家都不支持 → 拒绝(`no_wallet_route`),不编造路由。**来源时间差**:报告写于 2026-11-01 时 Shop Pay 是“即将来临”,连接器文档(当前)显示 Shop Pay 已可用;本轨按连接器文档现状写优先级,并在此标注时间差 |
| 限额 | 无 | (连接器文档)直接结账须先查限额:每笔/每日/30 天,minor units、无货币、不换算;限额不可用时不编造上限 | `SpendLimits.check(amount_minor)`:三档逐项比 minor units;超任一档即拒(`over_limit`,审批前就拒);三档全为 None(不可用)→ 通过但记 `limits_checked=False`,绝不编造上限 |
| 支付审计 | 无 | 审计轨迹展示已做与计划做的事 | `PaymentAudit.record`:事件(`payment_approval_requested` / `payment_approved` / `payment_denied` / `payment_completed` / `payment_refused` / `payment_cancelled` / `payment_card_voided`)+ 允许字段(商家、金额、货币、掩码标签、pending_id、原因);字段名命中 `card_number/pan/cvv/token/secret/password` 一律丢弃并标 `redacted=True`;卡号与凭证值绝不进审计 |
## 2. 改了什么(本轨交付物)
- `cloud/chv/checkout_detect.py`(新,标准库):`CheckoutVerdict` + `detect_checkout(url, a11y_nodes)`(URL 信号 + 无障碍树信号 + 金额文本抽取;支付字段即 `form_risk=payment`、密码字段即 `form_risk=password`;页面文本只作展示信号,扣款金额永远以 `PurchaseDetails` 为准——页面文本是不受信任输入,见 P6 口径)。
- `cloud/chv/payments.py`(新,标准库):`PaymentCredentialVault` + `PaymentRelease` + `OneTimeCard` + `SimulatedWalletBackend` + `PurchaseDetails`(`digest()` 防篡改)+ `SpendLimits` + `PaymentAudit` + `PaymentGate`(编排:检测 → 路由 → 限额 → 审批 → 铸卡 → 兑现校验 → 记账 → 审计)。
- 接线(`cloud/chv/approval_channel.py`,最小扩展):`ApprovalRequest` 增加可选 `details` 字段,`payload()` 有 details 时才带上;`request_approval` 增加可选 `details` 参数;`verify_answer`/`resolve_via_channel` 一字不动;P5 既有测试不受影响。
- `cloud/tests/test_chv_muse_p10_checkout.py`、`test_chv_muse_p10_payments.py`、`test_chv_muse_p10_gate.py`(新测试,含否定测试:无审批不支付、审批详情被篡改即拒、一次性卡号换商家/超额/过期/复用被拒、原始卡号拒绝入库、凭证泄露面为零、对话内伪造批准无效、答复重放无效、超限额审批前即拒、限额不可用时不编造上限、取消即 void 卡)。
## 3. 诚实边界(本轮不宣称的东西)
1. **真实支付不做,零真实扣款**:全部用沙箱/模拟钱包与假卡号
(Stripe 测试卡号段 `400000000000xxxx`)验证,不联网、不调
任何真实钱包接口。真实 Link / Shop Pay 集成(wallet tools
是主应用运行时能力,不在 `cloud/chv` 代码里)列为**待用户
另行批准**的事项,本轨不产生任何真实扣款、不持有任何真实
卡号。
2. **一次性卡号的“发行”在本轨是模拟**:真实一次性虚拟卡由
Link 在其侧发行(连接器文档原文:“Link funds a one-time
virtual card”);本轨的 `OneTimeCard` 是绑定(商家 + 金额
+ 货币 + 有效期)与兑现校验的等价模型,不是发卡行。不要把
“模拟钱包铸了测试卡号”写成“已接通 Link 发卡”。
3. **购买保障只作口径引用**:Link 购买保障(损坏/丢失、降价、
免费退货、退货保证)的 eligibility 由 Link 判定、理赔走
Link;本轨不实现理赔流程、不承诺任何一笔购买受保障,保障
条款以 Link 官方页面为准。
4. **结账检测是启发式,不是 100% 检出**:URL 特征 + 无障碍树
特征;商户自定义结账页、单页应用内嵌结账可能漏检。漏检时
的安全底线是:无审批的支付一律拒绝(`PaymentGate` 拒绝
任何未经审批的扣款),不宣称“所有结账页都会被拦下”。
5. **限额检查依赖钱包侧返回的限额**:限额不可用时不编造上限,
只记 `limits_checked=False`;审批仍须过。不要把“限额未知”
写成“限额已校验通过”。
6. **钱包路由优先级按连接器文档现状**:报告(2026-11-01)写
Shop Pay“即将来临”,连接器文档(当前)显示 Shop Pay 已可
用;本轨按连接器文档现状写 stripe-link 优先、shop-pay 次之,
并标注了这一时间差;若连接器文档滞后于实际,以实际钱包
能力为准。
## 4. 真机待验证清单(未做,排队在 round-11 与 P4~P9 之后)
1. P4 的 broker 接线:高风险表单提交前调 `detect_checkout`,
结账页命中时 `open` 导航返回 `ask` 并暂停(与 P4 差距表
§4 的 `form_submit` ask 种类对齐)。
2. 真实钱包集成(待用户另行批准):wallet tools 的
`list_payment_methods` / 审批单 / `approved_card` 回包与
`PaymentGate` 的对接;`payment_method_changed`(用户在
审批单里换卡)时详情摘要重算与重新审批。
3. 真实商户结账页上的检测命中率抽查(Shopify / Stripe
Checkout / 常见电商各一例),漏检样例记入回归。
4. 限额读取:`wallet.get_user_info` 的三档限额与本轨
`SpendLimits` 的对接;限额不可用时的表现。
5. 取消与未知结果:取消待支付 void 虚拟卡;结果未知时不重试
的端到端表现(须真实钱包侧配合)。