fix(account): preflight the RPC chain in prepareCreateWithPasskey too (#445) - #448
Merged
Merged
Conversation
…#445) #439/#442 added `assertChainMatchesRpc` to prepareGuardianSetup and submitCreateWithPasskey and missed the third entry point — which happens to be the one that matters most for the goal that PR stated. `prepareCreateWithPasskey` is where `prepareCreateAccountWithPasskey` mints the CREATE_ACCOUNT digest and the WebAuthn challenge the user's device actually signs. Checking only in submitCreateWithPasskey is after the fact: by then the one-time ceremony has been spent on a digest bound to a chain the deploy will not land on. #442 stopped the bad broadcast; it did not stop the wasted ceremony it claimed to. This adds the check where the digest is minted, so a CHAIN_ID/ETH_RPC_URL mismatch costs a 500 instead of a Face ID prompt. One line plus the helper that already exists. Tests (2 new, 13 in this spec, 74 total): across 11155111/10/8453 the digest is minted only after the chain is confirmed, and on mismatch `prepareCreateAccountWithPasskey` is never called — no challenge is handed out. Verified non-vacuous by mutation: removing the new preflight fails 4. Gates: backend type-check + 74 tests + lint + format:check (whole tree, both workspaces) green. Closes #445 Claude-Session: https://claude.ai/code/session_01BxmyQj2A82DfFXu97kKACk
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
clestons
approved these changes
Aug 1, 2026
clestons
left a comment
There was a problem hiding this comment.
Review — #448 (fix(account): preflight the RPC chain in prepareCreateWithPasskey too, closes #445)
结论:APPROVE
补上了我在 #442 review 里标记、Codex PK 独立挖出的那个缺口:prepareCreateWithPasskey 是 prepareCreateAccountWithPasskey(SDK)真正铸造 CREATE_ACCOUNT digest 和用户设备要签的 WebAuthn challenge 的地方,之前只在 submitCreateWithPasskey 检查是"事后"——链不一致的代价之前是"白做一次 Face ID,之后才被拒",现在变成"直接 500,challenge 都不会发出去"。改动很小:一行 assertChainMatchesRpc() 调用,复用 #442 已经建好的 helper,不是新逻辑。
验证
- 本地实跑:
npx jest74/74 全绿(9 suite,新增 2 条:跨 11155111/10/8453 确认 digest 只在链确认之后才铸造;不一致时prepareCreateAccountWithPasskey从未被调用,challenge 根本不会发出去);npx tsc --noEmit干净。 - 读了完整方法确认:新加的检查是函数里第一个异步/链相关操作,前面只有一个同步的
dailyLimit > 0校验,没有任何东西会在检查之前被浪费——放置位置确实是最早可能的点。
PK Summary
- R1 DeepSeek(flash):R1a+R1b 都跑了。R1a 提了个排序疑虑(检查放在
findUserById之前"可能拒绝合法用户"),但它自己套用"反证规则"后自己撤回了,最终 finding 是空的——这是个不错的校准信号,不是瞎猜完就完事,会自己核实站不站得住。R1b 提了(Medium)"链检查在用户查找之前,用户不存在时可能泄露链不一致信息"——我核实这条不成立:assertChainMatchesRpc根本不依赖userId,是纯服务端配置检查,合法用户和不存在的用户在链不一致的情况下会拿到完全相同的错误(都在findUserById之前就被拦下),不存在"用户是否存在"的信息差。 - Codex PK:独立 worktree。确认了我对 R1b 的驳回是对的(补充说明:这条检查最多泄露"全局部署配置错了"这种信息,不构成 R1b 声称的那种泄露);确认放置位置确实是最早点;专门检查了有没有第 4 个账户创建/passkey 入口漏掉——排查了
createWithP256Guardians,确认那是遗留的单次/无 owner WebAuthn 路径,不是另一个 prepare/challenge 铸造点,三个入口(prepareGuardianSetup/prepareCreateWithPasskey/submitCreateWithPasskey)现在确实都盖到了。
自评 — #448
- 轮数:R1 DeepSeek(R1a+R1b 真跑)+ 我独立分析(含验证检查在函数里的绝对位置)+ 本地实跑 74/74 测试+tsc 干净 + Codex PK(1 轮,专门排查第 4 入口)。
- 机械证据:
npx jest74/74、npx tsc --noEmit干净,独立 worktree 真跑。 - DeepSeek flash 评级:3/5 —— 没有直接可行动的真发现,但 R1a 自己提出又自己驳回的过程是个好信号(没有为了显得有用硬凑一条站不住的 finding,输出前有一层自我核实);R1b 那条被 Codex 独立确认不成立。样本 12/20,这条 chain-consistency 系列(#434→#441/#442→#448)是个很好的例子:从"完整 4-round 挖出问题"到"follow-up 一行修复+精准回归测试",链条走得很扎实。
- 与 skill 设计一致,R1a+R1b 双通道 + Codex PK 都跑了。
R1 DeepSeek(R1a+R1b)真跑 + 独立分析(验证检查位置)+ 本地实跑 74/74 测试+tsc 干净 + Codex PK 1 轮(专门排查有无第 4 个漏掉的入口)。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #445
我在 #442 里漏掉的那个入口
#439/#442 给
AccountService加了assertChainMatchesRpc,接在prepareGuardianSetup和submitCreateWithPasskey上,漏了第三个 —— 而恰好是对那个 PR 自己宣称的目标最关键的那个。prepareCreateWithPasskey是prepareCreateAccountWithPasskey铸造 CREATE_ACCOUNT digest 和用户设备实际要签的 WebAuthn challenge 的地方。只在submitCreateWithPasskey检查是事后:到那一步,一次性仪式已经花在一个绑定到错误链的 digest 上了。#442 拦住了错误的广播,但没有拦住它自己声称要拦的「白费一次仪式」。 这个 PR 把校验放到 digest 被铸造的地方,于是 CHAIN_ID/ETH_RPC_URL 不一致的代价从「一次 Face ID 提示 + 之后被拒」变成「一个 500」。
改动是一行 + 已存在的 helper。
测试(新增 2 个,本 spec 13 个,后端共 74)
跨
11155111 / 10 / 8453:digest 只在链确认之后才铸造。不一致时prepareCreateAccountWithPasskey从未被调用 —— challenge 根本不会发出去。变异验证非空测:去掉新加的 preflight 挂 4 个。
Gates
backend type-check + 74 tests + lint +
format:check(整棵树,两个 workspace)全绿。Claude-Session: https://claude.ai/code/session_01BxmyQj2A82DfFXu97kKACk