Skip to content

Add FtpEngine/FtpLibrary tests#1487

Open
Raavi29 wants to merge 14 commits intoOWASP:masterfrom
Raavi29:add-ftp-tests
Open

Add FtpEngine/FtpLibrary tests#1487
Raavi29 wants to merge 14 commits intoOWASP:masterfrom
Raavi29:add-ftp-tests

Conversation

@Raavi29
Copy link
Copy Markdown

@Raavi29 Raavi29 commented Apr 1, 2026

Proposed change

Summary

Adds unit tests for nettacker/core/lib/ftp.py, bringing coverage from 0% to 100% (12 statements, 0 missed).

Tests Added

  • TestFtpLibraryBruteForce - 8 tests covering:
    1. Successful login returns correct dict with host/port/username/password
    2. connect(), login(), close() each called with correct arguments
    3. ftplib.error_perm raised on wrong credentials
    4. TimeoutError raised on connection timeout
    5. TImeout value correctly passed to FTP constructor
  • TestFtpEngine - 2 tests for engine instantiation and library assignment

Approach

FtpLibrary uses self.client (assigned to ftplib.FTP) rather than calling ftplib.FTP directly. All tests use patch.object(FtpLibrary, 'client') to intercept network calls - no real FTP connections made.

Tests run

python -m pytest tests/core/lib/test_ftp.py -v — 10 passed

Type of change

  • New core framework functionality
  • Bugfix (non-breaking change which fixes an issue)
  • Code refactoring without any functionality changes
  • New or existing module/payload change
  • Documentation/localization improvement
  • Test coverage improvement
  • Dependency upgrade
  • Other improvement (best practice, cleanup, optimization, etc)

Checklist

  • I've followed the contributing guidelines
  • I have digitally signed all my commits in this PR
  • I've run make pre-commit and confirm it didn't generate any warnings/changes
  • I've run make test, I confirm all tests passed locally
  • I've added/updated any relevant documentation in the docs/ folder
  • I've linked this PR with an open issue
  • I've tested and verified that my code works as intended and resolves the issue as described
  • I have attached screenshots demonstrating my code works as intended
  • I've checked all other open PRs to avoid submitting duplicate work
  • I confirm that the code and comments in this PR are not direct unreviewed outputs of AI
  • I confirm that I am the Sole Responsible Author for every line of code, comment, and design decision

Raavi29 added 5 commits March 25, 2026 08:21
- Tests weak algorithms: sha1, md5, md2, md4
- Tests case insensitivity (uppercase input)
- Tests safe algorithms: sha256, sha512, sha384
- Tests edge cases: empty string, random string
- All 11 tests passing

Part of improving test coverage for GSoC 2026
- TestIsSingleIPv4: 12 tests for IPv4 address validation
- TestIsSingleIPv6: 10 tests including None bug documentation
- TestIsIPv4Range: 8 tests (documents naming swap with is_ipv4_cidr)
- TestIsIPv4CIDR: 7 tests (documents naming swap with is_ipv4_range)
- TestIsIPv6Range: 6 tests for IPv6 dash-range detection
- TestIsIPv6CIDR: 8 tests for IPv6 CIDR detection
- TestGenerateIPRange: 7 tests covering both code branches
- TestGetIPRange: 4 tests using unittest.mock for HTTP isolation

Coverage: nettacker/core/ip.py 0% -> 83%
Note: is_ipv4_range/is_ipv4_cidr and is_ipv6_range/is_ipv6_cidr
appear to have swapped names - documented in test docstrings
- TestFtpLibraryBruteForce: 8 tests covering successful login,
  method call verification, error_perm on wrong password,
  timeout handling, and return value structure
- TestFtpEngine: 2 tests for engine instantiation and library assignment
- All network calls mocked via patch.object(FtpLibrary, 'client')

Coverage: nettacker/core/lib/ftp.py 0% -> 100%
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 1, 2026

Summary by CodeRabbit

  • New Features

    • Added FTPS (FTP over SSL/TLS) support for secure file transfers
  • Bug Fixes

    • Improved FTP connection handling to ensure connections are properly closed even when errors occur during connection or authentication
  • Tests

    • Added comprehensive unit tests for FTP and FTPS operations

Walkthrough

Added comprehensive unit tests for FTP and FTPS functionality alongside an error-handling improvement. Tests validate FTP/FTPS engine initialization, brute-force method behavior, exception propagation, and timeout handling. Production code now wraps connection operations in try-finally to ensure proper resource cleanup.

Changes

Cohort / File(s) Summary
FTP/FTPS Test Suite
tests/core/lib/test_ftp.py, tests/core/lib/test_ftps.py
Added 147 lines of unit tests covering FtpEngine/FtpsEngine initialization, FtpLibrary/FtpsLibrary inheritance, successful and error-path brute-force execution, timeout parameter forwarding, and FTP connection lifecycle management.
FTP Library Implementation
nettacker/core/lib/ftp.py
Refactored brute_force method to wrap connect and login operations in try-finally, ensuring connection.close() executes even when exceptions occur during connection or authentication.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add FtpEngine/FtpLibrary tests' directly and concisely describes the main change: adding test coverage for FTP-related classes.
Description check ✅ Passed The description is comprehensive and directly related to the changeset, detailing tests added, coverage improvements, testing approach, and test results.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (1)
tests/core/lib/test_ftp.py (1)

65-75: Minor redundancy with test_successful_login_returns_dict.

This test checks that keys exist in the result, but test_successful_login_returns_dict already implicitly verifies this by asserting specific values for the same keys. Consider consolidating or removing this test to reduce duplication.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/core/lib/test_ftp.py` around lines 65 - 75, The
test_result_contains_all_keys test is redundant because
test_successful_login_returns_dict already asserts the same keys/values; remove
test_result_contains_all_keys (or merge its intent into
test_successful_login_returns_dict) to avoid duplicate assertions—locate the
test function named test_result_contains_all_keys in tests/core/lib/test_ftp.py
that patches FtpLibrary.client and either delete that test or replace it with a
focused assertion (e.g., only validate structure differences not covered by
test_successful_login_returns_dict) so the suite remains concise.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@tests/core/lib/test_ssl.py`:
- Around line 19-38: Add a test that verifies non-prefix matches for the current
"unsafe_algo in algo" behavior of is_weak_hash_algo by asserting that an
algorithm string with the weak token not at the start (e.g.,
"rsaWithSHA1Encryption") returns True; locate the test cases in
tests/core/lib/test_ssl.py alongside the other weak-hash tests and add a new
test method (mirroring naming style like test_sha1_uppercase_is_weak) that calls
is_weak_hash_algo("rsaWithSHA1Encryption") and asserts True so the suite will
detect changes from containment to startswith logic.

In `@tests/test_ip.py`:
- Around line 261-296: Add a success-path unit test (e.g.,
test_success_returns_generated_range) that mocks nettacker.core.ip.requests.get
to return a valid API payload (set return_value.content to JSON bytes matching
the structure get_ip_range expects), call get_ip_range with a sample IP, and
assert the result is the non-fallback range by comparing it to the output of
generate_ip_range for that sample IP (i.e., result ==
generate_ip_range(sample_ip) and result != [sample_ip]); use the same patch
style as the existing tests.
- Line 1: Move the test file so it mirrors the package layout: create
tests/core/ and move tests/test_ip.py to tests/core/test_ip.py; then update the
test's import(s) to import the production module using its package path (e.g.,
from nettacker.core.ip import <function_or_class_under_test> or import
nettacker.core.ip as ip) so pytest discovers it under the mirrored layout and
the tests reference the correct module symbols.
- Around line 93-97: The test currently catches any Exception using
pytest.raises((TypeError, Exception)); narrow this to the specific expected
exception by replacing the tuple with TypeError only so test_none_raises asserts
that is_single_ipv6(None) raises a TypeError (the error coming from
netaddr.valid_ipv6(None)), updating the pytest.raises usage in the
test_none_raises function accordingly.

---

Nitpick comments:
In `@tests/core/lib/test_ftp.py`:
- Around line 65-75: The test_result_contains_all_keys test is redundant because
test_successful_login_returns_dict already asserts the same keys/values; remove
test_result_contains_all_keys (or merge its intent into
test_successful_login_returns_dict) to avoid duplicate assertions—locate the
test function named test_result_contains_all_keys in tests/core/lib/test_ftp.py
that patches FtpLibrary.client and either delete that test or replace it with a
focused assertion (e.g., only validate structure differences not covered by
test_successful_login_returns_dict) so the suite remains concise.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 008303d2-a33b-4a14-a10f-73a78e9185a6

📥 Commits

Reviewing files that changed from the base of the PR and between f4c1bbf and b92e5c6.

📒 Files selected for processing (4)
  • .github/workflows/ci_cd.yml
  • tests/core/lib/test_ftp.py
  • tests/core/lib/test_ssl.py
  • tests/test_ip.py

Raavi29 added a commit to Raavi29/Nettacker that referenced this pull request Apr 2, 2026
Addresses CodeRabbit feedback on PR OWASP#1487 - all previous weak hash
tests had the weak token at the start of the string. This test
confirms is_weak_hash_algo uses 'in' not 'startswith', so weak tokens
are detected anywhere in the algorithm string.
@Raavi29
Copy link
Copy Markdown
Author

Raavi29 commented Apr 2, 2026

Latest commits: removed tests/test_ip.py (duplicate of tests/core/test_ip.py), restored original 574-line SSL test suite that was accidentally stripped from this branch.

@Raavi29 Raavi29 requested a review from pUrGe12 April 2, 2026 11:51
Copy link
Copy Markdown
Collaborator

@arkid15r arkid15r left a comment

Choose a reason for hiding this comment

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

Hi @Raavi29
Thanks for adding this!

I updated code with some improvements, please feel free to modify if you have suggestions.

@arkid15r arkid15r enabled auto-merge April 3, 2026 01:07
@arkid15r arkid15r changed the title Add tests for FtpLibrary - 10 tests, 100% coverage of ftp.py Add FtpEngine/FtpLibrary tests Apr 3, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
tests/core/lib/test_ftp.py (1)

43-54: Minor: unused mock unpacking.

Line 46 unpacks the fixture but discards both values. Since the fixture is needed to activate the patch but neither mock is used in this test, you could simplify by removing the unpacking.

💅 Optional simplification
     def test_successful_login_returns_dict(
-        self, ftp_client_mocks, host, port, username, password, timeout
+        self, ftp_client_mocks, host, port, username, password, timeout  # noqa: ARG002
     ):
-        _, _ = ftp_client_mocks
         lib = FtpLibrary()
         result = lib.brute_force(host, port, username, password, timeout)

Alternatively, if linter complains about unused argument, the # noqa comment or a leading underscore (_ftp_client_mocks) signals intentional non-use.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/core/lib/test_ftp.py` around lines 43 - 54, The test
test_successful_login_returns_dict unnecessarily unpacks the ftp_client_mocks
fixture into two discarded values; remove the "_, _ = ftp_client_mocks"
unpacking (or rename the parameter to _ftp_client_mocks or add a "# noqa"
comment) so the fixture still activates the patch without introducing unused
variables—update the test_successful_login_returns_dict function accordingly.
tests/core/lib/test_ftps.py (1)

23-31: Consider verifying close() is called for consistency.

The FTP tests verify close() is called after brute_force(). For consistency and completeness, consider adding that assertion here too—even though FtpsLibrary inherits from FtpLibrary, explicit verification documents the expected behavior for FTPS clients.

💡 Optional addition
     `@patch.object`(FtpsLibrary, "client")
     def test_brute_force_uses_ftp_tls_client(self, mock_ftp_tls_class):
         mock_connection = MagicMock(spec=ftplib.FTP_TLS)
         mock_ftp_tls_class.return_value = mock_connection
 
         lib = FtpsLibrary()
         lib.brute_force("10.0.0.1", 21, "user", "pass", timeout=30)
 
         mock_ftp_tls_class.assert_called_once_with(timeout=30)
+        mock_connection.close.assert_called_once()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/core/lib/test_ftps.py` around lines 23 - 31, The test
test_brute_force_uses_ftp_tls_client should also assert that the FTPS client is
closed; after calling FtpsLibrary().brute_force(...) add an assertion on the
mocked FTPS connection (mock_connection) that close() was called (e.g.,
mock_connection.close.assert_called_once()) to mirror the FTP test and document
expected cleanup behavior for FtpsLibrary.client and brute_force.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@tests/core/lib/test_ftp.py`:
- Around line 43-54: The test test_successful_login_returns_dict unnecessarily
unpacks the ftp_client_mocks fixture into two discarded values; remove the "_, _
= ftp_client_mocks" unpacking (or rename the parameter to _ftp_client_mocks or
add a "# noqa" comment) so the fixture still activates the patch without
introducing unused variables—update the test_successful_login_returns_dict
function accordingly.

In `@tests/core/lib/test_ftps.py`:
- Around line 23-31: The test test_brute_force_uses_ftp_tls_client should also
assert that the FTPS client is closed; after calling
FtpsLibrary().brute_force(...) add an assertion on the mocked FTPS connection
(mock_connection) that close() was called (e.g.,
mock_connection.close.assert_called_once()) to mirror the FTP test and document
expected cleanup behavior for FtpsLibrary.client and brute_force.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0551b06a-797f-4f02-9b72-6ba52ab3bfd8

📥 Commits

Reviewing files that changed from the base of the PR and between b92e5c6 and c0e8a91.

📒 Files selected for processing (3)
  • nettacker/core/lib/ftp.py
  • tests/core/lib/test_ftp.py
  • tests/core/lib/test_ftps.py

@arkid15r arkid15r disabled auto-merge April 3, 2026 01:15
Copy link
Copy Markdown
Collaborator

@arkid15r arkid15r left a comment

Choose a reason for hiding this comment

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

@Raavi29 please note

Commits must have verified signatures.

@Raavi29
Copy link
Copy Markdown
Author

Raavi29 commented Apr 4, 2026

@Raavi29 please note

Commits must have verified signatures.

Thank you for the improvements and the feedback. GPG signing is now configured and the latest commit is signed. Apologies for the delay on that.

@Raavi29 Raavi29 requested a review from arkid15r April 4, 2026 04:19
Copilot AI review requested due to automatic review settings April 10, 2026 10:05
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds automated test coverage for the FTP/FTPS libraries and tightens FTP connection cleanup behavior within Nettacker’s core libraries.

Changes:

  • Added unit tests for FtpLibrary/FtpEngine behavior and error propagation.
  • Added basic unit tests for FtpsLibrary/FtpsEngine inheritance and client usage.
  • Updated FtpLibrary.brute_force to always close the FTP connection via try/finally.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
tests/core/lib/test_ftp.py New unit tests covering FtpLibrary.brute_force success paths, error propagation, and timeout forwarding.
tests/core/lib/test_ftps.py New unit tests validating FTPS engine/library wiring and that FTP_TLS client is used.
nettacker/core/lib/ftp.py Ensures connection.close() is executed even when connect()/login() raises.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants