Files
explorer-monorepo/frontend/FRONTEND_TASKS_AND_REVIEW.md
2026-03-02 12:14:13 -08:00

14 KiB
Raw Permalink Blame History

Frontend: Full Task List (Critical → Optional) + Detail Review

Completed (as of last pass): C1C4, M1M4, H4, H5, L2, L4, and React useEffect dependency warnings (useCallback + deps). H1/H2/H3: SPA already uses escapeHtml and encodeURIComponent in breadcrumbs, watchlist, block cards, tx rows, and API error messages; block breadcrumb identifier now escaped. M2: Table has keyExtractor; addresses and transactions pages pass stable keys; search uses stable result keys. M3: Named constants (FETCH_TIMEOUT_MS, RPC_HEALTH_TIMEOUT_MS, FETCH_MAX_RETRIES, RETRY_DELAY_MS, API_BASE). H4: rpcCall uses getRpcUrl(). H5: _blocksScrollAnimationId cancelled in switchToView and before re-running loadLatestBlocks. L4: addresses.ts and transactions.ts API modules in use.

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 H1H3.


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: L1L4 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: no response.ok check; setTransaction(data.data) can run on 4xx/5xx. Action: Add to critical list as C2 (done above).
  • search/index.tsx No response.ok check; 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.innerHTML uses shortenHash(userAddress). If userAddress were ever from an untrusted source, it should be escaped. Action: Use escapeHtml(shortenHash(userAddress)) for consistency (in H1).
  • loadGasAndNetworkStats (2509) el.innerHTML uses gasGwei, 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 The contract in href="#/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' addr should be validated or encoded so the URL cannot be malformed. Action: Use encodeURIComponent(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, contractAddress are interpolated into onclick strings. A single quote in any of them breaks the attribute and can lead to script execution. Action: Consistently use escapeHtml(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 scrollAnimationId in a scope that can be cleared when switching view). When switchToView or the blocks view is left, or before the next loadLatestBlocks run, call cancelAnimationFrame(scrollAnimationId). Optionally clear the interval/timeout if any. In task H5.

2.5 React: API shape and Table key (additional)

  • Home page recentBlocks Uses response.data from blocksApi.list; if response.data is undefined (e.g. backend returns items), recentBlocks.map will throw. Action: Covered by C4 and M4 (normalize in client).
  • Table key Using rowIndex is 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; prefer key={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 identifier in innerHTML and in href; both display and href need to be safe. Action: H1, H2.
  • No other raw dangerouslySetInnerHTML in React components; no additional React XSS findings.
  • transactions/[hash].tsx No validation of hash format (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 C4s decision (normalize in client vs. backend contract).