Skip to content

Commit 4a605f5

Browse files
committed
Added escapeHtml method to string package
refs https://github.com/TryGhost/Team/issues/1871 This method is required in a couple of places where we want to escape HTML manually, such as the email template.
1 parent 6082ff4 commit 4a605f5

File tree

5 files changed

+357
-6
lines changed

5 files changed

+357
-6
lines changed

packages/string/lib/escapeHtml.js

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* escapeHTML
3+
*
4+
* Escape a string to be used as text or an attribute in HTML
5+
*
6+
* @param {string} string - the string we want to escape
7+
* @returns {string} The escaped string
8+
*/
9+
module.exports = function (string) {
10+
const htmlChars = {
11+
'&': '&',
12+
'"': '"',
13+
'\'': ''',
14+
'<': '&lt;',
15+
'>': '&gt;'
16+
};
17+
return string.replace(/[&"'<>]/g, c => htmlChars[c]);
18+
};

packages/string/lib/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
module.exports.stripInvisibleChars = require('./stripInvisibleChars');
22
module.exports.slugify = require('./slugify');
3+
module.exports.escapeHtml = require('./escapeHtml');

packages/string/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
},
2121
"devDependencies": {
2222
"c8": "7.12.0",
23+
"jsdom": "^20.0.0",
2324
"mocha": "10.0.0",
2425
"should": "13.2.3",
2526
"sinon": "14.0.0"
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
require('./utils');
2+
3+
const escapeHtml = require('../lib').escapeHtml;
4+
const jsdom = require('jsdom');
5+
6+
describe('Escape HTML', function () {
7+
it('escapes all characters', function () {
8+
const result = escapeHtml(`<tag>"Black" & 'White'</tag>`);
9+
result.should.equal(`&lt;tag&gt;&quot;Black&quot; &amp; &apos;White&apos;&lt;/tag&gt;`);
10+
});
11+
12+
it('produces valid HTML attributes', function () {
13+
const myAttribute = `><tag>"Black" & 'White'</tag>`;
14+
const htmls = [
15+
`<input value="${escapeHtml(myAttribute)}" type="text" />`,
16+
`<input value='${escapeHtml(myAttribute)}' type='text' />`
17+
];
18+
19+
for (const html of htmls) {
20+
const dom = new jsdom.JSDOM(html);
21+
should(dom.window.document.querySelector('input').getAttribute('value')).equal(myAttribute);
22+
should(dom.window.document.querySelector('input').value).equal(myAttribute);
23+
}
24+
});
25+
26+
it('produces valid HTML text', function () {
27+
const texts = [
28+
`<tag>"Black" & 'White'</tag>`,
29+
`<![CDATA[This is no data]]>`,
30+
`<!--This is no comment-->`,
31+
`</p>This doesn't end<p>`
32+
];
33+
const htmls = texts.map(text => [
34+
`<p>${escapeHtml(text)}</p>`
35+
]);
36+
37+
for (const [index, html] of htmls.entries()) {
38+
const text = texts[index];
39+
const dom = new jsdom.JSDOM(html);
40+
should(dom.window.document.querySelector('p').textContent).equal(text);
41+
should(dom.window.document.querySelector('p').innerHTML).not.equal(text);
42+
}
43+
});
44+
});

0 commit comments

Comments
 (0)