Widget Security
NodeLoom widgets are protected by multi-layer XSS sanitization, CSS security filtering, rate limiting, token-based authentication, and visitor metadata sanitization. This defense-in-depth approach ensures that widgets are safe to embed on any website.
Multi-Layer XSS Sanitization
Widget content passes through three independent sanitization layers. Each layer operates at a different point in the data flow, ensuring that even if one layer is bypassed, the others catch malicious content.
| Layer | Where | What It Does |
|---|---|---|
| Layer 1: Backend | Server-side | Strips script tags, iframe elements, event handler attributes (onclick, onerror, etc.), and other dangerous HTML before storing content in the database. |
| Layer 2: Dashboard | Client-side (dashboard) | Sanitizes content when previewing widget customizations in the dashboard. Protects admins from XSS in user-submitted content during configuration. |
| Layer 3: Widget Embed | Client-side (visitor page) | Sanitizes all rendered content in the embedded widget before it reaches the visitor's browser DOM. This is the final defense layer. |
Defense in depth
Backend Sanitization
The backend sanitizer uses an allowlist that permits common formatting tags (bold, italic, links, lists, tables) while stripping dangerous elements. NodeLoom extends this allowlist to also permit style and class attributes on all elements, which are needed for custom branding.
<script>alert('xss')</script> → removed entirely
<iframe src="evil.com"></iframe> → removed entirely
<img onerror="alert(1)" src="x"> → <img src="x">
<a onclick="steal()">click</a> → <a>click</a>
<div onmouseover="exploit()"> → <div>Client-Side Sanitization
Both the dashboard preview and the widget embed sanitize content before rendering. The client-side sanitizer handles edge cases like mutation XSS, encoding tricks, and browser-specific parsing quirks.
CSS Sanitization
Custom CSS entered by widget administrators is sanitized to remove dangerous properties and values that could be used for code execution or data exfiltration.
| Blocked Pattern | Risk | Example |
|---|---|---|
expression() | Executes JavaScript in older IE versions. | width: expression(alert(1)) |
@import | Loads external stylesheets that could contain malicious rules. | @import url(evil.css) |
-moz-binding | Loads XBL bindings that can execute JavaScript in Firefox. | -moz-binding: url(exploit.xml#xss) |
javascript: URLs | Executes JavaScript when used in CSS url() values. | background: url(javascript:alert(1)) |
CSS is powerful
Rate Limiting
Widget API endpoints are protected by rate limiting to prevent abuse and denial-of-service attacks. Limits are enforced per widget token and per visitor identifier.
Rate limits are configurable per widget from the dashboard. When a limit is exceeded, the API returns HTTP 429 (Too Many Requests) and the widget displays a user-friendly cooldown message.
Token Authentication
Every widget request is authenticated using a unique widget token. The token identifies the widget configuration, connected workflow, and workspace. Invalid or revoked tokens receive HTTP 401 responses.
Widget tokens can be revoked and regenerated from the dashboard at any time. Revoking a token immediately disables the widget on all pages where it is embedded. A new token must be deployed to resume operation.
Visitor Metadata Sanitization
The widget collects minimal metadata about visitors (browser type, page URL, referrer) to provide context to the AI agent. This metadata is sanitized before being processed:
| Protection | Description |
|---|---|
| Size limits | Each metadata field is capped at a maximum length to prevent oversized payloads. Fields exceeding the limit are truncated. |
| Entry limits | The total number of metadata entries per session is capped. Additional entries beyond the limit are discarded. |
| Type validation | Metadata values are validated to match expected types (string, number, boolean). Unexpected types are rejected. |
| HTML stripping | Any HTML in metadata values is stripped to prevent injection through metadata fields. |
Minimal data collection