diff --git a/package.json b/package.json index b92c40c..11bb48e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "vue-safe-html", - "version": "2.1.0", + "version": "2.2.0", "description": "A Vue directive which renders sanitised HTML dynamically", "main": "dist/main.js", "repository": "git@github.com:ecosia/vue-safe-html.git", diff --git a/src/utils.js b/src/utils.js index 0ea23dd..5492f75 100644 --- a/src/utils.js +++ b/src/utils.js @@ -6,9 +6,32 @@ */ // eslint-disable-next-line import/prefer-default-export export const sanitizeHTML = (htmlString, allowedTags = []) => { + // Add an optional white space to the allowed tags + const allowedTagsWhiteSpaced = allowedTags.map((tag) => `${tag}\\s*`); + + // Remove tag attributes + // The solution for this was found on: + // https://stackoverflow.com/questions/4885891/regex-for-removing-all-attributes-from-a-paragraph + const htmlWithoutAttributes = htmlString.replace(/<(\w+)(.|[\r\n])*?>/g, '<$1>'); + const expression = (allowedTags.length > 0) ? - `<(?!((?:/s*)?(?:${allowedTags.join('|')})))([^>])+>` : - '<[^>]*>'; - const regExp = new RegExp(expression, 'g'); - return htmlString.replace(regExp, ''); + // Regex explanation + // Note: \ needs to be escaped in the final expression + // '<' Match the starting tag + // '(' Create a matching group + // '?!' Use negative lookup + // we only want to match the tags that are not in the allowedTags array + // '\s*?' Optional match of any white space charater before optional / + // '\/?' Matches / zero to one time for the closing tag + // '\s*?' Optional match of any white space charater after optional / + // '(${allowedTags.join('|')})>' matching group of the allowed tags + // ')' close the matching group of negative lookup + // '\w*[^<>]*' matches any word that isn't in the excluded group + // '>' Match closing tagq + `<(?!\\s*\\/?\\s*(${allowedTagsWhiteSpaced.join('|')})>)\\w*[^<>]*>` : + // Strips all tags + '<(\\/?\\w*)\\w*[^<>]*>'; + + const regExp = new RegExp(expression, 'gm'); + return htmlWithoutAttributes.replace(regExp, ''); }; diff --git a/src/utils.test.js b/src/utils.test.js index 099c20e..4db69a9 100644 --- a/src/utils.test.js +++ b/src/utils.test.js @@ -14,5 +14,40 @@ describe('Utils', () => { const expected = 'An html
string'; expect(utils.sanitizeHTML(given, allowedTags)).toBe(expected); }); + + it('Strips input tags', () => { + const allowedTags = ['strong', 'i']; + const given = '

An input field

'; + const expected = 'An input field'; + expect(utils.sanitizeHTML(given, allowedTags)).toBe(expected); + }); + + it('Strips similar tags', () => { + const allowedTags = ['p', 'b', 's']; + const given = 'Test1 Test2
quote
'; + const expected = 'Test1 Test2 quote'; + expect(utils.sanitizeHTML(given, allowedTags)).toBe(expected); + }); + + it('Considers whitespaces', () => { + const allowedTags = ['p']; + const given = '

Test1

Test2'; + const expected = '

Test1

Test2'; + expect(utils.sanitizeHTML(given, allowedTags)).toBe(expected); + }); + + it('Removes all tags with empty allowed tags', () => { + const allowedTags = []; + const given = '

Test1

Test2 < i>Test3'; + const expected = 'Test1 Test2 Test3'; + expect(utils.sanitizeHTML(given, allowedTags)).toBe(expected); + }); + + it('Removes attributes from html', () => { + const allowedTags = ['p']; + const given = '

Test1

Test2'; + const expected = '

Test1

Test2'; + expect(utils.sanitizeHTML(given, allowedTags)).toBe(expected); + }); }); });