-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Remove dependencies and update Vite config (#90)
* Vite config fix attempt * export css separately * point at dist folder * skip sanitization on the server * bump version * revert sanitize * switch to use basic html sanitize * bump version * decodeHtmlEntities * lowercase entity * bump version * fix named entity to be case insensitive --------- Co-authored-by: tblackwell-tm <t.blackwell@techmodal.com>
- Loading branch information
1 parent
805557b
commit 9e4f131
Showing
10 changed files
with
191 additions
and
399 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1 @@ | ||
22.11.0 | ||
22.13.1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,131 @@ | ||
import DOMPurify from 'isomorphic-dompurify'; | ||
/** | ||
* Basic HTML sanitization function. | ||
* | ||
* Note this doesn't use DOMParser or any other browser APIs. | ||
*/ | ||
export function sanitize( | ||
html: string, | ||
options: { allowedTags?: string[] } = {}, | ||
) { | ||
const { allowedTags = [] } = options; | ||
|
||
type SanitizeOptions = { | ||
allowedAttributes?: string[]; | ||
allowedTags?: string[]; | ||
}; | ||
// Short-circuit: if there are no < or > characters, return the input as is | ||
if (!/<|>/.test(html)) { | ||
return html; | ||
} | ||
|
||
let previous = ''; | ||
let current = html; | ||
|
||
// Ensure allowedTags are treated as whole words using `\b` | ||
const allowedPattern = | ||
allowedTags.length > 0 ? `\\b(${allowedTags.join('|')})\\b` : '(?!)'; // "(?!)" ensures an empty list matches nothing | ||
|
||
// Regex to remove disallowed tags (fully removes both opening & closing tags) | ||
const tagPattern = new RegExp(`</?(?!${allowedPattern})\\w+[^>]*>`, 'gi'); | ||
|
||
// Regex to strip all attributes from allowed tags | ||
const attributePattern = /<(\w+)[^>]*>/gi; | ||
|
||
while (previous !== current) { | ||
previous = current; | ||
|
||
// Remove disallowed tags completely | ||
current = current.replace(tagPattern, ''); | ||
|
||
export function sanitize(html: string, options: SanitizeOptions = {}): string { | ||
const domPurifyOptions = { | ||
ALLOWED_ATTR: options.allowedAttributes ?? [], | ||
ALLOWED_TAGS: options.allowedTags ?? [], | ||
}; | ||
// Remove all attributes from allowed tags | ||
current = current.replace(attributePattern, '<$1>'); | ||
} | ||
|
||
return DOMPurify.sanitize(html, domPurifyOptions); | ||
return current; | ||
} | ||
|
||
export function decodeHtmlEntities(html: string) { | ||
const textarea = document.createElement('textarea'); | ||
textarea.innerHTML = html; | ||
const entityDictionary: Record<string, string> = { | ||
'&': '&', | ||
'<': '<', | ||
'>': '>', | ||
'"': '"', | ||
''': "'", | ||
''': "'", | ||
'/': '/', | ||
'`': '`', | ||
'=': '=', | ||
' ': ' ', | ||
'©': '©', | ||
'®': '®', | ||
'™': '™', | ||
'€': '€', | ||
'£': '£', | ||
'¥': '¥', | ||
'¢': '¢', | ||
''': "'", | ||
'§': '§', | ||
'¶': '¶', | ||
'±': '±', | ||
'×': '×', | ||
'÷': '÷', | ||
'«': '«', | ||
'»': '»', | ||
'“': '“', | ||
'”': '”', | ||
'‘': '‘', | ||
'’': '’', | ||
'…': '…', | ||
'·': '·', | ||
'•': '•', | ||
'–': '–', | ||
'—': '—', | ||
'α': 'α', | ||
'β': 'β', | ||
'γ': 'γ', | ||
'δ': 'δ', | ||
'π': 'π', | ||
'σ': 'σ', | ||
'ω': 'ω', | ||
'μ': 'μ', | ||
'τ': 'τ', | ||
'φ': 'φ', | ||
'χ': 'χ', | ||
'ψ': 'ψ', | ||
'θ': 'θ', | ||
}; | ||
|
||
/** | ||
* Decode HTML entities in a string. | ||
*/ | ||
export function decodeHtmlEntities(html: string): string { | ||
return html.replace(/&([^;]+);/g, (entity, entityCode) => { | ||
// Check dictionary for named entities (case-insensitive) | ||
const namedEntity = entityDictionary[entity.toLowerCase()]; | ||
|
||
if (namedEntity !== undefined) { | ||
return namedEntity; | ||
} | ||
|
||
// Handle decimal numeric entities | ||
if (entityCode.startsWith('#')) { | ||
let code: number; | ||
|
||
// Handle hexadecimal entities (&#x...) | ||
if (entityCode.startsWith('#x') || entityCode.startsWith('#X')) { | ||
code = parseInt(entityCode.slice(2), 16); | ||
} else { | ||
// Handle decimal entities (&#...) | ||
code = parseInt(entityCode.slice(1), 10); | ||
} | ||
|
||
// Use String.fromCodePoint instead of String.fromCharCode to handle all Unicode | ||
if (!isNaN(code)) { | ||
try { | ||
return String.fromCodePoint(code); | ||
} catch (e) { | ||
// Return the original entity if the code point is invalid | ||
return entity; | ||
} | ||
} | ||
} | ||
|
||
return textarea.value; | ||
// Return unchanged if not recognized | ||
return entity; | ||
}); | ||
} |
Oops, something went wrong.