Design Decision: Raw HTML Allowed
This component intentionally allows raw HTML injection in rendering callbacks to give developers full control over content display. This is a conscious design choice, not a bug. If you display user-generated content, you must sanitize it yourself.
Overview
The MultiSelect component uses innerHTML for rendering custom content in several callbacks.
This provides maximum flexibility for creating rich, interactive content but requires developers to be
aware of XSS (Cross-Site Scripting) implications when displaying untrusted data.
Callbacks Allowing HTML Injection
The following callbacks output is rendered using innerHTML and will execute any HTML/JavaScript:
| Callback | Used In | Risk |
|---|---|---|
renderOptionContentCallback | Dropdown options | HTML Injection |
renderBadgeContentCallback | Selected item badges | HTML Injection |
renderSelectedItemContentCallback | Selected items popover | HTML Injection |
renderGroupLabelContentCallback | Group headers | HTML Injection |
getIconCallback | Option icons | HTML Injection |
getSubtitleCallback | Option subtitles | HTML Injection |
getDisplayValueCallback | Option titles, badge text | HTML Injection |
getBadgeDisplayCallback | Badge text | HTML Injection |
getCounterCallback | Count badges | HTML Injection |
getBadgeTooltipCallback | Badge tooltips | HTML if HTMLElement |
customStylesCallback | Style tag injection | CSS Injection |
Safe Callbacks
The following callbacks are safe - their output is escaped or used as data only:
| Callback | Usage | Status |
|---|---|---|
getValueCallback | Data lookup (ID extraction) | Safe |
getSearchValueCallback | Search filtering | Safe |
getGroupCallback | Group name extraction | Safe |
getDisabledCallback | Boolean check | Safe |
getBadgeClassCallback | CSS class names only | Safe |
getSelectedItemClassCallback | CSS class names only | Safe |
beforeSearchCallback | Search term transformation | Safe |
searchCallback | Returns data array | Safe |
addNewCallback | Returns item object | Safe |
selectCallback | Event handler | Safe |
deselectCallback | Event handler | Safe |
changeCallback | Event handler | Safe |
getRemoveButtonTooltipCallback | Title attribute (escaped) | Safe |
getValueFormatCallback | Form value formatting | Safe |
Why Allow Raw HTML?
Benefits
- Full control over rendering
- Rich content with images, icons, badges
- Complex layouts (flexbox, grid)
- Custom styling per item
- Interactive elements in options
- No limitations on creativity
Your Responsibility
- Sanitize user-generated content
- Validate data from external APIs
- Use a sanitization library (DOMPurify, sanitize-html)
- Escape special characters when needed
- Review third-party data sources
Sanitization Examples
Using DOMPurify
Sanitizing with DOMPurify
Simple Text Escaping
Simple HTML Escaping
Safe Static Content
Trusted Data (No Sanitization)
When to Sanitize
| Data Source | Sanitization Required? | Example |
|---|---|---|
| Your own database (controlled) | Usually No | Product catalog, predefined options |
| User input (forms, comments) | Yes - Always | User names, descriptions, tags |
| External APIs | Yes - Recommended | GitHub users, third-party data |
| URL parameters | Yes - Always | Search queries, filters from URL |
| Static hardcoded values | No | Country list, status options |
Key Takeaways
- This is intentional: Raw HTML support is a feature, not a vulnerability
- You control the data: Only you know if your data is trusted
- Sanitize at the boundary: Clean data before it enters rendering callbacks
- Use established libraries: DOMPurify, sanitize-html, or your framework's built-in sanitizer
- When in doubt, sanitize: It's better to over-sanitize than to expose an XSS vulnerability