Node-RED Config Node Tooltip: Multi-line Rendering and XSS Protection

There is a fix for rendering multi-line tooltips for config node validation errors in Node-RED, this explains how the approach preserves security against Cross-Site Scripting (XSS) vulnerabilities.


1. Problem: HTML Escaping and Usability

  • Previously, config node error tooltips used a string with <br> tags to indicate line breaks.
  • Due to the popover implementation, all function-returned strings were rendered using .text(), escaping HTML, so <br> appeared as literal text.
  • Rendering as HTML (with .html()) would allow line breaks, but would also introduce XSS risk if any part of the message was user-controlled.

2. The Fix: Array-based Multi-line Tooltips

Implementation

  • The error message is now passed as an array:
    • The first element is the main error label (e.g., "Invalid properties:").
    • Each subsequent element is a validation error (e.g., " - authUrl").
  • The popover script was updated to detect arrays:
    • Each array element is rendered as a text node.
    • A line break (<br>) is inserted between each line.
    • No HTML is interpreted from the error strings themselves.

Example

const errorMsg = [
  RED._("editor.errors.invalidProperties"),
  ...node.validationErrors.map(e => "  - " + e)
];
RED.popover.tooltip(target, errorMsg);

Popover rendering logic:

if (typeof result === 'string') {
    contentDiv.text(result);
} else if (Array.isArray(result)) {
    result.forEach(function(line, idx) {
        if (idx > 0) contentDiv.append(document.createElement('br'));
        contentDiv.append(document.createTextNode(line));
    });
} else {
    contentDiv.append(result);
}

3. Security: XSS Protection

  • No HTML is ever interpreted from the error message array.
  • Each line is rendered using document.createTextNode(line), which escapes all HTML and JavaScript.
  • Even if a validation error contains user-controlled content (e.g., a property name like <img src=x onerror=alert(1)>), it will be displayed as plain text, not executed.
  • This approach is as safe as the original .text() rendering, but allows for visually clean, multi-line tooltips.

4. Result

  • Tooltips for config node errors now display each error on its own line, improving readability and usability.
  • The code remains robust against XSS, as no user input is ever rendered as HTML.
  • This pattern can be safely reused for any tooltip content that requires multi-line display and may include user input.

5. References


Is this something that is worth creating a PR for, I figure it is a non-breaking change.

An alternative would be simply to use html instead of text but pass it through Node-RED's built in DOMPurify function.