QuikChat provides built-in XSS (Cross-Site Scripting) protection through optional content sanitization. By default, QuikChat does not sanitize content (backward compatible), allowing developers full control over content handling.
When user input is displayed in a chat widget without sanitization, it can execute malicious scripts:
// User types: <script>alert('XSS')</script>
chat.messageAddNew(userInput, 'User'); // ⚠️ Script will execute!You should enable sanitization when:
- Displaying user-generated content
- Showing messages from untrusted sources
- Building multi-user chat applications
- Accepting input from external APIs
Sanitization might not be necessary when:
- Only displaying programmer-controlled content
- Building internal tools with trusted users
- Intentionally displaying HTML/Markdown content
- Using your own sanitization layer
const chat = new quikchat('#chat', onSend, {
sanitizer: quikchat.sanitizers.escapeHTML
});const chat = new quikchat('#chat');
chat.setSanitizer(quikchat.sanitizers.escapeHTML);Escapes HTML entities to prevent script execution:
// Input: <script>alert('xss')</script>Hello
// Output: <script>alert('xss')</script>HelloRemoves all HTML tags but preserves text content:
// Input: <b>Bold</b> and <i>italic</i> text
// Output: Bold and italic textYou can provide your own sanitization function:
// Example: Using DOMPurify
const chat = new quikchat('#chat', onSend, {
sanitizer: (content) => DOMPurify.sanitize(content)
});
// Example: Custom markdown sanitizer
const chat = new quikchat('#chat', onSend, {
sanitizer: (content) => {
// Allow only specific markdown elements
return content
.replace(/[<>]/g, '') // Remove HTML brackets
.replace(/\*\*(.*?)\*\*/g, '<b>$1</b>') // Bold
.replace(/\*(.*?)\*/g, '<i>$1</i>'); // Italic
}
});const chat = new quikchat('#chat', (instance, message) => {
// User input is automatically sanitized when displayed
instance.messageAddNew(message, 'User', 'right');
// Send to backend (sanitize server-side too!)
sendToServer(message);
}, {
sanitizer: quikchat.sanitizers.escapeHTML
});const chat = new quikchat('#chat');
// Escape HTML for user messages
chat.setSanitizer(quikchat.sanitizers.escapeHTML);
chat.messageAddNew(userInput, 'User', 'right');
// Allow HTML for system messages (trusted source)
chat.setSanitizer(null);
chat.messageAddNew('<b>System:</b> User joined', 'System', 'center');
// Re-enable for next user message
chat.setSanitizer(quikchat.sanitizers.escapeHTML);const chat = new quikchat('#chat', null, {
sanitizer: quikchat.sanitizers.escapeHTML
});
// Safe streaming - each append is sanitized
const msgId = chat.messageAddNew('', 'Assistant', 'left');
streamResponse.on('data', (chunk) => {
chat.messageAppendContent(msgId, chunk); // Sanitized automatically
});- escapeHTML: ~2-5% overhead for typical use
- stripHTML: ~1-3% overhead
- Custom sanitizers: Varies (DOMPurify: ~10-20% overhead)
Based on 1000 messages:
- Without sanitizer: ~40ms
- With escapeHTML: ~42ms (5% overhead)
- Per message cost: ~0.002ms
- Use built-in sanitizers when possible (optimized for performance)
- Avoid heavy sanitizers for streaming content
- Consider batching for bulk message additions
- Enable virtual scrolling for large message volumes
Always sanitize on both client and server:
// Client-side
const chat = new quikchat('#chat', null, {
sanitizer: quikchat.sanitizers.escapeHTML
});
// Server-side (Node.js example)
app.post('/message', (req, res) => {
const sanitized = escapeHtml(req.body.message);
broadcast(sanitized);
});QuikChat is CSP-compatible. Add appropriate headers:
<meta http-equiv="Content-Security-Policy"
content="default-src 'self'; style-src 'self' 'unsafe-inline';">Keep QuikChat updated for the latest security improvements:
npm update quikchatTry these payloads to verify sanitization:
// XSS attempts
'<script>alert("XSS")</script>'
'<img src=x onerror=alert("XSS")>'
'<svg onload=alert("XSS")>'
'javascript:alert("XSS")'
// HTML injection
'<marquee>Annoying</marquee>'
'<style>body{display:none}</style>'describe('Security', () => {
test('should prevent XSS', () => {
const chat = new quikchat('#chat', null, {
sanitizer: quikchat.sanitizers.escapeHTML
});
chat.messageAddNew('<script>window.xss=true</script>', 'User');
expect(window.xss).toBeUndefined();
expect(document.querySelector('.quikchat-message-content').innerHTML)
.toBe('<script>window.xss=true</script>');
});
});// Before (no sanitization)
const chat = new quikchat('#chat', handleMessage);
// After (with sanitization)
const chat = new quikchat('#chat', handleMessage, {
sanitizer: quikchat.sanitizers.escapeHTML
});
// Or update existing instance
existingChat.setSanitizer(quikchat.sanitizers.escapeHTML);// Option 1: Use markdown library with built-in sanitization
const chat = new quikchat('#chat', null, {
sanitizer: (content) => {
const html = markdownLibrary.render(content);
return DOMPurify.sanitize(html);
}
});
// Option 2: Whitelist specific HTML tags
const chat = new quikchat('#chat', null, {
sanitizer: (content) => {
return DOMPurify.sanitize(content, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a', 'code'],
ALLOWED_ATTR: ['href']
});
}
});A: Built-in sanitizers add minimal overhead (2-5%). Heavy sanitizers like DOMPurify may add 10-20% overhead.
A: Yes, use a custom sanitizer that allows safe HTML tags or use a markdown parser with sanitization.
A: No, to maintain backward compatibility. You must explicitly enable it.
A: It protects against XSS via message content. Always implement server-side validation and sanitization too.
A: Yes, use setSanitizer() to change the sanitizer dynamically.
For security issues, please email security@quikchat.com rather than using public issue trackers.
For questions and discussions, visit our GitHub Discussions.