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

143 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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|to|identifier|...)` must use `escapeHtml(shortenHash(...))`. Locations (grep): breadcrumbs (2243, 2247, 2254, 2259, 2280, 2284, 2288, 2292), block cards (2628), transaction rows (27942796, 2909, 29942996), bridge table (3108, 3144), internal tx/logs (3526, 3543), token balances (3746), address internal txs (3774), address tx table (3832), token detail (3887, 3905), NFT detail (3935, 3943), statusEl (1639, 1722). Also escape any other API-derived text in those same innerHTML strings (e.g. `displayBalance`, `val` if from API). | 1 | XSS defense in depth. |
| **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).