summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWaylan Limberg <waylan.limberg@icloud.com>2022-05-04 14:13:56 -0400
committerGitHub <noreply@github.com>2022-05-04 14:13:56 -0400
commitce73b2754fb754c29d719c9980ae2f4aa4912f4d (patch)
tree6e11d57b842b72db7f3ae0784c05ee2f1342cd34
parented417a1a555bf2206ceea84ba29ce37322ca8261 (diff)
downloadpython-markdown-ce73b2754fb754c29d719c9980ae2f4aa4912f4d.tar.gz
Ensure fenced code attributes are properly escaped.
Fixes #1247.
-rw-r--r--docs/change_log/index.md1
-rw-r--r--markdown/extensions/fenced_code.py25
-rw-r--r--tests/test_syntax/extensions/test_fenced_code.py18
3 files changed, 29 insertions, 15 deletions
diff --git a/docs/change_log/index.md b/docs/change_log/index.md
index 5320fc7..b9fa97b 100644
--- a/docs/change_log/index.md
+++ b/docs/change_log/index.md
@@ -7,6 +7,7 @@ Python-Markdown Change Log
* Disallow square brackets in reference link ids (#1209).
* Retain configured `pygments_style` after first code block (#1240).
+* Ensure fenced code attributes are properly escaped (#1247).
Nov 17, 2021: version 3.3.6 (a bug-fix release).
diff --git a/markdown/extensions/fenced_code.py b/markdown/extensions/fenced_code.py
index 9be0ca0..409166a 100644
--- a/markdown/extensions/fenced_code.py
+++ b/markdown/extensions/fenced_code.py
@@ -22,6 +22,7 @@ from ..preprocessors import Preprocessor
from .codehilite import CodeHilite, CodeHiliteExtension, parse_hl_lines
from .attr_list import get_attrs, AttrListExtension
from ..util import parseBoolValue
+from ..serializers import _escape_attrib_html
import re
@@ -120,30 +121,24 @@ class FencedBlockPreprocessor(Preprocessor):
else:
id_attr = lang_attr = class_attr = kv_pairs = ''
if lang:
- lang_attr = ' class="{}{}"'.format(self.config.get('lang_prefix', 'language-'), lang)
+ prefix = self.config.get('lang_prefix', 'language-')
+ lang_attr = f' class="{prefix}{_escape_attrib_html(lang)}"'
if classes:
- class_attr = ' class="{}"'.format(' '.join(classes))
+ class_attr = f' class="{_escape_attrib_html(" ".join(classes))}"'
if id:
- id_attr = ' id="{}"'.format(id)
+ id_attr = f' id="{_escape_attrib_html(id)}"'
if self.use_attr_list and config and not config.get('use_pygments', False):
# Only assign key/value pairs to code element if attr_list ext is enabled, key/value pairs
# were defined on the code block, and the `use_pygments` key was not set to True. The
# `use_pygments` key could be either set to False or not defined. It is omitted from output.
- kv_pairs = ' ' + ' '.join(
- '{k}="{v}"'.format(k=k, v=v) for k, v in config.items() if k != 'use_pygments'
+ kv_pairs = ''.join(
+ f' {k}="{_escape_attrib_html(v)}"' for k, v in config.items() if k != 'use_pygments'
)
- code = '<pre{id}{cls}><code{lang}{kv}>{code}</code></pre>'.format(
- id=id_attr,
- cls=class_attr,
- lang=lang_attr,
- kv=kv_pairs,
- code=self._escape(m.group('code'))
- )
+ code = self._escape(m.group('code'))
+ code = f'<pre{id_attr}{class_attr}><code{lang_attr}{kv_pairs}>{code}</code></pre>'
placeholder = self.md.htmlStash.store(code)
- text = '{}\n{}\n{}'.format(text[:m.start()],
- placeholder,
- text[m.end():])
+ text = f'{text[:m.start()]}\n{placeholder}\n{text[m.end():]}'
else:
break
return text.split("\n")
diff --git a/tests/test_syntax/extensions/test_fenced_code.py b/tests/test_syntax/extensions/test_fenced_code.py
index 76c8769..2cdde98 100644
--- a/tests/test_syntax/extensions/test_fenced_code.py
+++ b/tests/test_syntax/extensions/test_fenced_code.py
@@ -374,6 +374,24 @@ class TestFencedCode(TestCase):
extensions=[markdown.extensions.fenced_code.FencedCodeExtension(lang_prefix='lang-')]
)
+ def testFencedCodeEscapedAttrs(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ '''
+ ``` { ."weird #"foo bar=">baz }
+ # Some python code
+ ```
+ '''
+ ),
+ self.dedent(
+ '''
+ <pre id="&quot;foo"><code class="language-&quot;weird" bar="&quot;&gt;baz"># Some python code
+ </code></pre>
+ '''
+ ),
+ extensions=['fenced_code', 'attr_list']
+ )
+
class TestFencedCodeWithCodehilite(TestCase):