Portal UX: terms disclosure + consent on EVM/Terra sign pages #20

Merged
PlasticDigits merged 2 commits from fix/issue-2-sign-page-terms into main 2026-08-10 02:19:28 +00:00
PlasticDigits commented 2026-08-10 00:51:12 +00:00 (Migrated from gitlab.com)

Summary

Closes #2. EVM and Terra Classic sign pages now show the full latest terms text before wallet interaction, with a consistent checkbox consent gate.

  • Shared web/src/signShell.ts used by /sign/evm and /sign/terra-classic
  • On load: fetch getTermsLatest + getTermsContent once; show version + effective date + scrollable body
  • Consent gate: Connect & sign disabled until terms load successfully and the user checks I have read and agree to the Terms & Conditions
  • Terms rendered via text nodes only (no innerHTML)
  • Clear loading / error states; polite aria-live status region
  • After wallet connect, if signed_latest already, show success without re-signing
  • Docs/invariants: README, skills/portal-sign-disclosure/SKILL.md, gap analysis crosslinks
  • Pin broken ox pkg.pr.new lockfile entry via npm override so installs resolve

Out of scope (per issue): Solana / Telegram sign pages, bot copy, redesign/i18n/multi-wallet.

Checkbox only (not scroll-to-bottom). CTA stays disabled until:

  1. Terms metadata + full text load successfully, and
  2. User checks the consent checkbox.

UI gate is notice/consent UX, not auth — server still requires a valid wallet signature.

Test plan

  • Unit: web/src/signShell.test.ts (XSS-as-text, consent enable, load error)
  • Unit suite: npm run test:web (11 passed)
  • E2E: cd web && npm run test:e2e (12 passed, retested)
  • Manual: /sign/evm?property=cl8y.com and /sign/terra-classic?property=cl8y.com show current terms after sync
  • Manual: CTA disabled until checkbox; then mock/real wallet sign works
  • Manual: long terms scrollable at mobile width
  • Confirm Solana/Telegram pages unchanged

Acceptance criteria checklist

  • EVM + Terra Classic display full latest terms before wallet interaction
  • Version label + effective date visible near terms
  • Terms rendered safely (no HTML injection)
  • User cannot start Connect & sign without consent gate (documented above)
  • Loading and error states for terms fetch are clear
  • EVM e2e passes; sign-page e2e asserts terms visibility + gate
  • Solana/Telegram not required (postponed)
## Summary Closes #2. EVM and Terra Classic sign pages now show the **full latest terms text** before wallet interaction, with a consistent **checkbox consent gate**. - Shared `web/src/signShell.ts` used by `/sign/evm` and `/sign/terra-classic` - On load: fetch `getTermsLatest` + `getTermsContent` once; show version + effective date + scrollable body - **Consent gate:** Connect & sign disabled until terms load successfully **and** the user checks *I have read and agree to the Terms & Conditions* - Terms rendered via text nodes only (no `innerHTML`) - Clear loading / error states; polite `aria-live` status region - After wallet connect, if `signed_latest` already, show success without re-signing - Docs/invariants: README, `skills/portal-sign-disclosure/SKILL.md`, gap analysis crosslinks - Pin broken `ox` pkg.pr.new lockfile entry via npm override so installs resolve **Out of scope (per issue):** Solana / Telegram sign pages, bot copy, redesign/i18n/multi-wallet. ## Consent gate (for reviewers) Checkbox only (not scroll-to-bottom). CTA stays disabled until: 1. Terms metadata + full text load successfully, and 2. User checks the consent checkbox. UI gate is notice/consent UX, not auth — server still requires a valid wallet signature. ## Test plan - [x] Unit: `web/src/signShell.test.ts` (XSS-as-text, consent enable, load error) - [x] Unit suite: `npm run test:web` (11 passed) - [x] E2E: `cd web && npm run test:e2e` (12 passed, retested) - [ ] Manual: `/sign/evm?property=cl8y.com` and `/sign/terra-classic?property=cl8y.com` show current terms after sync - [ ] Manual: CTA disabled until checkbox; then mock/real wallet sign works - [ ] Manual: long terms scrollable at mobile width - [ ] Confirm Solana/Telegram pages unchanged ## Acceptance criteria checklist - [x] EVM + Terra Classic display full latest terms before wallet interaction - [x] Version label + effective date visible near terms - [x] Terms rendered safely (no HTML injection) - [x] User cannot start Connect & sign without consent gate (documented above) - [x] Loading and error states for terms fetch are clear - [x] EVM e2e passes; sign-page e2e asserts terms visibility + gate - [x] Solana/Telegram not required (postponed)
PlasticDigits commented 2026-08-10 01:06:56 +00:00 (Migrated from gitlab.com)

RECOMMEND: ACCEPT

Review summary

Thorough review of !20 against GitLab #2 (acceptance criteria, attack/abuse table, verification checklist), the MR diff (d2e4e74), local npm run test:web (11/11), security audit of changed code, and gap analysis across packages. Issue #2 scope is satisfied. No new medium+ security issues in the diff. Residual repo risks are pre-existing / out of scope for this MR.

Closes checklist vs #2:

Acceptance criterion Verdict
EVM + Terra show full latest terms before wallet interaction Met (web/src/signShell.ts + page wiring)
Version + effective date visible Met
Safe render (no HTML injection) Met (textContent / el; unit covers XSS-as-text)
Consent gate blocks Connect & sign Met (checkbox-only; documented in MR)
Clear loading / error states Met (role="alert" on load failure)
EVM e2e + sign-page terms assertions Met (web/e2e/evm-sign.spec.ts, web/e2e/sign-pages.spec.ts)
Solana / Telegram not required Met (explicitly postponed)

Nice-to-haves from #2: already-signed short-circuit after wallet connect is implemented for EVM/Terra; manual verification items on the MR remain unchecked.


Security audit (MR delta)

No new medium/high/critical findings in changed code.

Area Result
XSS / HTML injection from terms API Mitigated — termsBody.textContent = content; unit asserts <script> stays text (signShell.test.ts)
Query property / app_name injection Mitigated — rendered via text nodes
Consent-gate bypass (DevTools / direct API) Expected — UI gate is notice/consent UX; server still requires valid wallet signature
Access control / privileges No new privileged endpoints; uses existing public terms APIs
Database leaks Not touched by this MR
Rust API / bot Untouched
Prompt injection in skills/docs Docs-only; skill correctly states checkbox ≠ auth
Open redirect_uri Pre-existing (web/src/ui.ts); not weakened
CSP / clickjacking Pre-existing; no new sinks

Attack classes (requested coverage)

Class Applicability
Common DeFi attacks (reentrancy, flash loan, sandwich, etc.) N/A — no contracts / no on-chain settlement
Common smart-contract attacks N/A — none in repo
Oracle manipulation / tokenomic / economic attacks N/A — off-chain clickwrap only
Gas optimizations N/A — no on-chain gas surface
Database leak vectors N/A for this MR — no schema/query changes
Common web attacks on portal Covered above; XSS mitigated; redirect residual pre-existing

Pre-existing critical paths (do not block #2; still relevant ops risk)

Documented in gaps/GAP_1786322222.md and not introduced by this MR:

  1. Telegram WebApp HMAC incorrect
  2. Solana verify envelope ≠ portal signMessage
  3. Terra Classic Keplr ADR-036 / signArbitrary mismatch (disclosure UX now works; end-to-end Terra sign remains broken)

Gap analysis (MR-scoped)

Features / UX

  • Closed by !20: on-page full terms + version/date + checkbox consent for /sign/evm and /sign/terra-classic; shared renderSignShell; fetch-once; a11y live region + labeled terms region; scrollable body CSS.
  • Still open (out of scope per #2): Solana/Telegram disclosure; scroll-to-bottom proof-of-read (checkbox only — allowed by issue); home hub still metadata-only; no download/print; content_sha256 not shown; mobile wallet extensions still weak.

Testing

  • Present (good): unit XSS + consent + load error; e2e terms visibility + gate on EVM/Terra; e2e EVM mock-wallet accept after consent. Local web unit 11/11 green.
  • Gaps (nice-to-have, not blockers): e2e terms API failure; e2e home→sign navigation; e2e malicious property; e2e already-signed short-circuit; mobile viewport long-terms; Terra full-stack wallet e2e (blocked by pre-existing Terra verify bug).

Packages

Package MR impact
web/ Primary implementation
packages/cl8y-clickwrap/ README cross-link only
api/, bot/ Untouched
Root ox override (supply-chain fix for broken pkg.pr.new pin), gap analysis, skill

DRY / readability / best practices

  • Good extraction of shared shell; invariants documented in signShell.ts + skills/portal-sign-disclosure/SKILL.md.
  • Minor a11y inconsistency: wallet onSign errors set className="error" but not role="alert" (load failures do).

CI

  • MR pipeline (2745891423) passed but only ran lint:gitleaks (other jobs lack merge_request_event rules in .gitlab-ci.yml).
  • Branch push pipeline failed lint:rust / lint:rust-bot on pre-existing rustfmt (no Rust changes in this MR); web/e2e jobs skipped due to stage failure.
  • Recommend fixing CI rules / rustfmt on main separately so web tests actually gate MRs — not a reason to reject !20’s web change.

Verdict

ACCEPT for merge against #2. Implementation matches acceptance criteria, safe rendering is tested, consent gate is documented, and no security regressions were introduced. Follow-ups (Solana/Telegram disclosure, open-redirect allowlist, Terra/Solana/Telegram verify fixes, CI job rules) belong in separate issues/MRs.

RECOMMEND: ACCEPT ## Review summary Thorough review of !20 against GitLab #2 (acceptance criteria, attack/abuse table, verification checklist), the MR diff (`d2e4e74`), local `npm run test:web` (11/11), security audit of changed code, and gap analysis across packages. **Issue #2 scope is satisfied.** No new medium+ security issues in the diff. Residual repo risks are pre-existing / out of scope for this MR. Closes checklist vs #2: | Acceptance criterion | Verdict | |---|---| | EVM + Terra show full latest terms before wallet interaction | Met (`web/src/signShell.ts` + page wiring) | | Version + effective date visible | Met | | Safe render (no HTML injection) | Met (`textContent` / `el`; unit covers XSS-as-text) | | Consent gate blocks Connect & sign | Met (checkbox-only; documented in MR) | | Clear loading / error states | Met (`role="alert"` on load failure) | | EVM e2e + sign-page terms assertions | Met (`web/e2e/evm-sign.spec.ts`, `web/e2e/sign-pages.spec.ts`) | | Solana / Telegram not required | Met (explicitly postponed) | Nice-to-haves from #2: already-signed short-circuit after wallet connect is implemented for EVM/Terra; manual verification items on the MR remain unchecked. --- ## Security audit (MR delta) **No new medium/high/critical findings in changed code.** | Area | Result | |---|---| | XSS / HTML injection from terms API | Mitigated — `termsBody.textContent = content`; unit asserts `<script>` stays text (`signShell.test.ts`) | | Query `property` / `app_name` injection | Mitigated — rendered via text nodes | | Consent-gate bypass (DevTools / direct API) | Expected — UI gate is notice/consent UX; server still requires valid wallet signature | | Access control / privileges | No new privileged endpoints; uses existing public terms APIs | | Database leaks | Not touched by this MR | | Rust API / bot | Untouched | | Prompt injection in skills/docs | Docs-only; skill correctly states checkbox ≠ auth | | Open `redirect_uri` | **Pre-existing** (`web/src/ui.ts`); not weakened | | CSP / clickjacking | **Pre-existing**; no new sinks | ### Attack classes (requested coverage) | Class | Applicability | |---|---| | Common DeFi attacks (reentrancy, flash loan, sandwich, etc.) | **N/A** — no contracts / no on-chain settlement | | Common smart-contract attacks | **N/A** — none in repo | | Oracle manipulation / tokenomic / economic attacks | **N/A** — off-chain clickwrap only | | Gas optimizations | **N/A** — no on-chain gas surface | | Database leak vectors | **N/A for this MR** — no schema/query changes | | Common web attacks on portal | Covered above; XSS mitigated; redirect residual pre-existing | ### Pre-existing critical paths (do not block #2; still relevant ops risk) Documented in `gaps/GAP_1786322222.md` and **not introduced by this MR**: 1. Telegram WebApp HMAC incorrect 2. Solana verify envelope ≠ portal `signMessage` 3. Terra Classic Keplr ADR-036 / `signArbitrary` mismatch (disclosure UX now works; end-to-end Terra sign remains broken) --- ## Gap analysis (MR-scoped) ### Features / UX - **Closed by !20:** on-page full terms + version/date + checkbox consent for `/sign/evm` and `/sign/terra-classic`; shared `renderSignShell`; fetch-once; a11y live region + labeled terms region; scrollable body CSS. - **Still open (out of scope per #2):** Solana/Telegram disclosure; scroll-to-bottom proof-of-read (checkbox only — allowed by issue); home hub still metadata-only; no download/print; `content_sha256` not shown; mobile wallet extensions still weak. ### Testing - **Present (good):** unit XSS + consent + load error; e2e terms visibility + gate on EVM/Terra; e2e EVM mock-wallet accept after consent. Local web unit 11/11 green. - **Gaps (nice-to-have, not blockers):** e2e terms API failure; e2e home→sign navigation; e2e malicious `property`; e2e already-signed short-circuit; mobile viewport long-terms; Terra full-stack wallet e2e (blocked by pre-existing Terra verify bug). ### Packages | Package | MR impact | |---|---| | `web/` | Primary implementation | | `packages/cl8y-clickwrap/` | README cross-link only | | `api/`, `bot/` | Untouched | | Root | `ox` override (supply-chain fix for broken pkg.pr.new pin), gap analysis, skill | ### DRY / readability / best practices - Good extraction of shared shell; invariants documented in `signShell.ts` + `skills/portal-sign-disclosure/SKILL.md`. - Minor a11y inconsistency: wallet `onSign` errors set `className="error"` but not `role="alert"` (load failures do). ### CI - MR pipeline (`2745891423`) **passed** but only ran `lint:gitleaks` (other jobs lack `merge_request_event` rules in `.gitlab-ci.yml`). - Branch push pipeline failed `lint:rust` / `lint:rust-bot` on **pre-existing rustfmt** (no Rust changes in this MR); web/e2e jobs skipped due to stage failure. - Recommend fixing CI rules / rustfmt on `main` separately so web tests actually gate MRs — not a reason to reject !20’s web change. --- ## Verdict **ACCEPT** for merge against #2. Implementation matches acceptance criteria, safe rendering is tested, consent gate is documented, and no security regressions were introduced. Follow-ups (Solana/Telegram disclosure, open-redirect allowlist, Terra/Solana/Telegram verify fixes, CI job rules) belong in separate issues/MRs.
PlasticDigits commented 2026-08-10 01:54:11 +00:00 (Migrated from gitlab.com)

mentioned in issue #2

mentioned in issue #2
PlasticDigits commented 2026-08-10 02:01:26 +00:00 (Migrated from gitlab.com)

added 6 commits

  • 74a80e65 - Fix Terra Classic Keplr signing with ADR-036 verify.
  • 62d8d76c - Harden Terra ADR-036 CI, abuse tests, and portal invariants.
  • 6ec0af69 - Stabilize Playwright e2e startup on CI.
  • 16a75c63 - Harden portal/API ops security for issue #3.
  • f5c490ff - Fix integration test Config after rebase onto Terra ADR-036.
  • 993757b2 - Show full terms and consent gate on EVM/Terra sign pages.

Compare with previous version

added 6 commits <ul><li>74a80e65 - Fix Terra Classic Keplr signing with ADR-036 verify.</li><li>62d8d76c - Harden Terra ADR-036 CI, abuse tests, and portal invariants.</li><li>6ec0af69 - Stabilize Playwright e2e startup on CI.</li><li>16a75c63 - Harden portal/API ops security for issue #3.</li><li>f5c490ff - Fix integration test Config after rebase onto Terra ADR-036.</li><li>993757b2 - Show full terms and consent gate on EVM/Terra sign pages.</li></ul> [Compare with previous version](/PlasticDigits/cl8y-ecosystem-legal/-/merge_requests/2/diffs?diff_id=1951271867&start_sha=d2e4e743002ec29739a800f2cccb0e576ad311bc)
PlasticDigits commented 2026-08-10 02:05:10 +00:00 (Migrated from gitlab.com)

added 5 commits

  • 03849d3b - 1 commit from branch main
  • b0725585 - Harden portal/API ops security for issue #3.
  • 8294413c - Fix integration test Config after rebase onto Terra ADR-036.
  • c5fdf1a3 - Remove unused PrehashSigner import in integration tests.
  • 0fe6e398 - Show full terms and consent gate on EVM/Terra sign pages.

Compare with previous version

added 5 commits <ul><li>03849d3b - 1 commit from branch <code>main</code></li><li>b0725585 - Harden portal/API ops security for issue #3.</li><li>8294413c - Fix integration test Config after rebase onto Terra ADR-036.</li><li>c5fdf1a3 - Remove unused PrehashSigner import in integration tests.</li><li>0fe6e398 - Show full terms and consent gate on EVM/Terra sign pages.</li></ul> [Compare with previous version](/PlasticDigits/cl8y-ecosystem-legal/-/merge_requests/2/diffs?diff_id=1951273637&start_sha=993757b2a04d16bf8a3953813ee0265a2e640b7a)
PlasticDigits commented 2026-08-10 02:15:16 +00:00 (Migrated from gitlab.com)

added 1 commit

  • aa457dcd - Update Terra e2e for consent gate after sign-shell rebase.

Compare with previous version

added 1 commit <ul><li>aa457dcd - Update Terra e2e for consent gate after sign-shell rebase.</li></ul> [Compare with previous version](/PlasticDigits/cl8y-ecosystem-legal/-/merge_requests/2/diffs?diff_id=1951278638&start_sha=0fe6e398d13031b78a3585b3571712fe0d90407b)
PlasticDigits commented 2026-08-10 02:19:28 +00:00 (Migrated from gitlab.com)

mentioned in commit b9f4a29fbb

mentioned in commit b9f4a29fbbdf09b39d38e30fc7a9ecb2f391dfb1
PlasticDigits (Migrated from gitlab.com) merged commit b9f4a29fbb into main 2026-08-10 02:19:28 +00:00
PlasticDigits commented 2026-08-10 02:19:30 +00:00 (Migrated from gitlab.com)

Merged to main via git merge (tip aa457dc) because CI quota blocked glab mr merge. Includes Terra e2e consent-gate fix. Commit is on main.

Merged to `main` via git merge (tip `aa457dc`) because CI quota blocked `glab mr merge`. Includes Terra e2e consent-gate fix. Commit is on `main`.
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
code/cl8y-ecosystem-legal!20
No description provided.