fix(protocol): centralize safe HTML rendering and versioned cross-language API contracts #43
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
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?
Scope and source
Priority: P1. Gap coverage: GAP-014, GAP-048, GAP-049. Derived from GAP_1791945837.md, reviewed code commit
f8dbe93df953a78b17cb3304b717dcf84c4cd517. Currente476a68da09645c2308cbf47b90211fcaffcb7afadds only the report, so implementation findings remain applicable. Report evidence is local reproduction/source inspection unless explicitly proposed; no production exploit or current live-readiness claim.Coverage and sequencing: gap backlog tracker. Workstream 08. Dependencies: 05.
Current codebase
The client sanitizer unwraps disallowed parents without sanitizing descendants; no-DOM fallback uses regex. Sections/comments/analysis render raw HTML, although server ammonia protects normal current writes. Rust/TS payloads, section rules and address/amount rules are duplicated; link/entity serialization and JS character-versus-UTF8 byte limits drift. Numeric entity parsing can throw and UI threshold is hardcoded. DTOs/errors/statuses are loose and OpenAPI is a handwritten stub; large handlers duplicate transaction/signing logic.
Why this implementation is needed
Display boundaries need protection against untrusted/legacy responses while signing must stay deterministic. Shared contracts and transactional services reduce security-sensitive drift and make capabilities/new lifecycle features reviewable.
Relevant files
frontend/src/utils/sanitizeProposalHtml.tsfrontend/src/utils/proposalSections.tsfrontend/src/utils/votingPayload.tsfrontend/src/components/proposal/ProposalSectionFields.tsxfrontend/src/pages/VoteDetailPage.tsxfrontend/src/services/operatorVoting.tsoperator-voting/src/html.rsoperator-voting/src/proposal_sections.rsoperator-voting/src/payload.rsoperator-voting/src/api.rsoperator-voting/src/db.rsoperator-voting/src/error.rsRecommended solution
Use a maintained sanitizer at one safe-render component for every HTML display; fail safely without a DOM. Keep render sanitation separate from authoritative server canonicalization and never silently rewrite signed bytes. Define versioned schemas/shared golden fixtures and generate typed API DTOs/OpenAPI from real handlers, including endpoints/errors/responses/capabilities/thresholds. Match UTF8 bytes with TextEncoder and safe consistent entity decoding. Factor domain mutations into transactional services and typed chain/state/action enums; centralize frontend signing/mutations and reusable section/address conversions while preserving chain-specific checks. Avoid refactoring the wallet port merely to reduce line counts.
Constraints and guardrails
Preserve standalone offchain advisory voting, no new smart contracts or archive-node dependency, BSC as a core electorate, raw 18-decimal integer balances and separate Terra/BSC address identities. Keep Legal fail-closed transactional UI and the proven DEX Terra/Bridge EVM connectors; no browser BSC balance RPC, seed/key prompt or embedded admin/model credential. API must retain a restricted role separate from the ledger writer. Freeze at actual opening, never at draft creation or a client-supplied height. Agents own design, implementation, review and evidence under repository guidance; do not shift approvals/tests to the human or invent PASS evidence.
Acceptance criteria
All body/section/comment/analysis displays neutralize active descendants and unsafe URL schemes. Shared canonical hashes/signing vectors agree for Unicode, links, entities and multiline limits. Metadata reflects configured proposal threshold. Generated OpenAPI and frontend contracts match actual success/error shapes, and transactional invariants from 05 remain intact.
Test plan: supported paths, failure paths and concurrency
Nested wrapper, legacy body, structured sections, comments and analysis through actual rendering. Empty/valid/malformed HTML, SVG/MathML, numeric entities inside/outside Unicode range, emoji, combining characters, links and byte-boundary limits. Browser/server canonical golden tests, no-DOM behavior and protocol-version compatibility. Contract-test every route/status/error/capability with Terra and BSC payloads; rerun lifecycle/concurrency after extraction.
Test plan: attack, hack and abuse vectors
Event handlers, javascript/data URLs, malformed nesting, SVG/MathML and mutation XSS; DOM clobbering, hostile imported/API content, entity exceptions and huge inputs; forged status/chain values, canonicalization ambiguity and changed payload versions. Current probe is not proof of stored XSS through valid server-sanitized writes; verify actual attack reachability.
Verification and completion criteria
Attach render attack corpus results, cross-language golden fixtures and generated OpenAPI contract test output. Confirm signatures of unchanged canonical messages remain valid and coverage includes every HTML entry point; server sanitizer remains authoritative.
Record commit and pinned toolchain, commands/results and sanitized artifacts. Relevant Rust changes require
cargo test --workspace --libplus isolated Postgres integration (explicit guarded LEDGER_TEST_DATABASE_URL); frontend changes requirenpm testand production build plus relevant browser cases. Run secret scanning for new fixtures/config. Exercise fresh/upgrade migrations when schema/grants change; report skipped/live-only checks explicitly.Implementation status after main merge PR #53, commit
cd07f00f32f04ad02f8b760ed34588fd9645919e:Services are paused while existing storage is audited and the corrected pinned release is prepared. No production wallet transaction or hardware-wallet/Legal QA is claimed. This is an interim report; the issue remains open and the final comment will map the original criteria to actual live results and any unverified cases.
Implemented safe rendering, authoritative canonical signing and generated API contracts through PRs #53/#54 and subsequent followups. Accepted release:
5cd9363307dc1607de952ac3243193c1a16304c7at 2026-10-14T14:07:21Z, after PR #59 merged with CI passing. Verification evidence, frontend documentation and invariants/agent guidance.Verification: Rust 1.88.0, locked dependencies and fresh guarded PostgreSQL16/SCRAM infrastructure; command
CARGO_BUILD_JOBS=2 scripts/verify-release.sh. Release13 passed 74 unit/supervisor tests, 121 integration tests (66 ledger hardening), 245 schema-validated API responses and 146 frontend tests, plus production build, both nginx checks and Gitleaks. Separate Chromium evidence: 11 UI regressions, nine hostile-rendering tests and one real second-origin protocol test, using Node24.21.0, Playwright1.62.1 and Chromium151.0.7922.34.Release12 was a failed historical run; the expected late-supervisor-exit fixture race was corrected without weakening financial assertions, then passed four fresh-database repetitions and the full release13 rerun. Earlier public sampling recorded one unexplained 503 at 14:00:58Z (44/45); the next check returned 200. It is retained in final live evidence rather than counted as a pass. The subsequent two extended public windows were fully green (90/90), alongside all 1080 private observations; acceptance is based on those final windows and does not claim transient failures are impossible.
The hostile Chromium corpus uses isolated imported-response fixtures and simulated compose access through the Legal test hatch. It proves reachable display protection, not stored XSS through valid server-sanitized writes or real-wallet/Legal QA. Frontend production gating and the existing wallet stacks remain intact. No exhaustive route×status Cartesian matrix or funded-holder/hardware/mobile-wallet coverage is claimed. All required issue criteria are checked against the evidence above; no mandatory verification item remains open.
Final evidence is published through PR #60. All mandatory body/comment criteria are satisfied by the checklist above; closing this issue.