diff --git a/src/app/components/editor/output.ts b/src/app/components/editor/output.ts index d6136d9..c5ecc6d 100644 --- a/src/app/components/editor/output.ts +++ b/src/app/components/editor/output.ts @@ -5,6 +5,7 @@ import { BlockType } from './types'; import { CustomElement } from './slate'; import { parseBlockMD, parseInlineMD } from '../../plugins/markdown'; import { findAndReplace } from '../../utils/findAndReplace'; +import { sanitizeForRegex } from '../../utils/regex'; export type OutputOptions = { allowTextFormatting?: boolean; @@ -179,7 +180,7 @@ export const customHtmlEqualsPlainText = (customHtml: string, plain: string): bo export const trimCustomHtml = (customHtml: string) => customHtml.replace(/$/g, '').trim(); export const trimCommand = (cmdName: string, str: string) => { - const cmdRegX = new RegExp(`^(\\s+)?(\\/${cmdName})([^\\S\n]+)?`); + const cmdRegX = new RegExp(`^(\\s+)?(\\/${sanitizeForRegex(cmdName)})([^\\S\n]+)?`); const match = str.match(cmdRegX); if (!match) return str; diff --git a/src/app/hooks/useAsyncSearch.ts b/src/app/hooks/useAsyncSearch.ts index 3fe7ee5..3852d3b 100644 --- a/src/app/hooks/useAsyncSearch.ts +++ b/src/app/hooks/useAsyncSearch.ts @@ -10,6 +10,7 @@ import { matchQuery, ResultHandler, } from '../utils/AsyncSearch'; +import { sanitizeForRegex } from '../utils/regex'; export type UseAsyncSearchOptions = AsyncSearchOption & { matchOptions?: MatchQueryOption; @@ -55,8 +56,8 @@ export const orderSearchItems = ( // we will consider "_" as word boundary char. // because in more use-cases it is used. (like: emojishortcode) - const boundaryRegex = new RegExp(`(\\b|_)${query}`); - const perfectBoundaryRegex = new RegExp(`(\\b|_)${query}(\\b|_)`); + const boundaryRegex = new RegExp(`(\\b|_)${sanitizeForRegex(query)}`); + const perfectBoundaryRegex = new RegExp(`(\\b|_)${sanitizeForRegex(query)}(\\b|_)`); orderedItems.sort((i1, i2) => { const str1 = performMatch(getItemStr(i1, query), query, options); diff --git a/src/app/plugins/react-custom-html-parser.tsx b/src/app/plugins/react-custom-html-parser.tsx index 6a4d053..cd683e3 100644 --- a/src/app/plugins/react-custom-html-parser.tsx +++ b/src/app/plugins/react-custom-html-parser.tsx @@ -21,7 +21,7 @@ import { mxcUrlToHttp, } from '../utils/matrix'; import { getMemberDisplayName } from '../utils/room'; -import { EMOJI_PATTERN, URL_NEG_LB } from '../utils/regex'; +import { EMOJI_PATTERN, sanitizeForRegex, URL_NEG_LB } from '../utils/regex'; import { getHexcodeForEmoji, getShortcodeFor } from './emoji'; import { findAndReplace } from '../utils/findAndReplace'; import { @@ -171,7 +171,7 @@ export const scaleSystemEmoji = (text: string): (string | JSX.Element)[] => ); export const makeHighlightRegex = (highlights: string[]): RegExp | undefined => { - const pattern = highlights.join('|'); + const pattern = highlights.map(sanitizeForRegex).join('|'); if (!pattern) return undefined; return new RegExp(pattern, 'gi'); }; diff --git a/src/app/utils/regex.ts b/src/app/utils/regex.ts index d716906..0b98b0e 100644 --- a/src/app/utils/regex.ts +++ b/src/app/utils/regex.ts @@ -1,3 +1,9 @@ +/** + * https://www.npmjs.com/package/escape-string-regexp + */ +export const sanitizeForRegex = (unsafeText: string): string => + unsafeText.replace(/[|\\{}()[\]^$+*?.]/g, '\\$&').replace(/-/g, '\\x2d'); + export const HTTP_URL_PATTERN = `https?:\\/\\/(?:www\\.)?(?:[^\\s)]*)(?