Skip to content

Commit d9bc9c7

Browse files
authored
Merge pull request #282 from marp-team/preserve-comments-in-html-block
Preserve HTML comments within html block
2 parents 967693e + 43740fd commit d9bc9c7

3 files changed

Lines changed: 82 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
### Fixed
88

99
- Refactor auto scaling component ([#276](https://github.com/marp-team/marp-core/pull/276))
10+
- Preserve HTML comments within html block ([#282](https://github.com/marp-team/marp-core/pull/282))
1011

1112
### Changed
1213

src/html/html.ts

Lines changed: 31 additions & 2 deletions
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

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

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

0 commit comments

Comments
 (0)