diff --git a/.github/workflows/qt6-tests.yml b/.github/workflows/qt6-tests.yml
index 54c17470..46a142b8 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 vermin requests || true
+ pip install pyfakefs PySide6 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 2e5f8c61..ba3b8a1f 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
@@ -154,7 +154,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"
@@ -168,7 +168,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 9716868b..bfc94781 100644
--- a/AddonCatalogCacheCreator.py
+++ b/AddonCatalogCacheCreator.py
@@ -39,7 +39,7 @@
import requests
import subprocess
from typing import List
-import xml.etree.ElementTree
+from xml.etree.ElementTree import ParseError as XmlParseError
import zipfile
import AddonCatalog
@@ -282,7 +282,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 a8d320bd..674d8b78 100644
--- a/Resources/translations/run_translation_cycle.py
+++ b/Resources/translations/run_translation_cycle.py
@@ -36,7 +36,7 @@
import time
from functools import lru_cache
from urllib.parse import quote_plus
-from urllib.request import Request, urlopen, urlretrieve
+from urllib.request import Request, urlopen, urlretrieve, urlparse
CROWDIN_API_URL = "https://api.crowdin.com/api/v2"
CROWDIN_API_PROJECT_ID = "freecad-addons"
@@ -85,6 +85,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:
@@ -136,6 +139,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 953acd37..42ebaa94 100644
--- a/addonmanager_utilities.py
+++ b/addonmanager_utilities.py
@@ -423,6 +423,11 @@ 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 != "http" and parse_result.scheme != "https":
+ raise ValueError(
+ f"Invalid URL scheme: {parse_result.scheme} (only http and https are supported)"
+ )
if (
fci.FreeCADGui
and method is None
diff --git a/addonmanager_workers_startup.py b/addonmanager_workers_startup.py
index b100afde..949c31b9 100644
--- a/addonmanager_workers_startup.py
+++ b/addonmanager_workers_startup.py
@@ -27,7 +27,7 @@
import json
import os
from typing import List
-import xml.etree.ElementTree
+from xml.etree.ElementTree import ParseError as XmlParseError
import zipfile
from PySideWrapper import QtCore
@@ -134,7 +134,7 @@ def _get_custom_addons(self):
repo.installed_version = repo.installed_metadata.version
repo.updated_timestamp = os.path.getmtime(md_file)
repo.verify_url_and_branch(addon["url"], addon["branch"])
- except xml.etree.ElementTree.ParseError:
+ except XmlParseError:
fci.Console.PrintWarning(
f"An invalid or corrupted package.xml file was installed for custom addon {name}... ignoring the bad data.\n"
)
@@ -512,14 +512,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 f7f210e4..4ec2756e 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