Skip to content

Commit ce73b27

Browse files
authored
Ensure fenced code attributes are properly escaped.
Fixes Python-Markdown#1247.
1 parent ed417a1 commit ce73b27

File tree

3 files changed

+29
-15
lines changed

3 files changed

+29
-15
lines changed

docs/change_log/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Python-Markdown Change Log
77

88
* Disallow square brackets in reference link ids (#1209).
99
* Retain configured `pygments_style` after first code block (#1240).
10+
* Ensure fenced code attributes are properly escaped (#1247).
1011

1112
Nov 17, 2021: version 3.3.6 (a bug-fix release).
1213

markdown/extensions/fenced_code.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
from .codehilite import CodeHilite, CodeHiliteExtension, parse_hl_lines
2323
from .attr_list import get_attrs, AttrListExtension
2424
from ..util import parseBoolValue
25+
from ..serializers import _escape_attrib_html
2526
import re
2627

2728

@@ -120,30 +121,24 @@ def run(self, lines):
120121
else:
121122
id_attr = lang_attr = class_attr = kv_pairs = ''
122123
if lang:
123-
lang_attr = ' class="{}{}"'.format(self.config.get('lang_prefix', 'language-'), lang)
124+
prefix = self.config.get('lang_prefix', 'language-')
125+
lang_attr = f' class="{prefix}{_escape_attrib_html(lang)}"'
124126
if classes:
125-
class_attr = ' class="{}"'.format(' '.join(classes))
127+
class_attr = f' class="{_escape_attrib_html(" ".join(classes))}"'
126128
if id:
127-
id_attr = ' id="{}"'.format(id)
129+
id_attr = f' id="{_escape_attrib_html(id)}"'
128130
if self.use_attr_list and config and not config.get('use_pygments', False):
129131
# Only assign key/value pairs to code element if attr_list ext is enabled, key/value pairs
130132
# were defined on the code block, and the `use_pygments` key was not set to True. The
131133
# `use_pygments` key could be either set to False or not defined. It is omitted from output.
132-
kv_pairs = ' ' + ' '.join(
133-
'{k}="{v}"'.format(k=k, v=v) for k, v in config.items() if k != 'use_pygments'
134+
kv_pairs = ''.join(
135+
f' {k}="{_escape_attrib_html(v)}"' for k, v in config.items() if k != 'use_pygments'
134136
)
135-
code = '<pre{id}{cls}><code{lang}{kv}>{code}</code></pre>'.format(
136-
id=id_attr,
137-
cls=class_attr,
138-
lang=lang_attr,
139-
kv=kv_pairs,
140-
code=self._escape(m.group('code'))
141-
)
137+
code = self._escape(m.group('code'))
138+
code = f'<pre{id_attr}{class_attr}><code{lang_attr}{kv_pairs}>{code}</code></pre>'
142139

143140
placeholder = self.md.htmlStash.store(code)
144-
text = '{}\n{}\n{}'.format(text[:m.start()],
145-
placeholder,
146-
text[m.end():])
141+
text = f'{text[:m.start()]}\n{placeholder}\n{text[m.end():]}'
147142
else:
148143
break
149144
return text.split("\n")

tests/test_syntax/extensions/test_fenced_code.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,24 @@ def testFencedLanguageAltPrefix(self):
374374
extensions=[markdown.extensions.fenced_code.FencedCodeExtension(lang_prefix='lang-')]
375375
)
376376

377+
def testFencedCodeEscapedAttrs(self):
378+
self.assertMarkdownRenders(
379+
self.dedent(
380+
'''
381+
``` { ."weird #"foo bar=">baz }
382+
# Some python code
383+
```
384+
'''
385+
),
386+
self.dedent(
387+
'''
388+
<pre id="&quot;foo"><code class="language-&quot;weird" bar="&quot;&gt;baz"># Some python code
389+
</code></pre>
390+
'''
391+
),
392+
extensions=['fenced_code', 'attr_list']
393+
)
394+
377395

378396
class TestFencedCodeWithCodehilite(TestCase):
379397

0 commit comments

Comments
 (0)