- React: response.ok checks (address, transaction, search); block number validation; stable Table keys; API modules (addresses, transactions, blocks normalizer) - SPA: escapeHtml/safe URLs/onclick; getRpcUrl in rpcCall; cancel blocks rAF on view change; named constants; hash route decode - SPA: createBlockCardHtml + normalizeBlockDisplay (L1); DEBUG console gating; aria-live for errors; token/block/tx detail escaping - Docs: FRONTEND_REVIEW.md, FRONTEND_TASKS_AND_REVIEW.md; favicons; .gitignore *.tsbuildinfo Co-authored-by: Cursor <cursoragent@cursor.com>
13 KiB
13 KiB
Frontend: Full Task List (Critical → Optional) + Detail Review
Full parallel mode: Tasks in the same tier can be executed in parallel by different owners. Dependencies are called out where a later task requires an earlier one.
Quick reference (all tasks)
| Priority | ID | One-line description |
|---|---|---|
| Critical | C1 | Address page: check response.ok before setting state |
| Critical | C2 | Transaction detail page: check response.ok before setting state |
| Critical | C3 | Search page: check response.ok before setting results |
| Critical | C4 | Blocks API: confirm/normalize backend response shape { data } |
| High | H1 | SPA: escapeHtml(shortenHash(...)) and escape API text in all innerHTML |
| High | H2 | SPA: safe breadcrumb/href (encode identifier, contract, addr in URLs) |
| High | H3 | SPA: escape dynamic values in all onclick attributes |
| High | H4 | SPA: use getRpcUrl() in rpcCall() for failover |
| High | H5 | SPA: cancel blocks scroll requestAnimationFrame on view change |
| Medium | M1 | React: validate block number in blocks/[number].tsx |
| Medium | M2 | React: stable list keys (Table + search results) |
| Medium | M3 | SPA: named constants for timeouts/retries |
| Medium | M4 | Blocks API: normalizer for backend shape (if needed after C4) |
| Low | L1 | SPA: extract shared block card markup helper |
| Low | L2 | SPA: DEBUG flag for console logs |
| Low | L3 | A11y: live region for error messages |
| Low | L4 | React: shared API modules for transactions/addresses |
Part 1: Task List (Critical → Optional)
CRITICAL (P1) – Fix first; blocks correct behavior or security
| ID | Task | Owner / parallel | Notes |
|---|---|---|---|
| C1 | Address page: check response.ok – In src/pages/addresses/[address].tsx, before response.json() and setAddressInfo(data.data), check response.ok. On non-2xx: do not set state; set error/empty and optionally setLoading(false). |
1 | Prevents treating error body as address data. |
| C2 | Transaction detail page: check response.ok – In src/pages/transactions/[hash].tsx, same pattern: check response.ok before parsing and setTransaction(data.data). Handle 404/5xx without setting transaction state. |
1 | Same bug as C1. |
| C3 | Search page: check response.ok – In src/pages/search/index.tsx, check response.ok before setResults(data.results || []). On failure, set empty results and optionally show a message. |
1 | Avoids showing error payload as results. |
| C4 | Blocks API response shape – Confirm backend for /api/v1/blocks returns { data: Block[] } (or document contract). If it returns { items: [] } or similar, add a normalizer in src/services/api/blocks.ts (e.g. response.data = response.items ?? response.data) so response.data is always an array for list. |
1 | Prevents blocks.map / recentBlocks.map runtime errors. |
Parallel: C1, C2, C3, C4 can all be done in parallel.
HIGH (P2) – Security and correctness; no known exploit but reduces risk
| ID | Task | Owner / parallel | Notes |
|---|---|---|---|
| H1 | SPA: escape all shortenHash(...) in innerHTML – Every place that assigns to innerHTML and includes `shortenHash(hash |
address | from |
| H2 | SPA: safe breadcrumb and href attributes – In updateBreadcrumb, ensure identifier in href="#/address/' + identifier + '" cannot break the attribute. Use a safe encoding (e.g. encodeURIComponent(identifier) for the path segment, or ensure identifier is validated hex address). Same for token/nft breadcrumb links. |
1 | Prevents attribute breakout. |
| H3 | SPA: safe onclick attribute values – Every onclick="showAddressDetail('...')" / showBlockDetail / showTransactionDetail that injects a dynamic value (address, hash, block number) must use an escaped value so a quote in the value cannot break the attribute. Use escapeHtml(address) (or equivalent) for the value inside the quoted string. Applies to all dynamic onclick handlers in index.html (see grep list: 3108, 3144, 3526, 3543, 3638, 3746, 3774, 3832, 3887, 3905, 3935, 3943, and template literals with ${address}, ${hash}, ${from}, ${to}, etc.). |
1 | Prevents attribute injection / XSS. |
| H4 | RPC failover in rpcCall – In public/index.html, change rpcCall to use getRpcUrl() (await) instead of a single RPC_IP/RPC_FQDN, so RPC calls benefit from the same health-check and failover as elsewhere. |
1 | Consistency and resilience. |
| H5 | Blocks scroll: cancel requestAnimationFrame on view change – When leaving the blocks view (or when loadLatestBlocks runs again), cancel the animation loop. Store the requestAnimationFrame id (e.g. in a variable or on scrollContainer.dataset) and call cancelAnimationFrame(id) when switching view or before re-running the block list render. |
1 | Avoids leaking animation loop and unnecessary work. |
Parallel: H1, H2, H3 can be done together; H4 and H5 are independent and can run in parallel with each other and with H1–H3.
MEDIUM (P3) – Quality and maintainability
| ID | Task | Owner / parallel | Notes |
|---|---|---|---|
| M1 | Block number validation (React) – In src/pages/blocks/[number].tsx, validate params.number: reject non-numeric or NaN; show a clear “Invalid block number” (or “Block number required”) message instead of calling API with 0. |
1 | Better UX. |
| M2 | Stable list keys (React) – In src/components/common/Table.tsx, accept an optional keyExtractor prop; in pages that use Table (e.g. addresses, blocks), pass a stable key (e.g. tx.hash, block.number). In search results, use key={result.data?.hash ?? result.data?.address ?? index} instead of key={index}. |
1 | Avoids React reconciliation issues. |
| M3 | SPA: named constants – In public/index.html, replace magic numbers with named constants at the top of the script: e.g. FETCH_TIMEOUT_MS = 15000, RPC_HEALTH_TIMEOUT_MS = 5000, FETCH_MAX_RETRIES = 3, RETRY_DELAY_MS = 1000. |
1 | Maintainability. |
| M4 | API response normalization (blocks) – If backend is Blockscout-style, add in blocks.ts (or client): normalize { items: [] } → { data: [] } so all consumers get a consistent shape without each page handling both. |
1 | Depends on C4 decision; can follow C4. |
Parallel: M1, M2, M3, M4 can be done in parallel (M4 after C4 if adding normalizer).
LOW / OPTIONAL (P4)
| ID | Task | Owner / parallel | Notes |
|---|---|---|---|
| L1 | SPA: extract shared block card markup – Introduce a single helper (e.g. createBlockCardHtml(block, options)) used by home stats, blocks list, and any other block card HTML to reduce duplication. |
1 | Maintainability. |
| L2 | SPA: DEBUG flag for console – Gate console.log/console.warn (and optionally console.error) behind a flag (e.g. window.DEBUG_EXPLORER) so production builds can suppress verbose logs. |
1 | Optional. |
| L3 | A11y: live region for errors – Add an aria-live="polite" (or assertive) region for error messages and retry buttons so screen readers announce them. |
1 | Accessibility. |
| L4 | Shared API modules (React) – Add src/services/api/transactions.ts and addresses.ts (or similar) using the same apiClient, and refactor addresses/[address].tsx and transactions/[hash].tsx to use them with consistent error handling. |
1 | Consistency. |
Parallel: L1–L4 independent; all optional.
Part 2: Detail Review – Misses, Gaps, Additional Fixes
2.1 Additional React fetch bugs (misses from original review)
transactions/[hash].tsx– Same pattern as addresses: noresponse.okcheck;setTransaction(data.data)can run on 4xx/5xx. Action: Add to critical list as C2 (done above).search/index.tsx– Noresponse.okcheck;setResults(data.results || [])can set error payload. Action: Add as C3 (done above).
2.2 SPA: Gaps in escapeHtml / innerHTML
- Wallet status (1639, 1722) –
statusEl.innerHTMLusesshortenHash(userAddress). IfuserAddresswere ever from an untrusted source, it should be escaped. Action: UseescapeHtml(shortenHash(userAddress))for consistency (in H1). - loadGasAndNetworkStats (2509) –
el.innerHTMLusesgasGwei,blockTimeSec,tps. These are from API; escaping is low risk but recommended for defense in depth. Action: Escape these values (in H1 or small follow-up). - Token list:
#/token/' + contract– Thecontractinhref="#/token/' + contract + '"can break the attribute if it contains a quote. Action: Encode or validate; include in H2 (safe href/attributes). - External link (3800) –
'https://explorer.d-bis.org/address/' + addr + '/contract'–addrshould be validated or encoded so the URL cannot be malformed. Action: UseencodeURIComponent(addr)for the path segment (in H2).
2.3 SPA: onclick and attribute injection
- All dynamic onclick handlers – Values like
address,hash,from,to,block,txHash,contract,contractAddressare interpolated into onclick strings. A single quote in any of them breaks the attribute and can lead to script execution. Action: Consistently useescapeHtml(value)for every dynamic part inside the quoted argument (in H3). Example:onclick="showAddressDetail('" + escapeHtml(address) + "')"(and equivalent for template literals).
2.4 SPA: requestAnimationFrame leak (clarification)
- The blocks scroll animation starts inside
loadLatestBlocks()and is never cancelled. When the user navigates to Transactions or Home, the blocks view is hidden but the animation loop keeps running. Action: Store the rAF id (e.g.let scrollAnimationIdin a scope that can be cleared when switching view). WhenswitchToViewor the blocks view is left, or before the nextloadLatestBlocksrun, callcancelAnimationFrame(scrollAnimationId). Optionally clear the interval/timeout if any. In task H5.
2.5 React: API shape and Table key (additional)
- Home page
recentBlocks– Usesresponse.datafromblocksApi.list; ifresponse.datais undefined (e.g. backend returnsitems),recentBlocks.mapwill throw. Action: Covered by C4 and M4 (normalize in client). - Table key – Using
rowIndexis acceptable for static lists but can cause unnecessary re-renders or focus issues when list order changes. Action: M2 (stable keys). - Search results key –
key={index}is weak when results can change; preferkey={result.data?.hash ?? result.data?.address ?? result.data?.number ?? index}. Action: Include in M2.
2.6 Additional checks performed
- rpcCall – Confirmed it does not use
getRpcUrl(); single URL used. Action: H4. - Breadcrumb identifier – Multiple places use
identifierin innerHTML and inhref; both display and href need to be safe. Action: H1, H2. - No other raw
dangerouslySetInnerHTMLin React components; no additional React XSS findings. - transactions/[hash].tsx – No validation of
hashformat (e.g. 0x + 64 hex); invalid hash could trigger odd API/UI behavior. Optional: add validation and show “Invalid transaction hash” (can be P4).
2.7 Summary of additional fixes required
| Category | Fix |
|---|---|
| React fetch | C2 (transaction detail), C3 (search) – check response.ok and handle errors. |
| SPA innerHTML | H1 – include statusEl (1639, 1722), loadGasAndNetworkStats (2509); escape all shortenHash and API-derived text. |
| SPA href/attributes | H2 – encode/validate identifier and contract in hrefs; encode addr in external URL. |
| SPA onclick | H3 – escape every dynamic value in onclick (address, hash, from, to, block, txHash, contract, etc.). |
| SPA rAF | H5 – store and cancel requestAnimationFrame when leaving blocks view or re-running loadLatestBlocks. |
| Optional | Validate tx hash in transactions/[hash].tsx (P4); add transactions/addresses API modules (L4). |
Part 3: Execution Order (Full Parallel Mode)
- Wave 1 (all parallel): C1, C2, C3, C4, H1, H2, H3, H4, H5.
- Wave 2 (after C4/M4 if normalizer added): M1, M2, M3, M4.
- Wave 3 (optional): L1, L2, L3, L4.
No task in Wave 2 or 3 blocks another within the same wave; only M4 may depend on C4’s decision (normalize in client vs. backend contract).