Files
davila7__claude-code-templates/docs/js/utils.js
daniel 0f4aaa7217 security: Fix XSS vulnerabilities in docs site and dashboard
Static site (docs/js/):
- Add escapeHTML() utility to utils.js for sanitizing dynamic content
- Apply escapeHTML to component names, descriptions, categories, and
  install commands in card generation (search-functionality.js,
  index-events.js)
- Replace innerHTML with DOM methods for search category tags

Dashboard:
- Create shared TypeIcon component using DOMParser for safe SVG
  rendering (rejects malformed SVG via parsererror check)
- Remove all dangerouslySetInnerHTML usage from SearchModal,
  ComponentGrid, TrendingView, and MyComponentsView
- Clean up unused ICONS imports

Resolves CLA-23

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 23:13:17 -05:00

58 lines
1.8 KiB
JavaScript

// Shared Utility Functions
/**
* Escape HTML special characters to prevent XSS when injecting into innerHTML.
* Use this for any user-controlled or external data before inserting into HTML.
*/
function escapeHTML(str) {
if (typeof str !== 'string') return '';
return str
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#39;');
}
function copyToClipboard(text, message = 'Command copied to clipboard!') {
navigator.clipboard.writeText(text).then(() => {
showNotification(message, 'success');
}).catch(err => {
console.error('Failed to copy: ', err);
showNotification('Failed to copy command', 'error');
});
}
function showNotification(message, type = 'info') {
// Remove any existing notifications first
const existingNotifications = document.querySelectorAll('.notification');
existingNotifications.forEach(notif => {
if (notif.parentNode) {
notif.parentNode.removeChild(notif);
}
});
const notification = document.createElement('div');
notification.textContent = message;
notification.className = `notification notification-${type}`;
notification.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: ${type === 'success' ? '#10b981' : type === 'error' ? '#ef4444' : '#3b82f6'};
color: white;
padding: 12px 24px;
border-radius: 6px;
z-index: 10000;
font-family: system-ui, -apple-system, sans-serif;
animation: slideIn 0.3s ease;
`;
document.body.appendChild(notification);
setTimeout(() => {
if (notification.parentNode) {
notification.parentNode.removeChild(notification);
}
}, 3000);
}