diff --git a/requirements.txt b/requirements.txt index 90ee39b71b..8b92504e32 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,6 +8,7 @@ colorama>=0.4.3 cryptography==38.0.3 dask==2022.9.2 deepdiff==6.3.0 +defusedxml==0.7.1 expandvars==0.9.0 fastapi[all]==0.89.1 fastapi-caching[redis]==0.3.0 diff --git a/src/fides/api/service/connectors/saas/connector_registry_service.py b/src/fides/api/service/connectors/saas/connector_registry_service.py index aab226ada2..fe37d50ad1 100644 --- a/src/fides/api/service/connectors/saas/connector_registry_service.py +++ b/src/fides/api/service/connectors/saas/connector_registry_service.py @@ -40,7 +40,7 @@ replace_dataset_placeholders, replace_version, ) -from fides.api.util.unsafe_file_util import verify_zip +from fides.api.util.unsafe_file_util import verify_svg, verify_zip from fides.config import CONFIG @@ -214,6 +214,7 @@ def save_template(cls, db: Session, zip_file: ZipFile) -> None: ) elif info.filename.endswith(".svg"): if not icon_contents: + verify_svg(file_contents) icon_contents = str_to_b64_str(file_contents) else: raise ValidationError( diff --git a/src/fides/api/util/unsafe_file_util.py b/src/fides/api/util/unsafe_file_util.py index d8ef9bc627..5c5d99a409 100644 --- a/src/fides/api/util/unsafe_file_util.py +++ b/src/fides/api/util/unsafe_file_util.py @@ -1,10 +1,34 @@ from typing import Optional from zipfile import ZipFile +from defusedxml.ElementTree import fromstring + +from fides.api.common_exceptions import ValidationError + MAX_FILE_SIZE = 16 * 1024 * 1024 # 16 MB CHUNK_SIZE = 1024 +def verify_svg(contents: str) -> None: + """ + Verifies the provided SVG content. + + This function checks the given SVG content string for potential issues and throws an exception if any are found. + It first attempts to parse the SVG content using 'defusedxml.fromstring'. If the parsing is unsuccessful, this + will raise an exception, indicating that the SVG content may contain unsafe XML. + + :param contents: The SVG content as a string. + :raises ValidationError: If the SVG content contains unsafe XML or 'use xlink' + """ + try: + fromstring(contents) + except Exception: + raise ValidationError("SVG file contains unsafe XML.") + + if "use xlink" in contents: + raise ValidationError("SVG files with xlink references are not allowed.") + + def verify_zip(zip_file: ZipFile, max_file_size: Optional[int] = None) -> None: """ Function to safely verify the contents of zipped files. It prevents potential diff --git a/tests/ops/util/test_unsafe_file_util.py b/tests/ops/util/test_unsafe_file_util.py index d452bcf23c..3cadda9b07 100644 --- a/tests/ops/util/test_unsafe_file_util.py +++ b/tests/ops/util/test_unsafe_file_util.py @@ -3,10 +3,57 @@ import pytest -from fides.api.util.unsafe_file_util import verify_zip +from fides.api.common_exceptions import ValidationError +from fides.api.util.unsafe_file_util import verify_svg, verify_zip from tests.ops.test_helpers.saas_test_utils import create_zip_file +class TestVerifySvg: + def test_verify_svg(self): + verify_svg( + """ + + + """ + ) + + def test_verify_svg_no_laughing_allowed(self): + """Test "billion laughs attack" is prevented""" + with pytest.raises(ValidationError) as exc: + verify_svg( + """ + + + + + + + + + + + + ]> + + &lol9; + + """ + ) + assert "SVG file contains unsafe XML." in str(exc.value) + + def test_verify_svg_with_xlink(self): + with pytest.raises(ValidationError) as exc: + verify_svg( + """ + + + + """ + ) + assert "SVG files with xlink references are not allowed." in str(exc.value) + + class TestVerifyZip: @pytest.fixture def zip_file(self) -> BytesIO: