diff --git a/__tests__/ExpensiMark-HTML-test.js b/__tests__/ExpensiMark-HTML-test.js index 1a3d81c63..10cb52570 100644 --- a/__tests__/ExpensiMark-HTML-test.js +++ b/__tests__/ExpensiMark-HTML-test.js @@ -1786,14 +1786,14 @@ describe('when should keep raw input flag is enabled', () => { }); }); }); - + test('Test code fence within inline code', () => { let testString = 'Hello world `(```test```)` Hello world'; expect(parser.replace(testString)).toBe('Hello world `(
test
)` Hello world'); - + testString = 'Hello world `(```test\ntest```)` Hello world'; expect(parser.replace(testString)).toBe('Hello world `(
test
test
)` Hello world'); - + testString = 'Hello world ```(`test`)``` Hello world'; expect(parser.replace(testString)).toBe('Hello world
(`test`)
Hello world'); @@ -1900,12 +1900,9 @@ describe('Image markdown conversion to html tag', () => { expect(parser.replace(testString)).toBe(resultString); }); - // Currently any markdown used inside the square brackets is converted to html string in the alt attribute - // The attributes should only contain plain text, but it doesn't seem possible to convert markdown to plain text - // or let the parser know not to convert markdown to html for html attributes - xtest('Image with alt text containing markdown', () => { - const testString = '![*bold* _italic_ ~strike~](https://example.com/image.png)'; - const resultString = '*bold* _italic_ ~strike~'; + test('Image with alt text containing markdown', () => { + const testString = '![# fake-heading *bold* _italic_ ~strike~ [:-)]](https://example.com/image.png)'; + const resultString = '# fake-heading *bold* _italic_ ~strike~ [:-)]'; expect(parser.replace(testString)).toBe(resultString); }); diff --git a/__tests__/ExpensiMark-Markdown-test.js b/__tests__/ExpensiMark-Markdown-test.js index 2adce1683..ada9fd900 100644 --- a/__tests__/ExpensiMark-Markdown-test.js +++ b/__tests__/ExpensiMark-Markdown-test.js @@ -769,4 +769,10 @@ describe('Image tag conversion to markdown', () => { const resultString = '![https://example.com/image.png](https://example.com/image.png)'; expect(parser.htmlToMarkdown(testString)).toBe(resultString); }); + + test('Image with alt text containing escaped markdown', () => { + const testString = '*bold* _italic_ ~strike~'; + const resultString = '![*bold* _italic_ ~strike~](https://example.com/image.png)'; + expect(parser.htmlToMarkdown(testString)).toBe(resultString); + }); }); diff --git a/lib/ExpensiMark.js b/lib/ExpensiMark.js index 2c15f9bb9..5ec6a7340 100644 --- a/lib/ExpensiMark.js +++ b/lib/ExpensiMark.js @@ -120,13 +120,13 @@ export default class ExpensiMark { * Converts markdown style images to img tags e.g. ![Expensify](https://www.expensify.com/attachment.png) * We need to convert before linking rules since they will not try to create a link from an existing img * tag. + * Additional sanitization is done to the alt attribute to prevent parsing it further to html by later rules. */ { name: 'image', regex: MARKDOWN_IMAGE_REGEX, - - replacement: (match, g1, g2) => `${g1}`, - rawInputReplacement: (match, g1, g2) => `${g1}` + replacement: (match, g1, g2) => `${this.escapeMarkdownEntities(g1)}`, + rawInputReplacement: (match, g1, g2) => `${this.escapeMarkdownEntities(g1)}` }, /** @@ -954,4 +954,28 @@ export default class ExpensiMark { const linksInNew = this.extractLinksInMarkdownComment(newComment); return linksInOld === undefined || linksInNew === undefined ? [] : _.difference(linksInOld, linksInNew); } + + /** + * Replace MD characters with their HTML entity equivalent + * @param {String} text + * @return {String} + */ + escapeMarkdownEntities(text) { + // A regex pattern matching special MD characters we'd like to escape + const pattern = /([*_{}[\]#~])/g; + + // A map of MD characters to their HTML entity equivalent + const entities = { + '*': '*', + _: '_', + '{': '{', + '}': '}', + '[': '[', + ']': ']', + '#': '#', + '~': '~', + }; + + return text.replace(pattern, char => entities[char] || char); + } }