Skip to content

XSS Validation Filters Shielding Dynamic Node Identifier Strings #9

Description

@JamesEjembi

Problem Statement

Custom text values configured for node labels and network descriptions can serve as cross-site scripting (XSS) vectors. Node operators can set arbitrary displayName and description fields on their validator nodes via the configuration API. Without proper sanitization on the frontend, a malicious operator could inject <script> tags or onerror handlers into these fields, which would execute in the context of every other operator viewing the node dashboard, potentially exfiltrating session cookies or wallet keys.

Technical Bounds & Invariants

  • Input sources: node display name, node description, location string, contact email, website URL
  • Render contexts: HTML (React JSX), dangerouslySetInnerHTML is never used (must be banned)
  • Character encoding: UTF-8 with full Unicode support (emoji, CJK, RTL scripts allowed)
  • Sanitization must preserve legitimate Unicode and common formatting characters
  • Performance impact: sanitization must add less than 1ms per rendered string

Codebase Navigation Guide

  • Primary target: /src/components/network/NodeCard.tsx
  • Node data interface: /src/types/node.tsNode.displayName, Node.description, Node.location
  • List rendering: /src/components/network/NodeList.tsx
  • Detail view: /src/components/network/NodeDetailPanel.tsx

Step-by-Step Resolution Blueprint

  1. Install and configure DOMPurify as the sanitization library with strict settings: DOMPurify.sanitize(input, { ALLOWED_TAGS: [], ALLOWED_ATTR: [] }) — no HTML tags or attributes are permitted; plain text only
  2. Create a utility function sanitizeNodeField(input: string, field: 'displayName' | 'description' | 'location' | 'contactEmail' | 'websiteUrl') -> string in /src/utils/sanitize.ts that: (a) strips HTML tags via DOMPurify, (b) normalizes Unicode to NFC form via input.normalize('NFC'), (c) removes control characters except \n, \t, and printable Unicode via regex /[^\S\n\t\w\s\p{L}\p{N}\p{P}\p{Sc}]/gu, (d) trims whitespace, (e) enforces per-field max lengths (displayName: 50 chars, description: 500 chars, etc.)
  3. Add a custom ESLint rule in eslint.config.js that flags any use of dangerouslySetInnerHTML across the entire codebase and fails the CI build
  4. Create a SafeText component: <SafeText text={rawString} maxLength={50} /> that applies sanitizeNodeField and renders the sanitized output inside a <span> with title attribute showing the full sanitized value for truncation cases
  5. Add a CSP (Content Security Policy) header in next.config.js: script-src 'self'; object-src 'none'; to provide a defense-in-depth layer even if sanitization fails
  6. Write a property-based test using fast-check that generates 500 random malicious strings (with embedded <script>, javascript:, onerror=, data:text/html, etc.) and asserts the output of sanitizeNodeField contains no angle brackets, no event handlers, and no protocol-based XSS vectors
  7. Add a regression test in Playwright that: (a) uses the API to set a node's display name to <img src=x onerror=alert(1)>, (b) navigates to the NodeCard, (c) asserts no alert dialog appears (using page.on('dialog') with a fail handler)

Metadata

Metadata

Assignees

Labels

Complexity: HardcoreExtremely difficult, high-complexity engineering taskGrantFox OSSIssue tracked in GrantFox OSSLayer: UI-CoreCore UI layer architectural concernMaybe RewardedIssue may be eligible for a GrantFox rewardOfficial CampaignCampaign: Official CampaignType: Web3-IntegrationWeb3 wallet and blockchain integration issue

Type

No type

Fields

No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions