diff --git a/lib/confluence-client.js b/lib/confluence-client.js index 84802bd..7d0d41c 100644 --- a/lib/confluence-client.js +++ b/lib/confluence-client.js @@ -1073,10 +1073,11 @@ class ConfluenceClient { // so they appear as literal characters in the CDATA output const decodedCode = code.replace(/\n$/, '') .replace(/"/g, '"') - .replace(/&/g, '&') .replace(/</g, '<') - .replace(/>/g, '>'); - return `${language}`; + .replace(/>/g, '>') + .replace(/&/g, '&'); // & last to avoid double-decoding + const safeCode = decodedCode.replace(/]]>/g, ']]]]>'); + return `${language}`; }); // Convert inline code diff --git a/tests/confluence-client.test.js b/tests/confluence-client.test.js index 5f40248..3ce55ec 100644 --- a/tests/confluence-client.test.js +++ b/tests/confluence-client.test.js @@ -327,6 +327,16 @@ describe('ConfluenceClient', () => { expect(result).toContain('

Cell 1

'); }); + test('should escape CDATA terminators in code blocks', () => { + const markdown = '```xml\n\n```'; + const result = client.markdownToStorage(markdown); + + expect(result).toContain(''); + // The literal ]]> from user code should not appear unescaped + expect(result).not.toContain('some data]]>'); + }); + test('should convert links to smart link format on Cloud instances', () => { const markdown = '[Example Link](https://example.com)'; const result = client.markdownToStorage(markdown);