Summary
Closing the purchase modal with Escape bypasses purchase-state cleanup. A later single-bucket modal can submit the previous fan-out buckets instead of the newly displayed recommendation.
Discovered during independent Claude Fable 5.1 review of LeanerCloud/cloud-commitments-cli#2071. This predates that PR and remains present on main at eac9a62a88d49cbb30abf1dc3943037dd1d58b0b.
Current behavior and reproduction
- Select recommendations forming at least two supported buckets and open Purchase.
- Press Escape without submitting.
- Select a different single-bucket recommendation and open Purchase again.
- Click Send for Approval and inspect the confirmation/request payloads using a fixture backend, not real cloud purchases.
frontend/src/modal.ts:107-110 calls only closeModal(el) on Escape. The explicit close-button handler in frontend/src/app.ts:245-249 additionally calls clearPurchaseModalRecommendations() and clearFanOutBuckets(). openPurchaseModal sets single-row state without clearing the old fan-out state. handleExecutePurchase checks getFanOutBuckets() first, so the old buckets win over the visible single-row selection.
Expected behavior
Every supported close path must discard purchase-specific state. Reopening a modal must submit exactly the selection displayed in that modal.
Proposed fix and verification
Route Escape and explicit close through the same purchase cleanup without changing unrelated modal behavior. Add a regression that opens fan-out, presses Escape through the real keyboard handler, opens a different single-bucket selection, and asserts on the actual intercepted executePurchase payload. Verify it fails before the fix and passes afterward.
Also make the submit router distinguish no fan-out state (null) from an all-skipped fan-out ([]) introduced by LeanerCloud/cloud-commitments-cli#2071. The latter must not fall through into stale single-row state. This second path is currently prevented by a disabled button; do not describe it as a confirmed live exploit.
Evidence and limits
Static review confirmed the control flow against the committed code. This follow-up has not yet been reproduced in a browser. Approval is still required for fan-out submissions, so the demonstrated code path concerns unintended approval requests, not an observed cloud charge.
References: LeanerCloud/cloud-commitments-cli#2071, LeanerCloud/cloud-commitments-cli#1903, LeanerCloud/cloud-commitments-cli#1904, and LeanerCloud/cloud-commitments-cli#2071 (comment).
Severity: high when stale purchase intent is submitted; limited audience and an explicit-close workaround. Kept separate from the in-flight button regression being repaired in LeanerCloud/cloud-commitments-cli#2071.
Summary
Closing the purchase modal with Escape bypasses purchase-state cleanup. A later single-bucket modal can submit the previous fan-out buckets instead of the newly displayed recommendation.
Discovered during independent Claude Fable 5.1 review of LeanerCloud/cloud-commitments-cli#2071. This predates that PR and remains present on main at
eac9a62a88d49cbb30abf1dc3943037dd1d58b0b.Current behavior and reproduction
frontend/src/modal.ts:107-110calls onlycloseModal(el)on Escape. The explicit close-button handler infrontend/src/app.ts:245-249additionally callsclearPurchaseModalRecommendations()andclearFanOutBuckets().openPurchaseModalsets single-row state without clearing the old fan-out state.handleExecutePurchasechecksgetFanOutBuckets()first, so the old buckets win over the visible single-row selection.Expected behavior
Every supported close path must discard purchase-specific state. Reopening a modal must submit exactly the selection displayed in that modal.
Proposed fix and verification
Route Escape and explicit close through the same purchase cleanup without changing unrelated modal behavior. Add a regression that opens fan-out, presses Escape through the real keyboard handler, opens a different single-bucket selection, and asserts on the actual intercepted executePurchase payload. Verify it fails before the fix and passes afterward.
Also make the submit router distinguish no fan-out state (
null) from an all-skipped fan-out ([]) introduced by LeanerCloud/cloud-commitments-cli#2071. The latter must not fall through into stale single-row state. This second path is currently prevented by a disabled button; do not describe it as a confirmed live exploit.Evidence and limits
Static review confirmed the control flow against the committed code. This follow-up has not yet been reproduced in a browser. Approval is still required for fan-out submissions, so the demonstrated code path concerns unintended approval requests, not an observed cloud charge.
References: LeanerCloud/cloud-commitments-cli#2071, LeanerCloud/cloud-commitments-cli#1903, LeanerCloud/cloud-commitments-cli#1904, and LeanerCloud/cloud-commitments-cli#2071 (comment).
Severity: high when stale purchase intent is submitted; limited audience and an explicit-close workaround. Kept separate from the in-flight button regression being repaired in LeanerCloud/cloud-commitments-cli#2071.