feat(frontend): gate mutative actions with CL8Y Legal clickwrap (#134) #156
No reviewers
Labels
No labels
agent:implement
agent:ready
backend
bug
cannot-reproduce
confirmed
desktop
docs
documentation
duplicate
enhancement
feature
frontend
good first issue
help wanted
high-risk
in-review
invalid
mobile
needs-triage
P0-critical
P1-high
P2-medium
P3-low
qa
QA
question
ready
report
responsive
security
security-escalate
smart-contract
solana
tablet
test-pass
ux
wallet-issue
wallet:keplr
wallet:metamask
wallet:station
wallet:walletconnect
wontfix
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
code/cl8y-bridge-monorepo!156
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/gl-134-legal-clickwrap"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
@plasticdigits/cl8y-clickwrap@0.1.1(GitLab npm project82547916, not npmjs) so mutative deposit / hash-submit / Solana execute CTAs require Legalsigned_latestfor propertybridge.cl8y.com.TransferForm, destination on Transfer Status / Verify / Solana execute). Header Connect controls stay outsideTermsGate(GL-137).window.location.hrefonly.Related #134 (kept open: Legal ops, portal QA, production deploy gate)
Legal ops (blocked outside this repo)
Merge is code-complete but production gating needs Legal confirmation:
bridge.cl8y.comregisteredCORS_ORIGINSincludeshttps://bridge.cl8y.com(andhttp://localhost:3000for local)VITE_REDIRECT_URI_ALLOWLISTincludeshttps://bridge.cl8y.com/sign/solanaproduction-ready (envelope vs API verify) — not skipped; Solana stays gated untilsigned_latestSolana interim policy
Same
Network: Solanagate as EVM/Terra. Legal/sign/solanais not production-ready; users remain blocked (fail closed). Do not silently skip Solana.Test plan
npx vitest run(SKIP_INTEGRATION=true) — 672 tests, including new clickwrap unit tests (run twice)npx tsc --noEmitand ESLint on changed filese2e/clickwrap.spec.tsagainst local Vite (Legal API mocked; unsigned / error / signed)signed_latest: true)Docs
docs/FRONTEND_BRIDGE_INVARIANTS.mdINV-FE-CLICKWRAP-1skills/agent-frontend-clickwrap.md(cross-linked from invariants +agent-frontend-bridge-chains.md)packages/frontend/README.md,docs/frontend.md,.env.exampleRECOMMEND: FIX
Reviewed against #134 (P1, security-escalate). The code fails closed correctly and does not silently skip Solana. Merge should wait on Legal ops and a few test/hygiene fixes — this is not a BLOCK (no prompt injection, no fail-open acceptance, no on-chain auth bypass).
Security
redirect_uriwindow.location.hrefonly. Portal allowlist is still the enforcer.sameOriginClickwrapRedirectUridoes not actually check origin — it only requires http(s) and no userinfo. Rename or asserturl.origin === window.location.originso a future caller cannot passhttps://evil.example.dangerouslySetInnerHTMLsigned_latestfrom URL / localStorageallowsMutativerequiresaccount && isSigned && !error;requireSignedLatestthrowsstatus-error.EVM/TerraClassic/Solana).bridge.cl8y.com; not derived from hostname.http:overrides. HTTPS env override is still a deploy-review item..npmrcpins@plasticdigits:registryto GitLab project82547916. Lockfile resolves0.1.1from that registry. Integrity is sha1, not sha512 — regenerate the lockfile hash. Caret^0.1.1is OK if CI always usesnpm ci.useAutoWithdrawSubmitblocks on loading/error/unsigned and callsrequireSignedLatestbefore submit.signed_latest(documented).#134 acceptance criteria
@plasticdigits/cl8y-clickwrap@0.1.1)bridge.cl8y.come2e/clickwrap.spec.tsnot run; EVM/Solana/status-page/account-switch not in e2eRequired before merge
bridge.cl8y.comregistered; APICORS_ORIGINSincludeshttps://bridge.cl8y.com(and localhost if needed); portalVITE_REDIRECT_URI_ALLOWLISTincludes the bridge. Without this, fail-closed code disables all production deposits. There is no feature flag./sign/solanapolicy with product (blocked until signed is OK; do not skip).e2e/clickwrap.spec.ts(unsigned / error / signed) and keep default mocksigned_latest: truefor the rest of the suite.sameOriginClickwrapRedirectUrito same-origin (or stop claiming it is).Testing gaps
allowsMutativedoes not include!loading; submit-timerequireSignedLatestand TermsGatefallbackmitigate this. Still worth asserting loading does not enable the CTA.Do not treat UI gating as cryptographic authorization — the MR documents that correctly. Fix the ops + origin helper + e2e run, then this is an ACCEPT.
mentioned in merge request !159
Follow-up (second pass): still RECOMMEND: FIX. Additional items not in the first note:
Medium — Solana execute swallows
requireSignedLatesterrors.SolanaRecipientExecutePanelcatch { return }after the Legal re-check. Fail-closed is right (execute does not run); silent no-op is not. If TermsGate still shows the button (stale signed UI / race / DevTools), the click does nothing with no error. Surface it likeTransferForm(setError/ panel error).Low — Fix-transfer
BridgeTermsGateuses recorded dest, not fix dest.TransferStatusPagewraps the Fix button withdestClickwrapKindfrom the recorded dest.handleFixTransferalready re-checksfix.fixParams.destType. If those differ, the UI can show Fix while the fix-chain wallet is unsigned (click then fails) or hide Fix while that wallet is already signed. Wrap Fix withbridgeChainKindFromConfigType(fix.fixParams.destType).Nits: Multiple
TermsGatemounts on Transfer Status (auto-submit hook + several wrappers) duplicate/signatures/statuson load/focus. Prefer exact@plasticdigits/cl8y-clickwrap@0.1.1over^0.1.1. InPROD, allowlistapi.terms.cl8y.com/terms.cl8y.cominstead of anyhttps://override.Playwright remains the merge bar: run
e2e/clickwrap.spec.tsand the transfer suite; extend beyond Terra source deposit (unsigned EVM, dest auto-submit blocked, Solana execute gated). Legal ops stay a production-deploy gate: this SPA fails closed if CORS/property/allowlist are missing — do not ship to https://bridge.cl8y.com until Legal confirms those boxes.mentioned in commit
9bc7d3951aadded 1 commit
9bc7d395- fix(frontend): harden Legal clickwrap gate from MR !156 review (#134)Compare with previous version
Review follow-up (commit
9bc7d39)Hardened the GL-134 clickwrap gate from the two RECOMMEND: FIX notes. Legal ops is still out of this repo.
Completed from review
redirect_urisameOriginClickwrapRedirectUri(href, expectedOrigin)now assertsurl.origin === window.location.origin(http(s) + no-userinfo is not enough).VITE_CLICKWRAP_*overrides are accepted only when origin ishttps://api.terms.cl8y.com/https://terms.cl8y.com. Other https hosts and allhttp:are ignored.@plasticdigits/cl8y-clickwrapis exact0.1.1(no caret). Lockfile integrity issha512-…of the GitLab tarball.allowsMutativewhile loading!loadingso a staleisSignedcannot enable a CTA mid-fetch.requireSignedLatestfailures surface on the panel (role="alert"), same as TransferForm / Verify.bridgeChainKindFromConfigType(fix.fixParams.destType), not recorded dest.brokenTransferFixnow setsdestType: 'solana'when the correct dest is Solana (execute still unsupported in the handler).getSignatureStatus(no result cache; submit re-check still hits the network).e2e/clickwrap.spec.tstwice (5 workers). Added unsigned EVM source (swap to Anvil). Default mock remainssigned_latest: true.skills/agent-frontend-clickwrap.md,skills/agent-frontend-bridge-chains.md, README,docs/frontend.md,.env.example.Tests run
SKIP_INTEGRATION=true npx vitest run— 676 passed (full suite + focused retest)npx tsc --noEmit+ ESLint on changed files (also pre-commit)e2e/clickwrap.spec.ts— 5/5 passed twice: disconnected, unsigned Terra, API error, signed Terra, unsigned EVMNot done (intentional)
bridge.cl8y.com, APICORS_ORIGINS, portalVITE_REDIRECT_URI_ALLOWLIST, Solana/sign/solanaproduction-ready) — still blocked outside this repo. Do not ship to https://bridge.cl8y.com until those boxes are confirmed. Fail-closed code will disable production deposits without them.networkidle(on-chain lookup poll); there is no simulated Solana wallet; wagmi mock has extra accounts but no switch UI. Dest/Solana execute remain covered by unit + submit-timerequireSignedLatest. Full-path e2e belongs on the verification project with a real deposited/approved transfer.Checklist to verify
submit-transferVITE_CLICKWRAP_API_BASE_URL=https://evil.exampleis ignoredchanged the description
changed the description
mentioned in commit
a5a7620ccfmentioned in issue #134
mentioned in issue #137