Skip to content

Commit a29d4f4

Browse files
committed
Merge pull request #282 from marp-team/preserve-comments-in-html-block
Preserve HTML comments within html block
1 parent 4a5b047 commit a29d4f4

File tree

3 files changed

+85
-2
lines changed

3 files changed

+85
-2
lines changed

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## [Unreleased]
44

5+
### Fixed
6+
7+
- Preserve HTML comments within html block ([#282](https://github.com/marp-team/marp-core/pull/282))
8+
59
### Changed
610

711
- Upgrade Marpit to [v2.2.2](https://github.com/marp-team/marpit/releases/v2.2.2) ([#281](https://github.com/marp-team/marp-core/pull/281))

src/html/html.ts

+31-2
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,26 @@ export function markdown(md): void {
2222
(original: (...args: any[]) => string) =>
2323
(...args) => {
2424
const ret = original(...args)
25+
26+
// Pick comments
27+
const splitted: string[] = []
28+
let pos = 0
29+
30+
while (pos < ret.length) {
31+
const startIdx = ret.indexOf('<!--', pos)
32+
let endIdx = startIdx !== -1 ? ret.indexOf('-->', startIdx + 4) : -1
33+
34+
if (endIdx === -1) {
35+
splitted.push(ret.slice(pos))
36+
break
37+
}
38+
39+
endIdx += 3
40+
splitted.push(ret.slice(pos, startIdx), ret.slice(startIdx, endIdx))
41+
pos = endIdx
42+
}
43+
44+
// Apply filter to each contents by XSS
2545
const allowList = {}
2646
const html: MarpOptions['html'] = md.options.html
2747

@@ -58,8 +78,17 @@ export function markdown(md): void {
5878
},
5979
})
6080

61-
const sanitized = filter.process(ret)
62-
return md.options.xhtmlOut ? xhtmlOutFilter.process(sanitized) : sanitized
81+
return splitted
82+
.map((part, idx) => {
83+
if (idx % 2 === 1) return part
84+
85+
const sanitized = filter.process(part)
86+
87+
return md.options.xhtmlOut
88+
? xhtmlOutFilter.process(sanitized)
89+
: sanitized
90+
})
91+
.join('')
6392
}
6493

6594
md.renderer.rules.html_inline = sanitizedRenderer(html_inline)

test/marp.ts

+50
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,56 @@ describe('Marp', () => {
214214
expect($('header > strong')).toHaveLength(1)
215215
expect($('footer > em')).toHaveLength(1)
216216
})
217+
218+
it('keeps raw HTML comments within valid HTML block', () => {
219+
const { html: $script, comments: comments$script } = marp().render(
220+
"<script><!--\nconst script = '<b>test</b>'\n--></script>"
221+
)
222+
expect($script).toContain("const script = '<b>test</b>'")
223+
expect(comments$script[0]).toHaveLength(0)
224+
225+
// Complex comment
226+
const complexComment = `
227+
<!--
228+
function matchwo(a,b)
229+
{
230+
231+
if (a < b && a < 0) then {
232+
return 1;
233+
234+
} else {
235+
236+
return 0;
237+
}
238+
}
239+
240+
// ex
241+
-->
242+
`.trim()
243+
const { html: $complex } = marp().render(
244+
`<script>${complexComment}</script>`
245+
)
246+
expect($complex).toContain(complexComment)
247+
248+
// NOTE: Marpit framework will collect the comment block if the whole of HTML block was comment
249+
const { html: $comment, comments: comments$comment } = marp().render(
250+
"<!--\nconst script = '<b>test</b>'\n-->"
251+
)
252+
expect($comment).not.toContain("const script = '<b>test</b>'")
253+
expect(comments$comment[0]).toHaveLength(1)
254+
})
255+
256+
it('sanitizes CDATA section', () => {
257+
// HTML Living Standard denys using CDATA in HTML context so must be sanitized
258+
const cdata = `
259+
<![CDATA[
260+
<p>XSS</p>
261+
<script>alert('XSS')</script>
262+
]]>
263+
`.trim()
264+
const { html } = marp().render(cdata)
265+
expect(html).not.toContain(cdata)
266+
})
217267
})
218268

219269
describe('with true', () => {

0 commit comments

Comments
 (0)