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-Policyheader 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,onerrorattributes) 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_metadataJSONB column.
4. Detailed Design & Implementation
Data Model / Schema
No new tables are required. The feature adds acodeContentproperty to thedataobject of the HTML node within thequests.canvas_metadataJSONB column.Node Object Modification:
Property
Type
Description
codeContentstringThe 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-canvasLogic Enhancement: Before updating the
queststable, the handler iterates through all nodes in the incomingcanvas_metadata.nodes. If a node is of typeCode, itsdata.codeContentis processed:The content is truncated to 50,000 characters.
The truncated content is passed through
DOMPurify.sanitize().The sanitized HTML replaces the original
codeContentin the payload before it's saved to the database.
Logic & Workflows
Authoring Workflow:
Creator adds a "HTML Node" to the canvas.
The
CodeNode.tsxcomponent renders a Monaco Editor instance, loading thecodeContentfrom the node's data.As the creator types, the content is saved to the client-side state.
A debounced save triggers a
PUTrequest to/api/creator/update-quest-canvaswith the updatedcanvas_metadata.
Security Workflow (Server-side):
The
update-quest-canvasAPI route receives the request.It iterates through all nodes. For each
Codenode:It applies the 50k character limit.
It uses
DOMPurifywith a strict configuration to strip out dangerous tags (<script>,<object>) and attributes (onerror,onload).The now-safe
canvas_metadatais saved to the database.
Rendering Workflow (Learner):
The Learner Player fetches the quest, including the sanitized
codeContent.The content is rendered inside an
<iframe>using thesrcdocattribute.The
<iframe>is configured with a strictsandboxattribute (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 addContent-Security-Policyheaders 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-canvasendpoint 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
dompurifyandjsdomtopackage.json.Rollout:
Deploy the new sanitization service and the updated API route logic.
Deploy the frontend components (
CodeNode.tsx,RenderHtmlNode.tsx).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.tswith 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-canvasAPI endpoint to ensure that when aCodenode 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:
This is likely due to the sanitization process stripping out necessary tags or attributes. Check the
DOMPurifyconfiguration inhtml-sanitizer.service.ts.It could also be due to the
sandboxrestrictions on the<iframe>. Check the browser's developer console for sandbox-related errors.
User reports their JavaScript is not working:
This is the intended behavior. The default
sandboxconfiguration andDOMPurifyshould block all scripts. Explain that this is a security measure.
Changelog
1.0 - Approved, Technical guide created for secure implementation, 2026-06-29