GitHub

@@ -0,0 +1,76 @@

1+

"""Playwright tests for markdown-svg-renderer.html."""

2+3+

import pathlib

4+5+

from playwright.sync_api import Page, expect

6+7+8+

test_dir = pathlib.Path(__file__).parent.absolute()

9+

root = test_dir.parent.absolute()

10+11+12+

def test_svg_is_rendered_raw_in_a_network_isolated_iframe(

13+

page: Page, unused_port_server

14+

):

15+

unused_port_server.start(root)

16+

leaked_urls = []

17+18+

def record_leak(route):

19+

leaked_urls.append(route.request.url)

20+

route.abort()

21+22+

page.route("**/svg-leak-probe-*", record_leak)

23+

page.goto(

24+

f"http://127.0.0.1:{unused_port_server.port}/markdown-svg-renderer.html"

25+

)

26+27+

svg = """<svg viewBox="0 0 120 80" xmlns="http://www.w3.org/2000/svg">

28+

<style>.wheel { fill: #22c55e; }</style>

29+

<defs><circle id="wheel" class="wheel" r="20"/></defs>

30+

<use href="#wheel" x="30" y="40"/>

31+

<use href="#wheel" x="90" y="40"/>

32+

<script>

33+

document.documentElement.setAttribute("data-script-ran", "yes");

34+

fetch("http://127.0.0.1:%s/svg-leak-probe-script");

35+

</script>

36+

<image href="http://127.0.0.1:%s/svg-leak-probe-image"/>

37+

<foreignObject width="10" height="10">

38+

<iframe xmlns="http://www.w3.org/1999/xhtml"

39+

src="http://127.0.0.1:%s/svg-leak-probe-frame"></iframe>

40+

</foreignObject>

41+

</svg>""" % ((unused_port_server.port,) * 3)

42+43+

page.locator("#input").fill(f"```svg\n{svg}\n```")

44+

block = page.locator("svg-block")

45+

expect(block).to_be_visible()

46+47+

# The SVG is not passed through an allowlist, so valid SVG features such

48+

# as style and use survive intact.

49+

assert block.get_attribute("data-svg") == svg + "\n"

50+51+

iframe_locator = page.locator("svg-block iframe")

52+

assert iframe_locator.get_attribute("sandbox") == ""

53+

csp = iframe_locator.get_attribute("csp")

54+

assert csp is not None

55+

assert "default-src 'none'" in csp

56+

assert "script-src 'none'" in csp

57+

assert "style-src 'unsafe-inline'" in csp

58+

assert "img-src data: blob:" in csp

59+60+

srcdoc = iframe_locator.get_attribute("srcdoc")

61+

assert srcdoc is not None

62+

assert srcdoc.startswith(

63+

'<!doctype html>\n<meta http-equiv="Content-Security-Policy"'

64+

)

65+66+

iframe_element = iframe_locator.element_handle()

67+

assert iframe_element is not None

68+

iframe = iframe_element.content_frame()

69+

assert iframe is not None

70+

expect(iframe.locator("use")).to_have_count(2)

71+

expect(iframe.locator("circle")).to_have_css("fill", "rgb(34, 197, 94)")

72+73+

page.wait_for_timeout(500)

74+

assert iframe.locator("svg").get_attribute("data-script-ran") is None

75+

assert leaked_urls == []

76+

Read the original on github.com ↗