Skip to content
Open
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
2 changes: 1 addition & 1 deletion .github/workflows/qt6-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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: |
Expand Down
6 changes: 3 additions & 3 deletions Addon.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
)
Expand All @@ -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"
)
Expand Down
6 changes: 3 additions & 3 deletions AddonCatalog.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions AddonCatalogCacheCreator.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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:
Expand Down
10 changes: 9 additions & 1 deletion Resources/translations/run_translation_cycle.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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")
Comment on lines +88 to +90
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The urlparse function is used here but is not imported. The import statement on line 38 only imports quote_plus from urllib.parse, but not urlparse. This will cause a NameError at runtime when this code is executed.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While urlparse is imported here from urllib.request (so no runtime error), it is a transitive name. The correct import should be from urllib.parse as it is the real source of the symbol.

https://docs.python.org/3/library/urllib.parse.html#urllib.parse.urlparse

request = Request(url, headers=headers, method=method, data=data)
request_result = urlopen(request)
if request_result.getcode() >= 300:
Expand Down Expand Up @@ -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")
Comment thread
chennes marked this conversation as resolved.

urlretrieve(response["url"], filename)
print("download of " + filename + " complete")

Expand Down
8 changes: 1 addition & 7 deletions addonmanager_icon_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,14 @@
# #
################################################################################

import defusedxml.ElementTree as ET
import re
import os
import struct
from typing import Optional, List

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

Expand Down
8 changes: 1 addition & 7 deletions addonmanager_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
5 changes: 5 additions & 0 deletions addonmanager_utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -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":
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In other parts you restrict to https, but here you allow http as well. Is this a special case?

raise ValueError(
f"Invalid URL scheme: {parse_result.scheme} (only http and https are supported)"
)
if (
fci.FreeCADGui
and method is None
Expand Down
14 changes: 4 additions & 10 deletions addonmanager_workers_startup.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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"
)
Expand Down Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
FreeCAD prior to 1.1 can see the InitGui.py file and run it. -->
<name>AddonManager</name>
<subdirectory>./</subdirectory>
<depend optional="false" type="python">defusedxml</depend>
</workbench>
</content>
</package>
Loading