Skip to content

Commit abfd828

Browse files
committed
Refactor dom sanitizer usage
- Move sanitizer logic into `domSanitizer` file - Avoid most direct use of dompurify types - Use clearer names to describe what properties do
1 parent 7b1c3d3 commit abfd828

File tree

13 files changed

+585
-391
lines changed

13 files changed

+585
-391
lines changed

src/vs/base/browser/dom.ts

Lines changed: 1 addition & 219 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@ import { IMouseEvent, StandardMouseEvent } from './mouseEvent.js';
1010
import { AbstractIdleValue, IntervalTimer, TimeoutTimer, _runWhenIdle, IdleDeadline } from '../common/async.js';
1111
import { BugIndicatingError, onUnexpectedError } from '../common/errors.js';
1212
import * as event from '../common/event.js';
13-
import dompurify from './dompurify/dompurify.js';
1413
import { KeyCode } from '../common/keyCodes.js';
1514
import { Disposable, DisposableStore, IDisposable, toDisposable } from '../common/lifecycle.js';
16-
import { RemoteAuthorities, Schemas } from '../common/network.js';
15+
import { RemoteAuthorities } from '../common/network.js';
1716
import * as platform from '../common/platform.js';
1817
import { URI } from '../common/uri.js';
1918
import { hash } from '../common/hash.js';
@@ -1652,223 +1651,6 @@ export function detectFullscreen(targetWindow: Window): IDetectedFullscreen | nu
16521651
return null;
16531652
}
16541653

1655-
// -- sanitize and trusted html
1656-
1657-
/**
1658-
* Hooks dompurify using `afterSanitizeAttributes` to check that all `href` and `src`
1659-
* attributes are valid.
1660-
*/
1661-
export function hookDomPurifyHrefAndSrcSanitizer(allowedProtocols: readonly string[], allowDataImages = false): IDisposable {
1662-
// https://github.com/cure53/DOMPurify/blob/main/demos/hooks-scheme-allowlist.html
1663-
1664-
// build an anchor to map URLs to
1665-
const anchor = document.createElement('a');
1666-
1667-
dompurify.addHook('afterSanitizeAttributes', (node) => {
1668-
// check all href/src attributes for validity
1669-
for (const attr of ['href', 'src']) {
1670-
if (node.hasAttribute(attr)) {
1671-
const attrValue = node.getAttribute(attr) as string;
1672-
if (attr === 'href' && attrValue.startsWith('#')) {
1673-
// Allow fragment links
1674-
continue;
1675-
}
1676-
1677-
anchor.href = attrValue;
1678-
if (!allowedProtocols.includes(anchor.protocol.replace(/:$/, ''))) {
1679-
if (allowDataImages && attr === 'src' && anchor.href.startsWith('data:')) {
1680-
continue;
1681-
}
1682-
1683-
node.removeAttribute(attr);
1684-
}
1685-
}
1686-
}
1687-
});
1688-
1689-
return toDisposable(() => {
1690-
dompurify.removeHook('afterSanitizeAttributes');
1691-
});
1692-
}
1693-
1694-
const defaultSafeProtocols = [
1695-
Schemas.http,
1696-
Schemas.https,
1697-
Schemas.command,
1698-
];
1699-
1700-
/**
1701-
* List of safe, non-input html tags.
1702-
*/
1703-
export const basicMarkupHtmlTags = Object.freeze([
1704-
'a',
1705-
'abbr',
1706-
'b',
1707-
'bdo',
1708-
'blockquote',
1709-
'br',
1710-
'caption',
1711-
'cite',
1712-
'code',
1713-
'col',
1714-
'colgroup',
1715-
'dd',
1716-
'del',
1717-
'details',
1718-
'dfn',
1719-
'div',
1720-
'dl',
1721-
'dt',
1722-
'em',
1723-
'figcaption',
1724-
'figure',
1725-
'h1',
1726-
'h2',
1727-
'h3',
1728-
'h4',
1729-
'h5',
1730-
'h6',
1731-
'hr',
1732-
'i',
1733-
'img',
1734-
'input',
1735-
'ins',
1736-
'kbd',
1737-
'label',
1738-
'li',
1739-
'mark',
1740-
'ol',
1741-
'p',
1742-
'pre',
1743-
'q',
1744-
'rp',
1745-
'rt',
1746-
'ruby',
1747-
'samp',
1748-
'small',
1749-
'small',
1750-
'source',
1751-
'span',
1752-
'strike',
1753-
'strong',
1754-
'sub',
1755-
'summary',
1756-
'sup',
1757-
'table',
1758-
'tbody',
1759-
'td',
1760-
'tfoot',
1761-
'th',
1762-
'thead',
1763-
'time',
1764-
'tr',
1765-
'tt',
1766-
'u',
1767-
'ul',
1768-
'var',
1769-
'video',
1770-
'wbr',
1771-
]);
1772-
1773-
export const trustedMathMlTags = Object.freeze([
1774-
'semantics',
1775-
'annotation',
1776-
'math',
1777-
'menclose',
1778-
'merror',
1779-
'mfenced',
1780-
'mfrac',
1781-
'mglyph',
1782-
'mi',
1783-
'mlabeledtr',
1784-
'mmultiscripts',
1785-
'mn',
1786-
'mo',
1787-
'mover',
1788-
'mpadded',
1789-
'mphantom',
1790-
'mroot',
1791-
'mrow',
1792-
'ms',
1793-
'mspace',
1794-
'msqrt',
1795-
'mstyle',
1796-
'msub',
1797-
'msup',
1798-
'msubsup',
1799-
'mtable',
1800-
'mtd',
1801-
'mtext',
1802-
'mtr',
1803-
'munder',
1804-
'munderover',
1805-
'mprescripts',
1806-
1807-
// svg tags
1808-
'svg',
1809-
'altglyph',
1810-
'altglyphdef',
1811-
'altglyphitem',
1812-
'circle',
1813-
'clippath',
1814-
'defs',
1815-
'desc',
1816-
'ellipse',
1817-
'filter',
1818-
'font',
1819-
'g',
1820-
'glyph',
1821-
'glyphref',
1822-
'hkern',
1823-
'line',
1824-
'lineargradient',
1825-
'marker',
1826-
'mask',
1827-
'metadata',
1828-
'mpath',
1829-
'path',
1830-
'pattern',
1831-
'polygon',
1832-
'polyline',
1833-
'radialgradient',
1834-
'rect',
1835-
'stop',
1836-
'style',
1837-
'switch',
1838-
'symbol',
1839-
'text',
1840-
'textpath',
1841-
'title',
1842-
'tref',
1843-
'tspan',
1844-
'view',
1845-
'vkern',
1846-
]);
1847-
1848-
1849-
export const defaultAllowedAttrs = Object.freeze(['href', 'data-href', 'data-command', 'target', 'title', 'name', 'src', 'alt', 'class', 'id', 'role', 'tabindex', 'style', 'data-code', 'width', 'height', 'align', 'x-dispatch', 'required', 'checked', 'placeholder', 'type', 'start']);
1850-
1851-
const defaultDomPurifyConfig = Object.freeze<dompurify.Config & { RETURN_TRUSTED_TYPE: true }>({
1852-
ALLOWED_TAGS: ['a', 'button', 'blockquote', 'code', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'hr', 'input', 'label', 'li', 'p', 'pre', 'select', 'small', 'span', 'strong', 'textarea', 'ul', 'ol'],
1853-
ALLOWED_ATTR: [...defaultAllowedAttrs],
1854-
RETURN_DOM: false,
1855-
RETURN_DOM_FRAGMENT: false,
1856-
RETURN_TRUSTED_TYPE: true
1857-
});
1858-
1859-
/**
1860-
* Sanitizes the given `value` and reset the given `node` with it.
1861-
*/
1862-
export function safeInnerHtml(node: HTMLElement, value: string, extraDomPurifyConfig?: dompurify.Config): void {
1863-
const hook = hookDomPurifyHrefAndSrcSanitizer(defaultSafeProtocols);
1864-
try {
1865-
const html = dompurify.sanitize(value, { ...defaultDomPurifyConfig, ...extraDomPurifyConfig });
1866-
node.innerHTML = html as unknown as string;
1867-
} finally {
1868-
hook.dispose();
1869-
}
1870-
}
1871-
18721654
type ModifierKey = 'alt' | 'ctrl' | 'shift' | 'meta';
18731655

18741656
export interface IModifierKeyStatus {

0 commit comments

Comments
 (0)