Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/moin/converters/_tests/test_html_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,11 +241,12 @@ def test_span_html_element(self, input, xpath):
# <page><body><div><p><a xlink:href="http://www.base-url.com/myPage.html">Test</a></p></div></body></page>
'/page/body/div/p/a[@xlink:href="http://www.base-url.com/myPage.html"]',
),
# verify invalid or forbidden uri schemes are removed
# only approved URI schemes are used in a "href"
# (others are handled as part of a local item name):
(
"""<html><p><a href="javascript:alert('hi')">Test</a></p></html>""",
# <page><body><p>javascript:alert('hi')</p></body></page>
"""/page/body/p[text()="javascript:alert('hi')"]""",
# <page><body><p><a xlink:href="wiki.local:javascript:alert%28'hi'%29">Text</a></p></body></page>
"""/page/body/p/a[text()="Test"][@xlink:href="wiki.local:javascript:alert%28'hi'%29"]""",
),
]

Expand Down
47 changes: 30 additions & 17 deletions src/moin/converters/_tests/test_rst_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,6 @@ def setup_class(self):
"</code>.</p></body></page>",
),
("a _`Link`", '<page><body><p>a <span id="link">Link</span></p></body></page>'),
(
"`Text <javascript:alert('xss')>`_",
'<page><body><p><admonition type="error">Text</admonition></p></body></page>',
),
(
"Text\n\n~~~~~\n\nTest",
'<page><body><p>Text</p><separator xhtml:class="moin-hr3" /><p>Test</p></body></page>',
Expand Down Expand Up @@ -317,13 +313,7 @@ def test_field_list(self, input, output):
'<page><body><p>Abra</p><span id="example" /><p>Abra <a xlink:href="wiki.local:#example">example</a> arba</p></body></page>',
),
(
"""
Abra example_ arba

.. _example:
.. _alias:

text""",
"Abra example_ arba\n\n.. _example:\n.. _alias:\n\ntext",
'<page><body><p>Abra <a xlink:href="wiki.local:#example">example</a> arba</p><span id="alias" /><span id="example" /><p>text</p></body></page>',
),
( # A reference_ with no matching target links to a local Wiki item.
Expand All @@ -336,32 +326,51 @@ def test_field_list(self, input, output):
),
(
"`Whitespace is\nnormalized\xA0& CÄSE is Kept.`_",
'<page><body><p><a xlink:href="wiki.local:Whitespace%20is%20normalized%20&amp;%20CÄSE%20is%20Kept.">Whitespace is\nnormalized\xA0&amp; CÄSE is Kept.</a></p></body></page>',
'<page><body><p><a xlink:href="wiki.local:Whitespace%20is%20normalized%20&amp;%20CÄSE%20is%20Kept.">'
"Whitespace is\nnormalized\xA0&amp; CÄSE is Kept.</a></p></body></page>",
),
( # in rST, reference-name matching is case insensitive:
"Chapter 1\n===============\n\nA reference to `chapter 1`_.\n",
'<page><body><h outline-level="1">Chapter 1</h><p>A reference to <a xlink:href="wiki.local:#Chapter_1">chapter 1</a>.</p></body></page>',
'<page><body><h outline-level="1">Chapter 1</h>'
'<p>A reference to <a xlink:href="wiki.local:#Chapter_1">chapter 1</a>.</p></body></page>',
),
( # check handling of non-ASCII chars:
"τίτλος\n^^^^^^\n\nA reference to `τίτλος`_.\n",
'<page><body><h outline-level="1">τίτλος</h><p>A reference to <a xlink:href="wiki.local:#A.2BA8QDrwPEA7sDvwPC-">τίτλος</a>.</p></body></page>',
'<page><body><h outline-level="1">τίτλος</h>'
'<p>A reference to <a xlink:href="wiki.local:#A.2BA8QDrwPEA7sDvwPC-">τίτλος</a>.</p></body></page>',
),
(
"§ With % strange & siLLY <title>\n"
"--------------------------------\n\n"
"Reference to `§ With % strange\n"
"& siLLY \\<title>`_.\n",
'<page><body><h outline-level="1">§ With % strange &amp; siLLY &lt;title&gt;</h>'
'<p>Reference to <a xlink:href="wiki.local:#A.2BAKc_With_.25_strange_.26_siLLY_.3Ctitle.3E">§ With % strange\n'
'<p>Reference to <a xlink:href="wiki.local:#A.2BAKc_With_.25_strange_.26_siLLY_.3Ctitle.3E">'
"§ With % strange\n"
"&amp; siLLY &lt;title&gt;</a>.</p></body></page>",
),
(
"http://www.python.org/",
'<page><body><p><a xlink:href="http://www.python.org/">http://www.python.org/</a></p></body></page>',
),
("http:Home", '<page><body><p><a xlink:href="wiki.local:Home">http:Home</a></p></body></page>'),
("`Home <http:Home>`_", '<page><body><p><a xlink:href="wiki.local:Home">Home</a></p></body></page>'),
( # legacy syntax for Wiki-internal links (use URI references without scheme instead)
"http:Home",
'<page><body><p><a xlink:href="wiki.local:Home">http:Home</a></p></body></page>',
),
("`<http:Home>`__", '<page><body><p><a xlink:href="wiki.local:Home">http:Home</a></p></body></page>'),
(
r"`<https:Home:\ alone>`__",
'<page><body><p><a xlink:href="wiki.local:Home:%20alone">https:Home: alone</a></p></body></page>',
),
( # no URI scheme: resolve as wiki-internal link
"`<Home>`__",
'<page><body><p><a xlink:href="wiki.local:Home">Home</a></p></body></page>',
),
(
r"`<Home:\ alone>`__",
'<page><body><p><a xlink:href="wiki.local:Home:%20alone">Home: alone</a></p></body></page>',
),
( # rST recognizes e-mail addresses
"mailto:[email protected]",
'<page><body><p><a xlink:href="mailto:[email protected]">mailto:[email protected]</a></p></body></page>',
),
Expand All @@ -373,6 +382,10 @@ def test_field_list(self, input, output):
"`Write to me`_ with your questions.\n\n.. _Write to me: [email protected]",
'<page><body><p><a xlink:href="mailto:[email protected]">Write to me</a> with your questions.</p></body></page>',
),
( # URI schemes not on the whitelist are interpreted as local wiki item names
"`Text <javascript:alert('xss')>`_",
"""<page><body><p><a xlink:href="wiki.local:javascript:alert%28'xss'%29">Text</a></p></body></page>""",
),
]

@pytest.mark.usefixtures("_app_ctx")
Expand Down
6 changes: 4 additions & 2 deletions src/moin/converters/docbook_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
# in case converters become an independent package
flaskg = None

from moin.constants.misc import URI_SCHEMES
from moin.utils.iri import Iri
from moin.utils.mime import Type, type_moin_document
from moin.utils.tree import moin_page, xlink, docbook, xml, html, xinclude
Expand Down Expand Up @@ -862,8 +863,9 @@ def visit_docbook_link(self, element, depth):
if linkend:
href = "".join(["#", linkend])
iri = Iri(href)
if iri.scheme is None:
iri.scheme = "wiki.local"
# ensure a safe scheme, fall back to wiki-internal reference:
if iri.scheme not in URI_SCHEMES:
iri = Iri("wiki.local:" + href)
attrib[xlink.href] = iri
return self.new_copy(moin_page.a, element, depth, attrib)

Expand Down
22 changes: 6 additions & 16 deletions src/moin/converters/html_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@

from markupsafe import escape

from moin.constants.misc import URI_SCHEMES
from moin.i18n import _
from moin.utils.iri import Iri
from moin.utils.tree import html, moin_page, xlink, xml
from moin.utils.mime import Type, type_moin_document

from . import default_registry
from ._util import allowed_uri_scheme, decode_data, normalize_split_text
from ._util import decode_data, normalize_split_text

from moin import log

Expand Down Expand Up @@ -425,21 +426,10 @@ def visit_xhtml_a(self, element):
href = element.get(html.href)
if self.base_url:
href = "".join([self.base_url, href])
if allowed_uri_scheme(href):
iri = Iri(href)
else:
# URI schemes that are not in the whitelist like: """<a href="javascript:alert('hi')">Test</a>"""
# are converted to: """javascript:alert('hi')"""
# TODO: don't drop the link text, convert to
#
# Test &gt;javascript:alert('hi')&lt;
#
# orr treat the href as wiki-local URI-reference:
#
# href="wiki.local:javascript:alert('hi')
return href
if iri.scheme is None:
iri.scheme = "wiki.local"
iri = Iri(href)
# ensure a safe scheme, fall back to wiki-internal reference
if iri.scheme not in URI_SCHEMES:
iri = Iri("wiki.local:" + href)
attrib[key] = iri
return self.new_copy(moin_page.a, element, attrib)

Expand Down
8 changes: 2 additions & 6 deletions src/moin/converters/markdown_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -372,13 +372,9 @@ def visit_a(self, element):
attrib[html.title_] = element.attrib.get("title")
href = postproc_text(self.markdown, element.attrib.get("href"))
iri = Iri(href)
# iri has authority, fragment, path, query, scheme = none,none,path,none
# Check, if the IRI scheme is whitelisted,
# if not, handle the IRI as wiki-local reference:
# ensure a safe scheme, fall back to wiki-internal reference
if iri.scheme not in URI_SCHEMES:
if iri.scheme:
iri.path = f"{iri.scheme}:{iri.path}"
iri.scheme = "wiki.local"
iri = Iri("wiki.local:" + href)
attrib[key] = iri
return self.new_copy(moin_page.a, element, attrib)

Expand Down
28 changes: 12 additions & 16 deletions src/moin/converters/rst_in.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@
# in case converters become an independent package
flaskg = None

from moin.constants.misc import URI_SCHEMES
from moin.utils.iri import Iri
from moin.utils.tree import html, moin_page, xlink, xinclude
from moin.utils.mime import Type, type_moin_document
from moin.wikiutil import anchor_name_from_text

from . import default_registry
from ._util import allowed_uri_scheme, decode_data, normalize_split_text
from ._util import decode_data, normalize_split_text

from moin import log

Expand Down Expand Up @@ -638,11 +639,6 @@ def visit_reference(self, node):
self.close_moin_page_node()
return

if not allowed_uri_scheme(refuri):
# TODO: prepend "wiki.local" as in "moin_in"?
self.visit_error(node)
return

if refuri == "" and "refid" in node:
# internal cross-links
refid = node["refid"]
Expand All @@ -652,16 +648,16 @@ def visit_reference(self, node):
if isinstance(target_node, nodes.section):
title = target_node[0]
refid = anchor_name_from_text(title.astext())
refuri = Iri(scheme="wiki.local", fragment=refid)

if isinstance(refuri, str) and refuri.startswith("http"):
if "://" not in refuri:
refuri = refuri.split(":")[1]
iri = Iri(refuri)
if iri.scheme is None:
iri.scheme = "wiki.local"
refuri = iri
self.open_moin_page_node(moin_page.a(attrib={xlink.href: refuri}))
iri = Iri(scheme="wiki.local", fragment=refid)
elif refuri.startswith("http") and "://" not in refuri:
# convert links like "http:Home" to wiki-internal references
iri = Iri("wiki.local:" + refuri.split(":", maxsplit=1)[1])
else:
# ensure a safe scheme, fall back to wiki-internal reference
iri = Iri(refuri)
if iri.scheme not in URI_SCHEMES:
iri = Iri("wiki.local:" + refuri)
self.open_moin_page_node(moin_page.a(attrib={xlink.href: iri}))

def depart_reference(self, node):
self.close_moin_page_node()
Expand Down