-
Notifications
You must be signed in to change notification settings - Fork 680
Expand file tree
/
Copy pathxss.ts
More file actions
90 lines (77 loc) · 2.66 KB
/
xss.ts
File metadata and controls
90 lines (77 loc) · 2.66 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
/**
* XSS Protection Utilities
* Provides functions to sanitize and escape user input to prevent XSS attacks
*/
/**
* Escape HTML entities to prevent XSS
* @param text - The text to escape
* @returns Escaped text safe for HTML insertion
*/
export function escapeHtml(text: string): string {
const div = document.createElement('div')
div.textContent = text
return div.innerHTML
}
/**
* Highlight keywords in text with XSS protection
* @param text - The original text
* @param keyword - The keyword to highlight
* @param highlightClass - CSS class for highlighted text
* @returns HTML string with highlighted keyword (XSS-safe)
*/
export function highlightKeyword(
text: string,
keyword: string,
highlightClass: string = 'highlight'
): string {
if (!keyword) return escapeHtml(text)
const escapedText = escapeHtml(text)
const escapedKeyword = escapeHtml(keyword)
// Use case-insensitive replace
const regex = new RegExp(escapedKeyword, 'gi')
return escapedText.replace(regex, (match) => `<span class="${highlightClass}">${match}</span>`)
}
/**
* Sanitize HTML content to remove potentially dangerous elements and attributes
* @param html - The HTML content to sanitize
* @returns Sanitized HTML
*/
export function sanitizeHtml(html: string): string {
// Create a temporary div to parse HTML
const temp = document.createElement('div')
temp.innerHTML = html
// List of allowed tags
const allowedTags = ['b', 'i', 'u', 'strong', 'em', 'span', 'p', 'br', 'a']
// List of allowed attributes
const allowedAttrs = ['class', 'href', 'title']
// Remove disallowed tags and attributes
const sanitize = (node: Node): void => {
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node as Element
// Check if tag is allowed
if (!allowedTags.includes(element.tagName.toLowerCase())) {
// Replace with text content
const textNode = document.createTextNode(element.textContent || '')
element.parentNode?.replaceChild(textNode, element)
return
}
// Remove disallowed attributes
Array.from(element.attributes).forEach((attr) => {
if (!allowedAttrs.includes(attr.name.toLowerCase())) {
element.removeAttribute(attr.name)
}
})
// For links, ensure they don't use javascript: protocol
if (element.tagName.toLowerCase() === 'a') {
const href = element.getAttribute('href') || ''
if (href.toLowerCase().startsWith('javascript:')) {
element.removeAttribute('href')
}
}
}
// Recursively sanitize child nodes
Array.from(node.childNodes).forEach(sanitize)
}
sanitize(temp)
return temp.innerHTML
}