cinny/src/util/sanitize.js
Ajay Bura 0b06bed1db
Refactor state & Custom editor (#1190)
* Fix eslint

* Enable ts strict mode

* install folds, jotai & immer

* Enable immer map/set

* change cross-signing alert anim to 30 iteration

* Add function to access matrix client

* Add new types

* Add disposable util

* Add room utils

* Add mDirect list atom

* Add invite list atom

* add room list atom

* add utils for jotai atoms

* Add room id to parents atom

* Add mute list atom

* Add room to unread atom

* Use hook to bind atoms with sdk

* Add settings atom

* Add settings hook

* Extract set settings hook

* Add Sidebar components

* WIP

* Add bind atoms hook

* Fix init muted room list atom

* add navigation atoms

* Add custom editor

* Fix hotkeys

* Update folds

* Add editor output function

* Add matrix client context

* Add tooltip to editor toolbar items

* WIP - Add editor to room input

* Refocus editor on toolbar item click

* Add Mentions - WIP

* update folds

* update mention focus outline

* rename emoji element type

* Add auto complete menu

* add autocomplete query functions

* add index file for editor

* fix bug in getPrevWord function

* Show room mention autocomplete

* Add async search function

* add use async search hook

* use async search in room mention autocomplete

* remove folds prefer font for now

* allow number array in async search

* reset search with empty query

* Autocomplete unknown room mention

* Autocomplete first room mention on tab

* fix roomAliasFromQueryText

* change mention color to primary

* add isAlive hook

* add getMxIdLocalPart to mx utils

* fix getRoomAvatarUrl size

* fix types

* add room members hook

* fix bug in room mention

* add user mention autocomplete

* Fix async search giving prev result after no match

* update folds

* add twemoji font

* add use state provider hook

* add prevent scroll with arrow key util

* add ts to custom-emoji and emoji files

* add types

* add hook for emoji group labels

* add hook for emoji group icons

* add emoji board with basic emoji

* add emojiboard in room input

* select multiple emoji with shift press

* display custom emoji in emojiboard

* Add emoji preview

* focus element on hover

* update folds

* position emojiboard properly

* convert recent-emoji.js to ts

* add use recent emoji hook

* add io.element.recent_emoji to account data evt

* Render recent emoji in emoji board

* show custom emoji from parent spaces

* show room emoji

* improve emoji sidebar

* update folds

* fix pack avatar and name fallback in emoji board

* add stickers to emoji board

* fix bug in emoji preview

* Add sticker icon in room input

* add debounce hook

* add search in emoji board

* Optimize emoji board

* fix emoji board sidebar divider

* sync emojiboard sidebar with scroll & update ui

* Add use throttle hook

* support custom emoji in editor

* remove duplicate emoji selection function

* fix emoji and mention spacing

* add emoticon autocomplete in editor

* fix string

* makes emoji size relative to font size in editor

* add option to render link element

* add spoiler in editor

* fix sticker in emoji board search using wrong type

* render custom placeholder

* update hotkey for block quote and block code

* add terminate search function in async search

* add getImageInfo to matrix utils

* send stickers

* add resize observer hook

* move emoji board component hooks in hooks dir

* prevent editor expand hides room timeline

* send typing notifications

* improve emoji style and performance

* fix imports

* add on paste param to editor

* add selectFile utils

* add file picker hook

* add file paste handler hook

* add file drop handler

* update folds

* Add file upload card

* add bytes to size util

* add blurHash util

* add await to js lib

* add browser-encrypt-attachment types

* add list atom

* convert mimetype file to ts

* add matrix types

* add matrix file util

* add file related dom utils

* add common utils

* add upload atom

* add room input draft atom

* add upload card renderer component

* add upload board component

* add support for file upload in editor

* send files with message / enter

* fix circular deps

* store editor toolbar state in local store

* move msg content util to separate file

* store msg draft on room switch

* fix following member not updating on msg sent

* add theme for folds component

* fix system default theme

* Add reply support in editor

* prevent initMatrix to init multiple time

* add state event hooks

* add async callback hook

* Show tombstone info for tombstone room

* fix room tombstone component border

* add power level hook

* Add room input placeholder component

* Show input placeholder for muted member
2023-06-12 16:45:23 +05:30

140 lines
3.8 KiB
JavaScript

import sanitizeHtml from 'sanitize-html';
const MAX_TAG_NESTING = 100;
let mx = null;
const permittedHtmlTags = [
'font', 'del', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',
'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub',
'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 's', 'code',
'hr', 'br', 'div', 'table', 'thead', 'tbody', 'tr', 'th',
'td', 'caption', 'pre', 'span', 'img', 'details', 'summary',
];
const urlSchemes = ['https', 'http', 'ftp', 'mailto', 'magnet'];
const permittedTagToAttributes = {
font: ['style', 'data-mx-bg-color', 'data-mx-color', 'color'],
span: ['style', 'data-mx-bg-color', 'data-mx-color', 'data-mx-spoiler', 'data-mx-maths', 'data-mx-pill', 'data-mx-ping'],
div: ['data-mx-maths'],
a: ['name', 'target', 'href', 'rel'],
img: ['width', 'height', 'alt', 'title', 'src', 'data-mx-emoticon'],
ol: ['start'],
code: ['class'],
};
function transformFontTag(tagName, attribs) {
return {
tagName,
attribs: {
...attribs,
style: `background-color: ${attribs['data-mx-bg-color']}; color: ${attribs['data-mx-color']}`,
},
};
}
function transformSpanTag(tagName, attribs) {
return {
tagName,
attribs: {
...attribs,
style: `background-color: ${attribs['data-mx-bg-color']}; color: ${attribs['data-mx-color']}`,
},
};
}
function transformATag(tagName, attribs) {
const userLink = decodeURIComponent(attribs.href).match(/^https?:\/\/matrix.to\/#\/(@.+:.+)/);
if (userLink !== null) {
// convert user link to pill
const userId = userLink[1];
const pill = {
tagName: 'span',
attribs: {
'data-mx-pill': userId,
},
};
if (userId === mx?.getUserId()) {
pill.attribs['data-mx-ping'] = undefined;
}
return pill;
}
const rex = /[\u{1f300}-\u{1f5ff}\u{1f900}-\u{1f9ff}\u{1f600}-\u{1f64f}\u{1f680}-\u{1f6ff}\u{2600}-\u{26ff}\u{2700}-\u{27bf}\u{1f1e6}-\u{1f1ff}\u{1f191}-\u{1f251}\u{1f004}\u{1f0cf}\u{1f170}-\u{1f171}\u{1f17e}-\u{1f17f}\u{1f18e}\u{3030}\u{2b50}\u{2b55}\u{2934}-\u{2935}\u{2b05}-\u{2b07}\u{2b1b}-\u{2b1c}\u{3297}\u{3299}\u{303d}\u{00a9}\u{00ae}\u{2122}\u{23f3}\u{24c2}\u{23e9}-\u{23ef}\u{25b6}\u{23f8}-\u{23fa}]/ug;
const newHref = attribs.href.replace(rex, (match) => `[e-${match.codePointAt(0).toString(16)}]`);
return {
tagName,
attribs: {
...attribs,
href: newHref,
rel: 'noopener',
target: '_blank',
},
};
}
function transformImgTag(tagName, attribs) {
const { src } = attribs;
if (src.startsWith('mxc://') === false) {
return {
tagName: 'a',
attribs: {
href: src,
rel: 'noopener',
target: '_blank',
},
text: attribs.alt || src,
};
}
return {
tagName,
attribs: {
...attribs,
src: mx?.mxcUrlToHttp(src),
},
};
}
export function sanitizeCustomHtml(matrixClient, body) {
mx = matrixClient;
return sanitizeHtml(body, {
allowedTags: permittedHtmlTags,
allowedAttributes: permittedTagToAttributes,
disallowedTagsMode: 'discard',
allowedSchemes: urlSchemes,
allowedSchemesByTag: {
a: urlSchemes,
},
allowedSchemesAppliedToAttributes: ['href'],
allowProtocolRelative: false,
allowedClasses: {
code: ['language-*'],
},
allowedStyles: {
'*': {
color: [/^#(?:[0-9a-fA-F]{3}){1,2}$/],
'background-color': [/^#(?:[0-9a-fA-F]{3}){1,2}$/],
},
},
transformTags: {
font: transformFontTag,
span: transformSpanTag,
a: transformATag,
img: transformImgTag,
},
nonTextTags: ['style', 'script', 'textarea', 'option', 'noscript', 'mx-reply'],
nestingLimit: MAX_TAG_NESTING,
});
}
export function sanitizeText(body) {
const tagsToReplace = {
'&': '&',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#39;',
};
return body.replace(/[&<>'"]/g, (tag) => tagsToReplace[tag] || tag);
}