Skip to content

Conversation

@berardifra
Copy link

Summary

This PR improves the SSH brute-force module reliability by fixing authentication success detection and making error handling consistent with Nettacker’s engine expectations.

What was changed

  • Updated SSH authentication logic to correctly detect successful logins
  • Ensured the module returns a non-empty dictionary only on real authentication success
  • Handled authentication, SSH protocol, and network errors safely without crashing scans
  • Kept behavior consistent with existing Nettacker modules

How it was tested

  • Local SSH server on 127.0.0.1 (OpenSSH)
  • Valid credentials → successful detection in ssh_brute
  • Invalid credentials → no false positives
  • Verified module execution through Nettacker CLI
  • Python syntax validation with py_compile

Impact

  • Prevents false negatives and unstable behavior in ssh_brute
  • Improves scan reliability without changing module interface
  • No breaking changes

Checklist

  • Code follows project structure
  • No YAML or interface changes required
  • Tested with real SSH service

- Ensure brute_force returns empty dict on failure and non-empty on success
- Prevent false positives caused by empty regex in ssh_brute module
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 14, 2025

Summary by CodeRabbit

  • Bug Fixes
    • Improved SSH connection handling with enhanced error handling for authentication and network-related failures.
    • Added timeout support for SSH operations to prevent indefinite hangs.
    • Strengthened connection validation to ensure only active connections are considered successful.
    • Enhanced resource cleanup to properly close SSH connections and handle cleanup errors gracefully.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Refactors SSH brute-force connection handling in a single file to add socket and Paramiko exception handling, timeout support, and stricter transport-based validation. The public function signature remains unchanged while internal logic gains more robust error handling with try/except/finally flow and explicit transport state verification before returning connection success.

Changes

Cohort / File(s) Summary
SSH Connection Robustness
nettacker/core/lib/ssh.py
Adds exception handling for socket, AuthenticationException, and SSHException; introduces timeout parameter (default 3); refactors connection sequence to try/except/finally; changes password access to kwargs.get(); verifies transport is active before returning success; returns connection dict with host, port, username, password only if transport present; all exceptions return empty dict; ensures connection closure in finally block.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Exception handling coverage: Verify all relevant socket and Paramiko exception types are correctly caught and handled (AuthenticationException, SSHException, socket errors, generic exceptions)
  • Transport verification logic: Ensure transport state check is the correct condition for success and that it properly validates connection readiness
  • Error swallowing in finally block: Confirm that suppressing close errors is intentional and doesn't mask underlying issues
  • Timeout parameter integration: Verify the timeout parameter is correctly passed and applied to the SSH connection attempt

Pre-merge checks and finishing touches

❌ 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%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: fixing SSH brute-force success detection and error handling, which aligns with the changeset's primary focus on improving authentication logic and exception handling.
Description check ✅ Passed The description is directly related to the changeset, detailing the SSH module improvements, testing methodology, and impact. It clearly explains what was changed and how it addresses authentication detection issues.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

✨ Issue Enrichment is now available for GitHub issues!

CodeRabbit can now help you manage issues more effectively:

  • Duplicate Detection — Identify similar or duplicate issues
  • Related Issues & PRs — Find relevant issues and PR's from your repository
  • Suggested Assignees — Find the best person to work on the issue
  • Implementation Planning — Generate detailed coding plans for engineers and agents
Disable automatic issue enrichment

To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

issue_enrichment:
  auto_enrich:
    enabled: false

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
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: 0

🧹 Nitpick comments (4)
nettacker/core/lib/ssh.py (4)

12-17: Consider removing unused *args and adding a docstring.

The *args parameter is never used. Based on BaseEngine.run() which calls action(**sub_step), only keyword arguments are passed. Additionally, per coding guidelines, public APIs in the nettacker package should have docstrings.

-    def brute_force(self, *args, **kwargs):
+    def brute_force(self, **kwargs):
+        """
+        Attempt SSH authentication with the given credentials.
+
+        Returns a dict with connection details on success, empty dict on failure.
+        """
         host = kwargs["host"]

22-40: Line 26 exceeds the 99-character limit.

The auth_strategy line is too long. Consider breaking it up for readability and to comply with the line length guideline.

         try:
+            if password:
+                auth_strategy = Password(username=username, password_getter=lambda: password)
+            else:
+                auth_strategy = NoneAuth(username=username)
+
             connection.connect(
                 hostname=host,
                 port=port,
-                auth_strategy=Password(username=username, password_getter=lambda: password) if password else NoneAuth(username=username),
+                auth_strategy=auth_strategy,
                 timeout=timeout,
                 look_for_keys=False,
                 allow_agent=False,
                 banner_timeout=timeout,
             )

The transport state verification before returning is a good defensive check—this correctly ensures the connection is truly established before reporting success.


48-49: Bare except Exception may hide unexpected bugs.

While catching all exceptions prevents scan crashes, it can mask programming errors. Consider logging unexpected exceptions at DEBUG level to aid troubleshooting.

+        except (socket.timeout, socket.error, ConnectionRefusedError, OSError):
+            return {}
         except Exception:
+            logging.debug("Unexpected error during SSH brute-force: %s:%s", host, port, exc_info=True)
             return {}

50-54: Consider narrowing the exception or adding debug logging in cleanup.

Silently swallowing all exceptions during close can hide issues. While cleanup failures shouldn't propagate, logging at DEBUG level helps diagnose connection issues.

         finally:
             try:
                 connection.close()
-            except Exception:
-                pass
+            except Exception:
+                logging.debug("Error closing SSH connection to %s:%s", host, port, exc_info=True)
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9b5ef1c and 9653142.

📒 Files selected for processing (1)
  • nettacker/core/lib/ssh.py (1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Use 4-space indents in Python code
Limit lines to 99 characters (ruff/ruff-format/isort profile=black)
Module and file names should use lower_snake_case
Function and variable names should use lower_snake_case
Class names should use PascalCase
Constants should use UPPER_SNAKE_CASE
Keep functions small and add type hints where practical

Files:

  • nettacker/core/lib/ssh.py
nettacker/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Add docstrings for public APIs in the nettacker package

Files:

  • nettacker/core/lib/ssh.py
nettacker/core/**

📄 CodeRabbit inference engine (AGENTS.md)

Place core libraries under nettacker/core/

Files:

  • nettacker/core/lib/ssh.py
🧬 Code graph analysis (1)
nettacker/core/lib/ssh.py (2)
nettacker/core/lib/base.py (1)
  • BaseEngine (28-313)
nettacker/core/lib/telnet.py (1)
  • brute_force (9-22)
🪛 Ruff (0.14.8)
nettacker/core/lib/ssh.py

12-12: Unused method argument: args

(ARG002)


35-40: Consider moving this statement to an else block

(TRY300)


48-48: Do not catch blind exception: Exception

(BLE001)


53-54: try-except-pass detected, consider logging the exception

(S110)


53-53: Do not catch blind exception: Exception

(BLE001)

🔇 Additional comments (1)
nettacker/core/lib/ssh.py (1)

1-5: LGTM!

The added imports for socket, AuthenticationException, and SSHException are appropriate for the robust error handling introduced in this PR.

Copy link
Contributor

@pUrGe12 pUrGe12 left a comment

Choose a reason for hiding this comment

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

You should remove the bloat code and please run pre-commit for ruff to handle the styling.

return {}
except SSHException:
return {}
except (socket.timeout, socket.error, ConnectionRefusedError, OSError):
Copy link
Contributor

Choose a reason for hiding this comment

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

You don't need to use both OSError and socket.error. Python3+ they are basically aliases. Also, you really aren't doing anything special by handling them separately.

port = kwargs["port"]
username = kwargs["username"]
password = kwargs["password"]
password = kwargs.get("password")
Copy link
Contributor

Choose a reason for hiding this comment

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

don't change what's not broken

import logging

from paramiko import SSHClient, AutoAddPolicy
import socket
Copy link
Contributor

Choose a reason for hiding this comment

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

you "probably" don't need this here... look at the other comment below

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.

2 participants