Skip to content

Commit e3e51de

Browse files
committed
fix(image): validate figure width option
1 parent 96d0f57 commit e3e51de

2 files changed

Lines changed: 41 additions & 3 deletions

File tree

‎src/mistune/directives/image.py‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,11 @@ def parse(self, block: "BlockParser", m: Match[str], state: "BlockState") -> Dic
124124
fig_attrs = {}
125125
if align:
126126
fig_attrs["align"] = align
127-
for k in ["figwidth", "figclass"]:
128-
if k in options:
129-
fig_attrs[k] = options[k]
127+
figwidth = options.get("figwidth")
128+
if figwidth and _num_re.fullmatch(figwidth):
129+
fig_attrs["figwidth"] = figwidth
130+
if "figclass" in options:
131+
fig_attrs["figclass"] = options["figclass"]
130132

131133
children = [{"type": "block_image", "attrs": image_attrs}]
132134
content = self.parse_directive_content(block, m, state)

‎tests/test_security_image.py‎

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from unittest import TestCase
2+
3+
from mistune import create_markdown
4+
from mistune.directives import FencedDirective, Figure, Image
5+
6+
7+
class TestImageDirectiveSecurity(TestCase):
8+
def test_image_directive_rejects_css_dimension_injection(self):
9+
md = create_markdown(escape=True, plugins=[FencedDirective([Image()])])
10+
html = md(
11+
"```{image} x.jpg\n"
12+
":width: 100vw;height:100vh;position:fixed;top:0\n"
13+
":height: 50%\n"
14+
":alt: <alt>\n"
15+
"```\n"
16+
)
17+
18+
self.assertIn('alt="&lt;alt&gt;"', html)
19+
self.assertIn('style="height:50%;"', html)
20+
self.assertNotIn("position:fixed", html)
21+
22+
def test_figure_directive_escapes_class_and_rejects_figwidth_css_injection(self):
23+
md = create_markdown(escape=True, plugins=[FencedDirective([Figure()])])
24+
html = md(
25+
"```{figure} x.jpg\n"
26+
':figclass: evil" onclick="alert(1)\n'
27+
":figwidth: 10px;position:fixed\n"
28+
"\n"
29+
"caption\n"
30+
"```\n"
31+
)
32+
33+
self.assertIn('class="figure evil&quot; onclick=&quot;alert(1)"', html)
34+
self.assertNotIn('onclick="alert(1)"', html)
35+
self.assertNotIn("position:fixed", html)
36+
self.assertNotIn('style="width:', html)

0 commit comments

Comments
 (0)