Embed HTML Custom Node

1. Front Matter

  • Title: Embed HTML Content in Quest Nodes

  • Author: Joshua Uriel Tribiana

  • Reviewers: JD Billate ( zcrnnn )

  • Creation Date: 2026-06-29

  • Status: Approved

  • References:

    • Issue: [3.9] Embed HTML Custom Node

2. Introduction & Goals

  • Problem Summary
    Creators need a way to include rich, interactive, or custom-styled content directly within a quest. This feature allows creators to embed custom HTML, CSS, and limited JavaScript, enabling experiences like interactive simulations, embedded forms, or unique content layouts that are not possible with standard node types. The primary technical challenge is enabling this flexibility while ensuring robust security to prevent Cross-Site Scripting (XSS) attacks, especially when quests are shared publicly.

  • Goals

    • Provide a "HTML Node" in the Visual Canvas editor.

    • Integrate a code editor (Monaco Editor) for an improved authoring experience.

    • Securely sanitize all HTML content on the server before saving to prevent XSS.

    • Render the sanitized HTML for learners within a sandboxed <iframe> to isolate it from the main application.

    • Enforce a 50,000-character limit on the HTML content to manage performance and data size.

    • Set a Content-Security-Policy header on public share pages to further mitigate risks.

  • Non-Goals

    • Execution of server-side code (e.g., PHP). This is a client-side only feature.

    • Direct access to the parent page's DOM, cookies, or localStorage.

    • Hosting for assets (images, scripts) referenced in the HTML. Creators must use absolute URLs to externally hosted assets.

    • A full-fledged IDE experience within the node.

  • Glossary

    • HTML Node: A specific node type on the Visual Canvas for embedding custom HTML.

    • Sanitization: The process of cleaning user-provided HTML to remove malicious code (e.g., <script> tags, onerror attributes) before it is stored or rendered.

    • Sandboxed <iframe>: An <iframe> with security attributes that restrict its capabilities, such as preventing it from running scripts, accessing the parent document, or making top-level navigation changes.

    • XSS (Cross-Site Scripting): A type of security vulnerability where malicious scripts are injected into otherwise benign and trusted websites.

3. High-Level Architecture

  • System Diagram

    +----------------------+

    | Creator edits HTML |

    | in Monaco Editor |

    +----------------------+

    |

    v

    +----------------------+

    | Code Node |

    +----------------------+

    |

    v

    +----------------------+

    | Debounced Save |

    +----------------------+

    |

    v

    +----------------------+

    | Update Canvas API |

    +----------------------+

    |

    v

    +----------------------+

    | HTML Sanitization |

    | (DOMPurify) |

    +----------------------+

    |

    v

    +----------------------+

    | Save Clean HTML |

    | to Database |

    +----------------------+

    |

    |

    =========== Quest Published ===========

    |

    v

    +----------------------+

    | Player Loads Quest |

    +----------------------+

    |

    v

    +----------------------+

    | Fetch HTML Content |

    +----------------------+

    |

    v

    +----------------------+

    | Render HTML Node |

    +----------------------+

    |

    v

    +----------------------+

    | Sandboxed iframe |

    | (srcdoc) |

    +----------------------+

    |

    v

    +----------------------+

    | Safe HTML Display |

    +----------------------+

  • Technologies Used

    • React: For frontend components.

    • Monaco Editor (@monaco-editor/react): For the code editing interface.

    • DOMPurify: Server-side library for HTML sanitization.

    • Next.js: For API routes and setting security headers.

    • Supabase (PostgreSQL): For storing the sanitized HTML in the canvas_metadata JSONB column.

4. Detailed Design & Implementation

  • Data Model / Schema
    No new tables are required. The feature adds a codeContent property to the data object of the HTML node within the quests.canvas_metadata JSONB column.

  • Node Object Modification:

    Property

    Type

    Description

    codeContent

    string

    The sanitized HTML content (max 50,000 characters).

  • Example Node JSON:

    json

    {

    "id": "node-html-456",

    "type": "Code",

    "position": { "x": 250, "y": 150 },

    "data": {

    "label": "Interactive Simulation",

    "codeContent": "<style>...</style><div>...</div>"

    }

    }

  • API Specification
    This feature uses the existing API for updating the quest canvas. The sanitization logic is added as a middleware step within this API route.

    • Endpoint: PUT /api/creator/update-quest-canvas

    • Logic Enhancement: Before updating the quests table, the handler iterates through all nodes in the incoming canvas_metadata.nodes. If a node is of type Code, its data.codeContent is processed:

      1. The content is truncated to 50,000 characters.

      2. The truncated content is passed through DOMPurify.sanitize().

      3. The sanitized HTML replaces the original codeContent in the payload before it's saved to the database.

  • Logic & Workflows

    1. Authoring Workflow:

      • Creator adds a "HTML Node" to the canvas.

      • The CodeNode.tsx component renders a Monaco Editor instance, loading the codeContent from the node's data.

      • As the creator types, the content is saved to the client-side state.

      • A debounced save triggers a PUT request to /api/creator/update-quest-canvas with the updated canvas_metadata.

    2. Security Workflow (Server-side):

      1. The update-quest-canvas API route receives the request.

      2. It iterates through all nodes. For each Code node:

        • It applies the 50k character limit.

        • It uses DOMPurify with a strict configuration to strip out dangerous tags (<script>, <object>) and attributes (onerror, onload).

        • The now-safe canvas_metadata is saved to the database.

      3. Rendering Workflow (Learner):

        • The Learner Player fetches the quest, including the sanitized codeContent.

        • The content is rendered inside an <iframe> using the srcdoc attribute.

        • The <iframe> is configured with a strict sandbox attribute (e.g., sandbox="allow-forms allow-modals allow-popups allow-presentation"), which prevents script execution, top-level navigation, and access to the parent origin.

  • Key Files:

    • components/quest-editor/visual-canvas/nodes/CodeNode.tsx: The creator-facing component with the Monaco Editor.

    • app/api/creator/update-quest-canvas/route.ts: The API endpoint where server-side sanitization is performed.

    • lib/security/html-sanitizer.service.ts: A new service file that configures and wraps DOMPurify.

    • components/learner/player/nodes/RenderHtmlNode.tsx: The learner-facing component that renders the sandboxed <iframe>.

    • middleware.ts: To be updated to add Content-Security-Policy headers for /share/* routes.

5. Infrastructure & Operations

Dependencies

  • Internal: Relies on the existing quest editor and player infrastructure.

  • External: DOMPurify (and its dependency, e.g., jsdom, if run in a Node.js environment).

Monitoring & Alerting

  • Standard monitoring for the PUT /api/creator/update-quest-canvas endpoint applies.

  • Logging: It is recommended to log an event whenever the sanitization service strips potentially malicious content, as this could indicate an attempt to probe for vulnerabilities.

Deployment Plan

  • Dependencies: Add dompurify and jsdom to package.json.

  • Rollout:

    1. Deploy the new sanitization service and the updated API route logic.

    2. Deploy the frontend components (CodeNode.tsx, RenderHtmlNode.tsx).

    3. This feature poses a security risk if misconfigured. A phased rollout behind a feature flag is highly recommended to allow for thorough security testing in a production environment before enabling it for all users.

6. Testing & Quality Assurance

Test Strategy

  • Unit Tests:

    • Test the html-sanitizer.service.ts with various malicious payloads (e.g., <script>alert('XSS')</script>, <img src=x onerror=alert(1)>) and verify that the dangerous elements are removed.

    • Test the character limit enforcement.

  • Integration Tests:

    • Test the update-quest-canvas API endpoint to ensure that when a Code node is submitted, the data stored in the database is the sanitized version, not the raw input.

  • End-to-End (E2E) / QA:

    • SEC-4 (XSS): As defined in FEATURE_AUDIT_MAY2026.md, create a quest with a malicious HTML payload. View it via a Public Share Link and confirm that the script does not execute. Test various XSS vectors.

    • Functional Flow: Create a quest with safe, complex HTML/CSS. Verify that it renders correctly for the learner.

    • Limit Test: Paste content exceeding 50,000 characters and verify it is truncated upon saving.

Known Limitations

  • The current implementation in the codebase is missing the sanitization and character limit enforcement, which is a critical security risk. This documentation describes the required, secure implementation.

  • Styling within the <iframe> is completely isolated. It will not inherit any styles from the main WyzQuests application.

7. Maintenance & Support

Troubleshooting

  • Embedded content does not render correctly:

    1. This is likely due to the sanitization process stripping out necessary tags or attributes. Check the DOMPurify configuration in html-sanitizer.service.ts.

    2. It could also be due to the sandbox restrictions on the <iframe>. Check the browser's developer console for sandbox-related errors.

  • User reports their JavaScript is not working:

    1. This is the intended behavior. The default sandbox configuration and DOMPurify should block all scripts. Explain that this is a security measure.

Changelog

1.0 - Approved, Technical guide created for secure implementation, 2026-06-29


Was this article helpful?