diff --git a/.github/workflows/qt6-tests.yml b/.github/workflows/qt6-tests.yml
index 2dda6cff..e026e58b 100644
--- a/.github/workflows/qt6-tests.yml
+++ b/.github/workflows/qt6-tests.yml
@@ -66,7 +66,7 @@ jobs:
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
- pip install pyfakefs "PySide6<6.11" vermin requests || true
+ pip install pyfakefs "PySide6<6.11" vermin requests defusedxml || true
- name: Run App tests
run: |
diff --git a/Addon.py b/Addon.py
index edea74f3..e1acda96 100644
--- a/Addon.py
+++ b/Addon.py
@@ -29,7 +29,7 @@
from typing import Dict, Set, List, Optional
from threading import Lock
from enum import IntEnum, auto
-import xml.etree.ElementTree
+from xml.etree.ElementTree import ParseError as XmlParseError
try:
import importlib.metadata as importlib_metadata
@@ -320,7 +320,7 @@ def load_metadata_file(self, file: str) -> None:
if os.path.exists(file):
try:
metadata = MetadataReader.from_file(file)
- except xml.etree.ElementTree.ParseError:
+ except XmlParseError:
fci.Console.PrintWarning(
"An invalid or corrupted package.xml file was found in the cache for"
)
@@ -339,7 +339,7 @@ def _load_installed_metadata(self) -> None:
if os.path.isfile(installed_metadata_path):
try:
self.installed_metadata = MetadataReader.from_file(installed_metadata_path)
- except xml.etree.ElementTree.ParseError:
+ except XmlParseError:
fci.Console.PrintWarning(
"An invalid or corrupted package.xml file was found in installation of"
)
diff --git a/AddonCatalog.py b/AddonCatalog.py
index ec140df0..ab930e97 100644
--- a/AddonCatalog.py
+++ b/AddonCatalog.py
@@ -25,7 +25,7 @@
import base64
import datetime
import os
-import xml.etree.ElementTree
+from xml.etree.ElementTree import ParseError as XmlParseError
from dataclasses import dataclass
import json
from hashlib import sha256
@@ -157,7 +157,7 @@ def instantiate_addon(self, addon_id: str) -> Addon:
if self.metadata:
try:
self._load_addon_metadata(addon, self.metadata)
- except xml.etree.ElementTree.ParseError:
+ except XmlParseError:
fci.Console.PrintWarning(
"An invalid or corrupted package.xml file was installed "
f"for {addon.display_name}\n"
@@ -171,7 +171,7 @@ def instantiate_addon(self, addon_id: str) -> Addon:
try:
package_file = os.path.join(fci.DataPaths().mod_dir, addon_id, "package.xml")
addon.installed_metadata = MetadataReader.from_file(package_file)
- except (FileNotFoundError, xml.etree.ElementTree.ParseError, RuntimeError):
+ except (FileNotFoundError, XmlParseError, RuntimeError):
pass # If there was an error, just ignore it, no metadata is not fatal
most_recent_mtime = AddonCatalogEntry.most_recent_mtime(addon_id)
diff --git a/AddonCatalogCacheCreator.py b/AddonCatalogCacheCreator.py
index 90fad047..b663aeea 100644
--- a/AddonCatalogCacheCreator.py
+++ b/AddonCatalogCacheCreator.py
@@ -37,7 +37,7 @@
import re
import requests
import subprocess
-import xml.etree.ElementTree
+from xml.etree.ElementTree import ParseError as XmlParseError
import zipfile
import AddonCatalog
@@ -309,7 +309,7 @@ def generate_cache_entry_from_package_xml(
metadata = addonmanager_metadata.MetadataReader.from_bytes(
cache_entry.package_xml.encode("utf-8")
)
- except xml.etree.ElementTree.ParseError:
+ except XmlParseError:
print(f"ERROR: Failed to parse XML from {path_to_package_xml}")
return None
except RuntimeError:
diff --git a/Resources/translations/run_translation_cycle.py b/Resources/translations/run_translation_cycle.py
index a4a57328..ed543623 100644
--- a/Resources/translations/run_translation_cycle.py
+++ b/Resources/translations/run_translation_cycle.py
@@ -34,7 +34,7 @@
import tempfile
import time
from functools import lru_cache
-from urllib.parse import quote_plus
+from urllib.parse import quote_plus, urlparse
from urllib.request import Request, urlopen, urlretrieve
CROWDIN_API_URL = "https://api.crowdin.com/api/v2"
@@ -84,6 +84,9 @@ def _make_api_req(self, url, extra_headers=None, method="GET", data=None):
headers["Content-Type"] = "application/json"
data = json.dumps(data).encode("utf-8")
+ parsed_url = urlparse(url)
+ if parsed_url.scheme != "https":
+ raise Exception("API requests must be made over HTTPS")
request = Request(url, headers=headers, method=method, data=data)
request_result = urlopen(request)
if request_result.getcode() >= 300:
@@ -135,6 +138,11 @@ def status(self):
def download(self, build_id):
filename = f"{self.project_identifier}.zip"
response = self._make_project_api_req(f"/translations/builds/{build_id}/download")
+
+ parsed_url = urlparse(response["url"])
+ if parsed_url.scheme != "https":
+ raise Exception("API requests must be made over HTTPS")
+
urlretrieve(response["url"], filename)
print("download of " + filename + " complete")
diff --git a/addonmanager_icon_utilities.py b/addonmanager_icon_utilities.py
index 1e6bc7b4..ff38bafc 100644
--- a/addonmanager_icon_utilities.py
+++ b/addonmanager_icon_utilities.py
@@ -19,6 +19,7 @@
# #
################################################################################
+import defusedxml.ElementTree as ET
import re
import os
import struct
@@ -26,13 +27,6 @@
from PySideWrapper import QtCore, QtGui, QtSvg
-try:
- # If this system provides a secure parser, use that:
- import defusedxml.ElementTree as ET
-except ImportError:
- # Otherwise fall back to the Python standard parser
- import xml.etree.ElementTree as ET
-
from Addon import Addon
import addonmanager_freecad_interface as fci
diff --git a/addonmanager_metadata.py b/addonmanager_metadata.py
index a8462bd9..86e3f7e3 100644
--- a/addonmanager_metadata.py
+++ b/addonmanager_metadata.py
@@ -25,18 +25,12 @@
from __future__ import annotations
from dataclasses import dataclass, field
+import defusedxml.ElementTree as ET
from enum import IntEnum, auto
from typing import Tuple, Dict, List, Optional
from addonmanager_licenses import get_license_manager
-try:
- # If this system provides a secure parser, use that:
- import defusedxml.ElementTree as ET
-except ImportError:
- # Otherwise fall back to the Python standard parser
- import xml.etree.ElementTree as ET
-
@dataclass
class Contact:
diff --git a/addonmanager_utilities.py b/addonmanager_utilities.py
index 97226a4c..4ee88662 100644
--- a/addonmanager_utilities.py
+++ b/addonmanager_utilities.py
@@ -424,6 +424,9 @@ def blocking_get(url: str, method=None) -> bytes:
succeeded, or an empty string if it failed, or returned no data. The method argument is
provided mainly for testing purposes."""
p = b""
+ parse_result = urllib.parse.urlparse(url)
+ if parse_result.scheme != "https":
+ raise ValueError(f"Invalid URL scheme: {parse_result.scheme} (only https is supported)")
if (
fci.FreeCADGui
and method is None
diff --git a/addonmanager_workers_startup.py b/addonmanager_workers_startup.py
index 0ca6cb63..672d820b 100644
--- a/addonmanager_workers_startup.py
+++ b/addonmanager_workers_startup.py
@@ -587,14 +587,8 @@ def check_macro(macro_wrapper: Addon) -> None:
macro_wrapper.set_status(Addon.Status.CANNOT_CHECK)
return
- try:
- hasher1 = hashlib.sha1(usedforsecurity=False)
- hasher2 = hashlib.sha1(usedforsecurity=False)
- except TypeError:
- # To continue to support Python 3.8, we need to fall back if the usedforsecurity
- # is not available. This code should be removed when we drop support for 3.8.
- hasher1 = hashlib.sha1()
- hasher2 = hashlib.sha1()
+ hasher1 = hashlib.sha1(usedforsecurity=False)
+ hasher2 = hashlib.sha1(usedforsecurity=False)
hasher1.update(macro_wrapper.macro.code.encode("utf-8"))
new_sha1 = hasher1.hexdigest()
test_file_one = os.path.join(fci.DataPaths().macro_dir, macro_wrapper.macro.filename)
diff --git a/package.xml b/package.xml
index 0b2ff4c9..abefffd3 100644
--- a/package.xml
+++ b/package.xml
@@ -22,6 +22,7 @@
FreeCAD prior to 1.1 can see the InitGui.py file and run it. -->
AddonManager
./
+ defusedxml