Files
explorer-monorepo/frontend/FRONTEND_TASKS_AND_REVIEW.md

141 lines
13 KiB
Markdown
Raw Normal View History

# 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|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).