Skip to content
Closed
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: 7 additions & 0 deletions changelog.d/_599-parse-raises-on-malformed-input.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Fixed
-----

* Stop ``parse()`` from raising on a backslash in a DOCTYPE entity value,
on character references outside the Unicode range, and on a GML
``srsName`` with a non-numeric EPSG code. Backslashes in entity values
are also no longer treated as escapes. (#599)
3 changes: 3 additions & 0 deletions feedparser/mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,9 @@ def handle_charref(self, ref):
c = int(ref[1:], 16)
else:
c = int(ref)
if c > 0x10FFFF or 0xD800 <= c <= 0xDFFF:
# Not a valid Unicode scalar value; use U+FFFD like HTML parsers.
c = 0xFFFD
text = chr(c).encode("utf-8")
self.elementstack[-1][2].append(text)

Expand Down
18 changes: 14 additions & 4 deletions feedparser/namespaces/georss.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,7 @@ def _end_gml_pos(self):
srs_dimension = context["where"].get("srsDimension", 2)
swap = True
if srs_name and "EPSG" in srs_name:
epsg = int(srs_name.split(":")[-1])
swap = bool(epsg in _geogCS)
swap = _epsg_is_geographic(srs_name)
geometry = _parse_georss_point(this, swap=swap, dims=srs_dimension)
if geometry:
self._save_where(geometry)
Expand All @@ -141,8 +140,7 @@ def _end_gml_poslist(self):
srs_dimension = context["where"].get("srsDimension", 2)
swap = True
if srs_name and "EPSG" in srs_name:
epsg = int(srs_name.split(":")[-1])
swap = bool(epsg in _geogCS)
swap = _epsg_is_geographic(srs_name)
geometry = _parse_poslist(this, self.ingeometry, swap=swap, dims=srs_dimension)
if geometry:
self._save_where(geometry)
Expand Down Expand Up @@ -190,6 +188,18 @@ def _gen_georss_coords(value, swap=True, dims=2):
return


def _epsg_is_geographic(srs_name):
"""Return True if an EPSG srsName names a geographic CRS.

Codes that are not integers are treated like a missing srsName.
"""
try:
epsg = int(srs_name.split(":")[-1])
except ValueError:
return True
return epsg in _geogCS


def _parse_georss_point(value, swap=True, dims=2):
# A point contains a single latitude-longitude pair, separated by
# whitespace. We'll also handle comma separators.
Expand Down
4 changes: 3 additions & 1 deletion feedparser/sanitizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,9 @@ def replace_doctype(data: bytes) -> tuple[str | None, bytes, dict[str, str]]:
+ b">\n<!ENTITY ".join(safe_entities)
+ b">\n]>"
)
data = RE_DOCTYPE_PATTERN.sub(replacement, head) + data
# Use a function so backslashes in entity values are not treated as
# escapes or group references by re.sub().
data = RE_DOCTYPE_PATTERN.sub(lambda _: replacement, head) + data

# Precompute the safe entities for the loose parser.
entities = {
Expand Down
9 changes: 9 additions & 0 deletions tests/illformed/charref_out_of_range.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<!--
Description: character references outside the Unicode range become U+FFFD
Expect: bozo and feed['title'] == 'a \ufffd b \ufffd c \ufffd d'
-->
<rss version="2.0">
<channel>
<title>a &#1114112; b &#x110000; c &#55296; d</title>
</channel>
</rss>
16 changes: 16 additions & 0 deletions tests/wellformed/geo/gml_point_epsg_not_numeric.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!--
Description: srsName with a non-numeric EPSG code is treated like a missing srsName
Expect: entries[0]['where']['type'] == 'Point' and entries[0]['where']['coordinates'] == (31.1732, 36.9382)
-->
<feed
xmlns="http://www.w3.org/2005/Atom"
xmlns:georss="http://www.georss.org/georss"
xmlns:gml="http://www.opengis.net/gml">
<entry>
<georss:where>
<gml:Point srsName="EPSG:unknown">
<gml:pos>36.9382 31.1732</gml:pos>
</gml:Point>
</georss:where>
</entry>
</feed>
16 changes: 16 additions & 0 deletions tests/wellformed/rss/entity_in_doctype_backslash.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<!--
Description: backslashes in a DOCTYPE entity value are kept as-is
Expect: not bozo and entries[0]['guid'] == r'C:\data\1\new'
-->

<!DOCTYPE rss [
<!ENTITY path "C:\data\1\new">
]>

<rss version="2.0">
<channel>
<item>
<guid isPermaLink='false'>&path;</guid>
</item>
</channel>
</rss>