From 54cd60c77a25acdf8ca8e20d7aaf88825a780e81 Mon Sep 17 00:00:00 2001 From: deacon Date: Sun, 15 Mar 2026 23:55:57 -0400 Subject: [PATCH 1/4] fix(deps): upgrade aioftp from ~=0.20.0 to ==0.27.2 aioftp ~=0.20.0 is flagged by the safety vulnerability database. Pin to 0.27.2 (latest stable) which resolves the advisory while maintaining full API compatibility with the existing FTP contact code (User, Permission, Server, ConnectionConditions, PathPermissions and worker are all present in the new release). Add tests/contacts/test_aioftp_version.py to assert the installed version meets the minimum threshold and that the server-side API surface expected by contact_ftp.py remains intact. --- requirements.txt | 2 +- tests/contacts/test_aioftp_version.py | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/contacts/test_aioftp_version.py diff --git a/requirements.txt b/requirements.txt index 42ee26a5c..36bbe3536 100644 --- a/requirements.txt +++ b/requirements.txt @@ -23,7 +23,7 @@ svglib==1.5.1 # debrief Markdown==3.8.1 # training dnspython==2.6.1 asyncssh==2.20.0 -aioftp~=0.20.0 +aioftp==0.27.2 packaging==23.2 croniter~=3.0.3 setuptools==78.1.1 diff --git a/tests/contacts/test_aioftp_version.py b/tests/contacts/test_aioftp_version.py new file mode 100644 index 000000000..3851d0099 --- /dev/null +++ b/tests/contacts/test_aioftp_version.py @@ -0,0 +1,24 @@ +"""Test that aioftp meets the minimum required version (>= 0.21.0) to avoid +the vulnerability flagged in aioftp ~= 0.20.0 by the safety DB.""" +import importlib.metadata + +import pytest + + +def test_aioftp_version_meets_minimum(): + """aioftp must be >= 0.21.0 to avoid CVE / safety-DB advisory for 0.20.x.""" + version_str = importlib.metadata.version("aioftp") + parts = tuple(int(p) for p in version_str.split(".")[:3]) + assert parts >= (0, 21, 0), ( + f"aioftp {version_str} is below the minimum required 0.21.0; " + "upgrade to 0.27.2 or later." + ) + + +def test_aioftp_server_api_intact(): + """Ensure the server-side API surface used by contact_ftp is available.""" + import aioftp + + for name in ("Server", "User", "Permission", "ConnectionConditions", + "PathPermissions", "worker"): + assert hasattr(aioftp, name), f"aioftp missing expected attribute: {name}" From fae2e5246b105212469747b42585eed1a1492fbb Mon Sep 17 00:00:00 2001 From: deacon Date: Mon, 16 Mar 2026 00:33:24 -0400 Subject: [PATCH 2/4] fix: address Copilot review feedback - Use packaging.version.Version for robust PEP 440 version comparison instead of manual split/int logic (avoids ValueError on pre/post suffixes) - Strengthen test_aioftp_server_api_intact to assert attributes are classes (not just present) and document that worker may be a function --- tests/contacts/test_aioftp_version.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/tests/contacts/test_aioftp_version.py b/tests/contacts/test_aioftp_version.py index 3851d0099..0562fba82 100644 --- a/tests/contacts/test_aioftp_version.py +++ b/tests/contacts/test_aioftp_version.py @@ -1,15 +1,16 @@ """Test that aioftp meets the minimum required version (>= 0.21.0) to avoid the vulnerability flagged in aioftp ~= 0.20.0 by the safety DB.""" import importlib.metadata +import inspect import pytest +from packaging.version import Version def test_aioftp_version_meets_minimum(): """aioftp must be >= 0.21.0 to avoid CVE / safety-DB advisory for 0.20.x.""" version_str = importlib.metadata.version("aioftp") - parts = tuple(int(p) for p in version_str.split(".")[:3]) - assert parts >= (0, 21, 0), ( + assert Version(version_str) >= Version("0.21.0"), ( f"aioftp {version_str} is below the minimum required 0.21.0; " "upgrade to 0.27.2 or later." ) @@ -19,6 +20,19 @@ def test_aioftp_server_api_intact(): """Ensure the server-side API surface used by contact_ftp is available.""" import aioftp - for name in ("Server", "User", "Permission", "ConnectionConditions", - "PathPermissions", "worker"): - assert hasattr(aioftp, name), f"aioftp missing expected attribute: {name}" + expected_classes = { + "Server": type, + "User": type, + "Permission": type, + "ConnectionConditions": type, + "PathPermissions": type, + } + for name, expected_type in expected_classes.items(): + obj = getattr(aioftp, name, None) + assert obj is not None, f"aioftp missing expected attribute: {name}" + assert isinstance(obj, type), ( + f"aioftp.{name} should be a class, got {type(obj)!r}" + ) + + # worker may be a function or coroutine function rather than a class + assert hasattr(aioftp, "worker"), "aioftp missing expected attribute: worker" From 91acc7dcca31dddf3e29b8a3af972d749136a5c2 Mon Sep 17 00:00:00 2001 From: deacon Date: Mon, 16 Mar 2026 09:56:58 -0400 Subject: [PATCH 3/4] Add callable() check for aioftp.worker and remove unused imports The hasattr check alone could produce false positives if worker exists but is non-callable. Also removes unused inspect and pytest imports. --- tests/contacts/test_aioftp_version.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/contacts/test_aioftp_version.py b/tests/contacts/test_aioftp_version.py index 0562fba82..ad7e0a598 100644 --- a/tests/contacts/test_aioftp_version.py +++ b/tests/contacts/test_aioftp_version.py @@ -1,9 +1,7 @@ """Test that aioftp meets the minimum required version (>= 0.21.0) to avoid the vulnerability flagged in aioftp ~= 0.20.0 by the safety DB.""" import importlib.metadata -import inspect -import pytest from packaging.version import Version @@ -36,3 +34,6 @@ def test_aioftp_server_api_intact(): # worker may be a function or coroutine function rather than a class assert hasattr(aioftp, "worker"), "aioftp missing expected attribute: worker" + assert callable(aioftp.worker), ( + f"aioftp.worker should be callable, got {type(aioftp.worker)!r}" + ) From 45fda51dccccaa1f86ce1d776b6f93dcd9eee5d6 Mon Sep 17 00:00:00 2001 From: Joshua Klosterman Date: Wed, 1 Apr 2026 17:57:39 -0400 Subject: [PATCH 4/4] Update test to use expected_type --- tests/contacts/test_aioftp_version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/contacts/test_aioftp_version.py b/tests/contacts/test_aioftp_version.py index ad7e0a598..d1436033f 100644 --- a/tests/contacts/test_aioftp_version.py +++ b/tests/contacts/test_aioftp_version.py @@ -28,7 +28,7 @@ def test_aioftp_server_api_intact(): for name, expected_type in expected_classes.items(): obj = getattr(aioftp, name, None) assert obj is not None, f"aioftp missing expected attribute: {name}" - assert isinstance(obj, type), ( + assert isinstance(obj, expected_type), ( f"aioftp.{name} should be a class, got {type(obj)!r}" )