CVE-2023-27586: Server-Side Request Forgery (SSRF) (CWE-918)
value: CairoSVG is an SVG converter based on Cairo, a 2D graphics library. Prior to version 2.7.0, Cairo can send requests to external hosts when processing SVG files. A malicious actor could send a specially crafted SVG file that allows them to perform a server-side request forgery or denial of service. Version 2.7.0 disables CairoSVG's ability to access other files online by default.
CWE description: The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
CVSS v3 base score: 7.1
Published: 2023-03-20T16:15Z
Repository: https://github.com/Kozea/CairoSVG
Language: Python
Files changed: cairosvg/__main__.py, cairosvg/parser.py, cairosvg/surface.py
Commit date: 2023-03-20 15:31:13 +0100
Commit message: Don’t allow fetching external files unless explicitly asked for

Diff: diff --git a/cairosvg/__main__.py b/cairosvg/__main__.py
index 3ff6b5d..0aad3d7 100644
--- a/cairosvg/__main__.py
+++ b/cairosvg/__main__.py
@@ -40,12 +40,12 @@ def main(argv=None, stdout=None, stdin=None):
     parser.add_argument(
         '-i', '--invert-images', action='store_true',
         help='replace every raster pixel with its complementary color')
     parser.add_argument(
         '-u', '--unsafe', action='store_true',
-        help='resolve XML entities and allow very large files '
-             '(WARNING: vulnerable to XXE attacks and various DoS)')
+        help='fetch external files, resolve XML entities and allow very large '
+             'files (WARNING: vulnerable to XXE attacks and various DoS)')
     parser.add_argument(
         '--output-width', default=None, type=float,
         help='desired output width in pixels')
     parser.add_argument(
         '--output-height', default=None, type=float,
diff --git a/cairosvg/parser.py b/cairosvg/parser.py
index f0f3a82..61275f0 100644
--- a/cairosvg/parser.py
+++ b/cairosvg/parser.py
@@ -388,10 +388,16 @@ class Tree(Node):
             if len(bytestring) >= 2 and bytestring[:2] == b'\x1f\x8b':
                 bytestring = gzip.decompress(bytestring)
             tree = ElementTree.fromstring(
                 bytestring, forbid_entities=not unsafe,
                 forbid_external=not unsafe)
+
+        # Don’t allow fetching external files unless explicitly asked for
+        if 'url_fetcher' not in kwargs and not unsafe:
+            self.url_fetcher = (
+                lambda *args, **kwargs: b'<svg width="1" height="1"></svg>')
+
         self.xml_tree = tree
         root = cssselect2.ElementWrapper.from_xml_root(tree)
         style = parent.style if parent else css.parse_stylesheets(self, url)
         if element_id:
             for element in root.iter_subtree():
diff --git a/cairosvg/surface.py b/cairosvg/surface.py
index c5569e7..a2f7736 100644
--- a/cairosvg/surface.py
+++ b/cairosvg/surface.py
@@ -111,11 +111,12 @@ class Surface(object):
 
         :param dpi: The ratio between 1 inch and 1 pixel.
         :param parent_width: The width of the parent container in pixels.
         :param parent_height: The height of the parent container in pixels.
         :param scale: The ouptut scaling factor.
-        :param unsafe: A boolean allowing XML entities and very large files
+        :param unsafe: A boolean allowing external file access, XML entities
+                       and very large files
                        (WARNING: vulnerable to XXE attacks and various DoS).
 
         Specifiy the output with:
 
         :param write_to: The filename of file-like object where to write the

Vulnerable code:         help='resolve XML entities and allow very large files '
             '(WARNING: vulnerable to XXE attacks and various DoS)')
        :param unsafe: A boolean allowing XML entities and very large files
Fixed code:         help='fetch external files, resolve XML entities and allow very large '
             'files (WARNING: vulnerable to XXE attacks and various DoS)')

        # Don’t allow fetching external files unless explicitly asked for
        if 'url_fetcher' not in kwargs and not unsafe:
            self.url_fetcher = (
                lambda *args, **kwargs: b'<svg width="1" height="1"></svg>')

        :param unsafe: A boolean allowing external file access, XML entities
                       and very large files